aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:04:56 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-04-08 16:04:56 +0000
commit87e2860d8eb7447a0a5e05e92f5a2cc8b33c1202 (patch)
treeb9975efd1ab97f81ec518ea6324ea86da608ded9
parent1c0ac36028cc7a26eeaa6256c5e37711d95e0fbc (diff)
parent5c86fc9b9b042528f9af6ea7c53c22f892ed3b2f (diff)
downloadcsuite-aml_tz2_305400100.tar.gz
Change-Id: I2f9e39e9ddf0308a08cf96148bdb35de086733ef
-rw-r--r--Android.bp29
-rw-r--r--OWNERS5
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--harness/Android.bp20
-rw-r--r--harness/AndroidTest.xml20
-rw-r--r--harness/TEST_MAPPING8
-rw-r--r--harness/src/main/java/com/android/compatibility/AppCompatibilityTest.java671
-rw-r--r--harness/src/main/java/com/android/compatibility/AppCrawlerCompatibilityTest.java82
-rw-r--r--harness/src/main/java/com/android/compatibility/AppLaunchCompatibilityTest.java55
-rw-r--r--harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java221
-rw-r--r--harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiver.java56
-rw-r--r--harness/src/main/java/com/android/compatibility/targetprep/CheckGmsPreparer.java87
-rw-r--r--harness/src/main/java/com/android/compatibility/testtype/AppLaunchTest.java57
-rw-r--r--harness/src/main/java/com/android/csuite/config/AppRemoteFileResolver.java282
-rw-r--r--harness/src/main/java/com/android/csuite/config/ModuleGenerator.java257
-rw-r--r--harness/src/main/java/com/android/csuite/core/CommandLinePackageNameProvider.java39
-rw-r--r--harness/src/main/java/com/android/csuite/core/FileBasedPackageNameProvider.java60
-rw-r--r--harness/src/main/java/com/android/csuite/core/PackageNameProvider.java31
-rw-r--r--harness/src/main/java/com/android/csuite/core/SystemPackageUninstaller.java296
-rw-r--r--harness/src/main/resources/META-INF/services/com.android.tradefed.config.remote.IRemoteFileResolver1
-rw-r--r--harness/src/main/resources/config/csuite-base.xml9
-rw-r--r--harness/src/main/resources/config/launch.xml (renamed from integration_tests/csuite_test_template.xml)15
-rw-r--r--harness/src/test/java/com/android/compatibility/AppCompatibilityTestTest.java334
-rw-r--r--harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java (renamed from harness/src/test/java/com/android/csuite/CSuiteUnitTests.java)20
-rw-r--r--harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiverTest.java57
-rw-r--r--harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java483
-rw-r--r--harness/src/test/java/com/android/compatibility/targetprep/CheckGmsPreparerTest.java289
-rw-r--r--harness/src/test/java/com/android/compatibility/testtype/AppLaunchTestTest.java68
-rw-r--r--harness/src/test/java/com/android/csuite/config/AppRemoteFileResolverTest.java279
-rw-r--r--harness/src/test/java/com/android/csuite/config/ModuleGeneratorTest.java321
-rw-r--r--harness/src/test/java/com/android/csuite/core/CommandLinePackageNameProviderTest.java45
-rw-r--r--harness/src/test/java/com/android/csuite/core/FileBasedPackageNameProviderTest.java116
-rw-r--r--harness/src/test/java/com/android/csuite/core/SystemAppUninstallerTest.java425
-rw-r--r--harness/src/test/java/com/android/csuite/testing/Correspondences.java47
-rw-r--r--harness/src/test/java/com/android/csuite/testing/CorrespondencesTest.java50
-rw-r--r--harness/src/test/java/com/android/csuite/testing/MoreAsserts.java59
-rw-r--r--harness/src/test/java/com/android/csuite/testing/MoreAssertsTest.java86
-rw-r--r--instrumentation/launch/Android.bp9
-rw-r--r--instrumentation/launch/src/main/java/com/android/compatibilitytest/AppCompatibility.java121
-rw-r--r--integration_tests/Android.bp116
-rw-r--r--integration_tests/TEST_MAPPING10
-rw-r--r--integration_tests/csuite_cli_test.py33
-rw-r--r--integration_tests/csuite_crash_detection_test.py104
-rw-r--r--integration_tests/csuite_crash_on_launch_test_app/Android.bp27
-rwxr-xr-xintegration_tests/csuite_crash_on_launch_test_app/AndroidManifest.xml27
-rw-r--r--integration_tests/csuite_crash_on_launch_test_app/TestAppActivity.java30
-rw-r--r--integration_tests/csuite_no_crash_test_app/Android.bp27
-rwxr-xr-xintegration_tests/csuite_no_crash_test_app/AndroidManifest.xml27
-rw-r--r--integration_tests/csuite_no_crash_test_app/TestAppActivity.java29
-rw-r--r--integration_tests/csuite_test_utils.py283
-rw-r--r--pylib/Android.bp42
-rw-r--r--pylib/csuite_test.py107
-rw-r--r--pylintrc427
-rw-r--r--test_targets/csuite-app-launch/Android.bp22
-rw-r--r--test_targets/csuite-app-launch/template.xml28
-rw-r--r--test_targets/csuite-pre-installed-app-launch/Android.bp22
-rw-r--r--test_targets/csuite-pre-installed-app-launch/template.xml25
-rw-r--r--test_targets/csuite-system-app-launch/Android.bp22
-rw-r--r--test_targets/csuite-system-app-launch/template.xml28
-rw-r--r--test_targets/csuite-test-package-launch/Android.bp22
-rw-r--r--test_targets/csuite-test-package-launch/template.xml29
-rw-r--r--tools/csuite-tradefed/Android.bp8
-rw-r--r--tools/csuite-tradefed/AndroidTest.xml20
-rw-r--r--tools/csuite-tradefed/TEST_MAPPING8
-rw-r--r--tools/csuite-tradefed/src/scripts/csuite-tradefed5
-rw-r--r--tools/csuite_test/Android.bp20
-rw-r--r--tools/csuite_test/csuite_test.go132
-rw-r--r--tools/csuite_test/csuite_test_test.go286
-rw-r--r--tools/csuite_test/go.mod14
-rw-r--r--tools/script/Android.bp47
-rw-r--r--tools/script/csuite_test_template.xml23
-rw-r--r--tools/script/generate_module.py371
-rw-r--r--tools/script/generate_module_test.py245
-rw-r--r--tools/script/generate_module_unittest.py203
74 files changed, 1933 insertions, 6047 deletions
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index 5295e32..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2020 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-python_defaults {
- name: "csuite_python_defaults",
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
-}
diff --git a/OWNERS b/OWNERS
deleted file mode 100644
index b78e055..0000000
--- a/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-adirao@google.com
-fdeng@google.com
-hzalek@google.com
-yuexima@google.com
-zhuoyao@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 91c7914..948fa79 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,5 @@
[Builtin Hooks]
bpfmt = true
-gofmt = true
google_java_format = true
pylint = true
xmllint = true
diff --git a/harness/Android.bp b/harness/Android.bp
index 5111c1b..2dec437 100644
--- a/harness/Android.bp
+++ b/harness/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
java_library_host {
name: "csuite-harness",
srcs: [
@@ -27,9 +23,6 @@ java_library_host {
libs: [
"tradefed",
],
- static_libs: [
- "compatibility-tradefed",
- ]
}
java_test_host {
@@ -37,17 +30,14 @@ java_test_host {
srcs: [
"src/test/java/**/*.java",
],
- static_libs: [
- "tradefed",
+ libs: [
"csuite-harness",
- "compatibility-tradefed",
- "guava-testlib",
- "jimfs",
+ "tradefed",
+ ],
+ static_libs: [
"mockito-host",
"objenesis",
"testng",
],
- test_options: {
- unit_test: true,
- },
+ test_suites: ["general-tests"],
}
diff --git a/harness/AndroidTest.xml b/harness/AndroidTest.xml
new file mode 100644
index 0000000..f3fb637
--- /dev/null
+++ b/harness/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Executes the C-Suite harness unit tests">
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.compatibility.CSuiteUnitTests" />
+ </test>
+</configuration>
diff --git a/harness/TEST_MAPPING b/harness/TEST_MAPPING
new file mode 100644
index 0000000..50ed1cd
--- /dev/null
+++ b/harness/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "postsubmit": [
+ {
+ "name": "csuite-harness-tests",
+ "host": true
+ }
+ ]
+}
diff --git a/harness/src/main/java/com/android/compatibility/AppCompatibilityTest.java b/harness/src/main/java/com/android/compatibility/AppCompatibilityTest.java
new file mode 100644
index 0000000..74c4907
--- /dev/null
+++ b/harness/src/main/java/com/android/compatibility/AppCompatibilityTest.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2012 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.IConfiguration;
+import com.android.tradefed.config.IConfigurationReceiver;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.DeviceUnresponsiveException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.LogcatReceiver;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.ByteArrayInputStreamSource;
+import com.android.tradefed.result.CompatibilityTestResult;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.IShardableTest;
+import com.android.tradefed.testtype.ITestFilterReceiver;
+import com.android.tradefed.testtype.InstrumentationTest;
+import com.android.tradefed.util.AaptParser;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.PublicApkUtil;
+import com.android.tradefed.util.PublicApkUtil.ApkInfo;
+import com.android.tradefed.util.RunUtil;
+import com.android.tradefed.util.StreamUtil;
+
+import com.google.common.base.Strings;
+
+import org.json.JSONException;
+import org.junit.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test that determines application compatibility. The test iterates through the apks in a given
+ * directory. The test installs, launches, and uninstalls each apk.
+ */
+public abstract class AppCompatibilityTest
+ implements IDeviceTest,
+ IRemoteTest,
+ IShardableTest,
+ IConfigurationReceiver,
+ ITestFilterReceiver {
+
+ @Option(
+ name = "product",
+ description = "The product, corresponding to the borgcron job product arg.")
+ private String mProduct;
+
+ @Option(
+ name = "base-dir",
+ description = "The directory of the results excluding the date.",
+ importance = Option.Importance.ALWAYS)
+ // TODO(b/36786754): Add `mandatory = true` when cmdfiles are moved over
+ private File mBaseDir;
+
+ @Option(
+ name = "date",
+ description =
+ "The date to run, in the form YYYYMMDD. If not set, then the latest "
+ + "results will be used.")
+ private String mDate;
+
+ @Option(name = "test-label", description = "Unique test identifier label.")
+ private String mTestLabel = "AppCompatibility";
+
+ @Option(
+ name = "reboot-after-apks",
+ description = "Reboot the device after a centain number of apks. 0 means no reboot.")
+ private int mRebootNumber = 100;
+
+ @Option(
+ name = "fallback-to-apk-scan",
+ description =
+ "Fallback to scanning for apks in base directory if ranking information "
+ + "is missing.")
+ private boolean mFallbackToApkScan = false;
+
+ @Option(
+ name = "retry-count",
+ description = "Number of times to retry a failed test case. 0 means no retry.")
+ private int mRetryCount = 5;
+
+ @Option(name = "include-filter", description = "The include filter of the test names to run.")
+ protected Set<String> mIncludeFilters = new HashSet<>();
+
+ @Option(name = "exclude-filter", description = "The exclude filter of the test names to run.")
+ protected Set<String> mExcludeFilters = new HashSet<>();
+
+ private static final long DOWNLOAD_TIMEOUT_MS = 60 * 1000;
+ private static final int DOWNLOAD_RETRIES = 3;
+ private static final long JOIN_TIMEOUT_MS = 5 * 60 * 1000;
+ private static final int LOGCAT_SIZE_BYTES = 20 * 1024 * 1024;
+
+ private ITestDevice mDevice;
+ private LogcatReceiver mLogcat;
+ private IConfiguration mConfiguration;
+
+ // The number of tests run so far
+ private int mTestCount = 0;
+
+ // indicates the current sharding setup
+ private int mShardCount = 1;
+ private int mShardIndex = 0;
+
+ protected final String mLauncherPackage;
+ protected final String mRunnerClass;
+ protected final String mPackageBeingTestedKey;
+
+ protected AppCompatibilityTest(
+ String launcherPackage, String runnerClass, String packageBeingTestedKey) {
+ this.mLauncherPackage = launcherPackage;
+ this.mRunnerClass = runnerClass;
+ this.mPackageBeingTestedKey = packageBeingTestedKey;
+ }
+
+ /**
+ * Creates and sets up an instrumentation test with information about the test runner as well as
+ * the package being tested (provided as a parameter).
+ */
+ protected abstract InstrumentationTest createInstrumentationTest(String packageBeingTested);
+
+ /** Sets up some default aspects of the instrumentation test. */
+ protected final InstrumentationTest createDefaultInstrumentationTest(
+ String packageBeingTested) {
+ InstrumentationTest instrTest = new InstrumentationTest();
+ instrTest.setPackageName(mLauncherPackage);
+ instrTest.setConfiguration(mConfiguration);
+ instrTest.addInstrumentationArg(mPackageBeingTestedKey, packageBeingTested);
+ instrTest.setRunnerName(mRunnerClass);
+ return instrTest;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public final void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+ CLog.d("Start of launch test run method. base-dir: %s", mBaseDir);
+ Assert.assertNotNull("Base dir cannot be null", mBaseDir);
+ Assert.assertTrue("Base dir should be a directory", mBaseDir.isDirectory());
+
+ if (mProduct == null) {
+ mProduct = mDevice.getProductType();
+ CLog.i("\"--product\" not specified, using property from device instead: %s", mProduct);
+ }
+ Assert.assertTrue(
+ String.format(
+ "Shard index out of range: expected [0, %d), got %d",
+ mShardCount, mShardIndex),
+ mShardIndex >= 0 && mShardIndex < mShardCount);
+
+ File apkDir = null;
+ try {
+ apkDir = PublicApkUtil.constructApkDir(mBaseDir.getPath(), mDate);
+ } catch (IOException e) {
+ CLog.e(e);
+ throw new RuntimeException(e);
+ }
+ CLog.d("apkDir: %s.", apkDir);
+ Assert.assertNotNull("Could not find the output dir", apkDir);
+ List<ApkInfo> apkList = null;
+ try {
+ apkList = shardApkList(PublicApkUtil.getApkList(mProduct, apkDir, mFallbackToApkScan));
+ } catch (IOException e) {
+ CLog.e(e);
+ throw new RuntimeException(e);
+ }
+ CLog.d("Completed sharding apkList. Number of items: %s", apkList.size());
+ Assert.assertNotNull("Could not download apk list", apkList);
+
+ apkList = filterApk(apkList);
+ CLog.d("Completed filtering apkList. Number of items: %s", apkList.size());
+
+ long start = System.currentTimeMillis();
+ listener.testRunStarted(mTestLabel, apkList.size());
+ mLogcat = new LogcatReceiver(getDevice(), LOGCAT_SIZE_BYTES, 0);
+ mLogcat.start();
+
+ try {
+ downloadAndTestApks(listener, apkDir, apkList);
+ } catch (InterruptedException e) {
+ CLog.e(e);
+ throw new RuntimeException(e);
+ } finally {
+ mLogcat.stop();
+ listener.testRunEnded(
+ System.currentTimeMillis() - start, new HashMap<String, Metric>());
+ }
+ }
+
+ /**
+ * Downloads and tests all the APKs in the apk list.
+ *
+ * @param listener The {@link ITestInvocationListener}.
+ * @param kharonDir The {@link File} of the CNS dir containing the APKs.
+ * @param apkList The sharded list of {@link ApkInfo} objects.
+ * @throws DeviceNotAvailableException
+ * @throws InterruptedException if a download thread was interrupted.
+ */
+ private void downloadAndTestApks(
+ ITestInvocationListener listener, File kharonDir, List<ApkInfo> apkList)
+ throws DeviceNotAvailableException, InterruptedException {
+ CLog.d("Started downloading and testing apks.");
+ ApkInfo testingApk = null;
+ File testingFile = null;
+ for (ApkInfo downloadingApk : apkList) {
+ ApkDownloadRunnable downloader = new ApkDownloadRunnable(kharonDir, downloadingApk);
+ Thread downloadThread = new Thread(downloader);
+ downloadThread.start();
+
+ testApk(listener, testingApk, testingFile);
+
+ try {
+ downloadThread.join(JOIN_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ FileUtil.deleteFile(downloader.getDownloadedFile());
+ throw e;
+ }
+ testingApk = downloadingApk;
+ testingFile = downloader.getDownloadedFile();
+ }
+ // One more time since the first time through the loop we don't test
+ testApk(listener, testingApk, testingFile);
+ CLog.d("Completed downloading and testing apks.");
+ }
+
+ /**
+ * Attempts to install and launch an APK and reports the results.
+ *
+ * @param listener The {@link ITestInvocationListener}.
+ * @param apkInfo The {@link ApkInfo} to run the test against.
+ * @param apkFile The downloaded {@link File}.
+ * @throws DeviceNotAvailableException
+ */
+ private void testApk(ITestInvocationListener listener, ApkInfo apkInfo, File apkFile)
+ throws DeviceNotAvailableException {
+ if (apkInfo == null || apkFile == null) {
+ CLog.d("apkInfo or apkFile is null.");
+ FileUtil.deleteFile(apkFile);
+ return;
+ }
+ CLog.d(
+ "Started testing package: %s, apk file: %s.",
+ apkInfo.packageName, apkFile.getAbsolutePath());
+
+ mTestCount++;
+ if (mRebootNumber != 0 && mTestCount % mRebootNumber == 0) {
+ mDevice.reboot();
+ }
+ mLogcat.clear();
+
+ TestDescription testId = createTestDescription(apkInfo.packageName);
+ listener.testStarted(testId, System.currentTimeMillis());
+
+ CompatibilityTestResult result = new CompatibilityTestResult();
+ result.rank = apkInfo.rank;
+ // Default to package name since name is a required field. This will be replaced by
+ // AaptParser in installApk()
+ result.name = apkInfo.packageName;
+ result.packageName = apkInfo.packageName;
+ result.versionString = apkInfo.versionString;
+ result.versionCode = apkInfo.versionCode;
+
+ try {
+ // Install the app, and also skip aapt check if we've fell back to apk scan
+ installApk(result, apkFile, mFallbackToApkScan);
+ boolean installationSuccess = result.status == null;
+
+ for (int i = 0; i <= mRetryCount; i++) {
+ if (installationSuccess) {
+ // Clear test result between retries
+ result.status = null;
+ result.message = null;
+ launchApk(result);
+ mDevice.executeShellCommand(
+ String.format("am force-stop %s", apkInfo.packageName));
+ }
+ if (result.status == null) {
+ result.status = CompatibilityTestResult.STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ if (installationSuccess) {
+ mDevice.uninstallPackage(result.packageName);
+ }
+ } finally {
+ reportResult(listener, testId, result);
+ try {
+ postLogcat(result, listener);
+ } catch (JSONException e) {
+ CLog.w("Posting failed: %s.", e.getMessage());
+ }
+ listener.testEnded(
+ testId, System.currentTimeMillis(), Collections.<String, String>emptyMap());
+ FileUtil.deleteFile(apkFile);
+ CLog.d("Completed testing package: %s.", apkInfo.packageName);
+ }
+ }
+
+ /**
+ * Checks that the file is correct and attempts to install it.
+ *
+ * <p>Will set the result status to error if the APK could not be installed or if it contains
+ * conflicting information.
+ *
+ * @param result the {@link CompatibilityTestResult} containing the APK info.
+ * @param apkFile the APK file to install.
+ * @throws DeviceNotAvailableException
+ */
+ private void installApk(CompatibilityTestResult result, File apkFile, boolean skipAaptCheck)
+ throws DeviceNotAvailableException {
+ if (!skipAaptCheck) {
+ CLog.d("Parsing apk file: %s.", apkFile.getAbsolutePath());
+ AaptParser parser = AaptParser.parse(apkFile);
+ if (parser == null) {
+ CLog.d(
+ "Failed to parse apk file: %s, package: %s, error: %s.",
+ apkFile.getAbsolutePath(), result.packageName, result.message);
+ result.status = CompatibilityTestResult.STATUS_ERROR;
+ result.message = "aapt fail";
+ return;
+ }
+
+ result.name = parser.getLabel();
+
+ if (!equalsOrNull(result.packageName, parser.getPackageName())
+ || !equalsOrNull(result.versionString, parser.getVersionName())
+ || !equalsOrNull(result.versionCode, parser.getVersionCode())) {
+ CLog.d(
+ "Package info mismatch: want %s v%s (%s), got %s v%s (%s)",
+ result.packageName,
+ result.versionCode,
+ result.versionString,
+ parser.getPackageName(),
+ parser.getVersionCode(),
+ parser.getVersionName());
+ result.status = CompatibilityTestResult.STATUS_ERROR;
+ result.message = "package info mismatch";
+ return;
+ }
+ CLog.d("Completed parsing apk file: %s.", apkFile.getAbsolutePath());
+ }
+
+ try {
+ String error = mDevice.installPackage(apkFile, true);
+ if (error != null) {
+ result.status = CompatibilityTestResult.STATUS_ERROR;
+ result.message = error;
+ CLog.d(
+ "Failed to install apk file: %s, package: %s, error: %s.",
+ apkFile.getAbsolutePath(), result.packageName, result.message);
+ return;
+ }
+ } catch (DeviceUnresponsiveException e) {
+ result.status = CompatibilityTestResult.STATUS_ERROR;
+ result.message = "install timeout";
+ CLog.d(
+ "Installing apk file %s timed out, package: %s, error: %s.",
+ apkFile.getAbsolutePath(), result.packageName, result.message);
+ return;
+ }
+ CLog.d("Completed installing apk file %s.", apkFile.getAbsolutePath());
+ }
+
+ /**
+ * Method which attempts to launch an APK.
+ *
+ * <p>Will set the result status to failure if the APK could not be launched.
+ *
+ * @param result the {@link CompatibilityTestResult} containing the APK info.
+ * @throws DeviceNotAvailableException
+ */
+ private void launchApk(CompatibilityTestResult result) throws DeviceNotAvailableException {
+ CLog.d("Lauching package: %s.", result.packageName);
+ InstrumentationTest instrTest = createInstrumentationTest(result.packageName);
+ instrTest.setDevice(mDevice);
+
+ FailureCollectingListener failureListener = new FailureCollectingListener();
+ instrTest.run(failureListener);
+
+ if (failureListener.getStackTrace() != null) {
+ CLog.w("Failed to launch package: %s.", result.packageName);
+ result.status = CompatibilityTestResult.STATUS_FAILURE;
+ result.message = failureListener.getStackTrace();
+ }
+
+ CLog.d("Completed launching package: %s", result.packageName);
+ }
+
+ /** Helper method which reports a test failed if the status is either a failure or an error. */
+ private void reportResult(
+ ITestInvocationListener listener, TestDescription id, CompatibilityTestResult result) {
+ String message = result.message != null ? result.message : "unknown";
+ if (CompatibilityTestResult.STATUS_ERROR.equals(result.status)) {
+ listener.testFailed(id, "ERROR:" + message);
+ } else if (CompatibilityTestResult.STATUS_FAILURE.equals(result.status)) {
+ listener.testFailed(id, "FAILURE:" + message);
+ }
+ }
+
+ /** Helper method which posts the logcat. */
+ private void postLogcat(CompatibilityTestResult result, ITestInvocationListener listener)
+ throws JSONException {
+ InputStreamSource stream = null;
+ String header =
+ String.format(
+ "%s%s%s\n",
+ CompatibilityTestResult.SEPARATOR,
+ result.toJsonString(),
+ CompatibilityTestResult.SEPARATOR);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (InputStreamSource logcatData = mLogcat.getLogcatData()) {
+ try {
+ baos.write(header.getBytes());
+ StreamUtil.copyStreams(logcatData.createInputStream(), baos);
+ stream = new ByteArrayInputStreamSource(baos.toByteArray());
+ baos.flush();
+ baos.close();
+ } catch (IOException e) {
+ CLog.e("error inserting compatibility test result into logcat");
+ CLog.e(e);
+ // fallback to logcat data
+ stream = logcatData;
+ }
+ listener.testLog("logcat_" + result.packageName, LogDataType.LOGCAT, stream);
+ } finally {
+ StreamUtil.cancel(stream);
+ }
+ }
+
+ /** Helper method which takes a list of {@link ApkInfo} objects and returns the sharded list. */
+ private List<ApkInfo> shardApkList(List<ApkInfo> apkList) {
+ List<ApkInfo> shardedList = new ArrayList<>(apkList.size() / mShardCount + 1);
+ for (int i = mShardIndex; i < apkList.size(); i += mShardCount) {
+ shardedList.add(apkList.get(i));
+ }
+ return shardedList;
+ }
+
+ /**
+ * Helper method which takes a list of {@link ApkInfo} objects and returns the filtered list.
+ */
+ protected List<ApkInfo> filterApk(List<ApkInfo> apkList) {
+ List<ApkInfo> filteredList = new ArrayList<>();
+
+ for (ApkInfo apk : apkList) {
+ if (filterTest(apk.packageName)) {
+ filteredList.add(apk);
+ }
+ }
+
+ return filteredList;
+ }
+
+ /**
+ * Return true if a test matches one or more of the include filters AND does not match any of
+ * the exclude filters. If no include filters are given all tests should return true as long as
+ * they do not match any of the exclude filters.
+ */
+ protected boolean filterTest(String testName) {
+ if (mExcludeFilters.contains(testName)) {
+ return false;
+ }
+ if (mIncludeFilters.size() == 0 || mIncludeFilters.contains(testName)) {
+ return true;
+ }
+ return false;
+ }
+
+ /** Returns true if either object is null or if both objects are equal. */
+ private static boolean equalsOrNull(Object a, Object b) {
+ return a == null || b == null || a.equals(b);
+ }
+
+ /** Helper {@link Runnable} which downloads a file, and can be used in another thread. */
+ private class ApkDownloadRunnable implements Runnable {
+ private final File mKharonDir;
+ private final ApkInfo mApkInfo;
+
+ private File mDownloadedFile = null;
+
+ ApkDownloadRunnable(File kharonDir, ApkInfo apkInfo) {
+ mKharonDir = kharonDir;
+ mApkInfo = apkInfo;
+ }
+
+ @Override
+ public void run() {
+ // No-op if mApkInfo is null
+ if (mApkInfo == null) {
+ CLog.d("ApkInfo is null.");
+ return;
+ }
+
+ File sourceFile = new File(mKharonDir, mApkInfo.fileName);
+ try {
+ mDownloadedFile =
+ PublicApkUtil.downloadFile(
+ sourceFile, DOWNLOAD_TIMEOUT_MS, DOWNLOAD_RETRIES);
+ } catch (IOException e) {
+ // Log and ignore
+ CLog.e("Could not download apk from %s.", sourceFile);
+ CLog.e(e);
+ }
+ CLog.d("Completed downloading apk file: %s.", mDownloadedFile.getAbsolutePath());
+ }
+
+ public File getDownloadedFile() {
+ return mDownloadedFile;
+ }
+ }
+
+ @Override
+ public void setConfiguration(IConfiguration configuration) {
+ mConfiguration = configuration;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public void setDevice(ITestDevice device) {
+ mDevice = device;
+ }
+
+ /*
+ * {@inheritDoc}
+ */
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ /** Return a {@link IRunUtil} instance to execute commands with. */
+ IRunUtil getRunUtil() {
+ return RunUtil.getDefault();
+ }
+
+ private IRemoteTest getTestShard(int shardCount, int shardIndex) {
+ AppCompatibilityTest shard;
+ try {
+ shard = getClass().newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException(
+ "The class "
+ + getClass().getName()
+ + " has no public constructor with no arguments, but all subclasses of "
+ + AppCompatibilityTest.class.getName()
+ + " should",
+ e);
+ }
+ try {
+ OptionCopier.copyOptions(this, shard);
+ } catch (ConfigurationException e) {
+ CLog.e("Failed to copy test options: %s.", e.getMessage());
+ }
+ shard.mShardIndex = shardIndex;
+ shard.mShardCount = shardCount;
+ return shard;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<IRemoteTest> split(int shardCountHint) {
+ if (shardCountHint <= 1) {
+ // cannot shard or already sharded
+ return null;
+ }
+ Collection<IRemoteTest> shards = new ArrayList<>(shardCountHint);
+ for (int index = 0; index < shardCountHint; index++) {
+ shards.add(getTestShard(shardCountHint, index));
+ }
+ return shards;
+ }
+
+ /**
+ * Get a test description for use in logging. For compatibility with logs, this should be
+ * TestDescription(launcher package, package being run).
+ */
+ private TestDescription createTestDescription(String packageBeingTested) {
+ return new TestDescription(mLauncherPackage, packageBeingTested);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addIncludeFilter(String filter) {
+ checkArgument(!Strings.isNullOrEmpty(filter), "Include filter cannot be null or empty.");
+ mIncludeFilters.add(filter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAllIncludeFilters(Set<String> filters) {
+ checkNotNull(filters, "Include filters cannot be null.");
+ mIncludeFilters.addAll(filters);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearIncludeFilters() {
+ mIncludeFilters.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<String> getIncludeFilters() {
+ return Collections.unmodifiableSet(mIncludeFilters);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addExcludeFilter(String filter) {
+ checkArgument(!Strings.isNullOrEmpty(filter), "Exclude filter cannot be null or empty.");
+ mExcludeFilters.add(filter);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAllExcludeFilters(Set<String> filters) {
+ checkNotNull(filters, "Exclude filters cannot be null.");
+ mExcludeFilters.addAll(filters);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void clearExcludeFilters() {
+ mExcludeFilters.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Set<String> getExcludeFilters() {
+ return Collections.unmodifiableSet(mExcludeFilters);
+ }
+}
diff --git a/harness/src/main/java/com/android/compatibility/AppCrawlerCompatibilityTest.java b/harness/src/main/java/com/android/compatibility/AppCrawlerCompatibilityTest.java
new file mode 100644
index 0000000..5c0fe11
--- /dev/null
+++ b/harness/src/main/java/com/android/compatibility/AppCrawlerCompatibilityTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@OptionClass(alias = "app-compatibility-crawler")
+public final class AppCrawlerCompatibilityTest extends AppCompatibilityTest {
+ private static final String WALKMAN_RUN_MS_LABEL = "maxDuration";
+ private static final String WALKMAN_STEPS_LABEL = "maxSteps";
+
+ @Option(
+ name = "walkman-run-ms",
+ description = "Time to run walkman in msecs (only used if test-strategy=walkman).")
+ private int mWalkmanRunMs = 60 * 1000;
+
+ @Option(
+ name = "walkman-steps",
+ description =
+ "Max number of steps to run walkman (only used if test-strategy=walkman)."
+ + " -1 for no limit")
+ private int mWalkmanSteps = -1;
+
+ public AppCrawlerCompatibilityTest() {
+ super(
+ "com.google.android.apps.common.walkman.apps",
+ "com.google.android.apps.common.testing.testrunner"
+ + ".Google3InstrumentationTestRunner",
+ /*
+ * We are using /google/data/ro/teams/walkman/walkman.apk which has parameter
+ * "packages" unlike the up-to-date version in source which uses "package"
+ * see: com.google.android.apps.common.walkman.apps.EngineFactory::getCrawlEngine.
+ * This currently works with the up-to-date version in source, as well.
+ *
+ * Neither of these should be confused with "package_to_launch", which is used by
+ * AppCompatibilityRunner
+ */
+ "packages");
+ }
+
+ @Override
+ public InstrumentationTest createInstrumentationTest(String packageBeingTested) {
+ InstrumentationTest instrTest = createDefaultInstrumentationTest(packageBeingTested);
+
+ instrTest.addInstrumentationArg(WALKMAN_RUN_MS_LABEL, Integer.toString(mWalkmanRunMs));
+ instrTest.addInstrumentationArg(WALKMAN_STEPS_LABEL, Integer.toString(mWalkmanSteps));
+
+ String launcherClass = mLauncherPackage + ".WalkmanInstrumentationEntry";
+ instrTest.setClassName(launcherClass);
+ /*
+ * InstrumentationTest can't deduce the exact test to run, so we specify it
+ * manually. Note that the TestDescription we use here is a different one from
+ * the one returned by {@link TestStrategy#createTestDescription}.
+ *
+ * This list is required to be mutable, so we wrap in ArrayList.
+ */
+ instrTest.setTestsToRun(
+ new ArrayList<>(Arrays.asList(new TestDescription(launcherClass, "testEntry"))));
+
+ return instrTest;
+ }
+}
diff --git a/harness/src/main/java/com/android/compatibility/AppLaunchCompatibilityTest.java b/harness/src/main/java/com/android/compatibility/AppLaunchCompatibilityTest.java
new file mode 100644
index 0000000..dd1f648
--- /dev/null
+++ b/harness/src/main/java/com/android/compatibility/AppLaunchCompatibilityTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.testtype.InstrumentationTest;
+
+/** Uses AppCompatibilityRunner to check if the app starts correctly. */
+@OptionClass(alias = "app-compatibility")
+public final class AppLaunchCompatibilityTest extends AppCompatibilityTest {
+ private static final String APP_LAUNCH_TIMEOUT_LABEL = "app_launch_timeout_ms";
+ private static final String WORKSPACE_LAUNCH_TIMEOUT_LABEL = "workspace_launch_timeout_ms";
+
+ @Option(
+ name = "app-launch-timeout-ms",
+ description = "Time to wait for app to launch in msecs.")
+ private int mAppLaunchTimeoutMs = 15000;
+
+ @Option(
+ name = "workspace-launch-timeout-ms",
+ description = "Time to wait when launched back into the workspace in msecs.")
+ private int mWorkspaceLaunchTimeoutMs = 2000;
+
+ public AppLaunchCompatibilityTest() {
+ super(
+ "com.android.compatibilitytest",
+ "com.android.compatibilitytest.AppCompatibilityRunner",
+ "package_to_launch");
+ }
+
+ @Override
+ public InstrumentationTest createInstrumentationTest(String packageBeingTested) {
+ InstrumentationTest instrTest = createDefaultInstrumentationTest(packageBeingTested);
+ instrTest.addInstrumentationArg(
+ APP_LAUNCH_TIMEOUT_LABEL, Integer.toString(mAppLaunchTimeoutMs));
+ instrTest.addInstrumentationArg(
+ WORKSPACE_LAUNCH_TIMEOUT_LABEL, Integer.toString(mWorkspaceLaunchTimeoutMs));
+ return instrTest;
+ }
+}
diff --git a/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java
index 12e30f7..b742c2f 100644
--- a/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java
+++ b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparer.java
@@ -17,230 +17,99 @@
package com.android.compatibility.targetprep;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
-import com.android.csuite.core.SystemPackageUninstaller;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.Option;
-import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.BuildError;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.targetprep.TestAppInstallSetup;
-import com.android.tradefed.util.AaptParser.AaptVersion;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.util.concurrent.SimpleTimeLimiter;
-import com.google.common.util.concurrent.TimeLimiter;
-import com.google.common.util.concurrent.UncheckedTimeoutException;
import java.io.File;
-import java.time.Duration;
-import java.util.ArrayList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/** A Tradefed preparer that downloads and installs an app on the target device. */
public final class AppSetupPreparer implements ITargetPreparer {
- @VisibleForTesting
- static final String OPTION_WAIT_FOR_DEVICE_AVAILABLE_SECONDS =
- "wait-for-device-available-seconds";
-
- @VisibleForTesting
- static final String OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS =
- "exponential-backoff-multiplier-seconds";
+ public static final String OPTION_GCS_APK_DIR = "gcs-apk-dir";
- @VisibleForTesting static final String OPTION_TEST_FILE_NAME = "test-file-name";
- @VisibleForTesting static final String OPTION_INSTALL_ARG = "install-arg";
- @VisibleForTesting static final String OPTION_SETUP_TIMEOUT_MILLIS = "setup-timeout-millis";
- @VisibleForTesting static final String OPTION_MAX_RETRY = "max-retry";
- @VisibleForTesting static final String OPTION_AAPT_VERSION = "aapt-version";
- @VisibleForTesting static final String OPTION_INCREMENTAL_INSTALL = "incremental";
-
- @Option(name = "package-name", description = "Package name of testing app.")
+ @Option(name = "package-name", description = "Package name of the app being tested.")
private String mPackageName;
- @Option(
- name = OPTION_TEST_FILE_NAME,
- description = "the name of an apk file to be installed on device. Can be repeated.")
- private final List<File> mTestFiles = new ArrayList<>();
-
- @Option(name = OPTION_AAPT_VERSION, description = "The version of AAPT for APK parsing.")
- private AaptVersion mAaptVersion = AaptVersion.AAPT2;
-
- @Option(
- name = OPTION_INSTALL_ARG,
- description =
- "Additional arguments to be passed to install command, "
- + "including leading dash, e.g. \"-d\"")
- private final List<String> mInstallArgs = new ArrayList<>();
-
- @Option(
- name = OPTION_INCREMENTAL_INSTALL,
- description = "Enable packages to be installed incrementally.")
- private boolean mIncrementalInstallation = false;
-
- @Option(name = OPTION_MAX_RETRY, description = "Max number of retries upon TargetSetupError.")
- private int mMaxRetry = 0;
-
- @Option(
- name = OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS,
- description =
- "The exponential backoff multiplier for retries in seconds. "
- + "A value n means the preparer will wait for n^(retry_count) "
- + "seconds between retries.")
- private int mExponentialBackoffMultiplierSeconds = 0;
-
- @Option(
- name = OPTION_WAIT_FOR_DEVICE_AVAILABLE_SECONDS,
- description =
- "Timeout value for waiting for device available in seconds. "
- + "A negative value means not to check device availability.")
- private int mWaitForDeviceAvailableSeconds = -1;
-
- @Option(
- name = OPTION_SETUP_TIMEOUT_MILLIS,
- description =
- "Timeout value for a setUp operation. "
- + "Note that the timeout is not a global timeout and will "
- + "be applied to each retry attempt.")
- private long mSetupOnceTimeoutMillis = TimeUnit.MINUTES.toMillis(10);
-
- private final TestAppInstallSetup mTestAppInstallSetup;
- private final Sleeper mSleeper;
- private final TimeLimiter mTimeLimiter =
- SimpleTimeLimiter.create(Executors.newCachedThreadPool());
+ private final TestAppInstallSetup mAppInstallSetup;
public AppSetupPreparer() {
- this(new TestAppInstallSetup(), Sleepers.DefaultSleeper.INSTANCE);
+ this(null, new TestAppInstallSetup());
}
@VisibleForTesting
- public AppSetupPreparer(TestAppInstallSetup testAppInstallSetup, Sleeper sleeper) {
- mTestAppInstallSetup = testAppInstallSetup;
- mSleeper = sleeper;
+ public AppSetupPreparer(String packageName, TestAppInstallSetup appInstallSetup) {
+ this.mPackageName = packageName;
+ this.mAppInstallSetup = appInstallSetup;
}
/** {@inheritDoc} */
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo)
throws DeviceNotAvailableException, BuildError, TargetSetupError {
- checkArgumentNonNegative(mMaxRetry, OPTION_MAX_RETRY);
- checkArgumentNonNegative(
- mExponentialBackoffMultiplierSeconds,
- OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS);
- checkArgumentNonNegative(mSetupOnceTimeoutMillis, OPTION_SETUP_TIMEOUT_MILLIS);
-
- int runCount = 0;
- while (true) {
- TargetSetupError currentException;
- try {
- runCount++;
-
- ITargetPreparer handler =
- mTimeLimiter.newProxy(
- new ITargetPreparer() {
- @Override
- public void setUp(ITestDevice device, IBuildInfo buildInfo)
- throws DeviceNotAvailableException, BuildError,
- TargetSetupError {
- setUpOnce(device, buildInfo);
- }
- },
- ITargetPreparer.class,
- mSetupOnceTimeoutMillis,
- TimeUnit.MILLISECONDS);
- handler.setUp(device, buildInfo);
-
- break;
- } catch (TargetSetupError e) {
- currentException = e;
- } catch (UncheckedTimeoutException e) {
- currentException = new TargetSetupError(e.getMessage(), e);
- }
-
- waitForDeviceAvailable(device);
- if (runCount > mMaxRetry) {
- throw currentException;
- }
- CLog.w("setUp failed: %s. Run count: %d. Retrying...", currentException, runCount);
-
- try {
- mSleeper.sleep(
- Duration.ofSeconds(
- (int) Math.pow(mExponentialBackoffMultiplierSeconds, runCount)));
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new TargetSetupError(e.getMessage(), e);
- }
- }
- }
+ // TODO(b/147159584): Use a utility to get dynamic options.
+ String gcsApkDirOption = buildInfo.getBuildAttributes().get(OPTION_GCS_APK_DIR);
+ checkNotNull(gcsApkDirOption, "Option %s is not set.", OPTION_GCS_APK_DIR);
- private void setUpOnce(ITestDevice device, IBuildInfo buildInfo)
- throws DeviceNotAvailableException, BuildError, TargetSetupError {
- mTestAppInstallSetup.setAaptVersion(mAaptVersion);
+ File apkDir = new File(gcsApkDirOption);
+ checkArgument(
+ apkDir.isDirectory(),
+ String.format("GCS Apk Directory %s is not a directory", apkDir));
- try {
- OptionSetter setter = new OptionSetter(mTestAppInstallSetup);
- setter.setOptionValue("incremental", String.valueOf(mIncrementalInstallation));
- } catch (ConfigurationException e) {
- throw new TargetSetupError(e.getMessage(), e);
- }
+ File packageDir = new File(apkDir.getPath(), mPackageName);
+ checkArgument(
+ packageDir.isDirectory(),
+ String.format("Package directory %s is not a directory", packageDir));
- if (mPackageName != null) {
- SystemPackageUninstaller.uninstallPackage(mPackageName, device);
+ mAppInstallSetup.setAltDir(packageDir);
+
+ List<String> apkFilePaths;
+ try {
+ apkFilePaths = listApkFilePaths(packageDir);
+ } catch (IOException e) {
+ throw new TargetSetupError(
+ String.format("Failed to access files in %s.", packageDir), e);
}
- for (File testFile : mTestFiles) {
- mTestAppInstallSetup.addTestFile(testFile);
+ if (apkFilePaths.isEmpty()) {
+ throw new TargetSetupError(
+ String.format("Failed to find apk files in %s.", packageDir));
}
- for (String installArg : mInstallArgs) {
- mTestAppInstallSetup.addInstallArg(installArg);
+ if (apkFilePaths.size() == 1) {
+ mAppInstallSetup.addTestFileName(apkFilePaths.get(0));
+ } else {
+ mAppInstallSetup.addSplitApkFileNames(String.join(",", apkFilePaths));
}
- mTestAppInstallSetup.setUp(device, buildInfo);
+ mAppInstallSetup.setUp(device, buildInfo);
}
/** {@inheritDoc} */
@Override
public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
- mTestAppInstallSetup.tearDown(testInfo, e);
+ mAppInstallSetup.tearDown(testInfo, e);
}
- private void waitForDeviceAvailable(ITestDevice device) throws DeviceNotAvailableException {
- if (mWaitForDeviceAvailableSeconds < 0) {
- return;
- }
-
- device.waitForDeviceAvailable(1000L * mWaitForDeviceAvailableSeconds);
- }
-
- private void checkArgumentNonNegative(long val, String name) {
- checkArgument(val >= 0, "%s (%s) must not be negative", name, val);
- }
-
- @VisibleForTesting
- interface Sleeper {
- void sleep(Duration duration) throws InterruptedException;
- }
-
- static class Sleepers {
- enum DefaultSleeper implements Sleeper {
- INSTANCE;
-
- @Override
- public void sleep(Duration duration) throws InterruptedException {
- Thread.sleep(duration.toMillis());
- }
- }
-
- private Sleepers() {}
+ private List<String> listApkFilePaths(File downloadDir) throws IOException {
+ return Files.walk(Paths.get(downloadDir.getPath()))
+ .map(x -> x.getFileName().toString())
+ .filter(s -> s.endsWith(".apk"))
+ .collect(Collectors.toList());
}
}
diff --git a/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiver.java b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiver.java
new file mode 100644
index 0000000..7e50f3f
--- /dev/null
+++ b/harness/src/main/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiver.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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 com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetprep.ITargetPreparer;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.File;
+
+/**
+ * A Tradefed preparer that receives module preparer options and stores the values in IBuildInfo.
+ */
+public final class AppSetupPreparerConfigurationReceiver implements ITargetPreparer {
+
+ @Option(
+ name = AppSetupPreparer.OPTION_GCS_APK_DIR,
+ description = "GCS path where the test apk files are located.")
+ private File mOptionGcsApkDir;
+
+ public AppSetupPreparerConfigurationReceiver() {
+ this(null);
+ }
+
+ @VisibleForTesting
+ public AppSetupPreparerConfigurationReceiver(File optionGcsApkDir) {
+ mOptionGcsApkDir = optionGcsApkDir;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setUp(ITestDevice device, IBuildInfo buildInfo) {
+ if (mOptionGcsApkDir == null) {
+ return;
+ }
+ buildInfo.addBuildAttribute(
+ AppSetupPreparer.OPTION_GCS_APK_DIR, mOptionGcsApkDir.getPath());
+ }
+}
diff --git a/harness/src/main/java/com/android/compatibility/targetprep/CheckGmsPreparer.java b/harness/src/main/java/com/android/compatibility/targetprep/CheckGmsPreparer.java
deleted file mode 100644
index 8d40d04..0000000
--- a/harness/src/main/java/com/android/compatibility/targetprep/CheckGmsPreparer.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.tradefed.config.Option;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.targetprep.ITargetPreparer;
-import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.CommandResult;
-
-import com.google.common.annotations.VisibleForTesting;
-
-/**
- * Checks and recover GMS on a device.
- *
- * <p>This preparer checks whether the GMS process is running during setUp and tearDown. If GMS is
- * not running before the test, a reboot will be attempted to recover.
- */
-public final class CheckGmsPreparer implements ITargetPreparer {
-
- private static final long WAIT_FOR_BOOT_COMPLETE_TIMEOUT_MILLIS = 1000 * 60;
- @VisibleForTesting static final String CHECK_GMS_COMMAND = "pidof com.google.android.gms";
- @VisibleForTesting static final String OPTION_ENABLE = "enable";
-
- @Option(name = OPTION_ENABLE, description = "Enable GMS checks.")
- protected boolean mEnable = false;
-
- /** {@inheritDoc} */
- @Override
- public void setUp(TestInformation testInfo)
- throws TargetSetupError, DeviceNotAvailableException {
- if (!mEnable || isGmsRunning(testInfo)) {
- return;
- }
-
- CLog.e("Did not detect a running GMS process, rebooting device to recover");
- testInfo.getDevice().reboot();
- testInfo.getDevice().waitForBootComplete(WAIT_FOR_BOOT_COMPLETE_TIMEOUT_MILLIS);
-
- if (!isGmsRunning(testInfo)) {
- CLog.e("GMS process still not running, throwing error");
- mEnable = false;
- throw new TargetSetupError(
- "GMS required but did not detect a running GMS process after device reboot");
- }
- }
-
- private static boolean isGmsRunning(TestInformation testInfo)
- throws DeviceNotAvailableException {
- CommandResult res = testInfo.getDevice().executeShellV2Command(CHECK_GMS_COMMAND);
- if (res.getExitCode() == 0) {
- CLog.d("Detected a running GMS process with PID: %s", res.getStdout());
- return true;
- }
-
- CLog.e(
- "Check GMS command returned non zero exit code. Command: %s, Result: %s",
- CHECK_GMS_COMMAND, res.toString());
- return false;
- }
-
- /** {@inheritDoc} */
- @Override
- public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
- if (!mEnable || isGmsRunning(testInfo)) {
- return;
- }
-
- CLog.e("Did not detect a running GMS process on tearDown");
- }
-}
diff --git a/harness/src/main/java/com/android/compatibility/testtype/AppLaunchTest.java b/harness/src/main/java/com/android/compatibility/testtype/AppLaunchTest.java
index 7a52735..fb34dbf 100644
--- a/harness/src/main/java/com/android/compatibility/testtype/AppLaunchTest.java
+++ b/harness/src/main/java/com/android/compatibility/testtype/AppLaunchTest.java
@@ -59,12 +59,6 @@ import java.util.Set;
/** A test that verifies that a single app can be successfully launched. */
public class AppLaunchTest
implements IDeviceTest, IRemoteTest, IConfigurationReceiver, ITestFilterReceiver {
- @VisibleForTesting static final String SCREENSHOT_AFTER_LAUNCH = "screenshot-after-launch";
-
- @Option(
- name = SCREENSHOT_AFTER_LAUNCH,
- description = "Whether to take a screenshost after a package is launched.")
- private boolean mScreenshotAfterLaunch;
@Option(name = "package-name", description = "Package name of testing app.")
private String mPackageName;
@@ -85,9 +79,6 @@ public class AppLaunchTest
@Option(name = "exclude-filter", description = "The exclude filter of the test type.")
protected Set<String> mExcludeFilters = new HashSet<>();
- @Option(name = "dismiss-dialog", description = "Attempt to dismiss dialog from apps.")
- protected boolean mDismissDialog = false;
-
@Option(
name = "app-launch-timeout-ms",
description = "Time to wait for app to launch in msecs.")
@@ -97,22 +88,18 @@ public class AppLaunchTest
"com.android.compatibilitytest.AppCompatibilityRunner";
private static final String LAUNCH_TEST_PACKAGE = "com.android.compatibilitytest";
private static final String PACKAGE_TO_LAUNCH = "package_to_launch";
- private static final String ARG_DISMISS_DIALOG = "ARG_DISMISS_DIALOG";
private static final String APP_LAUNCH_TIMEOUT_LABEL = "app_launch_timeout_ms";
private static final int LOGCAT_SIZE_BYTES = 20 * 1024 * 1024;
- private static final int BASE_INSTRUMENTATION_TEST_TIMEOUT_MS = 10 * 1000;
private ITestDevice mDevice;
private LogcatReceiver mLogcat;
private IConfiguration mConfiguration;
- public AppLaunchTest() {
- this(null);
- }
+ public AppLaunchTest() {}
@VisibleForTesting
public AppLaunchTest(String packageName) {
- this(packageName, 0);
+ mPackageName = packageName;
}
@VisibleForTesting
@@ -126,23 +113,17 @@ public class AppLaunchTest
* the package being tested (provided as a parameter).
*/
protected InstrumentationTest createInstrumentationTest(String packageBeingTested) {
- InstrumentationTest instrumentationTest = new InstrumentationTest();
-
- instrumentationTest.setPackageName(LAUNCH_TEST_PACKAGE);
- instrumentationTest.setConfiguration(mConfiguration);
- instrumentationTest.addInstrumentationArg(PACKAGE_TO_LAUNCH, packageBeingTested);
- instrumentationTest.setRunnerName(LAUNCH_TEST_RUNNER);
- instrumentationTest.setDevice(mDevice);
- instrumentationTest.addInstrumentationArg(
+ InstrumentationTest instrTest = new InstrumentationTest();
+
+ instrTest.setPackageName(LAUNCH_TEST_PACKAGE);
+ instrTest.setConfiguration(mConfiguration);
+ instrTest.addInstrumentationArg(PACKAGE_TO_LAUNCH, packageBeingTested);
+ instrTest.setRunnerName(LAUNCH_TEST_RUNNER);
+ instrTest.setDevice(mDevice);
+ instrTest.addInstrumentationArg(
APP_LAUNCH_TIMEOUT_LABEL, Integer.toString(mAppLaunchTimeoutMs));
- instrumentationTest.addInstrumentationArg(
- ARG_DISMISS_DIALOG, Boolean.toString(mDismissDialog));
- int testTimeoutMs = BASE_INSTRUMENTATION_TEST_TIMEOUT_MS + mAppLaunchTimeoutMs * 2;
- instrumentationTest.setShellTimeout(testTimeoutMs);
- instrumentationTest.setTestTimeout(testTimeoutMs);
-
- return instrumentationTest;
+ return instrTest;
}
/*
@@ -207,21 +188,7 @@ public class AppLaunchTest
// Clear test result between retries.
launchPackage(testInfo, result);
if (result.status == CompatibilityTestResult.STATUS_SUCCESS) {
- break;
- }
- }
-
- if (mScreenshotAfterLaunch) {
- try (InputStreamSource screenSource = mDevice.getScreenshot()) {
- listener.testLog(
- mPackageName + "_screenshot_" + mDevice.getSerialNumber(),
- LogDataType.PNG,
- screenSource);
- } catch (DeviceNotAvailableException e) {
- CLog.e(
- "Device %s became unavailable while capturing screenshot, %s",
- mDevice.getSerialNumber(), e.toString());
- throw e;
+ return;
}
}
} finally {
diff --git a/harness/src/main/java/com/android/csuite/config/AppRemoteFileResolver.java b/harness/src/main/java/com/android/csuite/config/AppRemoteFileResolver.java
deleted file mode 100644
index 82fddb8..0000000
--- a/harness/src/main/java/com/android/csuite/config/AppRemoteFileResolver.java
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.config;
-
-import com.android.tradefed.build.BuildRetrievalError;
-import com.android.tradefed.config.ConfigurationException;
-import com.android.tradefed.config.DynamicRemoteFileResolver;
-import com.android.tradefed.config.Option;
-import com.android.tradefed.config.OptionClass;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.config.remote.IRemoteFileResolver;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Stopwatch;
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-
-import java.io.File;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Map;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.NotThreadSafe;
-
-/**
- * An implementation of {@code IRemoteFileResolver} for downloading Android apps.
- *
- * <p>The scheme supported by this resolver allows Trade Federation test configs to abstract the
- * actual service used to download Android app APK files. Note that this is a 'meta' resolver that
- * resolves abstract 'app://' URIs into a URI with a different scheme using a custom template. The
- * actual downloading of this resolved URI is then delegated to another registered {@link
- * IRemoteFileResolver} implementation. Variable placeholders in the URI template string are
- * expanded with corresponding values.
- *
- * <h2>Syntax and usage</h2>
- *
- * <p>References to apps in TradeFed test configs must have the following syntax:
- *
- * <blockquote>
- *
- * <b>{@code app://}</b><i>package-name</i>
- *
- * </blockquote>
- *
- * where <i>package-name</i> is the name of the application package such as:
- *
- * <blockquote>
- *
- * <table cellpadding=0 cellspacing=0 summary="layout">
- * <tr><td>{@code app://com.example.myapp}<td></tr>
- * </table>
- *
- * </blockquote>
- *
- * App APK files are downloaded to a directory and must be used in contexts that can handle File
- * objects pointing to directories.
- *
- * <h2>Configuration</h2>
- *
- * <p>The URI template to use is specified using the {@code dynamic-download-args} TradeFed
- * command-line argument:
- *
- * <blockquote>
- *
- * <pre>
- * --dynamic-download-args app:uri-template=file:///app_files/{package}
- * </pre>
- *
- * </blockquote>
- *
- * <p>Where {package} expands to the actual package name being downloaded. Any illegal URI
- * characters must also be properly escaped as expected by {@link java.net.URI}.
- *
- * <p><span style="font-weight: bold; padding-right: 1em">Usage Note:</span> The {@code
- * --enable-module-dynamic-download} flag must be set to {@code true} when used in test suites.
- *
- * @see com.android.tradefed.config.Option
- */
-@NotThreadSafe
-@OptionClass(alias = "app")
-public final class AppRemoteFileResolver implements IRemoteFileResolver {
-
- private static final String URI_SCHEME = "app";
- private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\{(\\w+)\\}");
-
- @VisibleForTesting static final String URI_TEMPLATE_OPTION = "uri-template";
-
- @Option(name = URI_TEMPLATE_OPTION)
- private String mUriTemplate;
-
- @Nullable private ITestDevice mPrimaryDevice;
-
- @Override
- public String getSupportedProtocol() {
- return URI_SCHEME;
- }
-
- @Override
- public void setPrimaryDevice(@Nullable ITestDevice primaryDevice) {
- this.mPrimaryDevice = primaryDevice;
- }
-
- @Override
- public File resolveRemoteFiles(File uriSchemeAndPathAsFile) throws BuildRetrievalError {
- // Note that this method is not really supported or even called by the framework. We
- // only override it to simplify automated null pointer testing.
- return resolveRemoteFiles(uriSchemeAndPathAsFile, ImmutableMap.of());
- }
-
- @Override
- public File resolveRemoteFiles(
- File uriSchemeAndPathAsFile, Map<String, String> uriQueryAndExtraParameters)
- throws BuildRetrievalError {
- URI appUri = checkAppUri(toUri(uriSchemeAndPathAsFile));
- Objects.requireNonNull(uriQueryAndExtraParameters);
-
- // TODO(hzalek): Remove this and make the corresponding option mandatory once test configs
- // are using app URIs.
- if (mUriTemplate == null) {
- CLog.w("Resolver is not properly configured, skipping resolution of URI (%s)", appUri);
- return null;
- }
-
- Preconditions.checkState(
- !mUriTemplate.isEmpty(),
- String.format("%s=%s is empty", URI_TEMPLATE_OPTION, mUriTemplate));
-
- String packageName = appUri.getAuthority();
- String expanded = expandVars(mUriTemplate, ImmutableMap.of("package", packageName));
-
- URI uri;
- try {
- uri = new URI(expanded);
- } catch (URISyntaxException e) {
- throw new IllegalStateException(
- String.format(
- "URI template (%s) did not expand to a a valid URI (%s)",
- URI_TEMPLATE_OPTION, mUriTemplate, expanded),
- e);
- }
-
- if (URI_SCHEME.equals(uri.getScheme())) {
- throw new BuildRetrievalError(
- String.format(
- "Providers must return URIs with a scheme different than '%s': %s > %s",
- URI_SCHEME, appUri, uri));
- }
-
- return resolveUriToFile(packageName, uri, uriQueryAndExtraParameters);
- }
-
- private static URI toUri(File uriSchemeAndPathAsFile) {
- try {
- // TradeFed forces a URI into a File instance which is lossy and forces us to attempt
- // restoring the original format here so we don't have to use regular expressions. Be
- // warned that using getAbsolutePath() will incorrectly strip the scheme.
- String path = uriSchemeAndPathAsFile.getPath();
- // Restore the original URI form since the first two forward slashes in the URI string
- // get normalized into one when stored as a file.
- path = path.replaceFirst(":/", "://");
- return new URI(path);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Could not parse provided URI", e);
- }
- }
-
- private static URI checkAppUri(URI uri) {
- String uriScheme = uri.getScheme();
- if (!URI_SCHEME.equals(uriScheme)) {
- throw new IllegalArgumentException(
- String.format("Unsupported scheme (%s) in provided URI (%s)", uriScheme, uri));
- }
-
- // Note that the below code accesses the 'authority' component of the URI and not 'path'
- // like the dynamic resolver implementation. The latter has to do so because the authority
- // component is no longer defined once the '//' gets converted to a single '/'.
- String packageName = uri.getAuthority();
- if (Strings.isNullOrEmpty(packageName)) {
- throw new IllegalArgumentException(
- String.format(
- "Invalid package name (%s) in provided URI (%s)", packageName, uri));
- }
-
- if (!Strings.isNullOrEmpty(uri.getPath())) {
- throw new IllegalArgumentException(
- String.format(
- "Path component (%s) incorrectly specified in provided URI (%s); "
- + "app URIs must be of the form 'app://com.example.app'",
- uri.getPath(), uri));
- }
-
- return uri;
- }
-
- private static String expandVars(CharSequence template, Map<String, String> vars) {
- StringBuilder sb = new StringBuilder();
- Matcher matcher = PLACEHOLDER_PATTERN.matcher(template);
- int position = 0;
-
- while (matcher.find()) {
- sb.append(template.subSequence(position, matcher.start(0)));
-
- String varName = matcher.group(1);
- String varValue = vars.get(varName);
-
- if (varValue == null) {
- throw new IllegalStateException(
- String.format(
- "URI template (%s) contains a placeholder for undefined var (%s)",
- template, varName));
- }
-
- sb.append(varValue);
- position = matcher.end(0);
- }
-
- sb.append(template.subSequence(position, template.length()));
- String expanded = sb.toString();
-
- CLog.i("Template (%s) expanded (%s) using vars (%s)", template, expanded, vars);
- return expanded;
- }
-
- private File resolveUriToFile(String packageName, URI uri, Map<String, String> params)
- throws BuildRetrievalError {
- DynamicRemoteFileResolver resolver = new DynamicRemoteFileResolver();
- resolver.setDevice(mPrimaryDevice);
- resolver.addExtraArgs(params);
-
- FileOptionSource optionSource = new FileOptionSource();
- Stopwatch stopwatch = Stopwatch.createStarted();
-
- try {
- OptionSetter setter = new OptionSetter(optionSource);
- setter.setOptionValue(FileOptionSource.OPTION_NAME, uri.toString());
- setter.validateRemoteFilePath(resolver);
- CLog.i("Resolution of files took %d ms", stopwatch.elapsed().toMillis());
- } catch (BuildRetrievalError e) {
- throw new BuildRetrievalError(
- String.format("Could not resolve URI (%s) for package '%s'", uri, packageName),
- e);
- } catch (ConfigurationException impossible) {
- throw new AssertionError(impossible);
- }
-
- if (!optionSource.file.exists()) {
- CLog.w("URI (%s) resolved to non-existent local file (%s)", uri, optionSource.file);
- } else {
- CLog.i("URI (%s) resolved to local file (%s)", uri, optionSource.file);
- }
-
- return optionSource.file;
- }
-
- /** This is required to resolve URIs since the remote resolver only deals with options. */
- private static final class FileOptionSource {
- static final String OPTION_NAME = "file";
-
- @Option(name = OPTION_NAME, mandatory = true)
- public File file;
- }
-}
diff --git a/harness/src/main/java/com/android/csuite/config/ModuleGenerator.java b/harness/src/main/java/com/android/csuite/config/ModuleGenerator.java
deleted file mode 100644
index 0760085..0000000
--- a/harness/src/main/java/com/android/csuite/config/ModuleGenerator.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.config;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.csuite.core.PackageNameProvider;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.IConfiguration;
-import com.android.tradefed.config.IConfigurationReceiver;
-import com.android.tradefed.config.Option;
-import com.android.tradefed.config.Option.Importance;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.targetprep.ITargetPreparer;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.IRemoteTest;
-import com.android.tradefed.testtype.IShardableTest;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.io.Resources;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A tool for generating TradeFed suite modules during runtime.
- *
- * <p>This class generates module config files into TradeFed's test directory at runtime using a
- * template. Since the content of the test directory relies on what is being generated in a test
- * run, there can only be one instance executing at a given time.
- *
- * <p>The intention of this class is to generate test modules at the beginning of a test run and
- * cleans up after all tests finish, which resembles a target preparer. However, a target preparer
- * is executed after the sharding process has finished. The only way to make the generated modules
- * available for sharding without making changes to TradeFed's core code is to disguise this module
- * generator as an instance of IShardableTest and declare it separately in test plan config. This is
- * hacky, and in the long term a TradeFed centered solution is desired. For more details, see
- * go/sharding-hack-for-module-gen. Note that since the generate step is executed as a test instance
- * and cleanup step is executed as a target preparer, there should be no saved states between
- * generating and cleaning up module files.
- *
- * <p>This module generator collects package names from all PackageNameProvider objects specified in
- * the test configs.
- *
- * <h2>Syntax and usage</h2>
- *
- * <p>References to package name providers in TradeFed test configs must have the following syntax:
- *
- * <blockquote>
- *
- * <b>&lt;object type="PACKAGE_NAME_PROVIDER" class="</b><i>provider_class_name</i><b>"/&gt;</b>
- *
- * </blockquote>
- *
- * where <i>provider_class_name</i> is the fully-qualified class name of an PackageNameProvider
- * implementation class.
- */
-public final class ModuleGenerator
- implements IRemoteTest,
- IShardableTest,
- IBuildReceiver,
- ITargetPreparer,
- IConfigurationReceiver {
-
- @VisibleForTesting static final String MODULE_FILE_EXTENSION = ".config";
- @VisibleForTesting static final String OPTION_TEMPLATE = "template";
- @VisibleForTesting static final String PACKAGE_NAME_PROVIDER = "PACKAGE_NAME_PROVIDER";
- private static final String TEMPLATE_PACKAGE_PATTERN = "\\{package\\}";
- private static final Collection<IRemoteTest> NOT_SPLITABLE = null;
-
- @Option(
- name = OPTION_TEMPLATE,
- description = "Module config template resource path.",
- importance = Importance.ALWAYS)
- private String mTemplate;
-
- private final TestDirectoryProvider mTestDirectoryProvider;
- private final ResourceLoader mResourceLoader;
- private final FileSystem mFileSystem;
- private IBuildInfo mBuildInfo;
- private IConfiguration mConfiguration;
-
- @Override
- public void setConfiguration(IConfiguration configuration) {
- mConfiguration = configuration;
- }
-
- public ModuleGenerator() {
- this(FileSystems.getDefault());
- }
-
- private ModuleGenerator(FileSystem fileSystem) {
- this(
- fileSystem,
- new CompatibilityTestDirectoryProvider(fileSystem),
- new ClassResourceLoader());
- }
-
- @VisibleForTesting
- ModuleGenerator(
- FileSystem fileSystem,
- TestDirectoryProvider testDirectoryProvider,
- ResourceLoader resourceLoader) {
- mFileSystem = fileSystem;
- mTestDirectoryProvider = testDirectoryProvider;
- mResourceLoader = resourceLoader;
- }
-
- @Override
- public void run(final TestInformation testInfo, final ITestInvocationListener listener) {
- // Intentionally left blank since this class is not really a test.
- }
-
- @Override
- public void setUp(TestInformation testInfo) {
- // Intentionally left blank.
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mBuildInfo = buildInfo;
- }
-
- /**
- * Generates test modules. Note that the implementation of this method is not related to
- * sharding in any way.
- */
- @Override
- public Collection<IRemoteTest> split() {
- try {
- // Executes the generate step.
- generateModules();
- } catch (IOException e) {
- throw new UncheckedIOException("Failed to generate modules", e);
- }
-
- return NOT_SPLITABLE;
- }
-
- /** Cleans up generated test modules. */
- @Override
- public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
- // Gets build info from test info as when the class is executed as a ITargetPreparer
- // preparer, it is not considered as a IBuildReceiver instance.
- mBuildInfo = testInfo.getBuildInfo();
-
- try {
- // Executes the clean up step.
- cleanUpModules();
- } catch (IOException ioException) {
- throw new UncheckedIOException("Failed to clean up generated modules", ioException);
- }
- }
-
- private Set<String> getPackageNames() throws IOException {
- Set<String> packages = new HashSet<>();
- for (Object provider : mConfiguration.getConfigurationObjectList(PACKAGE_NAME_PROVIDER)) {
- packages.addAll(((PackageNameProvider) provider).get());
- }
- return packages;
- }
-
- private void generateModules() throws IOException {
- String templateContent = mResourceLoader.load(mTemplate);
-
- for (String packageName : getPackageNames()) {
- validatePackageName(packageName);
- Files.write(
- getModulePath(packageName),
- templateContent.replaceAll(TEMPLATE_PACKAGE_PATTERN, packageName).getBytes());
- }
- }
-
- private void cleanUpModules() throws IOException {
- getPackageNames()
- .forEach(
- packageName -> {
- try {
- Files.delete(getModulePath(packageName));
- } catch (IOException ioException) {
- CLog.e(
- "Failed to delete the generated module for package "
- + packageName,
- ioException);
- }
- });
- }
-
- private Path getModulePath(String packageName) throws IOException {
- Path testsDir = mTestDirectoryProvider.get(mBuildInfo);
- return testsDir.resolve(packageName + MODULE_FILE_EXTENSION);
- }
-
- private static void validatePackageName(String packageName) {
- if (packageName.isEmpty() || packageName.matches(".*" + TEMPLATE_PACKAGE_PATTERN + ".*")) {
- throw new IllegalArgumentException(
- "Package name cannot be empty or contains package placeholder: "
- + TEMPLATE_PACKAGE_PATTERN);
- }
- }
-
- @VisibleForTesting
- interface ResourceLoader {
- String load(String resourceName) throws IOException;
- }
-
- private static final class ClassResourceLoader implements ResourceLoader {
- @Override
- public String load(String resourceName) throws IOException {
- return Resources.toString(
- getClass().getClassLoader().getResource(resourceName), StandardCharsets.UTF_8);
- }
- }
-
- @VisibleForTesting
- interface TestDirectoryProvider {
- Path get(IBuildInfo buildInfo) throws IOException;
- }
-
- private static final class CompatibilityTestDirectoryProvider implements TestDirectoryProvider {
- private final FileSystem mFileSystem;
-
- private CompatibilityTestDirectoryProvider(FileSystem fileSystem) {
- mFileSystem = fileSystem;
- }
-
- @Override
- public Path get(IBuildInfo buildInfo) throws IOException {
- return mFileSystem.getPath(
- new CompatibilityBuildHelper(buildInfo).getTestsDir().getPath());
- }
- }
-}
diff --git a/harness/src/main/java/com/android/csuite/core/CommandLinePackageNameProvider.java b/harness/src/main/java/com/android/csuite/core/CommandLinePackageNameProvider.java
deleted file mode 100644
index 33426ed..0000000
--- a/harness/src/main/java/com/android/csuite/core/CommandLinePackageNameProvider.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2021 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.csuite.core;
-
-import com.android.tradefed.config.Option;
-import com.android.tradefed.config.OptionClass;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** A package name provider that accepts package names via a command line option. */
-@OptionClass(alias = "command-line-package-name-provider")
-public final class CommandLinePackageNameProvider implements PackageNameProvider {
- @VisibleForTesting static final String PACKAGE = "package";
-
- @Option(name = PACKAGE, description = "App package names.")
- private final Set<String> mPackages = new HashSet<>();
-
- @Override
- public Set<String> get() {
- return mPackages;
- }
-}
diff --git a/harness/src/main/java/com/android/csuite/core/FileBasedPackageNameProvider.java b/harness/src/main/java/com/android/csuite/core/FileBasedPackageNameProvider.java
deleted file mode 100644
index 6f6af06..0000000
--- a/harness/src/main/java/com/android/csuite/core/FileBasedPackageNameProvider.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 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.csuite.core;
-
-import com.android.tradefed.config.Option;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/** A package name provider that accepts files that contains package names. */
-public final class FileBasedPackageNameProvider implements PackageNameProvider {
- @VisibleForTesting static final String PACKAGES_FILE = "packages-file";
- @VisibleForTesting static final String COMMENT_LINE_PREFIX = "#";
-
- @Option(
- name = PACKAGES_FILE,
- description =
- "File paths that contain package names separated by newline characters."
- + " Comment lines are supported only if the lines start with double slash."
- + " Trailing comments are not supported. Empty lines are ignored.")
- private final Set<File> mPackagesFiles = new HashSet<>();
-
- @Override
- public Set<String> get() throws IOException {
- Set<String> packages = new HashSet<>();
- for (File packagesFile : mPackagesFiles) {
- packages.addAll(
- Files.readAllLines(packagesFile.toPath()).parallelStream()
- .map(String::trim)
- .filter(this::isPackageName)
- .collect(Collectors.toSet()));
- }
- return packages;
- }
-
- private boolean isPackageName(String text) {
- // Check the text is not an empty string and not a comment line.
- return !text.isEmpty() && !text.startsWith(COMMENT_LINE_PREFIX);
- }
-}
diff --git a/harness/src/main/java/com/android/csuite/core/PackageNameProvider.java b/harness/src/main/java/com/android/csuite/core/PackageNameProvider.java
deleted file mode 100644
index 05709a1..0000000
--- a/harness/src/main/java/com/android/csuite/core/PackageNameProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2021 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.csuite.core;
-
-import java.io.IOException;
-import java.util.Set;
-
-/** Provides a list of package names. */
-public interface PackageNameProvider {
- /**
- * Returns a set of package names.
- *
- * @return the package names. An empty set is returned if no package names are to be provided.
- * @throws IOException if failed to get package names.
- */
- Set<String> get() throws IOException;
-}
diff --git a/harness/src/main/java/com/android/csuite/core/SystemPackageUninstaller.java b/harness/src/main/java/com/android/csuite/core/SystemPackageUninstaller.java
deleted file mode 100644
index 4ed6efe..0000000
--- a/harness/src/main/java/com/android/csuite/core/SystemPackageUninstaller.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.core;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import com.google.common.annotations.VisibleForTesting;
-
-import java.nio.file.Paths;
-import java.util.Arrays;
-
-/**
- * Uninstalls a system app.
- *
- * <p>This utility class may not restore the uninstalled system app after test completes.
- *
- * <p>The class may disable dm verity on some devices, and it does not re-enable it after
- * uninstalling a system app.
- */
-public final class SystemPackageUninstaller {
- @VisibleForTesting static final String OPTION_PACKAGE_NAME = "package-name";
- static final String SYSPROP_DEV_BOOTCOMPLETE = "dev.bootcomplete";
- static final String SYSPROP_SYS_BOOT_COMPLETED = "sys.boot_completed";
- static final long WAIT_FOR_BOOT_COMPLETE_TIMEOUT_MILLIS = 1000 * 60;
- @VisibleForTesting static final int MAX_NUMBER_OF_UPDATES = 100;
- @VisibleForTesting static final String PM_CHECK_COMMAND = "pm path android";
-
- public static void uninstallPackage(String packageName, ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- checkNotNull(packageName);
-
- if (!isPackageManagerRunning(device)) {
- CLog.w(
- "Package manager is not available on the device."
- + " Attempting to recover it by restarting the framework.");
- runAsRoot(
- device,
- () -> {
- stopFramework(device);
- startFramework(device);
- });
- if (!isPackageManagerRunning(device)) {
- throw new TargetSetupError("The package manager failed to start.");
- }
- }
-
- if (!isPackageInstalled(packageName, device)) {
- CLog.i("Package %s is not installed.", packageName);
- return;
- }
-
- // Attempts to uninstall the package/updates from user partition.
- // This method should be called before the other methods and requires
- // the framework to be running.
- removePackageUpdates(packageName, device);
-
- if (!isPackageInstalled(packageName, device)) {
- CLog.i("Package %s has been removed.", packageName);
- return;
- }
-
- String packageInstallDirectory = getPackageInstallDirectory(packageName, device);
- CLog.d("Install directory for package %s is %s", packageName, packageInstallDirectory);
-
- if (!isPackagePathSystemApp(packageInstallDirectory)) {
- CLog.w("%s is not a system app, skipping", packageName);
- return;
- }
-
- CLog.i("Uninstalling system app %s", packageName);
-
- runWithWritableFilesystem(
- device,
- () ->
- runWithFrameworkOff(
- device,
- () -> {
- removePackageInstallDirectory(packageInstallDirectory, device);
- removePackageData(packageName, device);
- }));
- }
-
- private interface PreparerTask {
- void run() throws TargetSetupError, DeviceNotAvailableException;
- }
-
- private static void runWithFrameworkOff(ITestDevice device, PreparerTask action)
- throws TargetSetupError, DeviceNotAvailableException {
- stopFramework(device);
-
- try {
- action.run();
- } finally {
- startFramework(device);
- }
- }
-
- private static void runWithWritableFilesystem(ITestDevice device, PreparerTask action)
- throws TargetSetupError, DeviceNotAvailableException {
- runAsRoot(
- device,
- () -> {
- // TODO(yuexima): The remountSystemWritable method may internally disable dm
- // verity on some devices. Consider restoring verity which would require a
- // reboot.
- device.remountSystemWritable();
-
- try {
- action.run();
- } finally {
- remountSystemReadOnly(device);
- }
- });
- }
-
- private static void runAsRoot(ITestDevice device, PreparerTask action)
- throws TargetSetupError, DeviceNotAvailableException {
- boolean disableRootAfterUninstall = false;
-
- if (!device.isAdbRoot()) {
- if (!device.enableAdbRoot()) {
- throw new TargetSetupError("Failed to enable adb root");
- }
-
- disableRootAfterUninstall = true;
- }
-
- try {
- action.run();
- } finally {
- if (disableRootAfterUninstall && !device.disableAdbRoot()) {
- throw new TargetSetupError("Failed to disable adb root");
- }
- }
- }
-
- private static void stopFramework(ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- // 'stop' is a blocking command.
- executeShellCommandOrThrow(device, "stop", "Failed to stop framework");
- // Set the boot complete flags to false. When the framework is started again, both flags
- // will be set to true by the system upon the completion of restarting. This allows
- // ITestDevice#waitForBootComplete to wait for framework start, and it only works
- // when adb is rooted.
- device.setProperty(SYSPROP_SYS_BOOT_COMPLETED, "0");
- device.setProperty(SYSPROP_DEV_BOOTCOMPLETE, "0");
- }
-
- private static void startFramework(ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- // 'start' is a non-blocking command.
- executeShellCommandOrThrow(device, "start", "Failed to start framework");
- // This wait only blocks if the boot completed flags are set to 0.
- device.waitForBootComplete(WAIT_FOR_BOOT_COMPLETE_TIMEOUT_MILLIS);
- }
-
- private static CommandResult executeShellCommandOrThrow(
- ITestDevice device, String command, String failureMessage)
- throws TargetSetupError, DeviceNotAvailableException {
- CommandResult commandResult = device.executeShellV2Command(command);
-
- if (commandResult.getStatus() != CommandStatus.SUCCESS) {
- throw new TargetSetupError(
- String.format("%s; Command result: %s", failureMessage, commandResult));
- }
-
- return commandResult;
- }
-
- private static CommandResult executeShellCommandOrLog(
- ITestDevice device, String command, String failureMessage)
- throws DeviceNotAvailableException {
- CommandResult commandResult = device.executeShellV2Command(command);
- if (commandResult.getStatus() != CommandStatus.SUCCESS) {
- CLog.e("%s. Command result: %s", failureMessage, commandResult);
- }
-
- return commandResult;
- }
-
- private static void remountSystemReadOnly(ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- executeShellCommandOrThrow(
- device,
- "mount -o ro,remount /system",
- "Failed to remount system partition as read only");
- }
-
- private static boolean isPackagePathSystemApp(String packagePath) {
- return packagePath.startsWith("/system/") || packagePath.startsWith("/product/");
- }
-
- private static void removePackageInstallDirectory(
- String packageInstallDirectory, ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- CLog.i("Removing package install directory %s", packageInstallDirectory);
- executeShellCommandOrThrow(
- device,
- String.format("rm -r %s", packageInstallDirectory),
- String.format(
- "Failed to remove system app package path %s", packageInstallDirectory));
- }
-
- private static void removePackageUpdates(String packageName, ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- CLog.i("Removing package updates for %s", packageName);
-
- // A system package may have update packages. If so, each `adb uninstall` call
- // only uninstalls the latest update. To remove all update packages we can
- // call uninstall repeatedly until the command fails.
- for (int i = 0; i < MAX_NUMBER_OF_UPDATES; i++) {
- String errMsg = device.uninstallPackage(packageName);
- if (errMsg != null) {
- CLog.d("Completed removing updates as the uninstall command returned: %s", errMsg);
- return;
- }
- CLog.i("Removed an update package for %s", packageName);
- }
-
- throw new TargetSetupError("Too many updates were uninstalled. Something must be wrong.");
- }
-
- private static void removePackageData(String packageName, ITestDevice device)
- throws DeviceNotAvailableException {
- String dataPath = String.format("/data/data/%s", packageName);
- CLog.i("Removing package data directory for %s", dataPath);
- executeShellCommandOrLog(
- device,
- String.format("rm -r %s", dataPath),
- String.format(
- "Failed to remove system app data %s from %s", packageName, dataPath));
- }
-
- private static boolean isPackageManagerRunning(ITestDevice device)
- throws DeviceNotAvailableException {
- return device.executeShellV2Command(PM_CHECK_COMMAND).getStatus() == CommandStatus.SUCCESS;
- }
-
- private static boolean isPackageInstalled(String packageName, ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- CommandResult commandResult =
- executeShellCommandOrThrow(
- device,
- String.format("pm list packages %s", packageName),
- "Failed to execute pm command");
-
- if (commandResult.getStdout() == null) {
- throw new TargetSetupError(
- String.format(
- "Failed to get pm command output: %s", commandResult.getStdout()));
- }
-
- return Arrays.asList(commandResult.getStdout().split("\\r?\\n"))
- .contains(String.format("package:%s", packageName));
- }
-
- private static String getPackageInstallDirectory(String packageName, ITestDevice device)
- throws TargetSetupError, DeviceNotAvailableException {
- CommandResult commandResult =
- executeShellCommandOrThrow(
- device,
- String.format("pm path %s", packageName),
- "Failed to execute pm command");
-
- if (commandResult.getStdout() == null
- || !commandResult.getStdout().startsWith("package:")) {
- throw new TargetSetupError(
- String.format(
- "Failed to get pm path command output %s", commandResult.getStdout()));
- }
-
- String packageInstallPath = commandResult.getStdout().substring("package:".length());
- return Paths.get(packageInstallPath).getParent().toString();
- }
-}
diff --git a/harness/src/main/resources/META-INF/services/com.android.tradefed.config.remote.IRemoteFileResolver b/harness/src/main/resources/META-INF/services/com.android.tradefed.config.remote.IRemoteFileResolver
deleted file mode 100644
index be67f9b..0000000
--- a/harness/src/main/resources/META-INF/services/com.android.tradefed.config.remote.IRemoteFileResolver
+++ /dev/null
@@ -1 +0,0 @@
-com.android.csuite.config.AppRemoteFileResolver
diff --git a/harness/src/main/resources/config/csuite-base.xml b/harness/src/main/resources/config/csuite-base.xml
index 03e418b..6431154 100644
--- a/harness/src/main/resources/config/csuite-base.xml
+++ b/harness/src/main/resources/config/csuite-base.xml
@@ -28,12 +28,5 @@
<result_reporter class="com.android.compatibility.common.tradefed.result.suite.CompatibilityProtoResultReporter" />
<result_reporter class="com.android.tradefed.result.suite.SuiteResultReporter" />
- <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparer">
- <option name="test-file-name" value="csuite-launch-instrumentation.apk"/>
- </target_preparer>
- <!-- Cleans generated module files after test -->
- <target_preparer class="com.android.csuite.config.ModuleGenerator" />
-
- <object type="PACKAGE_NAME_PROVIDER" class="com.android.csuite.core.CommandLinePackageNameProvider" />
- <object type="PACKAGE_NAME_PROVIDER" class="com.android.csuite.core.FileBasedPackageNameProvider" />
+ <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparerConfigurationReceiver" />
</configuration>
diff --git a/integration_tests/csuite_test_template.xml b/harness/src/main/resources/config/launch.xml
index 837716a..a1a158d 100644
--- a/integration_tests/csuite_test_template.xml
+++ b/harness/src/main/resources/config/launch.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
+<!-- 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.
@@ -13,11 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration>
- <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest">
- <option name="par-file-name" value="{MODULE}"/>
- <option name="inject-serial-option" value="true"/>
- <option name="use-test-output-file" value="true"/>
- <option name="test-timeout" value="10m"/>
- </test>
+<configuration description="C-Suite Compatibility Launch Test Plan">
+ <include name="csuite-base" />
+
+ <option name="plan" value="launch" />
+
+ <option name="compatibility:module-metadata-include-filter" key="plan" value="app-launch" />
</configuration>
diff --git a/harness/src/test/java/com/android/compatibility/AppCompatibilityTestTest.java b/harness/src/test/java/com/android/compatibility/AppCompatibilityTestTest.java
new file mode 100644
index 0000000..18f3368
--- /dev/null
+++ b/harness/src/test/java/com/android/compatibility/AppCompatibilityTestTest.java
@@ -0,0 +1,334 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tradefed.testtype.InstrumentationTest;
+import com.android.tradefed.util.PublicApkUtil.ApkInfo;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+@RunWith(JUnit4.class)
+public final class AppCompatibilityTestTest {
+
+ private ConcreteAppCompatibilityTest mSut;
+
+ private class ConcreteAppCompatibilityTest extends AppCompatibilityTest {
+
+ ConcreteAppCompatibilityTest() {
+ super(null, null, null);
+ }
+
+ @Override
+ protected InstrumentationTest createInstrumentationTest(String packageBeingTested) {
+ return null;
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mSut = new ConcreteAppCompatibilityTest();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void addIncludeFilter_nullIncludeFilter_throwsException() {
+ mSut.addIncludeFilter(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void addIncludeFilter_emptyIncludeFilter_throwsException() {
+ mSut.addIncludeFilter("");
+ }
+
+ @Test
+ public void addIncludeFilter_validIncludeFilter() {
+ mSut.addIncludeFilter("test_filter");
+
+ assertTrue(mSut.mIncludeFilters.contains("test_filter"));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void addAllIncludeFilters_nullIncludeFilter_throwsException() {
+ mSut.addAllIncludeFilters(null);
+ }
+
+ @Test
+ public void addAllIncludeFilters_validIncludeFilters() {
+ Set<String> test_filters = new TreeSet<>();
+ test_filters.add("filter_one");
+ test_filters.add("filter_two");
+
+ mSut.addAllIncludeFilters(test_filters);
+
+ assertTrue(mSut.mIncludeFilters.contains("filter_one"));
+ assertTrue(mSut.mIncludeFilters.contains("filter_two"));
+ }
+
+ @Test
+ public void clearIncludeFilters() {
+ mSut.addIncludeFilter("filter_test");
+
+ mSut.clearIncludeFilters();
+
+ assertTrue(mSut.mIncludeFilters.isEmpty());
+ }
+
+ @Test
+ public void getIncludeFilters() {
+ mSut.addIncludeFilter("filter_test");
+
+ assertEquals(mSut.mIncludeFilters, mSut.getIncludeFilters());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void addExcludeFilter_nullExcludeFilter_throwsException() {
+ mSut.addExcludeFilter(null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void addExcludeFilter_emptyExcludeFilter_throwsException() {
+ mSut.addExcludeFilter("");
+ }
+
+ @Test
+ public void addExcludeFilter_validExcludeFilter() {
+ mSut.addExcludeFilter("test_filter");
+
+ assertTrue(mSut.mExcludeFilters.contains("test_filter"));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void addAllExcludeFilters_nullExcludeFilters_throwsException() {
+ mSut.addAllExcludeFilters(null);
+ }
+
+ @Test
+ public void addAllExcludeFilters_validExcludeFilters() {
+ Set<String> test_filters = new TreeSet<>();
+ test_filters.add("filter_one");
+ test_filters.add("filter_two");
+
+ mSut.addAllExcludeFilters(test_filters);
+
+ assertTrue(mSut.mExcludeFilters.contains("filter_one"));
+ assertTrue(mSut.mExcludeFilters.contains("filter_two"));
+ }
+
+ @Test
+ public void clearExcludeFilters() {
+ mSut.addExcludeFilter("filter_test");
+
+ mSut.clearExcludeFilters();
+
+ assertTrue(mSut.mExcludeFilters.isEmpty());
+ }
+
+ @Test
+ public void getExcludeFilters() {
+ mSut.addExcludeFilter("filter_test");
+
+ assertEquals(mSut.mExcludeFilters, mSut.getExcludeFilters());
+ }
+
+ @Test
+ public void filterApk_withNoFilter() {
+ List<ApkInfo> testList = createApkList();
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(filteredList, testList);
+ }
+
+ @Test
+ public void filterApk_withRelatedIncludeFilters() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_one");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(convertList(filteredList), Arrays.asList("filter_one"));
+ }
+
+ @Test
+ public void filterApk_withUnrelatedIncludeFilters() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_three");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertTrue(filteredList.isEmpty());
+ }
+
+ @Test
+ public void filterApk_withRelatedExcludeFilters() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addExcludeFilter("filter_one");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(convertList(filteredList), Arrays.asList("filter_two"));
+ }
+
+ @Test
+ public void filterApk_withUnrelatedExcludeFilters() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addExcludeFilter("filter_three");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(filteredList, testList);
+ }
+
+ @Test
+ public void filterApk_withSameIncludeAndExcludeFilters() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_one");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertTrue(filteredList.isEmpty());
+ }
+
+ @Test
+ public void filterApk_withDifferentIncludeAndExcludeFilter() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_one");
+ mSut.addIncludeFilter("filter_two");
+ mSut.addExcludeFilter("filter_two");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(convertList(filteredList), Arrays.asList("filter_one"));
+ }
+
+ @Test
+ public void filterApk_withUnrelatedIncludeFilterAndRelatedExcludeFilter() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_three");
+ mSut.addExcludeFilter("filter_two");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertTrue(filteredList.isEmpty());
+ }
+
+ @Test
+ public void filterApk_withRelatedIncludeFilterAndUnrelatedExcludeFilter() {
+ List<ApkInfo> testList = createApkList();
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_three");
+
+ List<ApkInfo> filteredList = mSut.filterApk(testList);
+
+ assertEquals(convertList(filteredList), Arrays.asList("filter_one"));
+ }
+
+ private List<ApkInfo> createApkList() {
+ List<ApkInfo> testList = new ArrayList<>();
+ ApkInfo apk_info_one = new ApkInfo(0, "filter_one", "", "", "");
+ ApkInfo apk_info_two = new ApkInfo(0, "filter_two", "", "", "");
+ testList.add(apk_info_one);
+ testList.add(apk_info_two);
+ return testList;
+ }
+
+ private List<String> convertList(List<ApkInfo> apkList) {
+ List<String> convertedList = new ArrayList<>();
+ for (ApkInfo apkInfo : apkList) {
+ convertedList.add(apkInfo.packageName);
+ }
+ return convertedList;
+ }
+
+ @Test
+ public void filterTest_withEmptyFilter() {
+ assertTrue(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withRelatedIncludeFilter() {
+ mSut.addIncludeFilter("filter_one");
+
+ assertTrue(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withUnrelatedIncludeFilter() {
+ mSut.addIncludeFilter("filter_two");
+
+ assertFalse(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withRelatedExcludeFilter() {
+ mSut.addExcludeFilter("filter_one");
+
+ assertFalse(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withUnrelatedExcludeFilter() {
+ mSut.addExcludeFilter("filter_two");
+
+ assertTrue(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withSameIncludeAndExcludeFilters() {
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_one");
+
+ assertFalse(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withUnrelatedIncludeFilterAndRelatedExcludeFilter() {
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_two");
+
+ assertFalse(mSut.filterTest("filter_two"));
+ }
+
+ @Test
+ public void filterTest_withRelatedIncludeFilterAndUnrelatedExcludeFilter() {
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_two");
+
+ assertTrue(mSut.filterTest("filter_one"));
+ }
+
+ @Test
+ public void filterTest_withUnRelatedIncludeFilterAndUnrelatedExcludeFilter() {
+ mSut.addIncludeFilter("filter_one");
+ mSut.addExcludeFilter("filter_two");
+
+ assertFalse(mSut.filterTest("filter_three"));
+ }
+}
diff --git a/harness/src/test/java/com/android/csuite/CSuiteUnitTests.java b/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java
index 6eb1103..b87402e 100644
--- a/harness/src/test/java/com/android/csuite/CSuiteUnitTests.java
+++ b/harness/src/test/java/com/android/compatibility/CSuiteUnitTests.java
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.csuite;
+package com.android.compatibility;
+
+import com.android.compatibility.targetprep.AppSetupPreparerConfigurationReceiverTest;
+import com.android.compatibility.targetprep.AppSetupPreparerTest;
+import com.android.compatibility.testtype.AppLaunchTestTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -21,16 +25,10 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
- com.android.compatibility.targetprep.AppSetupPreparerTest.class,
- com.android.compatibility.targetprep.CheckGmsPreparerTest.class,
- com.android.compatibility.testtype.AppLaunchTestTest.class,
- com.android.csuite.config.AppRemoteFileResolverTest.class,
- com.android.csuite.config.ModuleGeneratorTest.class,
- com.android.csuite.core.CommandLinePackageNameProviderTest.class,
- com.android.csuite.core.FileBasedPackageNameProviderTest.class,
- com.android.csuite.core.SystemAppUninstallerTest.class,
- com.android.csuite.testing.CorrespondencesTest.class,
- com.android.csuite.testing.MoreAssertsTest.class,
+ AppCompatibilityTestTest.class,
+ AppLaunchTestTest.class,
+ AppSetupPreparerTest.class,
+ AppSetupPreparerConfigurationReceiverTest.class,
})
public final class CSuiteUnitTests {
// Intentionally empty.
diff --git a/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiverTest.java b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiverTest.java
new file mode 100644
index 0000000..e708a67
--- /dev/null
+++ b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerConfigurationReceiverTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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 com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+
+@RunWith(JUnit4.class)
+public final class AppSetupPreparerConfigurationReceiverTest {
+
+ @Test
+ public void setUp_noneNullGcsApkDirOption_putsInBuildInfo() {
+ File optionGcsApkDir = new File("dir");
+ AppSetupPreparerConfigurationReceiver preparer =
+ new AppSetupPreparerConfigurationReceiver(optionGcsApkDir);
+ IBuildInfo buildInfo = new BuildInfo();
+
+ preparer.setUp(null, buildInfo);
+
+ assertThat(buildInfo.getBuildAttributes())
+ .containsEntry(AppSetupPreparer.OPTION_GCS_APK_DIR, optionGcsApkDir.getPath());
+ }
+
+ @Test
+ public void setUp_nullGcsApkDirOption_doesNotPutInBuildInfo() {
+ File optionGcsApkDir = null;
+ AppSetupPreparerConfigurationReceiver preparer =
+ new AppSetupPreparerConfigurationReceiver(optionGcsApkDir);
+ IBuildInfo buildInfo = new BuildInfo();
+
+ preparer.setUp(null, buildInfo);
+
+ assertThat(buildInfo.getBuildAttributes())
+ .doesNotContainKey(AppSetupPreparer.OPTION_GCS_APK_DIR);
+ }
+}
diff --git a/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java
index e2e4352..7614f05 100644
--- a/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java
+++ b/harness/src/test/java/com/android/compatibility/targetprep/AppSetupPreparerTest.java
@@ -15,492 +15,125 @@
*/
package com.android.compatibility.targetprep;
+import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ArgsOptionParser;
-import com.android.tradefed.config.ConfigurationException;
-import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.targetprep.TestAppInstallSetup;
-import com.android.tradefed.util.AaptParser.AaptVersion;
-
-import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
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 static org.testng.Assert.assertThrows;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ListMultimap;
-
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.ArgumentCaptor;
-import org.mockito.internal.stubbing.answers.AnswersWithDelay;
-import org.mockito.stubbing.Answer;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Map;
@RunWith(JUnit4.class)
-public final class AppSetupPreparerTest {
- private static final ITestDevice NULL_DEVICE = null;
- private static final IBuildInfo NULL_BUILD_INFO = null;
- private static final String TEST_PACKAGE_NAME = "test.package.name";
- private static final Answer<Object> EMPTY_ANSWER = (i) -> null;
-
- @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
-
- @Test
- public void setUp_unresolvedAppUri_installs() throws Exception {
- String appUri = "app://com.example.app";
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_TEST_FILE_NAME, appUri)
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
-
- verify(installer).addTestFile(new File(appUri));
- }
-
- @Test
- public void tearDown_forwardsToInstaller() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- AppSetupPreparer preparer = new PreparerBuilder().setInstaller(installer).build();
- TestInformation testInfo = TestInformation.newBuilder().build();
-
- preparer.tearDown(testInfo, null);
-
- verify(installer).tearDown(testInfo, null);
- }
-
- @Test
- public void setUp_withinRetryLimit_doesNotThrowException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doThrow(new TargetSetupError("Still failing"))
- .doNothing()
- .when(installer)
- .setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
- }
-
- @Test
- public void setUp_exceedsRetryLimit_throwsException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doThrow(new TargetSetupError("Still failing"))
- .doThrow(new TargetSetupError("Still failing"))
- .doNothing()
- .when(installer)
- .setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .build();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_negativeTimeout_throwsException() throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setOption(AppSetupPreparer.OPTION_SETUP_TIMEOUT_MILLIS, "-1")
- .build();
-
- assertThrows(
- IllegalArgumentException.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_withinTimeout_doesNotThrowException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doAnswer(new AnswersWithDelay(10, EMPTY_ANSWER)).when(installer).setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_SETUP_TIMEOUT_MILLIS, "1000")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
- }
-
- @Test
- public void setUp_exceedsTimeout_throwsException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doAnswer(new AnswersWithDelay(10, EMPTY_ANSWER)).when(installer).setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_SETUP_TIMEOUT_MILLIS, "5")
- .build();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_timesOutWithoutExceedingRetryLimit_doesNotThrowException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doAnswer(new AnswersWithDelay(10, EMPTY_ANSWER))
- .doNothing()
- .when(installer)
- .setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .setOption(AppSetupPreparer.OPTION_SETUP_TIMEOUT_MILLIS, "5")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
- }
-
- @Test
- public void setUp_timesOutAndExceedsRetryLimit_doesNotThrowException() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doAnswer(new AnswersWithDelay(10, EMPTY_ANSWER)).when(installer).setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .setOption(AppSetupPreparer.OPTION_SETUP_TIMEOUT_MILLIS, "5")
- .build();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_zeroMaxRetry_runsOnce() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doNothing().when(installer).setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "0")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
-
- verify(installer).setUp(any(), any());
- }
+public class AppSetupPreparerTest {
- @Test
- public void setUp_positiveMaxRetryButNoException_runsOnlyOnce() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doNothing().when(installer).setUp(any(), any());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .build();
+ private static final String OPTION_GCS_APK_DIR = "gcs-apk-dir";
+ public static final ITestDevice NULL_DEVICE = null;
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
+ @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
- verify(installer).setUp(any(), any());
- }
+ private final IBuildInfo mBuildInfo = new BuildInfo();
+ private final TestAppInstallSetup mMockAppInstallSetup = mock(TestAppInstallSetup.class);
+ private final AppSetupPreparer mPreparer =
+ new AppSetupPreparer("package_name", mMockAppInstallSetup);
@Test
- public void setUp_negativeMaxRetry_throwsException() throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder().setOption(AppSetupPreparer.OPTION_MAX_RETRY, "-1").build();
+ public void setUp_gcsApkDirIsNull_throwsException()
+ throws DeviceNotAvailableException, TargetSetupError {
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, null);
- assertThrows(
- IllegalArgumentException.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
+ assertThrows(NullPointerException.class, () -> mPreparer.setUp(NULL_DEVICE, mBuildInfo));
}
@Test
- public void setUp_deviceNotAvailableAndWaitEnabled_throwsDeviceNotAvailableException()
- throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(
- mockInstallerThatThrows(
- new TargetSetupError("Connection reset by peer.")))
- .setOption(AppSetupPreparer.OPTION_WAIT_FOR_DEVICE_AVAILABLE_SECONDS, "1")
- .build();
- ITestDevice device = createUnavailableDevice();
+ public void setUp_gcsApkDirIsNotDir_throwsException()
+ throws IOException, DeviceNotAvailableException, TargetSetupError {
+ File tempFile = tempFolder.newFile("temp_file_name");
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, tempFile.getPath());
assertThrows(
- DeviceNotAvailableException.class, () -> preparer.setUp(device, NULL_BUILD_INFO));
+ IllegalArgumentException.class, () -> mPreparer.setUp(NULL_DEVICE, mBuildInfo));
}
@Test
- public void setUp_deviceAvailableAndWaitEnabled_doesNotChangeException() throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(
- mockInstallerThatThrows(
- new TargetSetupError("Connection reset by peer.")))
- .setOption(AppSetupPreparer.OPTION_WAIT_FOR_DEVICE_AVAILABLE_SECONDS, "1")
- .build();
- ITestDevice device = createAvailableDevice();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(device, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_deviceNotAvailableAndWaitDisabled_doesNotChangeException() throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(
- mockInstallerThatThrows(
- new TargetSetupError("Connection reset by peer.")))
- .setOption(AppSetupPreparer.OPTION_WAIT_FOR_DEVICE_AVAILABLE_SECONDS, "-1")
- .build();
- ITestDevice device = createUnavailableDevice();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(device, NULL_BUILD_INFO));
- }
-
- @Test
- public void setUp_negativeExponentialBackoffMultiplier_throwsIllegalArgumentException()
- throws Exception {
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setOption(
- AppSetupPreparer.OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS,
- "-1")
- .build();
+ public void setUp_packageDirDoesNotExist_throwsError()
+ throws IOException, DeviceNotAvailableException, TargetSetupError {
+ File gcsApkDir = tempFolder.newFolder("gcs_apk_dir");
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, gcsApkDir.getPath());
assertThrows(
- IllegalArgumentException.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
+ IllegalArgumentException.class, () -> mPreparer.setUp(NULL_DEVICE, mBuildInfo));
}
@Test
- public void setUp_testFileNameOptionSet_forwardsToInstaller() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- ArgumentCaptor<File> captor = ArgumentCaptor.forClass(File.class);
- doNothing().when(installer).addTestFile(captor.capture());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_TEST_FILE_NAME, "additional1.apk")
- .setOption(AppSetupPreparer.OPTION_TEST_FILE_NAME, "additional2.apk")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
+ public void setUp_apkDoesNotExist() throws Exception {
+ File gcsApkDir = tempFolder.newFolder("gcs_apk_dir");
+ createPackageFile(gcsApkDir, "package_name", "non_apk_file");
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, gcsApkDir.getPath());
- assertThat(captor.getAllValues())
- .containsAtLeast(new File("additional1.apk"), new File("additional2.apk"));
+ assertThrows(TargetSetupError.class, () -> mPreparer.setUp(NULL_DEVICE, mBuildInfo));
}
@Test
- public void setUp_installArgOptionSet_forwardsToInstaller() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
- doNothing().when(installer).addInstallArg(captor.capture());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_INSTALL_ARG, "-arg1")
- .setOption(AppSetupPreparer.OPTION_INSTALL_ARG, "-arg2")
- .build();
+ public void setUp_installSplitApk() throws Exception {
+ File gcsApkDir = tempFolder.newFolder("gcs_apk_dir");
+ File packageDir = new File(gcsApkDir.getPath(), "package_name");
+ createPackageFile(gcsApkDir, "package_name", "apk_name_1.apk");
+ createPackageFile(gcsApkDir, "package_name", "apk_name_2.apk");
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, gcsApkDir.getPath());
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
+ mPreparer.setUp(NULL_DEVICE, mBuildInfo);
- assertThat(captor.getAllValues()).containsExactly("-arg1", "-arg2");
+ verify(mMockAppInstallSetup).setAltDir(packageDir);
+ verify(mMockAppInstallSetup).addSplitApkFileNames("apk_name_2.apk,apk_name_1.apk");
+ verify(mMockAppInstallSetup).setUp(any(), any());
}
@Test
- public void setUp_installIncrementalOptionSet_forwardsToInstaller() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
+ public void setUp_installNonSplitApk() throws Exception {
+ File gcsApkDir = tempFolder.newFolder("gcs_apk_dir");
+ File packageDir = new File(gcsApkDir.getPath(), "package_name");
+ createPackageFile(gcsApkDir, "package_name", "apk_name_1.apk");
+ mBuildInfo.addBuildAttribute(OPTION_GCS_APK_DIR, gcsApkDir.getPath());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_INCREMENTAL_INSTALL, "true")
- .build();
+ mPreparer.setUp(NULL_DEVICE, mBuildInfo);
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
- String result = ArgsOptionParser.getOptionHelp(false, installer);
- System.out.println(result);
-
- assertThat(result).contains("incremental");
- }
-
- @Test
- public void setUp_aaptVersionOptionSet_forwardsToInstaller() throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- ArgumentCaptor<AaptVersion> captor = ArgumentCaptor.forClass(AaptVersion.class);
- doNothing().when(installer).setAaptVersion(captor.capture());
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setInstaller(installer)
- .setOption(AppSetupPreparer.OPTION_AAPT_VERSION, "AAPT2")
- .build();
-
- preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO);
-
- assertThat(captor.getValue()).isEqualTo(AaptVersion.AAPT2);
+ verify(mMockAppInstallSetup).setAltDir(packageDir);
+ verify(mMockAppInstallSetup).addTestFileName("apk_name_1.apk");
+ verify(mMockAppInstallSetup).setUp(any(), any());
}
@Test
- public void setUp_zeroExponentialBackoffMultiplier_noSleepBetweenRetries() throws Exception {
- FakeSleeper fakeSleeper = new FakeSleeper();
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setSleeper(fakeSleeper)
- .setInstaller(mockInstallerThatThrows(new TargetSetupError("Oops")))
- .setOption(
- AppSetupPreparer.OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS, "0")
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "1")
- .build();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- assertThat(fakeSleeper.getSleepHistory().get(0)).isEqualTo(Duration.ofSeconds(0));
- }
-
- @Test
- public void setUp_positiveExponentialBackoffMultiplier_sleepsBetweenRetries() throws Exception {
- FakeSleeper fakeSleeper = new FakeSleeper();
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setSleeper(fakeSleeper)
- .setInstaller(mockInstallerThatThrows(new TargetSetupError("Oops")))
- .setOption(
- AppSetupPreparer.OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS, "3")
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "3")
- .build();
-
- assertThrows(TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- assertThat(fakeSleeper.getSleepHistory().get(0)).isEqualTo(Duration.ofSeconds(3));
- assertThat(fakeSleeper.getSleepHistory().get(1)).isEqualTo(Duration.ofSeconds(9));
- assertThat(fakeSleeper.getSleepHistory().get(2)).isEqualTo(Duration.ofSeconds(27));
- }
-
- @Test
- public void setUp_interruptedDuringBackoff_throwsException() throws Exception {
- FakeSleeper fakeSleeper = new FakeInterruptedSleeper();
- AppSetupPreparer preparer =
- new PreparerBuilder()
- .setSleeper(fakeSleeper)
- .setInstaller(mockInstallerThatThrows(new TargetSetupError("Oops")))
- .setOption(
- AppSetupPreparer.OPTION_EXPONENTIAL_BACKOFF_MULTIPLIER_SECONDS, "3")
- .setOption(AppSetupPreparer.OPTION_MAX_RETRY, "3")
- .build();
-
- try {
- assertThrows(
- TargetSetupError.class, () -> preparer.setUp(NULL_DEVICE, NULL_BUILD_INFO));
- assertThat(Thread.currentThread().isInterrupted()).isTrue();
- assertThat(fakeSleeper.getSleepHistory().size()).isEqualTo(1);
- } finally {
- // Clear interrupt to not interfere with future tests.
- Thread.interrupted();
- }
- }
-
- private TestAppInstallSetup mockInstallerThatThrows(Exception e) throws Exception {
- TestAppInstallSetup installer = mock(TestAppInstallSetup.class);
- doThrow(e).when(installer).setUp(any(), any());
- return installer;
- }
-
- private File createPackageFile(String packageName, String apkName) throws IOException {
- Path packageDir =
- Files.createDirectories(
- Paths.get(tempFolder.newFolder("any").getAbsolutePath(), packageName));
- Files.createFile(packageDir.resolve(apkName));
-
- return packageDir.toFile();
- }
-
- private static ITestDevice createUnavailableDevice() throws Exception {
- ITestDevice device = mock(ITestDevice.class);
- when(device.getProperty(any())).thenReturn(null);
- doThrow(new DeviceNotAvailableException("_", "serial"))
- .when(device)
- .waitForDeviceAvailable(anyLong());
- return device;
- }
-
- private static ITestDevice createAvailableDevice() throws Exception {
- ITestDevice device = mock(ITestDevice.class);
- when(device.getProperty(any())).thenReturn("");
- when(device.waitForDeviceShell(anyLong())).thenReturn(true);
- doNothing().when(device).waitForDeviceAvailable(anyLong());
-
- return device;
- }
-
- private static class FakeSleeper implements AppSetupPreparer.Sleeper {
- private ArrayList<Duration> mSleepHistory = new ArrayList<>();
-
- @Override
- public void sleep(Duration duration) throws InterruptedException {
- mSleepHistory.add(duration);
- }
+ public void tearDown() throws Exception {
+ TestInformation testInfo = TestInformation.newBuilder().build();
- ArrayList<Duration> getSleepHistory() {
- return mSleepHistory;
- }
- }
+ mPreparer.tearDown(testInfo, null);
- private static class FakeInterruptedSleeper extends FakeSleeper {
- @Override
- public void sleep(Duration duration) throws InterruptedException {
- super.sleep(duration);
- throw new InterruptedException("_");
- }
+ verify(mMockAppInstallSetup, times(1)).tearDown(testInfo, null);
}
- private static final class PreparerBuilder {
-
- private AppSetupPreparer.Sleeper mSleeper = new FakeSleeper();
- private TestAppInstallSetup mInstaller = mock(TestAppInstallSetup.class);
- private final ListMultimap<String, String> mOptions = ArrayListMultimap.create();
-
- PreparerBuilder setSleeper(AppSetupPreparer.Sleeper sleeper) {
- this.mSleeper = sleeper;
- return this;
- }
-
- PreparerBuilder setInstaller(TestAppInstallSetup installer) {
- this.mInstaller = installer;
- return this;
- }
-
- PreparerBuilder setOption(String key, String value) {
- mOptions.put(key, value);
- return this;
- }
-
- AppSetupPreparer build() throws ConfigurationException {
- AppSetupPreparer preparer = new AppSetupPreparer(mInstaller, mSleeper);
- OptionSetter optionSetter = new OptionSetter(preparer);
-
- for (Map.Entry<String, String> e : mOptions.entries()) {
- optionSetter.setOptionValue(e.getKey(), e.getValue());
- }
+ private File createPackageFile(File parentDir, String packageName, String apkName)
+ throws IOException {
+ File packageDir =
+ Files.createDirectories(Paths.get(parentDir.getAbsolutePath(), packageName))
+ .toFile();
- return preparer;
- }
+ return Files.createFile(Paths.get(packageDir.getAbsolutePath(), apkName)).toFile();
}
}
diff --git a/harness/src/test/java/com/android/compatibility/targetprep/CheckGmsPreparerTest.java b/harness/src/test/java/com/android/compatibility/targetprep/CheckGmsPreparerTest.java
deleted file mode 100644
index 09f2141..0000000
--- a/harness/src/test/java/com/android/compatibility/targetprep/CheckGmsPreparerTest.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2020 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.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import com.android.ddmlib.Log;
-import com.android.ddmlib.Log.ILogOutput;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.build.BuildInfo;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.invoker.IInvocationContext;
-import com.android.tradefed.invoker.InvocationContext;
-import com.android.tradefed.invoker.TestInformation;
-import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import com.google.common.truth.Correspondence;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-
-@RunWith(JUnit4.class)
-public final class CheckGmsPreparerTest {
-
- private CheckGmsPreparer mPreparer;
- private LogCaptor mLogCaptor;
-
- @Before
- public void setUp() throws Exception {
- mPreparer = new CheckGmsPreparer();
- new OptionSetter(mPreparer).setOptionValue(CheckGmsPreparer.OPTION_ENABLE, "true");
-
- mLogCaptor = new LogCaptor();
- Log.addLogger(mLogCaptor);
- }
-
- @After
- public void tearDown() {
- Log.removeLogger(mLogCaptor);
- }
-
- @Test
- public void setUp_checkDisabledAndGmsAbsent_doesNotReboot() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsent();
- disablePreparer(mPreparer);
-
- mPreparer.setUp(createTestInfo(device));
-
- Mockito.verify(device, Mockito.never()).reboot();
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .doesNotContain("GMS");
- }
-
- @Test
- public void tearDown_checkDisabledAndGmsAbsent_doesNotLog() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsent();
- disablePreparer(mPreparer);
-
- mPreparer.tearDown(createTestInfo(device), null);
-
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .doesNotContain("GMS");
- }
-
- @Test
- public void tearDown_setUpThrows_doesNotCheck() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsent();
- TestInformation testInfo = createTestInfo(device);
- assertThrows(TargetSetupError.class, () -> mPreparer.setUp(testInfo));
- mLogCaptor.reset();
- Mockito.reset(device);
- Mockito.when(device.executeShellV2Command(Mockito.any()))
- .thenReturn(createFailedCommandResult());
-
- mPreparer.tearDown(testInfo, null);
-
- Mockito.verify(device, Mockito.never()).executeShellV2Command(Mockito.any());
- }
-
- @Test
- public void tearDown_setUpRecoveredGms_checksGms() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsentAndRecoverable();
- TestInformation testInfo = createTestInfo(device);
- mPreparer.setUp(testInfo);
- mLogCaptor.reset();
- Mockito.reset(device);
- Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
- .thenReturn(createSuccessfulCommandResult());
-
- mPreparer.tearDown(testInfo, null);
-
- Mockito.verify(device, Mockito.atLeast(1))
- .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
- }
-
- @Test
- public void tearDown_setUpFoundGms_checksGms() throws Exception {
- ITestDevice device = createDeviceWithGmsPresent();
- TestInformation testInfo = createTestInfo(device);
- mPreparer.setUp(testInfo);
- Mockito.reset(device);
- mLogCaptor.reset();
- Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
- .thenReturn(createSuccessfulCommandResult());
-
- mPreparer.tearDown(testInfo, null);
-
- Mockito.verify(device, Mockito.atLeast(1))
- .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
- }
-
- @Test
- public void setUp_gmsPresent_doesNotReboot() throws Exception {
- ITestDevice device = createDeviceWithGmsPresent();
-
- mPreparer.setUp(createTestInfo(device));
-
- Mockito.verify(device, Mockito.never()).reboot();
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .doesNotContain("GMS");
- }
-
- @Test
- public void setUp_gmsProcessRecoveredAfterReboot_doesNotThrow() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsentAndRecoverable();
-
- mPreparer.setUp(createTestInfo(device));
-
- Mockito.verify(device, Mockito.times(1)).reboot();
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .contains("GMS");
- }
-
- @Test
- public void setUp_gmsProcessNotRecoveredAfterReboot_throwsException() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsent();
-
- assertThrows(TargetSetupError.class, () -> mPreparer.setUp(createTestInfo(device)));
- Mockito.verify(device, Mockito.times(1)).reboot();
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .contains("GMS");
- }
-
- @Test
- public void tearDown_gmsProcessPresent_doesNotLog() throws Exception {
- ITestDevice device = createDeviceWithGmsPresent();
-
- mPreparer.tearDown(createTestInfo(device), null);
-
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .doesNotContain("GMS");
- }
-
- @Test
- public void tearDown_gmsProcessAbsent_logsError() throws Exception {
- ITestDevice device = createDeviceWithGmsAbsent();
-
- mPreparer.tearDown(createTestInfo(device), null);
-
- assertThat(mLogCaptor.getLogItems())
- .comparingElementsUsing(createContainsErrorLogCorrespondence())
- .contains("GMS");
- }
-
- private static void disablePreparer(CheckGmsPreparer preparer) throws Exception {
- new OptionSetter(preparer).setOptionValue(CheckGmsPreparer.OPTION_ENABLE, "false");
- }
-
- private static Correspondence<LogItem, String> createContainsErrorLogCorrespondence() {
- return Correspondence.from(
- (LogItem actual, String expected) -> {
- return actual.getLogLevel() == LogLevel.ERROR
- && actual.getMessage().contains(expected);
- },
- "has an error log that contains");
- }
-
- private static ITestDevice createDeviceWithGmsAbsentAndRecoverable() throws Exception {
- ITestDevice device = Mockito.mock(ITestDevice.class);
- Mockito.doReturn(createFailedCommandResult())
- .doReturn(createSuccessfulCommandResult())
- .when(device)
- .executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND);
- return device;
- }
-
- private static ITestDevice createDeviceWithGmsPresent() throws Exception {
- ITestDevice device = Mockito.mock(ITestDevice.class);
- Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
- .thenReturn(createSuccessfulCommandResult());
- return device;
- }
-
- private static ITestDevice createDeviceWithGmsAbsent() throws Exception {
- ITestDevice device = Mockito.mock(ITestDevice.class);
- Mockito.when(device.executeShellV2Command(CheckGmsPreparer.CHECK_GMS_COMMAND))
- .thenReturn(createFailedCommandResult());
- return device;
- }
-
- private static final class LogCaptor implements ILogOutput {
- private ArrayList<LogItem> mLogItems = new ArrayList<>();
-
- void reset() {
- mLogItems.clear();
- }
-
- ArrayList<LogItem> getLogItems() {
- return mLogItems;
- }
-
- @Override
- public void printLog(LogLevel logLevel, String tag, String message) {
- mLogItems.add(new LogItem(logLevel, tag, message));
- }
-
- @Override
- public void printAndPromptLog(LogLevel logLevel, String tag, String message) {
- printLog(logLevel, tag, message);
- }
- }
-
- private static final class LogItem {
- private LogLevel mLogLevel;
- private String mMessage;
-
- LogLevel getLogLevel() {
- return mLogLevel;
- }
-
- String getMessage() {
- return mMessage;
- }
-
- LogItem(LogLevel logLevel, String tag, String message) {
- mLogLevel = logLevel;
- mMessage = message;
- }
- }
-
- private static TestInformation createTestInfo(ITestDevice device) {
- IInvocationContext context = new InvocationContext();
- context.addAllocatedDevice("device1", device);
- context.addDeviceBuildInfo("device1", new BuildInfo());
- return TestInformation.newBuilder().setInvocationContext(context).build();
- }
-
- private static CommandResult createSuccessfulCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS);
- commandResult.setExitCode(0);
- return commandResult;
- }
-
- private static CommandResult createFailedCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.FAILED);
- commandResult.setExitCode(1);
- return commandResult;
- }
-}
diff --git a/harness/src/test/java/com/android/compatibility/testtype/AppLaunchTestTest.java b/harness/src/test/java/com/android/compatibility/testtype/AppLaunchTestTest.java
index 0c23cc3..4f9aa35 100644
--- a/harness/src/test/java/com/android/compatibility/testtype/AppLaunchTestTest.java
+++ b/harness/src/test/java/com/android/compatibility/testtype/AppLaunchTestTest.java
@@ -15,20 +15,16 @@
*/
package com.android.compatibility.testtype;
-import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
-import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.testtype.InstrumentationTest;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -43,13 +39,10 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.InOrder;
-import org.mockito.Mockito;
import java.util.HashMap;
import java.util.HashSet;
@@ -62,7 +55,6 @@ public final class AppLaunchTestTest {
private final ITestInvocationListener mMockListener = mock(ITestInvocationListener.class);
private static final String TEST_PACKAGE_NAME = "package_name";
private static final TestInformation NULL_TEST_INFORMATION = null;
- @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
@Test
public void run_testFailed() throws DeviceNotAvailableException {
@@ -85,28 +77,10 @@ public final class AppLaunchTestTest {
}
@Test
- public void run_takeScreenShot_savesToTestLog() throws Exception {
- InstrumentationTest instrumentationTest = createPassingInstrumentationTest();
- AppLaunchTest appLaunchTest = createLaunchTestWithInstrumentation(instrumentationTest);
- new OptionSetter(appLaunchTest)
- .setOptionValue(AppLaunchTest.SCREENSHOT_AFTER_LAUNCH, "true");
- ITestDevice mMockDevice = mock(ITestDevice.class);
- appLaunchTest.setDevice(mMockDevice);
- InputStreamSource screenshotData = new FileInputStreamSource(tempFolder.newFile());
- when(mMockDevice.getScreenshot()).thenReturn(screenshotData);
- when(mMockDevice.getSerialNumber()).thenReturn("SERIAL");
-
- appLaunchTest.run(NULL_TEST_INFORMATION, mMockListener);
-
- Mockito.verify(mMockListener, times(1))
- .testLog(Mockito.contains("screenshot"), Mockito.any(), Mockito.eq(screenshotData));
- }
-
- @Test
public void run_packageResetSuccess() throws DeviceNotAvailableException {
ITestDevice mMockDevice = mock(ITestDevice.class);
when(mMockDevice.executeShellV2Command(String.format("pm clear %s", TEST_PACKAGE_NAME)))
- .thenReturn(createSuccessfulCommandResult());
+ .thenReturn(new CommandResult(CommandStatus.SUCCESS));
AppLaunchTest appLaunchTest = createLaunchTestWithMockDevice(mMockDevice);
appLaunchTest.run(NULL_TEST_INFORMATION, mMockListener);
@@ -118,7 +92,7 @@ public final class AppLaunchTestTest {
public void run_packageResetError() throws DeviceNotAvailableException {
ITestDevice mMockDevice = mock(ITestDevice.class);
when(mMockDevice.executeShellV2Command(String.format("pm clear %s", TEST_PACKAGE_NAME)))
- .thenReturn(createFailedCommandResult());
+ .thenReturn(new CommandResult(CommandStatus.FAILED));
AppLaunchTest appLaunchTest = createLaunchTestWithMockDevice(mMockDevice);
appLaunchTest.run(NULL_TEST_INFORMATION, mMockListener);
@@ -129,7 +103,7 @@ public final class AppLaunchTestTest {
@Test
public void run_testRetry_passedAfterTwoFailings() throws Exception {
InstrumentationTest instrumentationTest = createPassingInstrumentationTestAfterFailing(2);
- AppLaunchTest appLaunchTest = createLaunchTestWithInstrumentation(instrumentationTest, 2);
+ AppLaunchTest appLaunchTest = createLaunchTestWithRetry(instrumentationTest, 2);
appLaunchTest.run(NULL_TEST_INFORMATION, mMockListener);
@@ -139,7 +113,7 @@ public final class AppLaunchTestTest {
@Test
public void run_testRetry_failedAfterThreeFailings() throws Exception {
InstrumentationTest instrumentationTest = createPassingInstrumentationTestAfterFailing(3);
- AppLaunchTest appLaunchTest = createLaunchTestWithInstrumentation(instrumentationTest, 2);
+ AppLaunchTest appLaunchTest = createLaunchTestWithRetry(instrumentationTest, 2);
appLaunchTest.run(NULL_TEST_INFORMATION, mMockListener);
@@ -395,10 +369,24 @@ public final class AppLaunchTestTest {
}
private AppLaunchTest createLaunchTestWithInstrumentation(InstrumentationTest instrumentation) {
- return createLaunchTestWithInstrumentation(instrumentation, 0);
+ AppLaunchTest appLaunchTest =
+ new AppLaunchTest(TEST_PACKAGE_NAME) {
+ @Override
+ protected InstrumentationTest createInstrumentationTest(
+ String packageBeingTested) {
+ return instrumentation;
+ }
+
+ @Override
+ protected CommandResult resetPackage() throws DeviceNotAvailableException {
+ return new CommandResult(CommandStatus.SUCCESS);
+ }
+ };
+ appLaunchTest.setDevice(mock(ITestDevice.class));
+ return appLaunchTest;
}
- private AppLaunchTest createLaunchTestWithInstrumentation(
+ private AppLaunchTest createLaunchTestWithRetry(
InstrumentationTest instrumentation, int retryCount) {
AppLaunchTest appLaunchTest =
new AppLaunchTest(TEST_PACKAGE_NAME, retryCount) {
@@ -410,7 +398,7 @@ public final class AppLaunchTestTest {
@Override
protected CommandResult resetPackage() throws DeviceNotAvailableException {
- return createSuccessfulCommandResult();
+ return new CommandResult(CommandStatus.SUCCESS);
}
};
appLaunchTest.setDevice(mock(ITestDevice.class));
@@ -418,7 +406,7 @@ public final class AppLaunchTestTest {
}
private AppLaunchTest createLaunchTestWithMockDevice(ITestDevice device) {
- AppLaunchTest appLaunchTest = new AppLaunchTest(TEST_PACKAGE_NAME, 0);
+ AppLaunchTest appLaunchTest = new AppLaunchTest(TEST_PACKAGE_NAME);
appLaunchTest.setDevice(device);
return appLaunchTest;
}
@@ -442,16 +430,4 @@ public final class AppLaunchTestTest {
.testEnded(anyObject(), anyLong(), (Map<String, String>) any());
inOrder.verify(listener, times(1)).testRunEnded(anyLong(), (HashMap<String, Metric>) any());
}
-
- private CommandResult createSuccessfulCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS);
- commandResult.setExitCode(0);
- return commandResult;
- }
-
- private CommandResult createFailedCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.FAILED);
- commandResult.setExitCode(1);
- return commandResult;
- }
}
diff --git a/harness/src/test/java/com/android/csuite/config/AppRemoteFileResolverTest.java b/harness/src/test/java/com/android/csuite/config/AppRemoteFileResolverTest.java
deleted file mode 100644
index 01c6ecf..0000000
--- a/harness/src/test/java/com/android/csuite/config/AppRemoteFileResolverTest.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.config;
-
-import static com.android.csuite.testing.Correspondences.instanceOf;
-import static com.android.csuite.testing.MoreAsserts.assertThrows;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.android.tradefed.build.BuildRetrievalError;
-import com.android.tradefed.config.ConfigurationException;
-import com.android.tradefed.config.Option;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.config.remote.IRemoteFileResolver;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.testing.NullPointerTester;
-
-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.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ServiceLoader;
-import java.util.jar.JarEntry;
-import java.util.jar.JarOutputStream;
-
-@RunWith(JUnit4.class)
-public final class AppRemoteFileResolverTest {
-
- private static final String PACKAGE_NAME = "com.example.app";
- private static final File APP_URI_FILE = uriToFile("app://" + PACKAGE_NAME);
- private static final ImmutableMap<String, String> EMPTY_PARAMS = ImmutableMap.of();
-
- @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- // Class sanity tests.
-
- @Test
- public void isServiceLoadable() throws Exception {
- ClassLoader classLoader = classLoaderWithProviders(AppRemoteFileResolver.class.getName());
-
- ServiceLoader<IRemoteFileResolver> serviceLoader =
- ServiceLoader.load(IRemoteFileResolver.class, classLoader);
-
- // Copy the list to provide better failure error messages since ServiceLoader's string
- // representation is not very informative.
- assertThat(ImmutableList.copyOf(serviceLoader))
- .comparingElementsUsing(instanceOf())
- .contains(AppRemoteFileResolver.class);
- }
-
- @Test
- public void nullPointers() {
- NullPointerTester tester = new NullPointerTester();
- tester.setDefault(File.class, APP_URI_FILE);
- tester.testAllPublicConstructors(AppRemoteFileResolver.class);
- tester.testAllPublicInstanceMethods(new AppRemoteFileResolver());
- }
-
- // URI validation tests.
-
- @Test
- public void unsupportedUriScheme_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithAnyTemplate();
- String uri = "gs://" + PACKAGE_NAME;
- File f = uriToFile(uri);
-
- Throwable thrown =
- assertThrows(
- IllegalArgumentException.class,
- () -> resolver.resolveRemoteFiles(f, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains("(gs)");
- assertThat(thrown).hasMessageThat().contains(uri);
- }
-
- @Test
- public void opaqueUri_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithAnyTemplate();
- File uri = uriToFile("app:" + PACKAGE_NAME);
-
- Throwable thrown =
- assertThrows(
- IllegalArgumentException.class,
- () -> resolver.resolveRemoteFiles(uri, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains("package name");
- }
-
- @Test
- public void uriHasPathComponent_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithAnyTemplate();
- File uri = uriToFile("app://" + PACKAGE_NAME + "/invalid");
-
- Throwable thrown =
- assertThrows(
- IllegalArgumentException.class,
- () -> resolver.resolveRemoteFiles(uri, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains("invalid");
- }
-
- // Template validation and expansion tests.
-
- @Test
- public void templateNotSet_returnsNull() throws Exception {
- AppRemoteFileResolver resolver = new AppRemoteFileResolver();
-
- File actual = resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS);
-
- assertThat(actual).isNull();
- }
-
- @Test
- public void emptyTemplate_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithTemplate("");
-
- Throwable thrown =
- assertThrows(
- IllegalStateException.class,
- () -> resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains(AppRemoteFileResolver.URI_TEMPLATE_OPTION);
- }
-
- @Test
- public void templateHasNoPlaceholders_returnsFileWithoutExpansion() throws Exception {
- File expected = temporaryFolder.newFolder();
- AppRemoteFileResolver resolver = newResolverWithTemplate(expected.toURI().toString());
-
- File actual = resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void templateContainsPlaceholderForUndefinedVar_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithTemplate("file://{undefined}");
-
- Throwable thrown =
- assertThrows(
- IllegalStateException.class,
- () -> resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains("undefined");
- }
-
- @Test
- public void templateExpandsToInvalidUri_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithTemplate("file:\\{package}");
-
- Throwable thrown =
- assertThrows(
- IllegalStateException.class,
- () -> resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains(AppRemoteFileResolver.URI_TEMPLATE_OPTION);
- }
-
- @Test
- public void templateContainsPlaceholder_resolvesUriToFile() throws Exception {
- File parent = temporaryFolder.newFolder();
- File expected = new File(parent, PACKAGE_NAME);
- String template = new File(parent, "{package}").toString();
- AppRemoteFileResolver resolver = newResolverWithTemplate(template);
- File uri = uriToFile("app://" + PACKAGE_NAME);
-
- File actual = resolver.resolveRemoteFiles(uri, EMPTY_PARAMS);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- @Test
- public void templateExpandsToAppUri_throwsException() throws Exception {
- AppRemoteFileResolver resolver = newResolverWithTemplate("app://{package}");
-
- Throwable thrown =
- assertThrows(
- BuildRetrievalError.class,
- () -> resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS));
-
- assertThat(thrown).hasMessageThat().contains("'app'");
- }
-
- @Test
- public void templateExpandsToUriWithUnsupportedScheme_returnsExpandedUri() throws Exception {
- String uri = "unsupported://" + PACKAGE_NAME;
- AppRemoteFileResolver resolver = newResolverWithTemplate(uri);
- File expected = uriToFile(uri);
-
- File actual = resolver.resolveRemoteFiles(APP_URI_FILE, EMPTY_PARAMS);
-
- assertThat(actual).isEqualTo(expected);
- }
-
- // Utility classes and methods.
-
- /**
- * Constructs a File from a URI string using the same logic TradeFed uses since it's tricky and
- * has some gotchas such as stripping slashes.
- */
- private static File uriToFile(String str) {
- FileOptionSource optionSource = new FileOptionSource();
-
- try {
- OptionSetter setter = new OptionSetter(optionSource);
- setter.setOptionValue(FileOptionSource.OPTION_NAME, str);
- } catch (ConfigurationException e) {
- throw new RuntimeException(e);
- }
-
- return optionSource.file;
- }
-
- private static final class FileOptionSource {
- static final String OPTION_NAME = "file";
-
- @Option(name = OPTION_NAME)
- public File file;
- }
-
- private static AppRemoteFileResolver newResolverWithAnyTemplate()
- throws ConfigurationException {
- return newResolverWithTemplate("file:///tmp/{package}");
- }
-
- private static AppRemoteFileResolver newResolverWithTemplate(String uriTemplate)
- throws ConfigurationException {
- AppRemoteFileResolver resolver = new AppRemoteFileResolver();
- OptionSetter setter = new OptionSetter(resolver);
- setter.setOptionValue("app:" + AppRemoteFileResolver.URI_TEMPLATE_OPTION, uriTemplate);
- return resolver;
- }
-
- private ClassLoader classLoaderWithProviders(String... lines) throws IOException {
- String service = IRemoteFileResolver.class.getName();
- File jar = temporaryFolder.newFile();
-
- try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar))) {
- JarEntry jarEntry = new JarEntry("META-INF/services/" + service);
-
- out.putNextEntry(jarEntry);
- PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, UTF_8));
-
- for (String line : lines) {
- writer.println(line);
- }
-
- writer.flush();
- }
-
- return new URLClassLoader(new URL[] {jar.toURI().toURL()});
- }
-}
diff --git a/harness/src/test/java/com/android/csuite/config/ModuleGeneratorTest.java b/harness/src/test/java/com/android/csuite/config/ModuleGeneratorTest.java
deleted file mode 100644
index a679828..0000000
--- a/harness/src/test/java/com/android/csuite/config/ModuleGeneratorTest.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.config;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import com.android.csuite.core.PackageNameProvider;
-import com.android.tradefed.build.BuildInfo;
-import com.android.tradefed.config.Configuration;
-import com.android.tradefed.config.IConfiguration;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.invoker.IInvocationContext;
-import com.android.tradefed.invoker.InvocationContext;
-import com.android.tradefed.invoker.TestInformation;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-import com.google.common.jimfs.Jimfs;
-import com.google.common.truth.IterableSubject;
-import com.google.common.truth.StringSubject;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-@RunWith(JUnit4.class)
-public final class ModuleGeneratorTest {
- private static final String TEST_PACKAGE_NAME1 = "test.package.name1";
- private static final String TEST_PACKAGE_NAME2 = "test.package.name2";
- private static final String PACKAGE_PLACEHOLDER = "{package}";
- private static final Exception NO_EXCEPTION = null;
-
- private final FileSystem mFileSystem =
- Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix());
-
- @Test
- public void tearDown_packageNamesProvided_deletesGeneratedModules() throws Exception {
- Path testsDir = createTestsDir();
- ModuleGenerator generator1 =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackage(TEST_PACKAGE_NAME1)
- .addPackage(TEST_PACKAGE_NAME2)
- .build();
- generator1.split();
- ModuleGenerator generator2 =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackage(TEST_PACKAGE_NAME1)
- .addPackage(TEST_PACKAGE_NAME2)
- .build();
-
- generator2.tearDown(createTestInfo(), NO_EXCEPTION);
-
- assertThatListDirectory(testsDir).isEmpty();
- }
-
- @Test
- public void tearDown_packageNamesNotProvided_doesNotThrowError() throws Exception {
- ModuleGenerator generator = createGeneratorBuilder().setTestsDir(createTestsDir()).build();
- generator.split();
-
- generator.tearDown(createTestInfo(), NO_EXCEPTION);
- }
-
- @Test
- public void split_packageNameIsEmptyString_throwsError() throws Exception {
- ModuleGenerator generator = createGeneratorBuilder().addPackage("").build();
-
- assertThrows(IllegalArgumentException.class, () -> generator.split());
- }
-
- @Test
- public void split_packageNameContainsPlaceholder_throwsError() throws Exception {
- ModuleGenerator generator =
- createGeneratorBuilder().addPackage("a" + PACKAGE_PLACEHOLDER + "b").build();
-
- assertThrows(IllegalArgumentException.class, () -> generator.split());
- }
-
- @Test
- public void split_multiplePackageNameProviders_generateModulesForAll() throws Exception {
- Path testsDir = createTestsDir();
- ModuleGenerator generator =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackageNameProvider(() -> ImmutableSet.of(TEST_PACKAGE_NAME1))
- // Simulates package providers providing duplicated package names.
- .addPackageNameProvider(() -> ImmutableSet.of(TEST_PACKAGE_NAME1))
- .addPackageNameProvider(() -> ImmutableSet.of(TEST_PACKAGE_NAME2))
- .build();
-
- generator.split();
-
- assertThatListDirectory(testsDir)
- .containsExactly(
- getModuleConfigFile(testsDir, TEST_PACKAGE_NAME1),
- getModuleConfigFile(testsDir, TEST_PACKAGE_NAME2));
- }
-
- @Test
- public void split_packageNameProviderThrowsException_throwsException() throws Exception {
- Path testsDir = createTestsDir();
- ModuleGenerator generator =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackageNameProvider(
- () -> {
- throw new IOException();
- })
- .build();
-
- assertThrows(UncheckedIOException.class, () -> generator.split());
- }
-
- @Test
- public void split_packageNamesNotProvided_doesNotGenerate() throws Exception {
- Path testsDir = createTestsDir();
- ModuleGenerator generator = createGeneratorBuilder().setTestsDir(testsDir).build();
-
- generator.split();
-
- assertThatListDirectory(testsDir).isEmpty();
- }
-
- @Test
- public void split_templateContainsPlaceholders_replacesPlaceholdersInOutput() throws Exception {
- Path testsDir = createTestsDir();
- String content = "hello placeholder%s%s world";
- ModuleGenerator generator =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackage(TEST_PACKAGE_NAME1)
- .addPackage(TEST_PACKAGE_NAME2)
- .setTemplateContent(
- String.format(content, PACKAGE_PLACEHOLDER, PACKAGE_PLACEHOLDER))
- .build();
-
- generator.split();
-
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME1)
- .isEqualTo(String.format(content, TEST_PACKAGE_NAME1, TEST_PACKAGE_NAME1));
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME2)
- .isEqualTo(String.format(content, TEST_PACKAGE_NAME2, TEST_PACKAGE_NAME2));
- }
-
- @Test
- public void split_templateDoesNotContainPlaceholder_outputsTemplateContent() throws Exception {
- Path testsDir = createTestsDir();
- String content = "no placeholder";
- ModuleGenerator generator =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackage(TEST_PACKAGE_NAME1)
- .addPackage(TEST_PACKAGE_NAME2)
- .setTemplateContent(content)
- .build();
-
- generator.split();
-
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME1).isEqualTo(content);
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME2).isEqualTo(content);
- }
-
- @Test
- public void split_templateContentIsEmpty_outputsTemplateContent() throws Exception {
- Path testsDir = createTestsDir();
- String content = "";
- ModuleGenerator generator =
- createGeneratorBuilder()
- .setTestsDir(testsDir)
- .addPackage(TEST_PACKAGE_NAME1)
- .addPackage(TEST_PACKAGE_NAME2)
- .setTemplateContent(content)
- .build();
-
- generator.split();
-
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME1).isEqualTo(content);
- assertThatModuleConfigFileContent(testsDir, TEST_PACKAGE_NAME2).isEqualTo(content);
- }
-
- private static StringSubject assertThatModuleConfigFileContent(
- Path testsDir, String packageName) throws IOException {
- return assertThat(
- new String(Files.readAllBytes(getModuleConfigFile(testsDir, packageName))));
- }
-
- private static IterableSubject assertThatListDirectory(Path dir) throws IOException {
- // Convert stream to list because com.google.common.truth.Truth8 is not available.
- return assertThat(
- Files.walk(dir)
- .filter(p -> !p.equals(dir))
- .collect(ImmutableList.toImmutableList()));
- }
-
- private static Path getModuleConfigFile(Path baseDir, String packageName) {
- return baseDir.resolve(packageName + ".config");
- }
-
- private Path createTestsDir() throws IOException {
- Path rootPath = mFileSystem.getPath("csuite");
- Files.createDirectories(rootPath);
- return Files.createTempDirectory(rootPath, "testDir");
- }
-
- private static TestInformation createTestInfo() {
- IInvocationContext context = new InvocationContext();
- context.addAllocatedDevice("device1", Mockito.mock(ITestDevice.class));
- context.addDeviceBuildInfo("device1", new BuildInfo());
- return TestInformation.newBuilder().setInvocationContext(context).build();
- }
-
- private GeneratorBuilder createGeneratorBuilder() throws IOException {
- return new GeneratorBuilder()
- .setFileSystem(mFileSystem)
- .setTemplateContent(MODULE_TEMPLATE_CONTENT)
- .setOption(ModuleGenerator.OPTION_TEMPLATE, "empty_path");
- }
-
- private static final class GeneratorBuilder {
- private final ListMultimap<String, String> mOptions = ArrayListMultimap.create();
- private final Set<String> mPackages = new HashSet<>();
- private final List<PackageNameProvider> mPackageNameProviders = new ArrayList<>();
- private Path mTestsDir;
- private String mTemplateContent;
- private FileSystem mFileSystem;
-
- GeneratorBuilder addPackage(String packageName) {
- mPackages.add(packageName);
- return this;
- }
-
- GeneratorBuilder addPackageNameProvider(PackageNameProvider packageNameProvider) {
- mPackageNameProviders.add(packageNameProvider);
- return this;
- }
-
- GeneratorBuilder setFileSystem(FileSystem fileSystem) {
- mFileSystem = fileSystem;
- return this;
- }
-
- GeneratorBuilder setTemplateContent(String templateContent) {
- mTemplateContent = templateContent;
- return this;
- }
-
- GeneratorBuilder setTestsDir(Path testsDir) {
- mTestsDir = testsDir;
- return this;
- }
-
- GeneratorBuilder setOption(String key, String value) {
- mOptions.put(key, value);
- return this;
- }
-
- ModuleGenerator build() throws Exception {
- ModuleGenerator generator =
- new ModuleGenerator(
- mFileSystem, buildInfo -> mTestsDir, resourcePath -> mTemplateContent);
-
- OptionSetter optionSetter = new OptionSetter(generator);
- for (Map.Entry<String, String> entry : mOptions.entries()) {
- optionSetter.setOptionValue(entry.getKey(), entry.getValue());
- }
-
- List<PackageNameProvider> packageNameProviders = new ArrayList<>(mPackageNameProviders);
- packageNameProviders.add(() -> mPackages);
-
- IConfiguration configuration = new Configuration("name", "description");
- configuration.setConfigurationObjectList(
- ModuleGenerator.PACKAGE_NAME_PROVIDER, packageNameProviders);
- generator.setConfiguration(configuration);
-
- return generator;
- }
- }
-
- private static final String MODULE_TEMPLATE_CONTENT =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
- + "<configuration description=\"description\">\n"
- + " <option name=\"package-name\" value=\"{package}\"/>\n"
- + " <target_generator class=\"some.generator.class\">\n"
- + " <option name=\"test-file-name\" value=\"app://{package}\"/>\n"
- + " </target_generator>\n"
- + " <test class=\"some.test.class\"/>\n"
- + "</configuration>";
-}
diff --git a/harness/src/test/java/com/android/csuite/core/CommandLinePackageNameProviderTest.java b/harness/src/test/java/com/android/csuite/core/CommandLinePackageNameProviderTest.java
deleted file mode 100644
index 41e6dc1..0000000
--- a/harness/src/test/java/com/android/csuite/core/CommandLinePackageNameProviderTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 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.csuite.core;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.tradefed.config.OptionSetter;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.util.Set;
-
-@RunWith(JUnit4.class)
-public final class CommandLinePackageNameProviderTest {
-
- @Test
- public void get_packageNamesProvided_returnsPackageNames() throws Exception {
- CommandLinePackageNameProvider provider = new CommandLinePackageNameProvider();
- String package1 = "package.name1";
- String package2 = "package.name2";
- OptionSetter optionSetter = new OptionSetter(provider);
- optionSetter.setOptionValue(CommandLinePackageNameProvider.PACKAGE, package1);
- optionSetter.setOptionValue(CommandLinePackageNameProvider.PACKAGE, package2);
-
- Set<String> packageNames = provider.get();
-
- assertThat(packageNames).containsExactly(package1, package2);
- }
-}
diff --git a/harness/src/test/java/com/android/csuite/core/FileBasedPackageNameProviderTest.java b/harness/src/test/java/com/android/csuite/core/FileBasedPackageNameProviderTest.java
deleted file mode 100644
index ee429b2..0000000
--- a/harness/src/test/java/com/android/csuite/core/FileBasedPackageNameProviderTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.core;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.tradefed.config.ConfigurationException;
-import com.android.tradefed.config.OptionSetter;
-
-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.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Set;
-
-@RunWith(JUnit4.class)
-public final class FileBasedPackageNameProviderTest {
- private static final String TEST_PACKAGE_NAME1 = "test.package.name1";
- private static final String TEST_PACKAGE_NAME2 = "test.package.name2";
- private static final String PACKAGE_PLACEHOLDER = "{package}";
- private static final Exception NO_EXCEPTION = null;
-
- @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
-
- @Test
- public void get_fileNotSpecified_returnsEmptySet() throws Exception {
- FileBasedPackageNameProvider provider = createProvider();
-
- Set<String> packageNames = provider.get();
-
- assertThat(packageNames).isEmpty();
- }
-
- @Test
- public void get_multipleFileSpecified_returnsAllEntries() throws Exception {
- String packageName1 = "a";
- String packageName2 = "b";
- String packageName3 = "c";
- String packageName4 = "d";
- FileBasedPackageNameProvider provider =
- createProvider(
- createPackagesFile(packageName1 + "\n" + packageName2),
- createPackagesFile(packageName3 + "\n" + packageName4));
-
- Set<String> packageNames = provider.get();
-
- assertThat(packageNames)
- .containsExactly(packageName1, packageName2, packageName3, packageName4);
- }
-
- @Test
- public void get_fileContainsEmptyLines_ignoresEmptyLines() throws Exception {
- String packageName1 = "a";
- String packageName2 = "b";
- FileBasedPackageNameProvider provider =
- createProvider(createPackagesFile(packageName1 + "\n \n\n" + packageName2));
-
- Set<String> packageNames = provider.get();
-
- assertThat(packageNames).containsExactly(packageName1, packageName2);
- }
-
- @Test
- public void get_fileContainsCommentLines_ignoresCommentLines() throws Exception {
- String packageName1 = "a";
- String packageName2 = "b";
- FileBasedPackageNameProvider provider =
- createProvider(
- createPackagesFile(
- packageName1
- + "\n"
- + FileBasedPackageNameProvider.COMMENT_LINE_PREFIX
- + " Some comments\n"
- + packageName2));
-
- Set<String> packageNames = provider.get();
-
- assertThat(packageNames).containsExactly(packageName1, packageName2);
- }
-
- private FileBasedPackageNameProvider createProvider(Path... packagesFiles)
- throws IOException, ConfigurationException {
- FileBasedPackageNameProvider provider = new FileBasedPackageNameProvider();
- OptionSetter optionSetter = new OptionSetter(provider);
- for (Path packagesFile : packagesFiles) {
- optionSetter.setOptionValue(
- FileBasedPackageNameProvider.PACKAGES_FILE, packagesFile.toString());
- }
- return provider;
- }
-
- private Path createPackagesFile(String content) throws IOException {
- Path tempFile = Files.createTempFile(tempFolder.getRoot().toPath(), "packages", ".txt");
- Files.write(tempFile, content.getBytes());
- return tempFile;
- }
-}
diff --git a/harness/src/test/java/com/android/csuite/core/SystemAppUninstallerTest.java b/harness/src/test/java/com/android/csuite/core/SystemAppUninstallerTest.java
deleted file mode 100644
index 30b77db..0000000
--- a/harness/src/test/java/com/android/csuite/core/SystemAppUninstallerTest.java
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.core;
-
-import static org.testng.Assert.assertThrows;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mockito;
-
-@RunWith(JUnit4.class)
-public final class SystemAppUninstallerTest {
- private static final ITestDevice NULL_DEVICE = null;
- private static final String TEST_PACKAGE_NAME = "test.package.name";
- private static final String SYSTEM_APP_INSTALL_DIRECTORY = "/system/app";
- private static final String CHECK_PACKAGE_INSTALLED_COMMAND_PREFIX = "pm list packages ";
- private static final String GET_PACKAGE_INSTALL_PATH_COMMAND_PREFIX = "pm path ";
- private static final String REMOVE_SYSTEM_APP_COMMAND_PREFIX =
- "rm -r " + SYSTEM_APP_INSTALL_DIRECTORY;
- private static final String REMOVE_APP_DATA_COMMAND_PREFIX = "rm -r /data/data";
- private static final String MOUNT_COMMAND_PREFIX = "mount";
-
- @Test
- public void uninstallPackage_packageNameIsNull_throws() throws Exception {
- assertThrows(
- NullPointerException.class,
- () ->
- SystemPackageUninstaller.uninstallPackage(
- null, createGoodDeviceWithAppNotInstalled()));
- }
-
- @Test
- public void uninstallPackage_frameworkNotRunning_startsFrameworkOrThrows() throws Exception {
- ITestDevice device = createGoodDeviceWithAppNotInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.eq(SystemPackageUninstaller.PM_CHECK_COMMAND)))
- .thenReturn(createFailedCommandResult());
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- Mockito.verify(device, Mockito.times(1)).executeShellV2Command(Mockito.eq("start"));
- }
-
- @Test
- public void uninstallPackage_packageIsNotInstalled_doesNotRemove() throws Exception {
- ITestDevice device = createGoodDeviceWithAppNotInstalled();
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(0)).executeShellV2Command(Mockito.startsWith("rm"));
- }
-
- @Test
- public void uninstallPackage_differentPackageWithSameNamePrefixInstalled_doesNotRemove()
- throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- // Mock the device as if the test package does not exist on device
- CommandResult commandResult = createSuccessfulCommandResult();
- commandResult.setStdout(String.format("package:%s_some_more_chars", TEST_PACKAGE_NAME));
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- CHECK_PACKAGE_INSTALLED_COMMAND_PREFIX)))
- .thenReturn(commandResult);
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(0)).executeShellV2Command(Mockito.startsWith("rm"));
- }
-
- @Test
- public void uninstallPackage_checkPackageInstalledCommandFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- CHECK_PACKAGE_INSTALLED_COMMAND_PREFIX)))
- .thenReturn(createFailedCommandResult());
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_getInstallDirectoryCommandFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- GET_PACKAGE_INSTALL_PATH_COMMAND_PREFIX)))
- .thenReturn(createFailedCommandResult());
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_packageIsNotSystemApp_doesNotRemove() throws Exception {
- ITestDevice device = createGoodDeviceWithUserAppInstalled();
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(0)).executeShellV2Command(Mockito.startsWith("rm"));
- }
-
- @Test
- public void uninstallPackage_adbAlreadyRooted_doesNotRootAgain() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.isAdbRoot()).thenReturn(true);
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(0)).enableAdbRoot();
- }
-
- @Test
- public void uninstallPackage_adbNotAlreadyRooted_rootAdbAndThenUnroot() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.isAdbRoot()).thenReturn(false);
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(1)).enableAdbRoot();
- Mockito.verify(device, Mockito.times(1)).disableAdbRoot();
- }
-
- @Test
- public void uninstallPackage_adbRootCommandFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.enableAdbRoot()).thenThrow(new DeviceNotAvailableException());
-
- assertThrows(
- DeviceNotAvailableException.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_adbRootFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.enableAdbRoot()).thenReturn(false);
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_adbDisableRootCommandFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.disableAdbRoot()).thenThrow(new DeviceNotAvailableException());
-
- assertThrows(
- DeviceNotAvailableException.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_adbDisableRootFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(device.disableAdbRoot()).thenReturn(false);
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_adbRemountFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.doThrow(new DeviceNotAvailableException()).when(device).remountSystemWritable();
-
- assertThrows(
- DeviceNotAvailableException.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_adbRemounted_mountReadOnlyAfterwards() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.doNothing().when(device).remountSystemWritable();
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(1)).remountSystemWritable();
- Mockito.verify(device, Mockito.times(1))
- .executeShellV2Command(Mockito.startsWith(MOUNT_COMMAND_PREFIX));
- }
-
- @Test
- public void uninstallPackage_mountReadOnlyFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(MOUNT_COMMAND_PREFIX)))
- .thenReturn(createFailedCommandResult());
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_removePackageInstallDirectoryFailed_throws() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(REMOVE_SYSTEM_APP_COMMAND_PREFIX)))
- .thenReturn(createFailedCommandResult());
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- @Test
- public void uninstallPackage_removePackageDataDirectoryFailed_doesNotThrow() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(REMOVE_APP_DATA_COMMAND_PREFIX)))
- .thenReturn(createFailedCommandResult());
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
- }
-
- @Test
- public void uninstallPackage_packageIsSystemApp_appRemoved() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(1))
- .executeShellV2Command(Mockito.startsWith(REMOVE_SYSTEM_APP_COMMAND_PREFIX));
- Mockito.verify(device, Mockito.times(1))
- .executeShellV2Command(Mockito.startsWith(REMOVE_APP_DATA_COMMAND_PREFIX));
- }
-
- @Test
- public void uninstallPackage_noUpdatePackagePresent_appRemoved() throws Exception {
- int numberOfUpdates = 0;
- ITestDevice device = createGoodDeviceWithSystemAppInstalled(numberOfUpdates);
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(numberOfUpdates + 1))
- .uninstallPackage(TEST_PACKAGE_NAME);
- Mockito.verify(device, Mockito.times(1))
- .executeShellV2Command(Mockito.startsWith(REMOVE_SYSTEM_APP_COMMAND_PREFIX));
- }
-
- @Test
- public void uninstallPackage_someUpdatePackagesPresent_appRemoved() throws Exception {
- int numberOfUpdates = 2;
- ITestDevice device = createGoodDeviceWithSystemAppInstalled(numberOfUpdates);
-
- SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device);
-
- Mockito.verify(device, Mockito.times(numberOfUpdates + 1))
- .uninstallPackage(TEST_PACKAGE_NAME);
- Mockito.verify(device, Mockito.times(1))
- .executeShellV2Command(Mockito.startsWith(REMOVE_SYSTEM_APP_COMMAND_PREFIX));
- }
-
- @Test
- public void uninstallPackage_tooManyUpdatePackagesPresent_throwsException() throws Exception {
- ITestDevice device =
- createGoodDeviceWithSystemAppInstalled(
- SystemPackageUninstaller.MAX_NUMBER_OF_UPDATES + 1);
-
- assertThrows(
- TargetSetupError.class,
- () -> SystemPackageUninstaller.uninstallPackage(TEST_PACKAGE_NAME, device));
- }
-
- private ITestDevice createGoodDeviceWithUserAppInstalled() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
-
- CommandResult commandResult = createSuccessfulCommandResult();
- commandResult.setStdout(
- String.format("package:/data/app/%s/%s.apk", TEST_PACKAGE_NAME, TEST_PACKAGE_NAME));
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- GET_PACKAGE_INSTALL_PATH_COMMAND_PREFIX)))
- .thenReturn(commandResult);
-
- return device;
- }
-
- private ITestDevice createGoodDeviceWithAppNotInstalled() throws Exception {
- ITestDevice device = createGoodDeviceWithSystemAppInstalled();
- CommandResult commandResult = createSuccessfulCommandResult();
- commandResult.setStdout("");
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- CHECK_PACKAGE_INSTALLED_COMMAND_PREFIX)))
- .thenReturn(commandResult);
- return device;
- }
-
- private ITestDevice createGoodDeviceWithSystemAppInstalled() throws Exception {
- return createGoodDeviceWithSystemAppInstalled(1);
- }
-
- private ITestDevice createGoodDeviceWithSystemAppInstalled(int numberOfUpdatesInstalled)
- throws Exception {
- ITestDevice device = Mockito.mock(ITestDevice.class);
- CommandResult commandResult;
-
- // Is framework running
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.eq(SystemPackageUninstaller.PM_CHECK_COMMAND)))
- .thenReturn(createSuccessfulCommandResult());
-
- // Uninstall updates
- String uninstallFailureMessage = "Failure [DELETE_FAILED_INTERNAL_ERROR]";
- if (numberOfUpdatesInstalled == 0) {
- Mockito.when(device.uninstallPackage(TEST_PACKAGE_NAME))
- .thenReturn(uninstallFailureMessage);
- } else {
- String[] uninstallResults = new String[numberOfUpdatesInstalled];
- uninstallResults[numberOfUpdatesInstalled - 1] = uninstallFailureMessage;
- Mockito.when(device.uninstallPackage(TEST_PACKAGE_NAME))
- .thenReturn(null, uninstallResults);
- }
-
- // List package
- commandResult = createSuccessfulCommandResult();
- commandResult.setStdout("package:" + TEST_PACKAGE_NAME);
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- CHECK_PACKAGE_INSTALLED_COMMAND_PREFIX)))
- .thenReturn(commandResult);
-
- // Get package path
- commandResult = createSuccessfulCommandResult();
- commandResult.setStdout(
- String.format(
- "package:%s/%s/%s.apk",
- SYSTEM_APP_INSTALL_DIRECTORY, TEST_PACKAGE_NAME, TEST_PACKAGE_NAME));
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(
- GET_PACKAGE_INSTALL_PATH_COMMAND_PREFIX)))
- .thenReturn(commandResult);
-
- // Adb root
- Mockito.when(device.isAdbRoot()).thenReturn(false);
- Mockito.when(device.enableAdbRoot()).thenReturn(true);
-
- // Adb remount
- Mockito.doNothing().when(device).remountSystemWritable();
-
- // Remove package install directory
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(REMOVE_SYSTEM_APP_COMMAND_PREFIX)))
- .thenReturn(createSuccessfulCommandResult());
-
- // Remove package data directory
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(REMOVE_APP_DATA_COMMAND_PREFIX)))
- .thenReturn(createSuccessfulCommandResult());
-
- // Restart framework
- Mockito.when(device.executeShellV2Command(ArgumentMatchers.eq("start")))
- .thenReturn(createSuccessfulCommandResult());
- Mockito.when(device.executeShellV2Command(ArgumentMatchers.eq("stop")))
- .thenReturn(createSuccessfulCommandResult());
-
- // Disable adb root
- Mockito.when(device.disableAdbRoot()).thenReturn(true);
-
- // Remount read only
- Mockito.when(
- device.executeShellV2Command(
- ArgumentMatchers.startsWith(MOUNT_COMMAND_PREFIX)))
- .thenReturn(createSuccessfulCommandResult());
-
- return device;
- }
-
- private static CommandResult createSuccessfulCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.SUCCESS);
- commandResult.setExitCode(0);
- return commandResult;
- }
-
- private static CommandResult createFailedCommandResult() {
- CommandResult commandResult = new CommandResult(CommandStatus.FAILED);
- commandResult.setExitCode(1);
- return commandResult;
- }
-}
diff --git a/harness/src/test/java/com/android/csuite/testing/Correspondences.java b/harness/src/test/java/com/android/csuite/testing/Correspondences.java
deleted file mode 100644
index c388e23..0000000
--- a/harness/src/test/java/com/android/csuite/testing/Correspondences.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.testing;
-
-import com.google.common.truth.Correspondence;
-
-/**
- * Useful implementations of {@link Correspondence}.
- *
- * <p>Correspondences are used with {@code IterableSubject.comparingElementsUsing(Correspondence)}
- * and allow for making assertions on elements besides simple equality. See {@link Correspondence}
- * for more information.
- */
-// TODO(hzalek): Move this into a dedicated testing library.
-public final class Correspondences {
-
- private static final Correspondence<Object, Class<?>> INSTANCE_OF =
- Correspondence.from(
- (Object obj, Class<?> clazz) -> {
- return clazz.isInstance(obj);
- },
- "is an instance of");
-
- /**
- * Returns a {@link Correspondence} that determines whether elements are instances of the class
- * they are compared to.
- */
- public static Correspondence<Object, Class<?>> instanceOf() {
- return INSTANCE_OF;
- }
-
- private Correspondences() {}
-}
diff --git a/harness/src/test/java/com/android/csuite/testing/CorrespondencesTest.java b/harness/src/test/java/com/android/csuite/testing/CorrespondencesTest.java
deleted file mode 100644
index 5a4dbcf..0000000
--- a/harness/src/test/java/com/android/csuite/testing/CorrespondencesTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.testing;
-
-import static com.android.csuite.testing.Correspondences.instanceOf;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class CorrespondencesTest {
-
- @Test(expected = AssertionError.class)
- public void instanceOf_comparedToDifferentClass_fails() {
- assertThat(stringList()).comparingElementsUsing(instanceOf()).contains(Integer.class);
- }
-
- @Test
- public void instanceOf_comparedToSuperType_succeeds() {
- assertThat(stringList()).comparingElementsUsing(instanceOf()).contains(Object.class);
- }
-
- @Test
- public void instanceOf_comparedToSameClass_succeeds() {
- assertThat(stringList()).comparingElementsUsing(instanceOf()).contains(String.class);
- }
-
- private static ImmutableList<String> stringList() {
- return ImmutableList.of("A", "B", "C");
- }
-}
diff --git a/harness/src/test/java/com/android/csuite/testing/MoreAsserts.java b/harness/src/test/java/com/android/csuite/testing/MoreAsserts.java
deleted file mode 100644
index 306dad4..0000000
--- a/harness/src/test/java/com/android/csuite/testing/MoreAsserts.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.testing;
-
-import static com.google.common.truth.Truth.assertThat;
-
-/** An additional set of assertion methods useful for writing tests. */
-// TODO(hzalek): Move this into a dedicated testing library.
-public final class MoreAsserts {
-
- /**
- * Facilitates the use of assertThrows from Java 8 by allowing method references to void methods
- * that declare checked exceptions and is not meant to be directly implemented.
- */
- public interface ThrowingRunnable {
- void run() throws Exception;
- }
-
- /**
- * Asserts that {@code runnable} throws an exception of type {@code expectedThrowable} when
- * executed. If it does, the exception object is returned. Otherwise, if it does not throw an
- * exception or does but of the unexpected type, an {@link AssertionError} is thrown.
- *
- * <p>This is mostly intended as a drop-in replacement of assertThrows that is only available in
- * JUnit 4.13 and later.
- *
- * @param message the identifying message for the {@link AssertionError}
- * @param expectedThrowable the expected type of the exception
- * @param runnable a function that is expected to throw an exception when executed
- * @return the exception thrown by {@code runnable}
- */
- public static <T extends Throwable> T assertThrows(
- Class<T> expectedClass, ThrowingRunnable runnable) {
- try {
- runnable.run();
- } catch (Throwable e) {
- assertThat(e).isInstanceOf(expectedClass);
- return expectedClass.cast(e);
- }
- throw new AssertionError(
- "Did not throw any when expected instance of: " + expectedClass.getName());
- }
-
- private MoreAsserts() {}
-}
diff --git a/harness/src/test/java/com/android/csuite/testing/MoreAssertsTest.java b/harness/src/test/java/com/android/csuite/testing/MoreAssertsTest.java
deleted file mode 100644
index e0a968f..0000000
--- a/harness/src/test/java/com/android/csuite/testing/MoreAssertsTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2020 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.csuite.testing;
-
-import static com.android.csuite.testing.MoreAsserts.assertThrows;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class MoreAssertsTest {
-
- @Test(expected = AssertionError.class)
- public void assertThrows_noExceptionThrown_fails() {
- assertThrows(
- Throwable.class,
- () -> {
- /* This comment works around a presubmit hook warning that complains about a
- * missing whitespace after the '{' which if added results in another warning
- * about the file being badly formatted. */
- });
- }
-
- @Test(expected = AssertionError.class)
- public void assertThrows_differentExceptionTypeThrown_fails() {
- assertThrows(
- IllegalArgumentException.class,
- () -> {
- throw new IllegalStateException();
- });
- }
-
- @Test(expected = AssertionError.class)
- public void assertThrows_superTypeOfExpectedExceptionTypeThrown_fails() {
- assertThrows(
- IllegalArgumentException.class,
- () -> {
- throw new RuntimeException();
- });
- }
-
- @Test
- public void assertThrows_expectedExceptionTypeThrown_returnsSameObject() {
- IllegalArgumentException expected = new IllegalArgumentException();
-
- IllegalArgumentException actual =
- assertThrows(
- IllegalArgumentException.class,
- () -> {
- throw expected;
- });
-
- assertThat(actual).isSameInstanceAs(expected);
- }
-
- @Test
- public void assertThrows_subTypeOfExpectedExceptionTypeThrown_returnsSameObject() {
- IllegalArgumentException expected = new IllegalArgumentException();
-
- RuntimeException actual =
- assertThrows(
- RuntimeException.class,
- () -> {
- throw expected;
- });
-
- assertThat(actual).isSameInstanceAs(expected);
- }
-}
diff --git a/instrumentation/launch/Android.bp b/instrumentation/launch/Android.bp
index 1b45c1a..22255f6 100644
--- a/instrumentation/launch/Android.bp
+++ b/instrumentation/launch/Android.bp
@@ -12,18 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_test_helper_app {
+android_test {
name: "csuite-launch-instrumentation",
static_libs: ["androidx.test.rules"],
// Include all test java files.
srcs: ["src/**/*.java"],
- // The value of min sdk version chosen here is the oldest sdk version we
- // have tested. Lower version numbers should also work.
- min_sdk_version: "29",
platform_apis: true,
manifest: "src/main/AndroidManifest.xml",
test_suites: [
diff --git a/instrumentation/launch/src/main/java/com/android/compatibilitytest/AppCompatibility.java b/instrumentation/launch/src/main/java/com/android/compatibilitytest/AppCompatibility.java
index 870076c..5918a62 100644
--- a/instrumentation/launch/src/main/java/com/android/compatibilitytest/AppCompatibility.java
+++ b/instrumentation/launch/src/main/java/com/android/compatibilitytest/AppCompatibility.java
@@ -34,13 +34,10 @@ import android.os.DropBoxManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.Preconditions;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -54,7 +51,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.stream.IntStream;
/** Application Compatibility Test that launches an application and detects crashes. */
@RunWith(AndroidJUnit4.class)
@@ -63,14 +59,15 @@ public final class AppCompatibility {
private static final String TAG = AppCompatibility.class.getSimpleName();
private static final String PACKAGE_TO_LAUNCH = "package_to_launch";
private static final String APP_LAUNCH_TIMEOUT_MSECS = "app_launch_timeout_ms";
- private static final String ARG_DISMISS_DIALOG = "ARG_DISMISS_DIALOG";
+ private static final String WORKSPACE_LAUNCH_TIMEOUT_MSECS = "workspace_launch_timeout_ms";
private static final Set<String> DROPBOX_TAGS = new HashSet<>();
private static final int MAX_CRASH_SNIPPET_LINES = 20;
private static final int MAX_NUM_CRASH_SNIPPET = 3;
- private static final int DELAY_AFTER_KEYEVENT_MILLIS = 500;
// time waiting for app to launch
private int mAppLaunchTimeout = 7000;
+ // time waiting for launcher home screen to show up
+ private int mWorkspaceLaunchTimeout = 2000;
private Context mContext;
private ActivityManager mActivityManager;
@@ -116,6 +113,10 @@ public final class AppCompatibility {
if (appLaunchTimeoutMsecs != null) {
mAppLaunchTimeout = Integer.parseInt(appLaunchTimeoutMsecs);
}
+ String workspaceLaunchTimeoutMsecs = mArgs.getString(WORKSPACE_LAUNCH_TIMEOUT_MSECS);
+ if (workspaceLaunchTimeoutMsecs != null) {
+ mWorkspaceLaunchTimeout = Integer.parseInt(workspaceLaunchTimeoutMsecs);
+ }
mInstrumentation.getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
// set activity controller to suppress crash dialogs and collects them by process name
@@ -140,59 +141,54 @@ public final class AppCompatibility {
@Test
public void testAppStability() throws Exception {
String packageName = mArgs.getString(PACKAGE_TO_LAUNCH);
- Preconditions.checkStringNotEmpty(
- packageName,
- String.format(
- "Missing argument, use %s to specify the package to launch",
- PACKAGE_TO_LAUNCH));
-
- Log.d(TAG, "Launching app " + packageName);
- Intent intent = getLaunchIntentForPackage(packageName);
- if (intent == null) {
- Log.w(TAG, String.format("Skipping %s; no launch intent", packageName));
- return;
- }
- long startTime = System.currentTimeMillis();
- launchActivity(packageName, intent);
-
- if (mArgs.getString(ARG_DISMISS_DIALOG, "false").equals("true")) {
- // Attempt to dismiss any dialogs which some apps display to 'gracefully' handle
- // errors. The dialog prevents the app from crashing thereby hiding issues. The
- // first key event is to select a default button on the error dialog if any while
- // the second event pushes the button.
- IntStream.range(0, 2)
- .forEach(i -> mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER));
- // Give the app process enough time to terminate after dismissing the error.
- Thread.sleep(DELAY_AFTER_KEYEVENT_MILLIS);
- }
-
- checkDropbox(startTime, packageName);
- if (mAppErrors.containsKey(packageName)) {
- StringBuilder message =
- new StringBuilder("Error(s) detected for package: ").append(packageName);
- List<String> errors = mAppErrors.get(packageName);
- for (int i = 0; i < MAX_NUM_CRASH_SNIPPET && i < errors.size(); i++) {
- String err = errors.get(i);
- message.append("\n\n");
- // limit the size of each crash snippet
- message.append(truncate(err, MAX_CRASH_SNIPPET_LINES));
+ if (packageName != null) {
+ Log.d(TAG, "Launching app " + packageName);
+ Intent intent = getLaunchIntentForPackage(packageName);
+ if (intent == null) {
+ Log.w(TAG, String.format("Skipping %s; no launch intent", packageName));
+ return;
}
- if (errors.size() > MAX_NUM_CRASH_SNIPPET) {
- message.append(
- String.format(
- "\n... %d more errors omitted ...",
- errors.size() - MAX_NUM_CRASH_SNIPPET));
+ long startTime = System.currentTimeMillis();
+ launchActivity(packageName, intent);
+ try {
+ checkDropbox(startTime, packageName);
+ if (mAppErrors.containsKey(packageName)) {
+ StringBuilder message =
+ new StringBuilder("Error(s) detected for package: ")
+ .append(packageName);
+ List<String> errors = mAppErrors.get(packageName);
+ for (int i = 0; i < MAX_NUM_CRASH_SNIPPET && i < errors.size(); i++) {
+ String err = errors.get(i);
+ message.append("\n\n");
+ // limit the size of each crash snippet
+ message.append(truncate(err, MAX_CRASH_SNIPPET_LINES));
+ }
+ if (errors.size() > MAX_NUM_CRASH_SNIPPET) {
+ message.append(
+ String.format(
+ "\n... %d more errors omitted ...",
+ errors.size() - MAX_NUM_CRASH_SNIPPET));
+ }
+ Assert.fail(message.toString());
+ }
+ // last check: see if app process is still running
+ Assert.assertTrue(
+ "app package \""
+ + packageName
+ + "\" no longer found in running "
+ + "tasks, but no explicit crashes were detected; check logcat for "
+ + "details",
+ processStillUp(packageName));
+ } finally {
+ returnHome();
}
- Assert.fail(message.toString());
+ } else {
+ Log.d(
+ TAG,
+ "Missing argument, use "
+ + PACKAGE_TO_LAUNCH
+ + " to specify the package to launch");
}
- // last check: see if app process is still running
- Assert.assertTrue(
- "app package \""
- + packageName
- + "\" no longer found in running "
- + "tasks, but no explicit crashes were detected; check logcat for "
- + "details",
- processStillUp(packageName));
}
/**
@@ -247,6 +243,19 @@ public final class AppCompatibility {
}
}
+ private void returnHome() {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Send the "home" intent and wait 2 seconds for us to get there
+ mContext.startActivity(homeIntent);
+ try {
+ Thread.sleep(mWorkspaceLaunchTimeout);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
private Intent getLaunchIntentForPackage(String packageName) {
UiModeManager umm = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
boolean isLeanback = umm.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
diff --git a/integration_tests/Android.bp b/integration_tests/Android.bp
deleted file mode 100644
index 84eaa2e..0000000
--- a/integration_tests/Android.bp
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2020 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.
-
-// The below module creates a standalone zip that end-to-end tests can depend
-// on for running the suite. This is a workaround since we can't use csuite.zip
-// which is defined in an external Makefile that Soong can't depend on.
-//
-// Besides listing jars we know the launcher script depends on which is
-// brittle, this is a hack for several reasons. First, we're listing our
-// dependencies in the tools attribute when we should be using the 'srcs'
-// attribute. Second, we're accessing jars using a path relative to a known
-// artifact location instead of using the Soong 'location' feature.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_genrule_host {
- name: "csuite_standalone_zip",
- cmd: "ANDROID_CSUITE=$(genDir)/android-csuite && " +
- "CSUITE_TOOLS=$${ANDROID_CSUITE}/tools && " +
- "CSUITE_TESTCASES=$${ANDROID_CSUITE}/testcases && " +
- "ANDROID_HOST_OUT=$$(dirname $(location :csuite-tradefed))/.. && " +
- "rm -rf $${CSUITE_TOOLS} && mkdir -p $${CSUITE_TOOLS} && " +
- "rm -rf $${CSUITE_TESTCASES} && mkdir -p $${CSUITE_TESTCASES} && " +
- "cp $(location :csuite-tradefed) $${CSUITE_TOOLS} && " +
- "cp $${ANDROID_HOST_OUT}/framework/csuite-tradefed.jar $${CSUITE_TOOLS} && " +
- "cp $(location :tradefed) $${CSUITE_TOOLS} && " +
- "cp $(location :compatibility-host-util) $${CSUITE_TOOLS} && " +
- // We skip copying the csuite-tradefed-tests jar since its location is
- // not straight-forward to deduce and not really necessary.
- "touch $${CSUITE_TOOLS}/csuite-tradefed-tests.jar && " +
- "cp $(location :csuite_generate_module) $${CSUITE_TOOLS} && " +
- "cp $(location :csuite-launch-instrumentation) $${CSUITE_TESTCASES} && " +
- "chmod a+x $${CSUITE_TOOLS}/csuite-tradefed && " +
- "$(location soong_zip) -o $(out) -d -C $(genDir) -D $${ANDROID_CSUITE}",
- out: ["csuite-standalone.zip"],
- srcs: [
- ":csuite-launch-instrumentation",
- ":tradefed",
- ":compatibility-host-util",
- ],
- tools: [
- "soong_zip",
- ":csuite-tradefed",
- ":csuite_generate_module",
- ],
-}
-
-python_library_host {
- name: "csuite_test_utils",
- srcs: [
- "csuite_test_utils.py",
- ],
- defaults: [
- "csuite_python_defaults",
- ],
- java_data: [
- "csuite_standalone_zip",
- ],
- libs: [
- "csuite_test",
- ],
-}
-
-python_test_host {
- name: "csuite_cli_test",
- srcs: [
- "csuite_cli_test.py",
- ],
- test_config_template: "csuite_test_template.xml",
- test_suites: [
- "general-tests",
- ],
- libs: [
- "csuite_test_utils",
- ],
- defaults: [
- "csuite_python_defaults",
- ],
-}
-
-python_test_host {
- name: "csuite_crash_detection_test",
- srcs: [
- "csuite_crash_detection_test.py",
- ],
- test_config_template: "csuite_test_template.xml",
- test_suites: [
- "general-tests",
- ],
- libs: [
- "csuite_test_utils",
- ],
- data: [
- ":csuite_crash_on_launch_test_app",
- ":csuite_no_crash_test_app",
- ],
- defaults: [
- "csuite_python_defaults",
- ],
- test_options: {
- unit_test: false,
- },
-}
diff --git a/integration_tests/TEST_MAPPING b/integration_tests/TEST_MAPPING
deleted file mode 100644
index 9d98a72..0000000
--- a/integration_tests/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "postsubmit": [
- {
- "name": "csuite_cli_test"
- },
- {
- "name": "csuite_crash_detection_test"
- }
- ]
-}
diff --git a/integration_tests/csuite_cli_test.py b/integration_tests/csuite_cli_test.py
deleted file mode 100644
index 84925e2..0000000
--- a/integration_tests/csuite_cli_test.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Lint as: python3
-#
-# Copyright 2020, 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.
-"""Tests the C-Suite command line interface."""
-
-import csuite_test_utils
-
-
-class CSuiteCliTest(csuite_test_utils.TestCase):
-
- def test_prints_suite_name(self):
- with csuite_test_utils.CSuiteHarness() as harness:
- completed_process = harness.run_and_wait(
- ['run', 'commandAndExit', 'version'])
-
- self.assertEqual(0, completed_process.returncode)
- self.assertIn('App Compatibility Test Suite', completed_process.stdout)
-
-
-if __name__ == '__main__':
- csuite_test_utils.main()
diff --git a/integration_tests/csuite_crash_detection_test.py b/integration_tests/csuite_crash_detection_test.py
deleted file mode 100644
index 9dd8a00..0000000
--- a/integration_tests/csuite_crash_detection_test.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# Lint as: python3
-#
-# Copyright 2020, 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.
-"""Tests C-Suite's crash detection behavior."""
-
-import csuite_test_utils
-
-
-class CrashDetectionTest(csuite_test_utils.TestCase):
-
- def setUp(self):
- super(CrashDetectionTest, self).setUp()
- self.adb = csuite_test_utils.Adb()
- self.repo = csuite_test_utils.PackageRepository()
- self.harness = csuite_test_utils.CSuiteHarness()
-
- def tearDown(self):
- super(CrashDetectionTest, self).tearDown()
- self.harness.cleanup()
- self.repo.cleanup()
-
- def test_no_crash_test_passes(self):
- test_app_package = 'android.csuite.nocrashtestapp'
- self.adb.run(['logcat', '-c'])
-
- completed_process = self.run_test(
- test_app_package=test_app_package,
- test_app_module='csuite_no_crash_test_app')
-
- self.expect_regex(completed_process.stdout, r"""PASSED\s*:\s*1""")
- self.expect_app_launched(test_app_package)
- self.expect_package_not_installed(test_app_package)
-
- def test_crash_on_launch_test_fails(self):
- test_app_package = 'android.csuite.crashonlaunchtestapp'
- self.adb.run(['logcat', '-c'])
-
- completed_process = self.run_test(
- test_app_package=test_app_package,
- test_app_module='csuite_crash_on_launch_test_app')
-
- self.expect_regex(completed_process.stdout, r"""FAILED\s*:\s*1""")
- self.expect_app_launched(test_app_package)
- self.expect_package_not_installed(test_app_package)
-
- def run_test(self, test_app_package, test_app_module):
- """Set up and run the launcher for a given test app."""
-
- # We don't check the return code since adb returns non-zero exit code if
- # the package does not exist.
- self.adb.uninstall(test_app_package, check=False)
- self.assert_package_not_installed(test_app_package)
-
- module_name = self.harness.add_module(test_app_package)
- self.repo.add_package_apks(
- test_app_package, csuite_test_utils.get_test_app_apks(test_app_module))
-
- file_resolver_class = 'com.android.csuite.config.AppRemoteFileResolver'
-
- return self.harness.run_and_wait([
- '--serial',
- csuite_test_utils.get_device_serial(),
- 'run',
- 'commandAndExit',
- 'launch',
- '-m',
- module_name,
- '--enable-module-dynamic-download',
- '--dynamic-download-args',
- '%s:uri-template=file://%s/{package}' %
- (file_resolver_class, self.repo.get_path())
- ])
-
- def expect_regex(self, s, regex):
- with self.subTest():
- self.assertRegex(s, regex)
-
- def assert_package_not_installed(self, package_name):
- self.assertNotIn(package_name, self.adb.list_packages())
-
- def expect_package_not_installed(self, package_name):
- with self.subTest():
- self.assert_package_not_installed(package_name)
-
- def expect_app_launched(self, tag):
- logcat_process = self.adb.run(['logcat', '-d', '-v', 'brief', '-s', tag])
- with self.subTest():
- self.assertIn('App launched', logcat_process.stdout)
-
-
-if __name__ == '__main__':
- csuite_test_utils.main()
diff --git a/integration_tests/csuite_crash_on_launch_test_app/Android.bp b/integration_tests/csuite_crash_on_launch_test_app/Android.bp
deleted file mode 100644
index ec0022b..0000000
--- a/integration_tests/csuite_crash_on_launch_test_app/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2020 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_test_helper_app {
- name: "csuite_crash_on_launch_test_app",
- srcs: [
- "*.java",
- ],
- sdk_version: "test_current",
- min_sdk_version: "29",
- target_sdk_version: "29",
-}
diff --git a/integration_tests/csuite_crash_on_launch_test_app/AndroidManifest.xml b/integration_tests/csuite_crash_on_launch_test_app/AndroidManifest.xml
deleted file mode 100755
index 496aefb..0000000
--- a/integration_tests/csuite_crash_on_launch_test_app/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2020 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package='android.csuite.crashonlaunchtestapp' >
- <application>
- <activity android:name="android.csuite.TestAppActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/integration_tests/csuite_crash_on_launch_test_app/TestAppActivity.java b/integration_tests/csuite_crash_on_launch_test_app/TestAppActivity.java
deleted file mode 100644
index 2838a18..0000000
--- a/integration_tests/csuite_crash_on_launch_test_app/TestAppActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2020 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 android.csuite;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public final class TestAppActivity extends Activity {
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- Log.i(getApplicationContext().getPackageName(), "App launched");
- throw new RuntimeException("Expected exception");
- }
-}
diff --git a/integration_tests/csuite_no_crash_test_app/Android.bp b/integration_tests/csuite_no_crash_test_app/Android.bp
deleted file mode 100644
index e9ee60a..0000000
--- a/integration_tests/csuite_no_crash_test_app/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2020 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_test_helper_app {
- name: "csuite_no_crash_test_app",
- srcs: [
- "*.java",
- ],
- sdk_version: "test_current",
- min_sdk_version: "29",
- target_sdk_version: "29",
-}
diff --git a/integration_tests/csuite_no_crash_test_app/AndroidManifest.xml b/integration_tests/csuite_no_crash_test_app/AndroidManifest.xml
deleted file mode 100755
index bd76721..0000000
--- a/integration_tests/csuite_no_crash_test_app/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2020 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package='android.csuite.nocrashtestapp' >
- <application>
- <activity android:name="android.csuite.TestAppActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/integration_tests/csuite_no_crash_test_app/TestAppActivity.java b/integration_tests/csuite_no_crash_test_app/TestAppActivity.java
deleted file mode 100644
index e61f484..0000000
--- a/integration_tests/csuite_no_crash_test_app/TestAppActivity.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 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 android.csuite;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public final class TestAppActivity extends Activity {
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- Log.i(getApplicationContext().getPackageName(), "App launched");
- }
-}
diff --git a/integration_tests/csuite_test_utils.py b/integration_tests/csuite_test_utils.py
deleted file mode 100644
index e72535f..0000000
--- a/integration_tests/csuite_test_utils.py
+++ /dev/null
@@ -1,283 +0,0 @@
-# Lint as: python3
-#
-# Copyright 2020, 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.
-"""Utilities for C-Suite integration tests."""
-
-import argparse
-import contextlib
-import logging
-import os
-import pathlib
-import shlex
-import shutil
-import stat
-import subprocess
-import sys
-import tempfile
-from typing import Sequence, Text
-import zipfile
-import csuite_test
-
-# Export symbols to reduce the number of imports tests have to list.
-TestCase = csuite_test.TestCase # pylint: disable=invalid-name
-get_device_serial = csuite_test.get_device_serial
-
-# Keep any created temporary directories for debugging test failures. The
-# directories do not need explicit removal since they are created using the
-# system's temporary-file facility.
-_KEEP_TEMP_DIRS = False
-
-
-class CSuiteHarness(contextlib.AbstractContextManager):
- """Interface class for interacting with the C-Suite harness.
-
- WARNING: Explicitly clean up created instances or use as a context manager.
- Not doing so will result in a ResourceWarning for the implicit cleanup which
- confuses the TradeFed Python test output parser.
- """
-
- def __init__(self):
- self._suite_dir = pathlib.Path(tempfile.mkdtemp(prefix='csuite'))
- logging.debug('Created harness directory: %s', self._suite_dir)
-
- with zipfile.ZipFile(_get_standalone_zip_path(), 'r') as f:
- f.extractall(self._suite_dir)
-
- # Add owner-execute permission on scripts since zip does not preserve them.
- self._launcher_binary = self._suite_dir.joinpath(
- 'android-csuite/tools/csuite-tradefed')
- _add_owner_exec_permission(self._launcher_binary)
-
- self._generate_module_binary = self._suite_dir.joinpath(
- 'android-csuite/tools/csuite_generate_module')
- _add_owner_exec_permission(self._generate_module_binary)
-
- self._testcases_dir = self._suite_dir.joinpath('android-csuite/testcases')
-
- def __exit__(self, unused_type, unused_value, unused_traceback):
- self.cleanup()
-
- def cleanup(self):
- if _KEEP_TEMP_DIRS:
- return
- shutil.rmtree(self._suite_dir, ignore_errors=True)
-
- def add_module(self, package_name: Text) -> Text:
- """Generates and adds a test module for the provided package."""
- module_name = 'csuite_%s' % package_name
-
- with tempfile.TemporaryDirectory() as o:
- out_dir = pathlib.Path(o)
- package_list_path = out_dir.joinpath('packages.list')
-
- package_list_path.write_text(package_name + '\n')
-
- flags = ['--package-list', package_list_path, '--root-dir', out_dir]
-
- _run_command([self._generate_module_binary] + flags)
-
- out_file_path = self._testcases_dir.joinpath(module_name + '.config')
- shutil.copy(
- out_dir.joinpath(package_name, 'AndroidTest.xml'), out_file_path)
-
- return module_name
-
- def run_and_wait(self, flags: Sequence[Text]) -> subprocess.CompletedProcess:
- """Starts the Tradefed launcher and waits for it to complete."""
-
- env = os.environ.copy()
-
- # Unset environment variables that would cause the script to think it's in a
- # build tree.
- env.pop('ANDROID_BUILD_TOP', None)
- env.pop('ANDROID_HOST_OUT', None)
-
- # Unset environment variables that would cause TradeFed to find test configs
- # other than the ones created by the test.
- env.pop('ANDROID_HOST_OUT_TESTCASES', None)
- env.pop('ANDROID_TARGET_OUT_TESTCASES', None)
-
- # Unset environment variables that might cause the suite to pick up a
- # connected device that wasn't explicitly specified.
- env.pop('ANDROID_SERIAL', None)
-
- # Set the environment variable that TradeFed requires to find test modules.
- env['ANDROID_TARGET_OUT_TESTCASES'] = self._testcases_dir
-
- return _run_command([self._launcher_binary] + flags, env=env)
-
-
-class PackageRepository(contextlib.AbstractContextManager):
- """A file-system based APK repository for use in tests.
-
- WARNING: Explicitly clean up created instances or use as a context manager.
- Not doing so will result in a ResourceWarning for the implicit cleanup which
- confuses the TradeFed Python test output parser.
- """
-
- def __init__(self):
- self._root_dir = pathlib.Path(tempfile.mkdtemp(prefix='csuite_apk_dir'))
- logging.info('Created repository directory: %s', self._root_dir)
-
- def __exit__(self, unused_type, unused_value, unused_traceback):
- self.cleanup()
-
- def cleanup(self):
- if _KEEP_TEMP_DIRS:
- return
- shutil.rmtree(self._root_dir, ignore_errors=True)
-
- def get_path(self) -> pathlib.Path:
- """Returns the path to the repository's root directory."""
- return self._root_dir
-
- def add_package_apks(self, package_name: Text,
- apk_paths: Sequence[pathlib.Path]):
- """Adds the provided package APKs to the repository."""
- apk_dir = self._root_dir.joinpath(package_name)
-
- # Raises if the directory already exists.
- apk_dir.mkdir()
- for f in apk_paths:
- shutil.copy(f, apk_dir)
-
-
-class Adb:
- """Encapsulates adb functionality to simplify usage in tests.
-
- Most methods in this class raise an exception if they fail to execute. This
- behavior can be overridden by using the check parameter.
- """
-
- def __init__(self,
- adb_binary_path: pathlib.Path = None,
- device_serial: Text = None):
- self._args = [adb_binary_path or 'adb']
-
- device_serial = device_serial or get_device_serial()
- if device_serial:
- self._args.extend(['-s', device_serial])
-
- def shell(self,
- args: Sequence[Text],
- check: bool = None) -> subprocess.CompletedProcess:
- """Runs an adb shell command and waits for it to complete.
-
- Note that the exit code of the returned object corresponds to that of
- the adb command and not the command executed in the shell.
-
- Args:
- args: a sequence of program arguments to pass to the shell.
- check: whether to raise if the process terminates with a non-zero exit
- code.
-
- Returns:
- An object representing a process that has finished and that can be
- queried.
- """
- return self.run(['shell'] + args, check)
-
- def run(self,
- args: Sequence[Text],
- check: bool = None) -> subprocess.CompletedProcess:
- """Runs an adb command and waits for it to complete."""
- return _run_command(self._args + args, check=check)
-
- def uninstall(self, package_name: Text, check: bool = None):
- """Uninstalls the specified package."""
- self.run(['uninstall', package_name], check=check)
-
- def list_packages(self) -> Sequence[Text]:
- """Lists packages installed on the device."""
- p = self.shell(['pm', 'list', 'packages'])
- return [l.split(':')[1] for l in p.stdout.splitlines()]
-
-
-def _run_command(args, check=True, **kwargs) -> subprocess.CompletedProcess:
- """A wrapper for subprocess.run that overrides defaults and adds logging."""
- env = kwargs.get('env', {})
-
- # Log the command-line for debugging failed tests. Note that we convert
- # tokens to strings for _shlex_join.
- env_str = ['env', '-i'] + ['%s=%s' % (k, v) for k, v in env.items()]
- args_str = [str(t) for t in args]
-
- # Override some defaults. Note that 'check' deviates from this pattern to
- # avoid getting warnings about using subprocess.run without an explicitly set
- # `check` parameter.
- kwargs.setdefault('capture_output', True)
- kwargs.setdefault('universal_newlines', True)
-
- logging.debug('Running command: %s', _shlex_join(env_str + args_str))
-
- return subprocess.run(args, check=check, **kwargs)
-
-
-def _add_owner_exec_permission(path: pathlib.Path):
- path.chmod(path.stat().st_mode | stat.S_IEXEC)
-
-
-def get_test_app_apks(app_module_name: Text) -> Sequence[pathlib.Path]:
- """Returns a test app's apk file paths."""
- return [_get_test_file(app_module_name + '.apk')]
-
-
-def _get_standalone_zip_path():
- """Returns the suite standalone zip file's path."""
- return _get_test_file('csuite-standalone.zip')
-
-
-def _get_test_file(name: Text) -> pathlib.Path:
- test_dir = _get_test_dir()
- test_file = test_dir.joinpath(name)
-
- if not test_file.exists():
- raise RuntimeError('Unable to find the file `%s` in the test execution dir '
- '`%s`; are you missing a data dependency in the build '
- 'module?' % (name, test_dir))
-
- return test_file
-
-
-def _shlex_join(split_command: Sequence[Text]) -> Text:
- """Concatenate tokens and return a shell-escaped string."""
- # This is an alternative to shlex.join that doesn't exist in Python versions
- # < 3.8.
- return ' '.join(shlex.quote(t) for t in split_command)
-
-
-def _get_test_dir() -> pathlib.Path:
- return pathlib.Path(__file__).parent
-
-
-def main():
- global _KEEP_TEMP_DIRS
-
- parser = argparse.ArgumentParser(parents=[csuite_test.create_arg_parser()])
- parser.add_argument(
- '--log-level',
- choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
- default='WARNING',
- help='sets the logging level threshold')
- parser.add_argument(
- '--keep-temp-dirs',
- type=bool,
- help='keeps any created temporary directories for debugging failures')
- args, unittest_argv = parser.parse_known_args(sys.argv)
-
- _KEEP_TEMP_DIRS = args.keep_temp_dirs
- logging.basicConfig(level=getattr(logging, args.log_level))
-
- csuite_test.run_tests(args, unittest_argv)
diff --git a/pylib/Android.bp b/pylib/Android.bp
deleted file mode 100644
index 319060f..0000000
--- a/pylib/Android.bp
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2020 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.
-
-// The below module creates a standalone zip that end-to-end tests can depend
-// on for running the suite. This is a workaround since we can't use csuite.zip
-// which is defined in an external Makefile that Soong can't depend on.
-//
-// Besides listing jars we know the launcher script depends on which is
-// brittle, this is a hack for several reasons. First, we're listing our
-// dependencies in the tools attribute when we should be using the 'srcs'
-// attribute. Second, we're accessing jars using a path relative to a known
-// artifact location instead of using the Soong 'location' feature.
-//
-// Normally we would just use java_genrule_host to avoid these hacks but can't
-// do that since Soong currently complains when a python_host_test depends on
-// that target since, although compatible, the arch variants (x86_64 and
-// common) don't exactly match.
-
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-python_library_host {
- name: "csuite_test",
- srcs: [
- "csuite_test.py",
- ],
- defaults: [
- "csuite_python_defaults",
- ],
-}
diff --git a/pylib/csuite_test.py b/pylib/csuite_test.py
deleted file mode 100644
index fa06ce3..0000000
--- a/pylib/csuite_test.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# Lint as: python3
-#
-# Copyright 2020, 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.
-"""CSuite-specific automated unit test functionality."""
-
-import argparse
-import sys
-from typing import Any, Sequence, Text
-import unittest
-
-# Export the TestCase class to reduce the number of imports tests have to list.
-TestCase = unittest.TestCase
-
-_DEVICE_SERIAL = None
-
-
-def get_device_serial() -> Text:
- """Returns the serial of the connected device."""
- if not _DEVICE_SERIAL:
- raise RuntimeError(
- 'Device serial is unset, did you call main in your test?')
- return _DEVICE_SERIAL
-
-
-def create_arg_parser(add_help: bool = False) -> argparse.ArgumentParser:
- """Creates a new parser that can handle the default command-line flags.
-
- The object returned by this function can be used by other modules that want to
- add their own command-line flags. The returned parser is intended to be passed
- to the 'parents' argument of ArgumentParser and extend the set of default
- flags with additional ones.
-
- Args:
- add_help: whether to add an option which simply displays the parser’s help
- message; this is typically false when used from other modules that want to
- use the returned parser as a parent argument parser.
-
- Returns:
- A new arg parser that can handle the default flags expected by this module.
- """
-
- # The below flags are passed in by the TF Python test runner.
- parser = argparse.ArgumentParser(add_help=add_help)
-
- parser.add_argument('-s', '--serial', help='the device serial')
- parser.add_argument(
- '--test-output-file',
- help='the file in which to store the test results',
- required=True)
-
- return parser
-
-
-def run_tests(args: Any, unittest_argv: Sequence[Text]) -> None:
- """Executes a set of Python unit tests.
-
- This function is typically used by modules that extend command-line flags.
- Callers create their own argument parser with this module's parser as a parent
- and parse the command-line. The resulting object is will contain the
- attributes expected by this module and is used to call this method.
-
- Args:
- args: an object that contains at least the set of attributes defined in
- objects returned when using the default argument parser.
- unittest_argv: the list of command-line arguments to forward to
- unittest.main.
- """
- global _DEVICE_SERIAL
-
- _DEVICE_SERIAL = args.serial
-
- with open(args.test_output_file, 'w') as test_output_file:
-
- # Note that we use a type and not an instance for 'testRunner' since
- # TestProgram forwards its constructor arguments when creating an instance
- # of the runner type. Not doing so would require us to make sure that the
- # parameters passed to TestProgram are aligned with those for creating a
- # runner instance.
- class TestRunner(unittest.TextTestRunner):
- """A test runner that writes test results to the TF-provided file."""
-
- def __init__(self, *args, **kwargs):
- super(TestRunner, self).__init__(
- stream=test_output_file, *args, **kwargs)
-
- # Setting verbosity is required to generate output that the TradeFed test
- # runner can parse.
- unittest.TestProgram(verbosity=3, testRunner=TestRunner, argv=unittest_argv)
-
-
-def main():
- """Executes a set of Python unit tests."""
- args, unittest_argv = create_arg_parser(add_help=True).parse_known_args(
- sys.argv)
- run_tests(args, unittest_argv)
diff --git a/pylintrc b/pylintrc
deleted file mode 100644
index c8c911f..0000000
--- a/pylintrc
+++ /dev/null
@@ -1,427 +0,0 @@
-# This Pylint rcfile contains a best-effort configuration to uphold the
-# best-practices and style described in the Google Python style guide:
-# https://google.github.io/styleguide/pyguide.html
-#
-# Its canonical open-source location is:
-# https://google.github.io/styleguide/pylintrc
-
-[MASTER]
-
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
-ignore=third_party
-
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=
-
-# Pickle collected data for later comparisons.
-persistent=no
-
-# List of plugins (as comma separated values of python modules names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Use multiple processes to speed up Pylint.
-jobs=4
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code
-extension-pkg-whitelist=
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
-confidence=
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-#enable=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once).You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use"--disable=all --enable=classes
-# --disable=W"
-disable=apply-builtin,
- backtick,
- bad-option-value,
- basestring-builtin,
- buffer-builtin,
- c-extension-no-member,
- cmp-builtin,
- cmp-method,
- coerce-builtin,
- coerce-method,
- delslice-method,
- div-method,
- duplicate-code,
- eq-without-hash,
- execfile-builtin,
- file-builtin,
- filter-builtin-not-iterating,
- fixme,
- getslice-method,
- global-statement,
- hex-method,
- idiv-method,
- implicit-str-concat-in-sequence,
- import-error,
- import-self,
- import-star-module-level,
- input-builtin,
- intern-builtin,
- invalid-str-codec,
- locally-disabled,
- long-builtin,
- long-suffix,
- map-builtin-not-iterating,
- metaclass-assignment,
- next-method-called,
- next-method-defined,
- no-absolute-import,
- no-else-break,
- no-else-continue,
- no-else-raise,
- no-else-return,
- no-member,
- no-self-use,
- nonzero-method,
- oct-method,
- old-division,
- old-ne-operator,
- old-octal-literal,
- old-raise-syntax,
- parameter-unpacking,
- print-statement,
- raising-string,
- range-builtin-not-iterating,
- raw_input-builtin,
- rdiv-method,
- reduce-builtin,
- relative-import,
- reload-builtin,
- round-builtin,
- setslice-method,
- signature-differs,
- standarderror-builtin,
- suppressed-message,
- sys-max-int,
- too-few-public-methods,
- too-many-ancestors,
- too-many-arguments,
- too-many-boolean-expressions,
- too-many-branches,
- too-many-instance-attributes,
- too-many-locals,
- too-many-public-methods,
- too-many-return-statements,
- too-many-statements,
- trailing-newlines,
- unichr-builtin,
- unicode-builtin,
- unpacking-in-except,
- useless-else-on-loop,
- useless-suppression,
- using-cmp-argument,
- xrange-builtin,
- zip-builtin-not-iterating,
-
-
-[REPORTS]
-
-# Set the output format. Available formats are text, parseable, colorized, msvs
-# (visual studio) and html. You can also give a reporter class, eg
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Put messages in a separate file for each module / package specified on the
-# command line instead of printing them on stdout. Reports (if any) will be
-# written in a file name "pylint_global.[txt|html]". This option is deprecated
-# and it will be removed in Pylint 2.0.
-files-output=no
-
-# Tells whether to display a full report or only the messages
-reports=no
-
-# Python expression which should return a note less than 10 (10 is the highest
-# note). You have access to the variables errors warning, statement which
-# respectively contain the number of errors / warnings messages and the total
-# number of statements analyzed. This is used by the global evaluation report
-# (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details
-#msg-template=
-
-
-[BASIC]
-
-# Good variable names which should always be accepted, separated by a comma
-good-names=main,_
-
-# Bad variable names which should always be refused, separated by a comma
-bad-names=
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Include a hint for the correct naming format with invalid-name
-include-naming-hint=no
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
-
-# Regular expression matching correct function names
-function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
-
-# Regular expression matching correct variable names
-variable-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct constant names
-const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct attribute names
-attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
-
-# Regular expression matching correct argument names
-argument-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class attribute names
-class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
-
-# Regular expression matching correct inline iteration names
-inlinevar-rgx=^[a-z][a-z0-9_]*$
-
-# Regular expression matching correct class names
-class-rgx=^_?[A-Z][a-zA-Z0-9]*$
-
-# Regular expression matching correct module names
-module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
-
-# Regular expression matching correct method names
-method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=10
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis. It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-
-[FORMAT]
-
-# Maximum number of characters on a single line.
-max-line-length=80
-
-# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
-# lines made too long by directives to pytype.
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=(?x)(
- ^\s*(\#\ )?<?https?://\S+>?$|
- ^\s*(from\s+\S+\s+)?import\s+.+$)
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=yes
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=
-
-# Maximum number of lines in a module
-max-module-lines=99999
-
-# String used as indentation unit. The internal Google style guide mandates 2
-# spaces. Google's externaly-published style guide says 4, consistent with
-# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google
-# projects (like TensorFlow).
-indent-string=' '
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=TODO
-
-
-[VARIABLES]
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# A regular expression matching the name of dummy variables (i.e. expectedly
-# not used).
-dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-additional-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,_cb
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
-
-
-[LOGGING]
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format
-logging-modules=logging,absl.logging,tensorflow.google.logging
-
-
-[SIMILARITIES]
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-
-[SPELLING]
-
-# Spelling dictionary name. Available dictionaries: none. To make it working
-# install python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to indicated private dictionary in
-# --spelling-private-dict-file option instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[IMPORTS]
-
-# Deprecated modules which should not be used, separated by a comma
-deprecated-modules=regsub,
- TERMIOS,
- Bastion,
- rexec,
- sets
-
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled)
-import-graph=
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled)
-ext-import-graph=
-
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled)
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant, absl
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
- __new__,
- setUp
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
- _fields,
- _replace,
- _source,
- _make
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls,
- class_
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=mcs
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "Exception"
-overgeneral-exceptions=StandardError,
- Exception,
- BaseException
diff --git a/test_targets/csuite-app-launch/Android.bp b/test_targets/csuite-app-launch/Android.bp
deleted file mode 100644
index 256d9f4..0000000
--- a/test_targets/csuite-app-launch/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2021 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-csuite_test {
- name: "csuite-app-launch",
- test_config_template: "template.xml"
-}
diff --git a/test_targets/csuite-app-launch/template.xml b/test_targets/csuite-app-launch/template.xml
deleted file mode 100644
index 52c4611..0000000
--- a/test_targets/csuite-app-launch/template.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Launches an app and check for crashes">
- <option name="package-name" value="{package}"/>
- <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparer">
- <option name="test-file-name" value="app://{package}"/>
- </target_preparer>
- <target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
- <option name="run-command" value="input keyevent KEYCODE_MENU"/>
- <option name="run-command" value="input keyevent KEYCODE_HOME"/>
- </target_preparer>
- <test class="com.android.compatibility.testtype.AppLaunchTest"/>
-</configuration> \ No newline at end of file
diff --git a/test_targets/csuite-pre-installed-app-launch/Android.bp b/test_targets/csuite-pre-installed-app-launch/Android.bp
deleted file mode 100644
index 539306a..0000000
--- a/test_targets/csuite-pre-installed-app-launch/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2021 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-csuite_test {
- name: "csuite-pre-installed-app-launch",
- test_config_template: "template.xml"
-}
diff --git a/test_targets/csuite-pre-installed-app-launch/template.xml b/test_targets/csuite-pre-installed-app-launch/template.xml
deleted file mode 100644
index 2d20306..0000000
--- a/test_targets/csuite-pre-installed-app-launch/template.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-<configuration description="Launches an app that exists on the device and check for crashes">
- <option name="package-name" value="{package}"/>
- <target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
- <option name="run-command" value="input keyevent KEYCODE_MENU"/>
- <option name="run-command" value="input keyevent KEYCODE_HOME"/>
- </target_preparer>
- <test class="com.android.compatibility.testtype.AppLaunchTest"/>
-</configuration> \ No newline at end of file
diff --git a/test_targets/csuite-system-app-launch/Android.bp b/test_targets/csuite-system-app-launch/Android.bp
deleted file mode 100644
index 2514740..0000000
--- a/test_targets/csuite-system-app-launch/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2021 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-csuite_test {
- name: "csuite-system-app-launch",
- test_config_template: "template.xml"
-}
diff --git a/test_targets/csuite-system-app-launch/template.xml b/test_targets/csuite-system-app-launch/template.xml
deleted file mode 100644
index 4d1181b..0000000
--- a/test_targets/csuite-system-app-launch/template.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Reinstalls a system app and check for launch crashes.">
- <option name="package-name" value="{package}"/>
- <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparer">
- <option name="test-file-name" value="app://{package}"/>
- </target_preparer>
- <target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
- <option name="run-command" value="input keyevent KEYCODE_MENU"/>
- <option name="run-command" value="input keyevent KEYCODE_HOME"/>
- </target_preparer>
- <test class="com.android.compatibility.testtype.AppLaunchTest"/>
-</configuration> \ No newline at end of file
diff --git a/test_targets/csuite-test-package-launch/Android.bp b/test_targets/csuite-test-package-launch/Android.bp
deleted file mode 100644
index 6cdd899..0000000
--- a/test_targets/csuite-test-package-launch/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2021 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-csuite_test {
- name: "csuite-test-package-launch",
- test_config_template: "template.xml"
-}
diff --git a/test_targets/csuite-test-package-launch/template.xml b/test_targets/csuite-test-package-launch/template.xml
deleted file mode 100644
index 9c97fd3..0000000
--- a/test_targets/csuite-test-package-launch/template.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Installs a test package with -t arg and check for launch crashes">
- <option name="package-name" value="{package}"/>
- <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparer">
- <option name="test-file-name" value="app://{package}"/>
- <option name="install-arg" value="-t"/>
- </target_preparer>
- <target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
- <option name="run-command" value="input keyevent KEYCODE_MENU"/>
- <option name="run-command" value="input keyevent KEYCODE_HOME"/>
- </target_preparer>
- <test class="com.android.compatibility.testtype.AppLaunchTest"/>
-</configuration> \ No newline at end of file
diff --git a/tools/csuite-tradefed/Android.bp b/tools/csuite-tradefed/Android.bp
index a441726..82d959e 100644
--- a/tools/csuite-tradefed/Android.bp
+++ b/tools/csuite-tradefed/Android.bp
@@ -12,10 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
tradefed_binary_host {
name: "csuite-tradefed",
wrapper: "src/scripts/csuite-tradefed",
@@ -37,7 +33,5 @@ java_test_host {
"tradefed",
"csuite-tradefed",
],
- test_options: {
- unit_test: true,
- },
+ test_suites: ["general-tests"],
}
diff --git a/tools/csuite-tradefed/AndroidTest.xml b/tools/csuite-tradefed/AndroidTest.xml
new file mode 100644
index 0000000..850750e
--- /dev/null
+++ b/tools/csuite-tradefed/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.compatibility.tradefed.CSuiteTradefedTest" />
+ </test>
+</configuration>
diff --git a/tools/csuite-tradefed/TEST_MAPPING b/tools/csuite-tradefed/TEST_MAPPING
new file mode 100644
index 0000000..89c2072
--- /dev/null
+++ b/tools/csuite-tradefed/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "postsubmit": [
+ {
+ "name": "csuite-tradefed-tests",
+ "host": true
+ }
+ ]
+}
diff --git a/tools/csuite-tradefed/src/scripts/csuite-tradefed b/tools/csuite-tradefed/src/scripts/csuite-tradefed
index f3b887a..4277884 100644
--- a/tools/csuite-tradefed/src/scripts/csuite-tradefed
+++ b/tools/csuite-tradefed/src/scripts/csuite-tradefed
@@ -83,6 +83,7 @@ JAR_DIR=${CSUITE_ROOT}/android-csuite/tools
TRADEFED_JAR="tradefed"
JARS="tradefed
+ hosttestlib
compatibility-host-util
csuite-tradefed
csuite-tradefed-tests"
@@ -98,7 +99,7 @@ OPTIONAL_JARS="
google-tf-prod-tests"
for JAR in $OPTIONAL_JARS; do
- if [ -f "${JAR_DIR}/${JAR}.jar" ]; then
+ if [ -f "$JAR.jar" ]; then
JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}.jar
fi;
done
@@ -118,4 +119,4 @@ for j in ${CSUITE_ROOT}/android-csuite/testcases/*.jar; do
JAR_PATH=${JAR_PATH}:$j
done
-java $RDBG_FLAG -cp ${JAR_PATH} -DCSUITE_ROOT=${CSUITE_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
+java $RDBG_FLAG -cp ${JAR_PATH} -DMTS_ROOT=${CSUITE_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
diff --git a/tools/csuite_test/Android.bp b/tools/csuite_test/Android.bp
deleted file mode 100644
index a1103dc..0000000
--- a/tools/csuite_test/Android.bp
+++ /dev/null
@@ -1,20 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-bootstrap_go_package {
- name: "soong-csuite",
- pkgPath: "android/soong/csuite",
- deps: [
- "blueprint",
- "soong-android",
- "soong-java",
- ],
- srcs: [
- "csuite_test.go",
- ],
- testSrcs: [
- "csuite_test_test.go",
- ],
- pluginFor: ["soong_build"],
-}
diff --git a/tools/csuite_test/csuite_test.go b/tools/csuite_test/csuite_test.go
deleted file mode 100644
index 74373f2..0000000
--- a/tools/csuite_test/csuite_test.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// 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 csuite
-
-import (
- "android/soong/android"
- "android/soong/java"
- "strings"
-)
-
-var (
- pctx = android.NewPackageContext("android/soong/csuite")
-)
-
-func init() {
- android.RegisterModuleType("csuite_test", CSuiteTestFactory)
-}
-
-type csuiteTestProperties struct {
- // Local path to a module template xml file.
- // The content of the template will be used to generate test modules at runtime.
- Test_config_template *string `android:"path"`
-
- // Local path to a test plan config xml to be included in the generated plan.
- Test_plan_include *string `android:"path"`
-}
-
-type CSuiteTest struct {
- // Java TestHost.
- java.TestHost
-
- // C-Suite test properties struct.
- csuiteTestProperties csuiteTestProperties
-}
-
-func (cSuiteTest *CSuiteTest) buildCopyConfigTemplateCommand(ctx android.ModuleContext, rule *android.RuleBuilder) string {
- if cSuiteTest.csuiteTestProperties.Test_config_template == nil {
- ctx.ModuleErrorf(`'test_config_template' is missing.`)
- }
- inputPath := android.PathForModuleSrc(ctx, *cSuiteTest.csuiteTestProperties.Test_config_template)
- genPath := android.PathForModuleGen(ctx, planConfigDirName, ctx.ModuleName()+configTemplateFileExtension)
- rule.Command().Textf("cp").Input(inputPath).Output(genPath)
- cSuiteTest.AddExtraResource(genPath)
- return genPath.Rel()
-}
-
-func (cSuiteTest *CSuiteTest) buildCopyPlanIncludeCommand(ctx android.ModuleContext, rule *android.RuleBuilder) string {
- if cSuiteTest.csuiteTestProperties.Test_plan_include == nil {
- return emptyPlanIncludePath
- }
- inputPath := android.PathForModuleSrc(ctx, *cSuiteTest.csuiteTestProperties.Test_plan_include)
- genPath := android.PathForModuleGen(ctx, planConfigDirName, "includes", ctx.ModuleName()+".xml")
- rule.Command().Textf("cp").Input(inputPath).Output(genPath)
- cSuiteTest.AddExtraResource(genPath)
- return strings.Replace(genPath.Rel(), "config/", "", -1)
-}
-
-func (cSuiteTest *CSuiteTest) buildWritePlanConfigRule(ctx android.ModuleContext, configTemplatePath string, planIncludePath string) {
- planName := ctx.ModuleName()
- content := strings.Replace(planTemplate, "{planName}", planName, -1)
- content = strings.Replace(content, "{templatePath}", configTemplatePath, -1)
- content = strings.Replace(content, "{planInclude}", planIncludePath, -1)
- genPath := android.PathForModuleGen(ctx, planConfigDirName, planName+planFileExtension)
- android.WriteFileRule(ctx, genPath, content)
- cSuiteTest.AddExtraResource(genPath)
-}
-
-func (cSuiteTest *CSuiteTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- rule := android.NewRuleBuilder(pctx, ctx)
-
- configTemplatePath := cSuiteTest.buildCopyConfigTemplateCommand(ctx, rule)
- planIncludePath := cSuiteTest.buildCopyPlanIncludeCommand(ctx, rule)
- cSuiteTest.buildWritePlanConfigRule(ctx, configTemplatePath, planIncludePath)
-
- rule.Build("CSuite", "generate C-Suite config files")
- cSuiteTest.TestHost.GenerateAndroidBuildActions(ctx)
-}
-
-func CSuiteTestFactory() android.Module {
- module := &CSuiteTest{}
- module.AddProperties(&module.csuiteTestProperties)
- installable := true
- autoGenConfig := false
- java.InitTestHost(&module.TestHost, &installable, []string{"csuite"}, &autoGenConfig)
-
- java.InitJavaModuleMultiTargets(module, android.HostSupported)
-
- return module
-}
-
-const (
- emptyPlanIncludePath = `empty`
- planConfigDirName = `config`
- configTemplateFileExtension = `.xml.template`
- planFileExtension = `.xml`
- planTemplate = `<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration>
- <test class="com.android.csuite.config.ModuleGenerator">
- <option name="template" value="{templatePath}" />
- </test>
- <include name="csuite-base" />
- <include name="{planInclude}" />
- <option name="plan" value="{planName}" />
-</configuration>
-`
-)
diff --git a/tools/csuite_test/csuite_test_test.go b/tools/csuite_test/csuite_test_test.go
deleted file mode 100644
index daf07b0..0000000
--- a/tools/csuite_test/csuite_test_test.go
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// 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 csuite
-
-import (
- "android/soong/android"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-)
-
-var buildDir string
-
-func TestBpContainsTestHostPropsThrowsError(t *testing.T) {
- ctx, _ := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template",
- data_native_bins: "bin"
- }
- `)
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
-
- android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
-}
-
-func TestBpContainsManifestThrowsError(t *testing.T) {
- ctx, _ := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template",
- test_config: "AndroidTest.xml"
- }
- `)
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
-
- android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
-}
-
-func TestBpMissingNameThrowsError(t *testing.T) {
- ctx, _ := createContextAndConfig(t, `
- csuite_test {
- test_config_template: "test_config.xml.template"
- }
- `)
-
- _, errs := ctx.ParseBlueprintsFiles("Android.bp")
-
- android.FailIfNoMatchingErrors(t, `'name' is missing`, errs)
-}
-
-func TestBpMissingTemplatePathThrowsError(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- }
- `)
-
- ctx.ParseBlueprintsFiles("Android.bp")
- _, errs := ctx.PrepareBuildActions(config)
-
- android.FailIfNoMatchingErrors(t, `'test_config_template' is missing`, errs)
-}
-
-func TestValidBpMissingPlanIncludeDoesNotThrowError(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-}
-
-func TestValidBpMissingPlanIncludeGeneratesPlanXmlWithoutPlaceholders(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
- content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
- if strings.Contains(content, "{") || strings.Contains(content, "}") {
- t.Errorf("The generated plan name contains a placeholder: %s", content)
- }
-}
-
-func TestGeneratedTestPlanContainsPlanName(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
- content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
- if !strings.Contains(content, "plan_name") {
- t.Errorf("The plan name is missing from the generated plan: %s", content)
- }
-}
-
-func TestGeneratedTestPlanContainsTemplatePath(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
- content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
- if !strings.Contains(content, "config/plan_name.xml.template") {
- t.Errorf("The template path is missing from the generated plan: %s", content)
- }
-}
-
-func TestTemplateFileCopyRuleExists(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- params := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common").Rule("CSuite")
- assertFileCopyRuleExists(t, params, "test_config.xml.template", "config/plan_name.xml.template")
-}
-
-func TestGeneratedTestPlanContainsPlanInclude(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template",
- test_plan_include: "include.xml"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
- content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
- if !strings.Contains(content, `"includes/plan_name.xml"`) {
- t.Errorf("The plan include path is missing from the generated plan: %s", content)
- }
-}
-
-func TestPlanIncludeFileCopyRuleExists(t *testing.T) {
- ctx, config := createContextAndConfig(t, `
- csuite_test {
- name: "plan_name",
- test_config_template: "test_config.xml.template",
- test_plan_include: "include.xml"
- }
- `)
-
- parseBpAndBuild(t, ctx, config)
-
- params := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common").Rule("CSuite")
- assertFileCopyRuleExists(t, params, "include.xml", "config/includes/plan_name.xml")
-}
-
-func TestMain(m *testing.M) {
- run := func() int {
- setUp()
- defer tearDown()
-
- return m.Run()
- }
-
- os.Exit(run())
-}
-
-func parseBpAndBuild(t *testing.T, ctx *android.TestContext, config android.Config) {
- _, parsingErrs := ctx.ParseBlueprintsFiles("Android.bp")
- _, buildErrs := ctx.PrepareBuildActions(config)
-
- android.FailIfErrored(t, parsingErrs)
- android.FailIfErrored(t, buildErrs)
-}
-
-func assertFileCopyRuleExists(t *testing.T, params android.TestingBuildParams, src string, dst string) {
- assertPathsContains(t, getAllInputPaths(params), src)
- assertWritablePathsContainsRel(t, getAllOutputPaths(params), dst)
- if !strings.HasPrefix(params.RuleParams.Command, "cp") {
- t.Errorf("'cp' command is missing.")
- }
-}
-
-func assertPathsContains(t *testing.T, paths android.Paths, path string) {
- for _, p := range paths {
- if p.String() == path {
- return
- }
- }
- t.Errorf("Cannot find expected path %s", path)
-}
-
-func assertWritablePathsContainsRel(t *testing.T, paths android.WritablePaths, relPath string) {
- for _, path := range paths {
- if path.Rel() == relPath {
- return
- }
- }
- t.Errorf("Cannot find expected relative path %s", relPath)
-}
-
-func getAllOutputPaths(params android.TestingBuildParams) android.WritablePaths {
- var paths []android.WritablePath
- if params.Output != nil {
- paths = append(paths, params.Output)
- }
- if params.ImplicitOutput != nil {
- paths = append(paths, params.ImplicitOutput)
- }
- if params.SymlinkOutput != nil {
- paths = append(paths, params.SymlinkOutput)
- }
- paths = append(paths, params.Outputs...)
- paths = append(paths, params.ImplicitOutputs...)
- paths = append(paths, params.SymlinkOutputs...)
-
- return paths
-}
-
-func getAllInputPaths(params android.TestingBuildParams) android.Paths {
- var paths []android.Path
- if params.Input != nil {
- paths = append(paths, params.Input)
- }
- if params.Implicit != nil {
- paths = append(paths, params.Implicit)
- }
- paths = append(paths, params.Inputs...)
- paths = append(paths, params.Implicits...)
-
- return paths
-}
-
-func setUp() {
- var err error
- buildDir, err = ioutil.TempDir("", "soong_csuite_test")
- if err != nil {
- panic(err)
- }
-}
-
-func tearDown() {
- os.RemoveAll(buildDir)
-}
-
-func createContextAndConfig(t *testing.T, bp string) (*android.TestContext, android.Config) {
- t.Helper()
- config := android.TestArchConfig(buildDir, nil, bp, nil)
- ctx := android.NewTestArchContext(config)
- ctx.RegisterModuleType("csuite_test", CSuiteTestFactory)
- ctx.Register()
-
- return ctx, config
-}
diff --git a/tools/csuite_test/go.mod b/tools/csuite_test/go.mod
deleted file mode 100644
index a373cd1..0000000
--- a/tools/csuite_test/go.mod
+++ /dev/null
@@ -1,14 +0,0 @@
-module android/soong/csuite
-
-require (
- android/soong v0.0.0
- github.com/google/blueprint v0.0.0
-)
-
-replace android/soong v0.0.0 => ../../../../../build/soong
-
-replace github.com/golang/protobuf v0.0.0 => ../../../../../external/golang-protobuf
-
-replace github.com/google/blueprint v0.0.0 => ../../../../../build/blueprint
-
-go 1.13 \ No newline at end of file
diff --git a/tools/script/Android.bp b/tools/script/Android.bp
deleted file mode 100644
index c1eb990..0000000
--- a/tools/script/Android.bp
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2020 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-python_binary_host {
- name: "csuite_generate_module",
- main: "generate_module.py",
- srcs: [
- "generate_module.py",
- ],
- defaults: [
- "csuite_python_defaults",
- ],
-}
-
-python_test_host {
- name: "generate_module_test",
- srcs: [
- "generate_module.py",
- "generate_module_test.py",
- ],
- libs: [
- "csuite_test",
- "pyfakefs",
- ],
- test_config_template: "csuite_test_template.xml",
- test_options: {
- unit_test: true,
- },
- defaults: [
- "csuite_python_defaults",
- ],
-}
diff --git a/tools/script/csuite_test_template.xml b/tools/script/csuite_test_template.xml
deleted file mode 100644
index 837716a..0000000
--- a/tools/script/csuite_test_template.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration>
- <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest">
- <option name="par-file-name" value="{MODULE}"/>
- <option name="inject-serial-option" value="true"/>
- <option name="use-test-output-file" value="true"/>
- <option name="test-timeout" value="10m"/>
- </test>
-</configuration>
diff --git a/tools/script/generate_module.py b/tools/script/generate_module.py
index 5bda01f..30ae7b4 100644
--- a/tools/script/generate_module.py
+++ b/tools/script/generate_module.py
@@ -1,6 +1,6 @@
-# Lint as: python3
+#!/usr/bin/env python
#
-# Copyright (C) 2019 The Android Open Source Project
+# Copyright (C) 2020 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.
@@ -13,237 +13,246 @@
# 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.
-"""This script generates C-Suite configuration files for a list of apps."""
+#
+# This script generates C-Suite configuration files for a list of apps.
import argparse
-import contextlib
import glob
import os
-import string
import sys
+from xml.dom import minidom
+from xml.etree import cElementTree as ET
+from xml.sax import saxutils
-from typing import IO, Set, Text
+from typing import IO, List, Text
_ANDROID_BP_FILE_NAME = 'Android.bp'
_ANDROID_XML_FILE_NAME = 'AndroidTest.xml'
-_AUTO_GENERATE_NOTE = 'THIS FILE WAS AUTO-GENERATED. DO NOT EDIT MANUALLY!'
-
-DEFAULT_BUILD_MODULE_TEMPLATE = string.Template("""\
-// 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.
-// ${auto_generate_note}
+_TF_TEST_APP_INSTALL_SETUP =\
+ 'com.android.tradefed.targetprep.TestAppInstallSetup'
+_CSUITE_APP_SETUP_PREPARER =\
+ 'com.android.compatibility.targetprep.AppSetupPreparer'
+_CSUITE_LAUNCH_TEST_CLASS =\
+ 'com.android.compatibility.testtype.AppLaunchTest'
-csuite_config {
- name: "csuite_${package_name}",
-}
-""")
+_CONFIG_TYPE_TARGET_PREPARER = 'target_preparer'
+_CONFIG_TYPE_TEST = 'test'
-DEFAULT_TEST_MODULE_TEMPLATE = string.Template("""\
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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
+def generate_all_modules_from_config(package_list_file_path, root_dir):
+ """Generate multiple test and build modules.
- 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.
--->
-<!-- ${auto_generate_note}-->
-
-<configuration description="Tests the compatibility of apps">
- <option key="plan" name="config-descriptor:metadata" value="app-launch"/>
- <option name="package-name" value="${package_name}"/>
- <target_preparer class="com.android.compatibility.targetprep.AppSetupPreparer">
- <option name="test-file-name" value="csuite-launch-instrumentation.apk"/>
- <option name="test-file-name" value="app://${package_name}"/>
- </target_preparer>
- <target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
- <option name="run-command" value="input keyevent KEYCODE_MENU"/>
- <option name="run-command" value="input keyevent KEYCODE_HOME"/>
- </target_preparer>
- <test class="com.android.compatibility.testtype.AppLaunchTest"/>
-</configuration>
-""")
-
-
-def generate_all_modules_from_config(package_list_file_path,
- root_dir,
- build_module_template_file_path=None,
- test_module_template_file_path=None):
- """Generate multiple test and build modules.
-
- Args:
+ Args:
package_list_file_path: path of a file containing package names.
root_dir: root directory that modules will be generated in.
- build_module_template_file_path: path of a file containing build module
- template.
- test_module_template_file_path: path of a file containing test module
- template.
- """
- build_module_template = DEFAULT_BUILD_MODULE_TEMPLATE
- test_module_template = DEFAULT_TEST_MODULE_TEMPLATE
- if build_module_template_file_path:
- with open(build_module_template_file_path, 'r') as f:
- build_module_template = string.Template(f.read())
- if test_module_template_file_path:
- with open(test_module_template_file_path, 'r') as f:
- test_module_template = string.Template(f.read())
-
- remove_existing_package_files(root_dir)
-
- with open(package_list_file_path) as fp:
- for line in parse_package_list(fp):
- _generate_module_files(line.strip(), root_dir, build_module_template,
- test_module_template)
+ """
+ remove_existing_package_files(root_dir)
+
+ with open(package_list_file_path) as fp:
+ for line in parse_package_list(fp):
+ _generate_module_files(line.strip(), root_dir)
def remove_existing_package_files(root_dir):
- for filename in glob.iglob(root_dir + '/**/AndroidTest.xml'):
- if _is_auto_generated(filename):
- os.remove(filename)
+ for filename in glob.iglob(root_dir + '**/AndroidTest.xml'):
+ if _is_auto_generated(filename):
+ os.remove(filename)
- for filename in glob.iglob(root_dir + '/**/Android.bp'):
- if _is_auto_generated(filename):
- os.remove(filename)
+ for filename in glob.iglob(root_dir + '**/Android.bp'):
+ if _is_auto_generated(filename):
+ os.remove(filename)
- _remove_empty_dirs(root_dir)
+ _remove_empty_dirs(root_dir)
def _is_auto_generated(filename):
- with open(filename, 'r') as f:
- return _AUTO_GENERATE_NOTE in f.read()
+ with open(filename, 'r') as f:
+ return 'auto-generated' in f.read()
def _remove_empty_dirs(path):
- for filename in os.listdir(path):
- file_path = os.path.join(path, filename)
- if os.path.isdir(file_path) and not os.listdir(file_path):
- os.rmdir(file_path)
+ for filename in os.listdir(path):
+ file_path = os.path.join(path, filename)
+ if os.path.isdir(file_path) and not os.listdir(file_path):
+ os.rmdir(file_path)
-def parse_package_list(package_list_file: IO[bytes]) -> Set[bytes]:
- packages = {line.strip() for line in package_list_file.readlines()}
- for package in packages:
- if package and not package.startswith('#'):
- yield package
+def parse_package_list(package_list_file: IO[bytes]) -> List[bytes]:
+ return {
+ line.strip() for line in package_list_file.readlines() if line.strip()}
-def _generate_module_files(package_name, root_dir, build_module_template,
- test_module_template):
- """Generate test and build modules for a single package.
+def _generate_module_files(package_name, root_dir):
+ """Generate test and build modules for a single package.
- Args:
+ Args:
package_name: package name of test and build modules.
root_dir: root directory that modules will be generated in.
- build_module_template: template for build module.
- test_module_template: template for test module.
- """
- package_dir = _create_package_dir(root_dir, package_name)
+ """
+ package_dir = _create_package_dir(root_dir, package_name)
- build_module_path = os.path.join(package_dir, _ANDROID_BP_FILE_NAME)
- test_module_path = os.path.join(package_dir, _ANDROID_XML_FILE_NAME)
+ build_module_path = os.path.join(package_dir, _ANDROID_BP_FILE_NAME)
+ test_module_path = os.path.join(package_dir, _ANDROID_XML_FILE_NAME)
- with open(build_module_path, 'w') as f:
- write_module(build_module_template, package_name, f)
+ with open(build_module_path, 'w') as f:
+ write_build_module(package_name, f)
- with open(test_module_path, 'w') as f:
- write_module(test_module_template, package_name, f)
+ with open(test_module_path, 'w') as f:
+ write_test_module(package_name, f)
def _create_package_dir(root_dir, package_name):
- package_dir_path = os.path.join(root_dir, package_name)
- os.mkdir(package_dir_path)
+ package_dir_path = os.path.join(root_dir, package_name)
+ os.mkdir(package_dir_path)
+
+ return package_dir_path
+
+
+def write_build_module(package_name: Text, out_file: IO[bytes]) -> Text:
+ build_module = _BUILD_MODULE_HEADER \
+ + _BUILD_MODULE_TEMPLATE.format(package_name=package_name)
+ out_file.write(build_module)
+
+
+def write_test_module(package_name: Text, out_file: IO[bytes]) -> Text:
+ configuration = ET.Element('configuration', {
+ 'description': 'Tests the compatibility of apps'
+ })
+ ET.SubElement(
+ configuration, 'option', {
+ 'name': 'config-descriptor:metadata',
+ 'key': 'plan',
+ 'value': 'csuite-launch'
+ }
+ )
+ ET.SubElement(
+ configuration, 'option', {
+ 'name': 'package-name',
+ 'value': package_name
+ }
+ )
+ test_file_name_option = {
+ 'name': 'test-file-name',
+ 'value': 'csuite-launch-instrumentation.apk'
+ }
+ _add_element_with_option(
+ configuration,
+ _CONFIG_TYPE_TARGET_PREPARER,
+ _TF_TEST_APP_INSTALL_SETUP,
+ options=[test_file_name_option]
+ )
+ _add_element_with_option(
+ configuration,
+ _CONFIG_TYPE_TARGET_PREPARER,
+ _CSUITE_APP_SETUP_PREPARER
+ )
+ _add_element_with_option(
+ configuration,
+ _CONFIG_TYPE_TEST,
+ _CSUITE_LAUNCH_TEST_CLASS
+ )
+
+ test_module = _TEST_MODULE_HEADER + _prettify(configuration)
+ out_file.write(test_module)
+
+
+def _add_element_with_option(elem, sub_elem, class_name, options=None):
+ if options is None:
+ options = []
+
+ new_elem = ET.SubElement(
+ elem, sub_elem, {
+ 'class': class_name,
+ }
+ )
+ for option in options:
+ ET.SubElement(
+ new_elem, 'option', option
+ )
+
+
+def _prettify(elem: ET.Element) -> Text:
+ declaration = minidom.Document().toxml()
+ parsed = minidom.parseString(ET.tostring(elem, 'utf-8'))
+
+ return saxutils.unescape(
+ parsed.toprettyxml(indent=' ')[len(declaration) + 1:])
+
+_BUILD_MODULE_HEADER = """// Copyright (C) 2020 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.
+
+// This file was auto-generated by test/app_compat/csuite/tools/script/generate_module.py.
+// Do not edit manually.
+
+"""
- return package_dir_path
+_BUILD_MODULE_TEMPLATE = """csuite_config {{
+ name: "csuite_{package_name}",
+}}
+"""
+_TEST_MODULE_HEADER = """<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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
-def write_module(template: string.Template, package_name: Text,
- out_file: IO[bytes]) -> Text:
- """Writes the build or test module for the provided package into a file."""
- test_module = template.substitute(
- package_name=package_name, auto_generate_note=_AUTO_GENERATE_NOTE)
- out_file.write(test_module)
+ 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.
+-->
+<!-- This file was auto-generated by test/app_compat/csuite/tools/script/generate_module.py.
+ Do not edit manually.
+-->
+
+"""
def _file_path(path):
- if os.path.isfile(path):
- return path
- raise argparse.ArgumentTypeError('%s is not a valid path' % path)
+ if os.path.isfile(path):
+ return path
+ raise argparse.ArgumentTypeError('%s is not a valid path' % path)
def _dir_path(path):
- if os.path.isdir(path):
- return path
- raise argparse.ArgumentTypeError('%s is not a valid path' % path)
-
-
-@contextlib.contextmanager
-def _redirect_sys_output(out, err):
- current_out, current_err = sys.stdout, sys.stderr
- try:
- sys.stdout, sys.stderr = out, err
- yield
- finally:
- sys.stdout, sys.stderr = current_out, current_err
-
-
-def parse_args(args, out=sys.stdout, err=sys.stderr):
- """Parses the provided sequence of arguments."""
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--package-list',
- type=_file_path,
- required=True,
- help='path of the file containing package names')
- parser.add_argument(
- '--root-dir',
- type=_dir_path,
- required=True,
- help='path of the root directory that' + 'modules will be generated in')
- parser.add_argument(
- '--test-module-template',
- type=_file_path,
- required=False,
- help='path of the file containing test module configuration template')
- parser.add_argument(
- '--build-module-template',
- type=_file_path,
- required=False,
- help='path of the file containing build module configuration template')
-
- # We redirect stdout and stderr to improve testability since ArgumentParser
- # always writes to those files. More specifically, the TradeFed python test
- # runner will choke parsing output that is not in the expected format.
- with _redirect_sys_output(out, err):
+ if os.path.isdir(path):
+ return path
+ raise argparse.ArgumentTypeError('%s is not a valid path' % path)
+
+
+def parse_args(args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--package_list',
+ type=_file_path,
+ required=True,
+ help='path of the file containing package names')
+ parser.add_argument('--root_dir',
+ type=_dir_path,
+ required=True,
+ help='path of the root directory that' +
+ 'modules will be generated in')
return parser.parse_args(args)
def main():
- parser = parse_args(sys.argv[1:])
- generate_all_modules_from_config(parser.package_list, parser.root_dir,
- parser.build_module_template,
- parser.test_module_template)
-
+ parser = parse_args(sys.argv[1:])
+ generate_all_modules_from_config(parser.package_list, parser.root_dir)
if __name__ == '__main__':
- main()
+ main()
diff --git a/tools/script/generate_module_test.py b/tools/script/generate_module_test.py
deleted file mode 100644
index 09370a0..0000000
--- a/tools/script/generate_module_test.py
+++ /dev/null
@@ -1,245 +0,0 @@
-# Lint as: python3
-#
-# Copyright (C) 2020 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.
-"""Tests for the generate_module package."""
-
-import io
-import os
-from xml.etree import cElementTree as ET
-import csuite_test
-import generate_module
-from pyfakefs import fake_filesystem_unittest
-
-_AUTO_GENERATE_NOTE = 'THIS FILE WAS AUTO-GENERATED. DO NOT EDIT MANUALLY!'
-
-
-class WriteTestModuleTest(csuite_test.TestCase):
-
- def test_output_contains_license(self):
- out = io.StringIO()
-
- generate_module.write_module(generate_module.DEFAULT_BUILD_MODULE_TEMPLATE,
- 'a.package.name', out)
-
- self.assertIn('Copyright', out.getvalue())
- self.assertIn('Android Open Source Project', out.getvalue())
-
- def test_output_is_valid_xml(self):
- out = io.StringIO()
-
- generate_module.write_module(generate_module.DEFAULT_TEST_MODULE_TEMPLATE,
- 'a.package.name', out)
-
- self.assert_valid_xml(out.getvalue())
-
- def test_output_contains_package_name(self):
- package_name = 'a.package.name'
- out = io.StringIO()
-
- generate_module.write_module(generate_module.DEFAULT_TEST_MODULE_TEMPLATE,
- 'a.package.name', out)
-
- self.assertIn(package_name, out.getvalue())
-
- def assert_valid_xml(self, xml_str: bytes) -> None:
- try:
- ET.parse(io.BytesIO(xml_str.encode('utf8')))
- except ET.ParseError as e:
- self.fail('Input \'%s\' is not a valid XML document: %s' % (xml_str, e))
-
-
-class WriteBuildModuleTest(csuite_test.TestCase):
-
- def test_output_contains_license(self):
- out = io.StringIO()
-
- generate_module.write_module(generate_module.DEFAULT_BUILD_MODULE_TEMPLATE,
- 'a.package.name', out)
-
- self.assertIn('Copyright', out.getvalue())
- self.assertIn('Android Open Source Project', out.getvalue())
-
- def test_output_is_valid_build_file(self):
- package_name = 'a.package.name'
- out = io.StringIO()
-
- generate_module.write_module(generate_module.DEFAULT_BUILD_MODULE_TEMPLATE,
- 'a.package.name', out)
-
- out_str = out.getvalue()
- self.assert_braces_balanced(out_str)
- self.assertIn('csuite_config', out_str)
- self.assertIn(package_name, out_str)
-
- def assert_braces_balanced(self, generated_str: bytes) -> None:
- """Checks whether all braces in the provided string are balanced."""
- count = 0
-
- for c in generated_str:
- if c == '{':
- count += 1
- elif c == '}':
- count -= 1
-
- if count < 0:
- break
-
- self.assertEqual(count, 0,
- 'Braces in \'%s\' are not balanced' % generated_str)
-
-
-class ParsePackageListTest(csuite_test.TestCase):
-
- def test_accepts_empty_lines(self):
- lines = io.StringIO('\n\n\npackage_name\n\n')
-
- package_list = generate_module.parse_package_list(lines)
-
- self.assertListEqual(['package_name'], list(package_list))
-
- def test_strips_trailing_whitespace(self):
- lines = io.StringIO(' package_name ')
-
- package_list = generate_module.parse_package_list(lines)
-
- self.assertListEqual(['package_name'], list(package_list))
-
- def test_duplicate_package_name(self):
- lines = io.StringIO('\n\npackage_name\n\npackage_name\n')
-
- package_list = generate_module.parse_package_list(lines)
-
- self.assertListEqual(['package_name'], list(package_list))
-
- def test_ignore_comment_lines(self):
- lines = io.StringIO('\n# Comments.\npackage_name\n')
-
- package_list = generate_module.parse_package_list(lines)
-
- self.assertListEqual(['package_name'], list(package_list))
-
-
-class ParseArgsTest(fake_filesystem_unittest.TestCase):
-
- def setUp(self):
- super(ParseArgsTest, self).setUp()
- self.setUpPyfakefs()
-
- def test_configuration_file_not_exist(self):
- package_list_file_path = '/test/package_list.txt'
- root_dir = '/test/modules'
- os.makedirs(root_dir)
-
- with self.assertRaises(SystemExit):
- generate_module.parse_args(
- ['--package-list', package_list_file_path, '--root-dir', root_dir],
- out=io.StringIO(),
- err=io.StringIO())
-
- def test_module_dir_not_exist(self):
- package_list_file_path = '/test/package_list.txt'
- package_name1 = 'package_name_1'
- package_name2 = 'package_name_2'
- self.fs.create_file(
- package_list_file_path, contents=(package_name1 + '\n' + package_name2))
- root_dir = '/test/modules'
-
- with self.assertRaises(SystemExit):
- generate_module.parse_args(
- ['--package-list', package_list_file_path, '--root-dir', root_dir],
- out=io.StringIO(),
- err=io.StringIO())
-
- def test_test_module_template_file_not_exist(self):
- package_list_file_path = '/test/package_list.txt'
- package_name1 = 'package_name_1'
- package_name2 = 'package_name_2'
- self.fs.create_file(
- package_list_file_path, contents=(package_name1 + '\n' + package_name2))
- root_dir = '/test/modules'
- os.makedirs(root_dir)
- template_file_path = '/test/template.txt'
-
- with self.assertRaises(SystemExit):
- generate_module.parse_args([
- '--package-list', package_list_file_path, '--root-dir', root_dir,
- '--test', template_file_path
- ],
- out=io.StringIO(),
- err=io.StringIO())
-
- def test_build_module_template_file_not_exist(self):
- package_list_file_path = '/test/package_list.txt'
- package_name1 = 'package_name_1'
- package_name2 = 'package_name_2'
- self.fs.create_file(
- package_list_file_path, contents=(package_name1 + '\n' + package_name2))
- root_dir = '/test/modules'
- os.makedirs(root_dir)
- template_file_path = '/test/template.txt'
-
- with self.assertRaises(SystemExit):
- generate_module.parse_args([
- '--package-list', package_list_file_path, '--root-dir', root_dir,
- '--template', template_file_path
- ],
- out=io.StringIO(),
- err=io.StringIO())
-
-
-class GenerateAllModulesFromConfigTest(fake_filesystem_unittest.TestCase):
-
- def setUp(self):
- super(GenerateAllModulesFromConfigTest, self).setUp()
- self.setUpPyfakefs()
-
- def test_creates_package_files(self):
- package_list_file_path = '/test/package_list.txt'
- package_name1 = 'package_name_1'
- package_name2 = 'package_name_2'
- self.fs.create_file(
- package_list_file_path, contents=(package_name1 + '\n' + package_name2))
- root_dir = '/test/modules'
- self.fs.create_dir(root_dir)
-
- generate_module.generate_all_modules_from_config(package_list_file_path,
- root_dir)
-
- self.assertTrue(
- os.path.exists(os.path.join(root_dir, package_name1, 'Android.bp')))
- self.assertTrue(
- os.path.exists(
- os.path.join(root_dir, package_name1, 'AndroidTest.xml')))
- self.assertTrue(
- os.path.exists(os.path.join(root_dir, package_name2, 'Android.bp')))
- self.assertTrue(
- os.path.exists(
- os.path.join(root_dir, package_name2, 'AndroidTest.xml')))
-
- def test_removes_all_existing_package_files(self):
- root_dir = '/test/'
- package_dir = '/test/existing_package/'
- self.fs.create_file(
- 'test/existing_package/AndroidTest.xml', contents=_AUTO_GENERATE_NOTE)
- self.fs.create_file(
- 'test/existing_package/Android.bp', contents=_AUTO_GENERATE_NOTE)
-
- generate_module.remove_existing_package_files(root_dir)
-
- self.assertFalse(os.path.exists(package_dir))
-
-
-if __name__ == '__main__':
- csuite_test.main()
diff --git a/tools/script/generate_module_unittest.py b/tools/script/generate_module_unittest.py
new file mode 100644
index 0000000..4d24162
--- /dev/null
+++ b/tools/script/generate_module_unittest.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2020 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.
+
+import io
+import os
+import unittest
+
+from lxml import etree
+from pyfakefs import fake_filesystem_unittest
+
+import generate_module
+
+
+class WriteTestModuleTest(unittest.TestCase):
+
+ def test_xml_is_valid(self):
+ package_name = 'package_name'
+ out = io.StringIO()
+
+ generate_module.write_test_module(package_name, out)
+
+ test_module_generated = out.getvalue()
+ self.assertTrue(self._contains_license(test_module_generated))
+ self.assertTrue(self._is_validate_xml(test_module_generated))
+
+ def _contains_license(self, generated_str: bytes) -> bool:
+ return 'Copyright' in generated_str and \
+ 'Android Open Source Project' in generated_str
+
+ def _is_validate_xml(self, xml_str: bytes) -> bool:
+ xmlschema_doc = etree.parse(
+ io.BytesIO('''<?xml version="1.0" encoding="UTF-8" ?>
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="configuration">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="option" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="target_preparer" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="test" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="description"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:schema>
+ '''.encode('utf8')))
+ xmlschema = etree.XMLSchema(xmlschema_doc)
+
+ xml_doc = etree.parse(io.BytesIO(xml_str.encode('utf8')))
+ result = xmlschema.validate(xml_doc)
+
+ return result
+
+
+class WriteBuildModuleTest(unittest.TestCase):
+
+ def test_build_file_is_valid(self):
+ package_name = 'package_name'
+ out = io.StringIO()
+
+ generate_module.write_build_module(package_name, out)
+
+ build_module_generated = out.getvalue()
+ self.assertTrue(self._contains_license(build_module_generated))
+ self.assertTrue(self._are_parentheses_balanced(build_module_generated))
+ self.assertIn('csuite_config', build_module_generated)
+ self.assertIn(package_name, build_module_generated)
+
+ def _contains_license(self, generated_str: bytes) -> bool:
+ return 'Copyright' in generated_str and \
+ 'Android Open Source Project' in generated_str
+
+ def _are_parentheses_balanced(self, generated_str: bytes) -> bool:
+ parenthese_count = 0
+
+ for elem in generated_str:
+ if elem == '{':
+ parenthese_count += 1
+ elif elem == '}':
+ parenthese_count -= 1
+
+ if parenthese_count < 0:
+ return False
+
+ return parenthese_count == 0
+
+
+class ParsePackageListTest(unittest.TestCase):
+
+ def test_accepts_empty_lines(self):
+ input = io.StringIO('\n\n\npackage_name\n\n')
+
+ package_list = generate_module.parse_package_list(input)
+
+ self.assertEqual(len(package_list), 1)
+ self.assertIn('package_name', package_list)
+ self.assertTrue(all(package_list))
+
+ def test_strips_trailing_whitespace(self):
+ input = io.StringIO(' package_name ')
+
+ package_list = generate_module.parse_package_list(input)
+
+ self.assertEqual(len(package_list), 1)
+ self.assertIn('package_name', package_list)
+ self.assertTrue(all(package_list))
+
+ def test_duplicate_package_name(self):
+ input = io.StringIO('\n\npackage_name\n\npackage_name\n')
+
+ package_list = generate_module.parse_package_list(input)
+
+ self.assertEqual(len(package_list), 1)
+ self.assertIn('package_name', package_list)
+ self.assertTrue(all(package_list))
+
+
+class ParseArgsTest(fake_filesystem_unittest.TestCase):
+
+ def setUp(self):
+ super(ParseArgsTest, self).setUp()
+ self.setUpPyfakefs()
+
+ def test_configuration_file_not_exist(self):
+ package_list_file_path = '/test/package_list.txt'
+ root_dir = '/test/modules'
+ os.makedirs(root_dir)
+
+ with self.assertRaises(SystemExit):
+ generate_module.parse_args(
+ ['--package_list', package_list_file_path,
+ '--root_dir', root_dir])
+
+ def test_module_dir_not_exist(self):
+ package_list_file_path = '/test/package_list.txt'
+ package_name1 = 'package_name_1'
+ package_name2 = 'package_name_2'
+ self.fs.create_file(package_list_file_path,
+ contents=(package_name1+'\n'+package_name2))
+ root_dir = '/test/modules'
+
+ with self.assertRaises(SystemExit):
+ generate_module.parse_args(
+ ['--package_list', package_list_file_path,
+ '--root_dir', root_dir])
+
+
+class GenerateAllModulesFromConfigTest(fake_filesystem_unittest.TestCase):
+
+ def setUp(self):
+ super(GenerateAllModulesFromConfigTest, self).setUp()
+ self.setUpPyfakefs()
+
+ def test_creates_package_files(self):
+ package_list_file_path = '/test/package_list.txt'
+ package_name1 = 'package_name_1'
+ package_name2 = 'package_name_2'
+ self.fs.create_file(package_list_file_path,
+ contents=(package_name1+'\n'+package_name2))
+ root_dir = '/test/modules'
+ self.fs.create_dir(root_dir)
+
+ generate_module.generate_all_modules_from_config(
+ package_list_file_path, root_dir)
+
+ self.assertTrue(os.path.exists(
+ os.path.join(root_dir, package_name1, 'Android.bp')))
+ self.assertTrue(os.path.exists(
+ os.path.join(root_dir, package_name1, 'AndroidTest.xml')))
+ self.assertTrue(os.path.exists(
+ os.path.join(root_dir, package_name2, 'Android.bp')))
+ self.assertTrue(os.path.exists(
+ os.path.join(root_dir, package_name2, 'AndroidTest.xml')))
+
+ def test_removes_all_existing_package_files(self):
+ root_dir = '/test/'
+ package_dir = '/test/existing_package/'
+ existing_package_file1 = 'test/existing_package/AndroidTest.xml'
+ existing_package_file2 = 'test/existing_package/Android.bp'
+ self.fs.create_file(existing_package_file1, contents='auto-generated')
+ self.fs.create_file(existing_package_file2, contents='auto-generated')
+
+ generate_module.remove_existing_package_files(root_dir)
+
+ self.assertFalse(os.path.exists(existing_package_file1))
+ self.assertFalse(os.path.exists(existing_package_file2))
+ self.assertFalse(os.path.exists(package_dir))
+
+
+if __name__ == '__main__':
+ unittest.main()