aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKailin Luo <karenluo@google.com>2019-12-20 18:24:13 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-12-20 18:24:13 +0000
commit8af0bdeca8593087c3ffde6cf7b41421e8497dcf (patch)
tree70ecdae645676482498f2fcdba837c4d29435a9e
parentd69bbd3a50a544b5555f7a4f16da1a6c88c823ec (diff)
parenta36ecca99e97d547256ed2b28868fe61153103c3 (diff)
downloadcsuite-8af0bdeca8593087c3ffde6cf7b41421e8497dcf.tar.gz
Merge "Create a preparer to download and install an app"
-rw-r--r--harness/Android.mk2
-rw-r--r--harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java164
-rw-r--r--harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java4
-rw-r--r--harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java209
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;
+ }
+}