diff options
author | Kailin Luo <karenluo@google.com> | 2019-12-20 18:24:13 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-12-20 18:24:13 +0000 |
commit | 8af0bdeca8593087c3ffde6cf7b41421e8497dcf (patch) | |
tree | 70ecdae645676482498f2fcdba837c4d29435a9e | |
parent | d69bbd3a50a544b5555f7a4f16da1a6c88c823ec (diff) | |
parent | a36ecca99e97d547256ed2b28868fe61153103c3 (diff) | |
download | csuite-8af0bdeca8593087c3ffde6cf7b41421e8497dcf.tar.gz |
Merge "Create a preparer to download and install an app"
4 files changed, 377 insertions, 2 deletions
diff --git a/harness/Android.mk b/harness/Android.mk index 2253ce7..c057891 100644 --- a/harness/Android.mk +++ b/harness/Android.mk @@ -42,7 +42,7 @@ LOCAL_SRC_FILES = $(call all-java-files-under, src/test/java) LOCAL_JAVA_LIBRARIES := tradefed csuite-harness -LOCAL_STATIC_JAVA_LIBRARIES := mockito-host objenesis +LOCAL_STATIC_JAVA_LIBRARIES := mockito-host objenesis testng LOCAL_MODULE := csuite-harness-tests diff --git a/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java new file mode 100644 index 0000000..18c0bf1 --- /dev/null +++ b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compatibility.targetprep; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.android.tradefed.build.IBuildInfo; +import com.android.tradefed.build.IDeviceBuildInfo; +import com.android.tradefed.config.Option; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.targetprep.ITargetCleaner; +import com.android.tradefed.targetprep.ITargetPreparer; +import com.android.tradefed.targetprep.TargetSetupError; +import com.android.tradefed.targetprep.TestAppInstallSetup; +import com.android.tradefed.util.FileUtil; + +import com.google.common.annotations.VisibleForTesting; + +import java.io.File; +import java.io.IOException; + +import java.nio.file.Files; +import java.nio.file.Paths; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * A Tradefed preparer that downloads and installs an app on the target device. + */ +public class AppSetupPreparer implements ITargetPreparer, ITargetCleaner { + + @Option( + name = "package-name", + description = "Package name of the app being tested." + ) + private String mPackageName; + + @Option( + name = "base-dir", + description = "The directory where app APKs are located.", + importance = Option.Importance.ALWAYS + ) + private File mBaseDir; + + private TestAppInstallSetup appInstallSetup; + + public AppSetupPreparer() { + this(null, null, new TestAppInstallSetup()); + } + + @VisibleForTesting + public AppSetupPreparer(String packageName, File baseDir, TestAppInstallSetup appInstallSetup) { + this.mPackageName = packageName; + this.mBaseDir = baseDir; + this.appInstallSetup = appInstallSetup; + } + + /** {@inheritDoc} */ + @Override + public void setUp(ITestDevice device, IBuildInfo buildInfo) + throws DeviceNotAvailableException, TargetSetupError { + + checkNotNull(mBaseDir, "mBaseDir cannot be null."); + checkArgument(mBaseDir.isDirectory(), + String.format("mBaseDir %s is not a directory", mBaseDir)); + + File downloadDir = prepareDownloadDir(buildInfo); + + try { + downloadPackage(downloadDir); + } catch (IOException e) { + throw new TargetSetupError + (String.format("Failed to download package from %s.", downloadDir), e); + } + appInstallSetup.setAltDir(downloadDir); + + List<String> apkFiles; + try { + apkFiles = listApkFiles(downloadDir); + } catch (IOException e) { + throw new TargetSetupError(String.format("Failed to access files in %s.", downloadDir), e); + } + + if (apkFiles.isEmpty()) { + throw new TargetSetupError(String.format("Failed to find apk files in %s.", downloadDir)); + } + + if (apkFiles.size() == 1) { + appInstallSetup.addTestFileName(apkFiles.get(0)); + } else { + appInstallSetup.addSplitApkFileNames(String.join(",", apkFiles)); + } + + appInstallSetup.setUp(device, buildInfo); + } + + /** {@inheritDoc} */ + @Override + public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) + throws DeviceNotAvailableException { + deleteDownloadDir(buildInfo); + appInstallSetup.tearDown(device, buildInfo, e); + } + + protected File prepareDownloadDir(IBuildInfo buildInfo) throws TargetSetupError { + File downloadDir = deleteDownloadDir(buildInfo); + + try { + Files.createDirectory(Paths.get(downloadDir.getPath())); + } catch (IOException e) { + throw new TargetSetupError( + String.format("Failed to create download directory %s.", downloadDir), e); + } + + return downloadDir; + } + + private File deleteDownloadDir(IBuildInfo buildInfo) { + File downloadDir = getDownloadDir(buildInfo); + FileUtil.recursiveDelete(downloadDir); + + return downloadDir; + } + + protected void downloadPackage(File destDir) throws IOException { + File sourceDir = new File(mBaseDir.getPath(), mPackageName); + + checkArgument(sourceDir.isDirectory(), + String.format("sourceDir %s is not a directory", sourceDir)); + + FileUtil.recursiveCopy(sourceDir, destDir); + } + + private List<String> listApkFiles(File downloadDir) throws IOException { + return Files.walk(Paths.get(downloadDir.getPath())) + .filter(s -> s.toString().endsWith("apk")) + .map(x -> x.getFileName().toString()).collect(Collectors.toList()); + } + + protected File getDownloadDir(IBuildInfo buildInfo) { + checkArgument(buildInfo instanceof IDeviceBuildInfo, + String.format("Provided buildInfo is not a %s", IDeviceBuildInfo.class.getCanonicalName())); + + return new File(((IDeviceBuildInfo) buildInfo).getTestsDir(), mPackageName); + } +} diff --git a/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java b/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java index 6da6c8c..f0b5fd6 100644 --- a/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java +++ b/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java @@ -16,6 +16,7 @@ package com.android.compatibility; import com.android.compatibility.AppCompatibilityTestTest; +import com.android.compatibility.targetprep.AppSetupPreparerTest; import com.android.compatibility.testtype.AppLaunchTestTest; import org.junit.runner.RunWith; @@ -24,8 +25,9 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ - AppLaunchTestTest.class, AppCompatibilityTestTest.class, + AppLaunchTestTest.class, + AppSetupPreparerTest.class, }) public final class CSuiteUnitTests { // Intentionally empty. diff --git a/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java new file mode 100644 index 0000000..3be6dc3 --- /dev/null +++ b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.compatibility.targetprep; + +import static com.google.common.base.Preconditions.checkArgument; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.testng.Assert.assertThrows; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.android.tradefed.build.IBuildInfo; +import com.android.tradefed.build.IDeviceBuildInfo; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.targetprep.TargetSetupError; +import com.android.tradefed.targetprep.TestAppInstallSetup; +import com.android.tradefed.util.FileUtil; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.IOException; + +@RunWith(JUnit4.class) +public final class AppSetupPreparerTest { + + @Rule public TemporaryFolder testSourceFolder = new TemporaryFolder(); + @Rule public TemporaryFolder testDestFolder = new TemporaryFolder(); + + private ITestDevice mockDevice; + private IDeviceBuildInfo mockDeviceBuildInfo; + private TestAppInstallSetup mockAppInstallSetup; + + @Before + public void setUp() throws Exception { + mockDevice = mock(ITestDevice.class); + + File testDir = testDestFolder.newFolder("download_dir"); + mockDeviceBuildInfo = mock(IDeviceBuildInfo.class); + when(mockDeviceBuildInfo.getTestsDir()).thenReturn(testDir); + + mockAppInstallSetup = mock(TestAppInstallSetup.class); + } + + @Test + public void setUp_baseDirIsNull_throwsException() + throws DeviceNotAvailableException, TargetSetupError { + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", null, mockAppInstallSetup); + + assertThrows(NullPointerException.class, + () -> preparer.setUp(mockDevice, mockDeviceBuildInfo)); + } + + @Test + public void setUp_baseDirIsNotDir_throwsException() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File tempFile = testSourceFolder.newFile("temp_file_name"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", tempFile, mockAppInstallSetup); + + assertThrows(IllegalArgumentException.class, + () -> preparer.setUp(mockDevice, mockDeviceBuildInfo)); + } + + @Test + public void setUp_packageDirDoesNotExist_throwsError() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + + assertThrows(IllegalArgumentException.class, + () -> preparer.setUp(mockDevice, mockDeviceBuildInfo)); + } + + @Test + public void prepareDownloadDir_containsStaleFiles() throws IOException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + File staleDownloadDir = testDestFolder.newFolder("stale_download_dir"); + File staleFile = new File(staleDownloadDir, "stale_file"); + staleFile.createNewFile(); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup) { + @Override + protected File getDownloadDir(IBuildInfo buildInfo) { + + return staleDownloadDir; + } + }; + + File downloadDir = preparer.prepareDownloadDir(mockDeviceBuildInfo); + + assertFalse(new File(downloadDir, "stale_file.apk").exists()); + } + + @Test + public void downloadPackage_success() throws IOException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + createPackageFile(baseDir, "package_name", "apk_name_1.apk"); + createPackageFile(baseDir, "package_name", "apk_name_2.apk"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + File destDownloadDir = testDestFolder.newFolder("dest_dir_name"); + + preparer.downloadPackage(destDownloadDir); + + assertTrue(new File(destDownloadDir, "apk_name_1.apk").exists()); + assertTrue(new File(destDownloadDir, "apk_name_2.apk").exists()); + } + + @Test + public void setUp_apkDoesNotExist() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + // Create a file in package_name folder, but the file extension is not apk. + createPackageFile(baseDir, "package_name", "non_apk_file"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + + assertThrows(TargetSetupError.class, + () -> preparer.setUp(mockDevice, mockDeviceBuildInfo)); + } + + @Test + public void setUp_installSplitApk() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + createPackageFile(baseDir, "package_name", "apk_name_1.apk"); + createPackageFile(baseDir, "package_name", "apk_name_2.apk"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + + preparer.setUp(mockDevice, mockDeviceBuildInfo); + + verify(mockAppInstallSetup, times(1)).setAltDir(any()); + verify(mockAppInstallSetup, times(1)).addSplitApkFileNames(anyString()); + verify(mockAppInstallSetup, times(1)).setUp(any(), any()); + } + + @Test + public void setUp_installNonSplitApk() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + createPackageFile(baseDir, "package_name", "apk_name_1.apk"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + + preparer.setUp(mockDevice, mockDeviceBuildInfo); + + verify(mockAppInstallSetup, times(1)).setAltDir(any()); + verify(mockAppInstallSetup, times(1)).addTestFileName(anyString()); + verify(mockAppInstallSetup, times(1)).setUp(any(), any()); + } + + @Test + public void tearDown() + throws IOException, DeviceNotAvailableException, TargetSetupError { + File baseDir = testSourceFolder.newFolder("base_dir"); + createPackageFile(baseDir, "package_name", "apk_name_1.apk"); + createPackageFile(baseDir, "package_name", "apk_name_2.apk"); + AppSetupPreparer preparer = + new AppSetupPreparer("package_name", baseDir, mockAppInstallSetup); + preparer.setUp(mockDevice, mockDeviceBuildInfo); + + preparer.tearDown(mockDevice, mockDeviceBuildInfo, mock(Throwable.class)); + + File destDir = preparer.getDownloadDir(mockDeviceBuildInfo); + assertFalse(new File(destDir, "apk_name_1.apk").exists()); + assertFalse(new File(destDir, "apk_name_2.apk").exists()); + verify(mockAppInstallSetup, times(1)).tearDown(any(), any(), any()); + } + + private File createPackageFile(File parentDir, String packageName, String apkName) + throws IOException { + File packageDir = new File(parentDir, packageName); + packageDir.mkdirs(); + File apkFile = new File(packageDir, apkName); + apkFile.createNewFile(); + + return apkFile; + } +} |