Merge "CP: Add EGL extensions to graphics device info" into nougat-cts-dev
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 541bcd6..b12792f 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -23,6 +23,9 @@
import its.device
from its.device import ItsSession
+CHART_DELAY = 1 # seconds
+
+
def main():
"""Run all the automated tests, saving intermediate files, and producing
a summary/report of the results.
@@ -223,6 +226,8 @@
cmd = ['python',
os.path.join(os.getcwd(), 'tools/load_scene.py'),
scene_arg, screen_id_arg]
+ else:
+ time.sleep(CHART_DELAY)
else:
# Skip scene validation for scene 5 running in parallel
if not merge_result_switch or scene != 'scene5':
diff --git a/apps/CameraITS/tools/wake_up_screen.py b/apps/CameraITS/tools/wake_up_screen.py
index 68a974a..f14c9f2 100644
--- a/apps/CameraITS/tools/wake_up_screen.py
+++ b/apps/CameraITS/tools/wake_up_screen.py
@@ -18,7 +18,8 @@
import time
DISPLAY_LEVEL = 96 # [0:255] Depends on tablet model. Adjust for best result.
-DISPLAY_WAIT = 0.5 # seconds. Screen commands take time to have effect
+DISPLAY_CMD_WAIT = 0.5 # seconds. Screen commands take time to have effect
+DISPLAY_TIMEOUT = 1800000 # ms.
def main():
@@ -38,23 +39,32 @@
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
cmd_ret = process.stdout.read()
screen_state = re.split(r'[s|=]', cmd_ret)[-1]
- if 'OFF' in screen_state:
- print 'Screen OFF. Turning ON.'
- wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
- subprocess.Popen(wakeup.split())
- time.sleep(DISPLAY_WAIT)
+ power_event = ('adb -s %s shell input keyevent POWER' % screen_id)
+ subprocess.Popen(power_event.split())
+ time.sleep(DISPLAY_CMD_WAIT)
+ if 'ON' in screen_state:
+ print 'Screen was ON. Toggling to refresh.'
+ subprocess.Popen(power_event.split())
+ time.sleep(DISPLAY_CMD_WAIT)
+ else:
+ print 'Screen was OFF. Powered ON.'
unlock = ('adb -s %s wait-for-device shell wm dismiss-keyguard'
% screen_id)
subprocess.Popen(unlock.split())
- time.sleep(DISPLAY_WAIT)
+ time.sleep(DISPLAY_CMD_WAIT)
# set brightness
print 'Tablet display brightness set to %d' % DISPLAY_LEVEL
bright = ('adb -s %s shell settings put system screen_brightness %d'
% (screen_id, DISPLAY_LEVEL))
subprocess.Popen(bright.split())
- time.sleep(DISPLAY_WAIT)
+ time.sleep(DISPLAY_CMD_WAIT)
+ # set screen to dim at max time (30min)
+ stay_bright = ('adb -s %s shell settings put system screen_off_timeout %d'
+ % (screen_id, DISPLAY_TIMEOUT))
+ subprocess.Popen(stay_bright.split())
+ time.sleep(DISPLAY_CMD_WAIT)
if __name__ == '__main__':
main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index bf7529a..50fd538 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2094,8 +2094,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_audio" />
- <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
- <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.microphone:android.hardware.usb.host" />
</activity>
<service android:name=".tv.MockTvInputService"
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index db9ad94..c8d86d08 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -18,7 +18,7 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.ResultHandler;
@@ -405,7 +405,7 @@
}
private void addSubPlan(String[] flatArgs) {
- SubPlanCreator creator = new SubPlanCreator();
+ SubPlanHelper creator = new SubPlanHelper();
try {
ArgsOptionParser optionParser = new ArgsOptionParser(creator);
optionParser.parse(Arrays.asList(flatArgs));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 7d198ae..d16b0e0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -19,7 +19,7 @@
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.result.TestRunHandler;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
+import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
import com.android.compatibility.common.util.IModuleResult;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
similarity index 89%
rename from common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
rename to common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9dbbcbb..950a129 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -33,11 +33,15 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+import com.android.tradefed.util.StreamUtil;
import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
@@ -50,7 +54,9 @@
/**
* Class for creating subplans from compatibility result XML.
*/
-public class SubPlanCreator {
+public class SubPlanHelper {
+
+ private static final String XML_EXT = ".xml";
// result types
public static final String PASSED = "passed";
@@ -108,22 +114,45 @@
IInvocationResult mResult = null;
/**
- * Create an empty {@link SubPlanCreator}.
+ * Create an empty {@link SubPlanHelper}.
* <p/>
* All {@link Option} fields must be populated via
* {@link com.android.tradefed.config.ArgsOptionParser}
*/
- public SubPlanCreator() {}
+ public SubPlanHelper() {}
/**
- * Create a {@link SubPlanCreator} using the specified option values.
+ * Create a {@link SubPlanHelper} using the specified option values.
*/
- public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
+ public SubPlanHelper(String name, int session, Collection<String> resultTypes) {
mSubPlanName = name;
mSessionId = session;
mResultTypes.addAll(resultTypes);
}
+ public static ISubPlan getSubPlanByName(CompatibilityBuildHelper buildHelper, String name) {
+ if (!name.endsWith(XML_EXT)) {
+ name = name + XML_EXT; // only append XML extension to name if not already there
+ }
+ InputStream subPlanInputStream = null;
+ try {
+ File subPlanFile = new File(buildHelper.getSubPlansDir(), name);
+ if (!subPlanFile.exists()) {
+ throw new IllegalArgumentException(
+ String.format("Could not retrieve subplan \"%s\"", name));
+ }
+ subPlanInputStream = new FileInputStream(subPlanFile);
+ ISubPlan subPlan = new SubPlan();
+ subPlan.parse(subPlanInputStream);
+ return subPlan;
+ } catch (FileNotFoundException | ParseException e) {
+ throw new RuntimeException(
+ String.format("Unable to find or parse subplan %s", name), e);
+ } finally {
+ StreamUtil.closeStream(subPlanInputStream);
+ }
+ }
+
/**
* Set the result from which to derive the subplan.
* @param result
@@ -333,7 +362,7 @@
mSubPlanName = createPlanName();
}
try {
- mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + ".xml");
+ mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + XML_EXT);
if (mSubPlanFile.exists()) {
throw new ConfigurationException(String.format("Subplan %s already exists",
mSubPlanName));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
index 6b87977..72fe373 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
@@ -54,6 +54,9 @@
BEFORE, AFTER, BOTH;
}
+ @Option(name = "throw-error", description = "Whether to throw error for device test failure")
+ protected boolean mThrowError = true;
+
@Option(name = "when", description = "When to instrument the apk", mandatory = true)
protected When mWhen = null;
@@ -73,7 +76,7 @@
try {
if (instrument(device, buildInfo)) {
logInfo("Target preparation successful");
- } else {
+ } else if (mThrowError) {
throw new TargetSetupError("Not all target preparation steps completed");
}
} catch (FileNotFoundException e) {
@@ -128,7 +131,12 @@
for (TestIdentifier test : testFailures.keySet()) {
success = false;
String trace = testFailures.get(test);
- logError("Target preparation step %s failed.\n%s", test.getTestName(), trace);
+ if (mThrowError) {
+ logError("Target preparation step %s failed.\n%s", test.getTestName(), trace);
+ } else {
+ logWarning("Target preparation step %s failed.\n%s", test.getTestName(),
+ trace);
+ }
}
}
return success;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 7609254..248b586 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -19,10 +19,12 @@
import com.android.compatibility.SuiteInfo;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
+import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.util.AbiUtils;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
@@ -33,7 +35,6 @@
import com.android.compatibility.common.util.TestStatus;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ArgsOptionParser;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.ConfigurationFactory;
import com.android.tradefed.config.IConfiguration;
@@ -150,20 +151,16 @@
@Option(name = MODULE_ARG_OPTION,
description = "the arguments to pass to a module. The expected format is"
- + "\"<module-name>:<arg-name>:<arg-value>\"",
+ + "\"<module-name>:<arg-name>:[<arg-key>:]<arg-value>\"",
importance = Importance.ALWAYS)
private List<String> mModuleArgs = new ArrayList<>();
@Option(name = TEST_ARG_OPTION,
description = "the arguments to pass to a test. The expected format is"
- + "\"<test-class>:<arg-name>:<arg-value>\"",
+ + "\"<test-class>:<arg-name>:[<arg-key>:]<arg-value>\"",
importance = Importance.ALWAYS)
private List<String> mTestArgs = new ArrayList<>();
- public enum RetryType {
- FAILED, NOT_EXECUTED;
- }
-
@Option(name = RETRY_OPTION,
shortName = 'r',
description = "retry a previous session's failed and not executed tests.",
@@ -605,117 +602,44 @@
*/
void setupFilters() throws DeviceNotAvailableException {
if (mRetrySessionId != null) {
- // Track --module/-m and --test/-t options to ensure we don't overwrite non-null
- // values on retry
- String newModuleName = mModuleName;
- String newTestName = mTestName;
-
- // Load the invocation result
- IInvocationResult result = null;
- try {
- result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
- if (result == null) {
- throw new IllegalArgumentException(String.format(
- "Could not find session with id %d", mRetrySessionId));
- }
-
- String oldBuildFingerprint = result.getBuildFingerprint();
- String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint");
- if (oldBuildFingerprint.equals(currentBuildFingerprint)) {
- CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s",
- CompatibilityBuildHelper.getDirSuffix(result.getStartTime()));
- } else {
- throw new IllegalArgumentException(String.format(
- "Device build fingerprint must match %s to retry session %d",
- oldBuildFingerprint, mRetrySessionId));
- }
-
- String retryCommandLineArgs = result.getCommandLineArgs();
- if (retryCommandLineArgs != null) {
- try {
- // parse the command-line string from the result file and set options
- ArgsOptionParser parser = new ArgsOptionParser(this);
- parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this));
- } catch (ConfigurationException e) {
- throw new RuntimeException(e);
- }
- }
-
- if ((mModuleName != null && mModuleName != newModuleName)
- || (mTestName != null && mTestName != newTestName)) {
- // These options cannot be changed on retry if non-null for the previous session
- CLog.w("Cannot override non-null value(s) from session %d for option(s) \"%s\""
- + " or \"%s\" on retry", mRetrySessionId, MODULE_OPTION, TEST_OPTION);
- }
-
- SubPlanCreator retryPlanCreator = new SubPlanCreator();
- retryPlanCreator.setResult(result);
- if (RetryType.FAILED.equals(mRetryType)) {
- // retry only failed tests
- retryPlanCreator.addResultType(SubPlanCreator.FAILED);
- } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
- // retry only not executed tests
- retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
- } else {
- // retry both failed and not executed tests
- retryPlanCreator.addResultType(SubPlanCreator.FAILED);
- retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
- }
- try {
- ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
- mIncludeFilters.addAll(retryPlan.getIncludeFilters());
- mExcludeFilters.addAll(retryPlan.getExcludeFilters());
- } catch (ConfigurationException e) {
- throw new RuntimeException ("Failed to create subplan for retry", e);
- }
- }
- if (mSubPlan != null) {
- try {
- File subPlanFile = new File(mBuildHelper.getSubPlansDir(), mSubPlan + ".xml");
- if (!subPlanFile.exists()) {
- throw new IllegalArgumentException(
- String.format("Could not retrieve subplan \"%s\"", mSubPlan));
- }
- InputStream subPlanInputStream = new FileInputStream(subPlanFile);
- ISubPlan subPlan = new SubPlan();
- subPlan.parse(subPlanInputStream);
+ RetryFilterHelper helper = new RetryFilterHelper(mBuildHelper, mRetrySessionId);
+ helper.validateBuildFingerprint(mDevice);
+ helper.setAllOptionsFrom(this);
+ helper.setCommandLineOptionsFor(this);
+ helper.populateRetryFilters();
+ mIncludeFilters = helper.getIncludeFilters();
+ mExcludeFilters = helper.getExcludeFilters();
+ helper.tearDown();
+ } else {
+ if (mSubPlan != null) {
+ ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan);
mIncludeFilters.addAll(subPlan.getIncludeFilters());
mExcludeFilters.addAll(subPlan.getExcludeFilters());
- } catch (FileNotFoundException | ParseException e) {
- throw new RuntimeException(
- String.format("Unable to find or parse subplan %s", mSubPlan), e);
}
- }
- if (mModuleName != null) {
- try {
- List<String> modules = ModuleRepo.getModuleNamesMatching(
- mBuildHelper.getTestsDir(), mModuleName);
- if (modules.size() == 0) {
- throw new IllegalArgumentException(
- String.format("No modules found matching %s", mModuleName));
- } else if (modules.size() > 1) {
- throw new IllegalArgumentException(String.format(
- "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
- mModuleName, ArrayUtil.join("\n", modules)));
- } else {
- String module = modules.get(0);
- cleanFilters(mIncludeFilters, module);
- cleanFilters(mExcludeFilters, module);
- mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
+ if (mModuleName != null) {
+ try {
+ List<String> modules = ModuleRepo.getModuleNamesMatching(
+ mBuildHelper.getTestsDir(), mModuleName);
+ if (modules.size() == 0) {
+ throw new IllegalArgumentException(
+ String.format("No modules found matching %s", mModuleName));
+ } else if (modules.size() > 1) {
+ throw new IllegalArgumentException(String.format("Multiple modules found"
+ + " matching %s:\n%s\nWhich one did you mean?\n",
+ mModuleName, ArrayUtil.join("\n", modules)));
+ } else {
+ String module = modules.get(0);
+ cleanFilters(mIncludeFilters, module);
+ cleanFilters(mExcludeFilters, module);
+ mIncludeFilters.add(
+ new TestFilter(mAbiName, module, mTestName).toString());
+ }
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
}
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- } else if (mTestName != null) {
- throw new IllegalArgumentException(
- "Test name given without module name. Add --module <module-name>");
- } else {
- // If a module has an arg, assume it's included
- for (String arg : mModuleArgs) {
- mIncludeFilters.add(arg.split(":")[0]);
+ } else if (mTestName != null) {
+ throw new IllegalArgumentException(
+ "Test name given without module name. Add --module <module-name>");
}
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 78995f0..89f6ded 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -299,7 +299,17 @@
}
if (args != null && args.size() > 0) {
for (Entry<String, String> entry : args.entrySet()) {
- config.injectOptionValue(entry.getKey(), entry.getValue());
+ String entryName = entry.getKey();
+ String entryValue = entry.getValue();
+ if (entryValue.contains(":")) {
+ // entryValue is key-value pair
+ String key = entryValue.split(":")[0];
+ String value = entryValue.split(":")[1];
+ config.injectOptionValue(entryName, key, value);
+ } else {
+ // entryValue is just the argument value
+ config.injectOptionValue(entryName, entryValue);
+ }
}
}
}
@@ -312,7 +322,17 @@
}
if (args != null && args.size() > 0) {
for (Entry<String, String> entry : args.entrySet()) {
- config.injectOptionValue(entry.getKey(), entry.getValue());
+ String entryName = entry.getKey();
+ String entryValue = entry.getValue();
+ if (entryValue.contains(":")) {
+ // entryValue is key-value pair
+ String key = entryValue.split(":")[0];
+ String value = entryValue.split(":")[1];
+ config.injectOptionValue(entryName, key, value);
+ } else {
+ // entryValue is just the argument value
+ config.injectOptionValue(entryName, entryValue);
+ }
}
}
addFiltersToTest(test, abi, name);
@@ -630,14 +650,20 @@
for (String arg : args) {
String[] parts = arg.split(":");
String target = parts[0];
- String key = parts[1];
- String value = parts[2];
+ String name = parts[1];
+ String value;
+ if (parts.length == 4) {
+ // key and value given, keep the pair delimited by ':' and stored as value
+ value = String.format("%s:%s", parts[2], parts[3]);
+ } else {
+ value = parts[2];
+ }
Map<String, String> map = argsMap.get(target);
if (map == null) {
map = new HashMap<>();
argsMap.put(target, map);
}
- map.put(key, value);
+ map.put(name, value);
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
new file mode 100644
index 0000000..9a68089
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 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.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.LightInvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.ArrayUtil;
+
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper for generating --include-filter and --exclude-filter values on compatibility retry.
+ */
+public class RetryFilterHelper {
+
+ @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+ description = "the subplan to run",
+ importance = Importance.IF_UNSET)
+ protected String mSubPlan;
+
+ @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+ description = "the include module filters to apply.",
+ importance = Importance.ALWAYS)
+ protected Set<String> mIncludeFilters = new HashSet<>();
+
+ @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+ description = "the exclude module filters to apply.",
+ importance = Importance.ALWAYS)
+ protected Set<String> mExcludeFilters = new HashSet<>();
+
+ @Option(name = CompatibilityTest.ABI_OPTION,
+ shortName = 'a',
+ description = "the abi to test.",
+ importance = Importance.IF_UNSET)
+ protected String mAbiName = null;
+
+ @Option(name = CompatibilityTest.MODULE_OPTION,
+ shortName = 'm',
+ description = "the test module to run.",
+ importance = Importance.IF_UNSET)
+ protected String mModuleName = null;
+
+ @Option(name = CompatibilityTest.TEST_OPTION,
+ shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+ description = "the test run.",
+ importance = Importance.IF_UNSET)
+ protected String mTestName = null;
+
+ @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+ description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+ + " of a certain status. Possible values include \"failed\" and \"not_executed\".",
+ importance = Importance.IF_UNSET)
+ protected RetryType mRetryType = null;
+
+ /* Instance variables handy for retreiving the result to be retried */
+ private CompatibilityBuildHelper mBuild = null;
+ private int mSessionId;
+
+ /* Sets to be populated by retry logic and returned by getter methods */
+ private Set<String> mRetryIncludes;
+ private Set<String> mRetryExcludes;
+
+ /**
+ * Constructor for a {@link RetryFilterHelper}. Requires a CompatibilityBuildHelper for
+ * retrieving previous sessions and the ID of the session to retry.
+ */
+ public RetryFilterHelper(CompatibilityBuildHelper build, int sessionId) {
+ mBuild = build;
+ mSessionId = sessionId;
+ }
+
+ /**
+ * Throws an {@link IllegalArgumentException} if the device build fingerprint doesn't match
+ * the fingerprint recorded in the previous session's result.
+ */
+ public void validateBuildFingerprint(ITestDevice device) throws DeviceNotAvailableException {
+ String oldBuildFingerprint = new LightInvocationResult(getResult()).getBuildFingerprint();
+ String currentBuildFingerprint = device.getProperty("ro.build.fingerprint");
+ if (!oldBuildFingerprint.equals(currentBuildFingerprint)) {
+ throw new IllegalArgumentException(String.format(
+ "Device build fingerprint must match %s to retry session %d",
+ oldBuildFingerprint, mSessionId));
+ }
+ }
+
+ /**
+ * Copy all applicable options from an input object to this instance of RetryFilterHelper.
+ */
+ public void setAllOptionsFrom(Object obj) {
+ clearOptions(); // Remove existing options first
+ OptionCopier.copyOptionsNoThrow(obj, this);
+ }
+
+ /**
+ * Set a single option on this instance of RetryFilterHelper
+ * @throws {@link ConfigurationException} if the option cannot be set.
+ */
+ public void setOption(String option, String value) throws ConfigurationException {
+ OptionSetter setter = new OptionSetter(this);
+ setter.setOptionValue(option, value);
+ }
+
+ /**
+ * Clear all option values of this RetryFilterHelper.
+ */
+ public void clearOptions() {
+ mSubPlan = null;
+ mIncludeFilters = new HashSet<>();
+ mExcludeFilters = new HashSet<>();
+ mModuleName = null;
+ mTestName = null;
+ mRetryType = null;
+ mAbiName = null;
+ }
+
+ /**
+ * Using command-line arguments from the previous session's result, set the input object's
+ * option values to the values applied in the previous session.
+ */
+ public void setCommandLineOptionsFor(Object obj) {
+ // only need light version to retrieve command-line args
+ IInvocationResult result = new LightInvocationResult(getResult());
+ String retryCommandLineArgs = result.getCommandLineArgs();
+ if (retryCommandLineArgs != null) {
+ try {
+ // parse the command-line string from the result file and set options
+ ArgsOptionParser parser = new ArgsOptionParser(obj);
+ parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, obj));
+ } catch (ConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Retrieve an instance of the result to retry using the instance variables referencing
+ * the build and the desired session ID. While it is faster to load this result once and
+ * store it as an instance variable, {@link IInvocationResult} objects are large, and
+ * memory is of greater concern.
+ */
+ public IInvocationResult getResult() {
+ IInvocationResult result = null;
+ try {
+ result = ResultHandler.findResult(mBuild.getResultsDir(), mSessionId);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (result == null) {
+ throw new IllegalArgumentException(String.format(
+ "Could not find session with id %d", mSessionId));
+ }
+ return result;
+ }
+
+ /**
+ * Populate mRetryIncludes and mRetryExcludes based on the options and the result set for
+ * this instance of RetryFilterHelper.
+ */
+ public void populateRetryFilters() {
+ mRetryIncludes = new HashSet<>(mIncludeFilters); // reset for each population
+ mRetryExcludes = new HashSet<>(mExcludeFilters); // reset for each population
+ if (RetryType.CUSTOM.equals(mRetryType)) {
+ Set<String> customIncludes = new HashSet<>(mIncludeFilters);
+ Set<String> customExcludes = new HashSet<>(mExcludeFilters);
+ if (mSubPlan != null) {
+ ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+ customIncludes.addAll(retrySubPlan.getIncludeFilters());
+ customExcludes.addAll(retrySubPlan.getExcludeFilters());
+ }
+ // If includes were added, only use those includes. Also use excludes added directly
+ // or by subplan. Otherwise, default to normal retry.
+ if (!customIncludes.isEmpty()) {
+ mRetryIncludes.clear();
+ mRetryIncludes.addAll(customIncludes);
+ mRetryExcludes.addAll(customExcludes);
+ return;
+ }
+ }
+ // remove any extra filtering options
+ // TODO(aaronholden) remove non-plan includes (e.g. those in cts-vendor-interface)
+ // TODO(aaronholden) remove non-known-failure excludes
+ mModuleName = null;
+ mTestName = null;
+ mSubPlan = null;
+ populateFiltersBySubPlan();
+ populatePreviousSessionFilters();
+ }
+
+ /* Generation of filters based on previous sessions is implemented thoroughly in SubPlanHelper,
+ * and retry filter generation is just a subset of the use cases for the subplan retry logic.
+ * Use retry type to determine which result types SubPlanHelper targets. */
+ private void populateFiltersBySubPlan() {
+ SubPlanHelper retryPlanCreator = new SubPlanHelper();
+ retryPlanCreator.setResult(getResult());
+ if (RetryType.FAILED.equals(mRetryType)) {
+ // retry only failed tests
+ retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+ } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
+ // retry only not executed tests
+ retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+ } else {
+ // retry both failed and not executed tests
+ retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+ retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+ }
+ try {
+ ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuild);
+ mRetryIncludes.addAll(retryPlan.getIncludeFilters());
+ mRetryExcludes.addAll(retryPlan.getExcludeFilters());
+ } catch (ConfigurationException e) {
+ throw new RuntimeException ("Failed to create subplan for retry", e);
+ }
+ }
+
+ /* Retrieves the options set via command-line on the previous session, and generates/adds
+ * filters accordingly */
+ private void populatePreviousSessionFilters() {
+ // Temporarily store options from this instance in another instance
+ RetryFilterHelper tmpHelper = new RetryFilterHelper(mBuild, mSessionId);
+ tmpHelper.setAllOptionsFrom(this);
+ // Copy command-line args from previous session to this RetryFilterHelper's options
+ setCommandLineOptionsFor(this);
+
+ mRetryIncludes.addAll(mIncludeFilters);
+ mRetryExcludes.addAll(mExcludeFilters);
+ if (mSubPlan != null) {
+ ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+ mRetryIncludes.addAll(retrySubPlan.getIncludeFilters());
+ mRetryExcludes.addAll(retrySubPlan.getExcludeFilters());
+ }
+ if (mModuleName != null) {
+ try {
+ List<String> modules = ModuleRepo.getModuleNamesMatching(
+ mBuild.getTestsDir(), mModuleName);
+ if (modules.size() == 0) {
+ throw new IllegalArgumentException(
+ String.format("No modules found matching %s", mModuleName));
+ } else if (modules.size() > 1) {
+ throw new IllegalArgumentException(String.format(
+ "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
+ mModuleName, ArrayUtil.join("\n", modules)));
+ } else {
+ String module = modules.get(0);
+ cleanFilters(mRetryIncludes, module);
+ cleanFilters(mRetryExcludes, module);
+ mRetryIncludes.add(new TestFilter(mAbiName, module, mTestName).toString());
+ }
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ } else if (mTestName != null) {
+ throw new IllegalArgumentException(
+ "Test name given without module name. Add --module <module-name>");
+ }
+
+ // Copy options for current session back to this instance
+ setAllOptionsFrom(tmpHelper);
+ }
+
+ /* Helper method designed to remove filters in a list not applicable to the given module */
+ private static void cleanFilters(Set<String> filters, String module) {
+ Set<String> cleanedFilters = new HashSet<String>();
+ for (String filter : filters) {
+ if (module.equals(TestFilter.createFrom(filter).getName())) {
+ cleanedFilters.add(filter); // Module name matches, filter passes
+ }
+ }
+ filters.clear();
+ filters.addAll(cleanedFilters);
+ }
+
+ /** Retrieve include filters to be applied on retry */
+ public Set<String> getIncludeFilters() {
+ return new HashSet<>(mRetryIncludes);
+ }
+
+ /** Retrieve exclude filters to be applied on retry */
+ public Set<String> getExcludeFilters() {
+ return new HashSet<>(mRetryExcludes);
+ }
+
+ /** Clears retry filters and internal storage of options, except buildInfo and session ID */
+ public void tearDown() {
+ clearOptions();
+ mRetryIncludes = null;
+ mRetryExcludes = null;
+ // keep references to buildInfo and session ID
+ }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
new file mode 100644
index 0000000..b5d3cd5
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.common.tradefed.util;
+
+/**
+ * Enum for --retry-type option value in compatibility testing.
+ */
+public enum RetryType {
+ FAILED,
+ NOT_EXECUTED,
+ CUSTOM;
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index 860c830..43e2837 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -21,15 +21,16 @@
import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
import com.android.compatibility.common.tradefed.result.MetadataReporterTest;
import com.android.compatibility.common.tradefed.result.ResultReporterTest;
-import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
+import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTestTest;
import com.android.compatibility.common.tradefed.testtype.ModuleDefTest;
import com.android.compatibility.common.tradefed.testtype.ModuleRepoTest;
import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
-import com.android.compatibility.common.tradefed.util.OptionHelperTest;
import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
+import com.android.compatibility.common.tradefed.util.OptionHelperTest;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelperTest;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -58,7 +59,8 @@
addTestSuite(PropertyCheckTest.class);
addTestSuite(SettingsPreparerTest.class);
addTestSuite(SubPlanTest.class);
- addTestSuite(SubPlanCreatorTest.class);
+ addTestSuite(SubPlanHelperTest.class);
+ addTestSuite(RetryFilterHelperTest.class);
}
public static Test suite() {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
new file mode 100644
index 0000000..a19eb30
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 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.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.ICaseResult;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
+import com.android.compatibility.common.util.InvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.compatibility.common.util.TestStatus;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.util.AbiUtils;
+import com.android.tradefed.util.FileUtil;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Set;
+
+public class SubPlanHelperTest extends TestCase {
+
+ // Values used to populate mock results
+ private static final String SUITE_NAME = "CTS";
+ private static final String SUITE_VERSION = "5.0";
+ private static final String SUITE_PLAN = "cts";
+ private static final String SUITE_BUILD = "12345";
+ private static final String NAME_A = "ModuleA";
+ private static final String NAME_B = "ModuleB";
+ private static final String ABI = "mips64";
+ private static final String ID_A = AbiUtils.createId(ABI, NAME_A);
+ private static final String ID_B = AbiUtils.createId(ABI, NAME_B);
+ private static final String BUILD_ID = "build_id";
+ private static final String BUILD_PRODUCT = "build_product";
+ private static final String EXAMPLE_BUILD_ID = "XYZ";
+ private static final String EXAMPLE_BUILD_PRODUCT = "wolverine";
+ private static final String DEVICE_A = "device123";
+ private static final String DEVICE_B = "device456";
+ private static final String CLASS_A = "android.test.Foor";
+ private static final String CLASS_B = "android.test.Bar";
+ private static final String METHOD_1 = "testBlah1";
+ private static final String METHOD_2 = "testBlah2";
+ private static final String METHOD_3 = "testBlah3";
+ private static final String METHOD_4 = "testBlah4";
+ private static final long START_MS = 1431586801000L;
+ private static final long END_MS = 1431673199000L;
+ private static final String REFERENCE_URL="http://android.com";
+ private static final String LOG_URL ="file:///path/to/logs";
+ private static final String COMMAND_LINE_ARGS = "cts -m CtsMyModuleTestCases";
+
+ private static final String SP_NAME = "testsubplan";
+ private static final String SP_SESSION = "0";
+ private static final String SP_RESULT_TYPE_FAILED = "failed";
+ private static final String SP_RESULT_TYPE_NOT_EXECUTED = "not_executed";
+
+ private CompatibilityBuildHelper mBuildHelper;
+ private SubPlanHelper mSubPlanHelper;
+
+ private File mResultsDir = null;
+ private File mResultDir = null;
+ private File mSubPlansDir = null;
+
+ @Override
+ public void setUp() throws Exception {
+ mResultsDir = FileUtil.createTempDir("results");
+ mResultDir = FileUtil.createTempDir("12345", mResultsDir);
+ mSubPlansDir = FileUtil.createTempDir("subplans");
+ mBuildHelper = new SpctMockCompatibilityBuildHelper(new BuildInfo("0", "", ""));
+ populateResults();
+
+ mSubPlanHelper = new SubPlanHelper();
+ ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanHelper);
+ optionParser.parse(Arrays.asList(
+ "-n", SP_NAME,
+ "--session", SP_SESSION,
+ "--result-type", SP_RESULT_TYPE_FAILED,
+ "--result-type", SP_RESULT_TYPE_NOT_EXECUTED));
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mResultsDir != null) {
+ FileUtil.recursiveDelete(mResultsDir);
+ }
+ if (mSubPlansDir != null) {
+ FileUtil.recursiveDelete(mSubPlansDir);
+ }
+ }
+
+ public void testCreateSubPlan() throws Exception {
+ ISubPlan plan = mSubPlanHelper.createSubPlan(mBuildHelper);
+ Set<String> planIncludes = plan.getIncludeFilters();
+ Set<String> planExcludes = plan.getExcludeFilters();
+ TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
+ TestFilter tf1 = new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
+ TestFilter tf3 = new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_3));
+ assertTrue(planIncludes.contains("CtsMyModuleTestCases")); // command-line '-m' arg
+ assertTrue(planIncludes.contains(mf1.toString())); // include module with not-executed test
+ assertTrue(planExcludes.contains(tf1.toString())); // exclude passing test in that module
+ assertTrue(planIncludes.contains(tf3.toString())); // include failure in executed module
+ }
+
+ private void populateResults() throws Exception {
+ // copied from ResultHandlerTest
+ IInvocationResult result = new InvocationResult();
+ result.setStartTime(START_MS);
+ result.setTestPlan(SUITE_PLAN);
+ result.addDeviceSerial(DEVICE_A);
+ result.addDeviceSerial(DEVICE_B);
+ result.addInvocationInfo(BUILD_ID, EXAMPLE_BUILD_ID);
+ result.addInvocationInfo(BUILD_PRODUCT, EXAMPLE_BUILD_PRODUCT);
+ IModuleResult moduleA = result.getOrCreateModule(ID_A);
+ moduleA.setDone(false);
+ ICaseResult moduleACase = moduleA.getOrCreateResult(CLASS_A);
+ ITestResult moduleATest1 = moduleACase.getOrCreateResult(METHOD_1);
+ moduleATest1.setResultStatus(TestStatus.PASS);
+ ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
+ moduleATest2.setResultStatus(null); // not executed test
+ moduleA.setNotExecuted(1);
+
+ IModuleResult moduleB = result.getOrCreateModule(ID_B);
+ moduleB.setDone(true);
+ ICaseResult moduleBCase = moduleB.getOrCreateResult(CLASS_B);
+ ITestResult moduleBTest3 = moduleBCase.getOrCreateResult(METHOD_3);
+ moduleBTest3.setResultStatus(TestStatus.FAIL);
+ ITestResult moduleBTest4 = moduleBCase.getOrCreateResult(METHOD_4);
+ moduleBTest4.setResultStatus(TestStatus.PASS);
+
+ // Serialize to file
+ ResultHandler.writeResults(SUITE_NAME, SUITE_VERSION, SUITE_PLAN, SUITE_BUILD,
+ result, mResultDir, START_MS, END_MS, REFERENCE_URL, LOG_URL,
+ COMMAND_LINE_ARGS);
+ }
+
+ private class SpctMockCompatibilityBuildHelper extends CompatibilityBuildHelper {
+
+ public SpctMockCompatibilityBuildHelper(IBuildInfo buildInfo) {
+ super(buildInfo);
+ }
+
+ @Override
+ public File getResultsDir() throws FileNotFoundException {
+ return mResultsDir;
+ }
+
+ @Override
+ public File getSubPlansDir() throws FileNotFoundException {
+ return mSubPlansDir;
+ }
+ }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
new file mode 100644
index 0000000..05d35ec
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.tradefed.config.OptionSetter;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link RetryFilterHelper}
+ */
+public class RetryFilterHelperTest extends TestCase {
+
+ private static final String TEST_STRING = "abcd";
+ private static final RetryType TEST_RETRY_TYPE = RetryType.FAILED;
+
+ public void testSetAllOptionsFrom() throws Exception {
+ RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+ RetryFilterHelper otherObj = new RetryFilterHelper(null, 0);
+ OptionSetter otherObjSetter = new OptionSetter(otherObj);
+ otherObjSetter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+ helper.setAllOptionsFrom(otherObj);
+ assertEquals(TEST_STRING, helper.mSubPlan);
+ }
+
+ public void testClearOptions() throws Exception {
+ RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+ OptionSetter setter = new OptionSetter(helper);
+ setter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.INCLUDE_FILTER_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.EXCLUDE_FILTER_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.ABI_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.MODULE_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_STRING);
+ setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_RETRY_TYPE.name());
+ helper.clearOptions();
+ assertTrue(helper.mSubPlan == null);
+ assertTrue(helper.mIncludeFilters.isEmpty());
+ assertTrue(helper.mExcludeFilters.isEmpty());
+ assertTrue(helper.mAbiName == null);
+ assertTrue(helper.mModuleName == null);
+ assertTrue(helper.mTestName == null);
+ assertTrue(helper.mRetryType == null);
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index a00b4eb..18db8df 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -46,7 +46,7 @@
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordSucceeds("1234", caseDescription);
@@ -56,7 +56,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false); // length not checked for this quality
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -66,7 +66,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordSucceeds("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -78,7 +78,7 @@
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient()); // failure
+ assertPasswordSufficiency(false); // failure
String caseDescription = "initial";
assertPasswordSucceeds("1234", caseDescription);
@@ -88,7 +88,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -98,7 +98,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordSucceeds("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -110,7 +110,7 @@
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordFails("1234", caseDescription); // can't change
@@ -120,7 +120,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -130,7 +130,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordFails("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription);
@@ -142,7 +142,7 @@
DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
dpm.getPasswordQuality(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
String caseDescription = "initial";
assertPasswordFails("1234", caseDescription);
@@ -152,7 +152,7 @@
dpm.setPasswordMinimumLength(mAdminComponent, 10);
caseDescription = "minimum password length = 10";
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
- assertFalse(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(false);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -162,7 +162,7 @@
caseDescription = "minimum password length = 4";
assertEquals(4, dpm.getPasswordMinimumLength(
mAdminComponent));
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
assertPasswordFails("1234", caseDescription);
assertPasswordFails("abcd", caseDescription);
@@ -371,6 +371,21 @@
private void assertPasswordSucceeds(String password, String restriction) {
boolean passwordResetResult = dpm.resetPassword(password, /* flags= */0);
assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
- assertTrue(dpm.isActivePasswordSufficient());
+ assertPasswordSufficiency(true);
+ }
+
+ private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
+ int retries = 15;
+ // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
+ while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
+ retries--;
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
+
}
}
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 6e9aead..2804d3b 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -159,6 +159,8 @@
android:exported="true"
android:launchMode="singleInstance"
/>
+ <activity android:name=".MultiWindowSupportObserver"
+ android:exported="true" />
</application>
</manifest>
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java b/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java
new file mode 100644
index 0000000..0d54912
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java
@@ -0,0 +1,18 @@
+package android.server.app;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MultiWindowSupportObserver extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int id = Resources.getSystem().getIdentifier("config_supportsMultiWindow", "bool", "android");
+ boolean support = Resources.getSystem().getBoolean(id);
+ Log.i(getClass().getSimpleName(), "HEAD=OK");
+ Log.i(getClass().getSimpleName(), "DROP=OK");
+ Log.i(getClass().getSimpleName(), "config_supportsMultiWindow="+support);
+ }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 3a21fda..09bb294 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -26,7 +26,9 @@
import java.lang.Exception;
import java.lang.Integer;
import java.lang.String;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -78,6 +80,11 @@
protected ITestDevice mDevice;
private HashSet<String> mAvailableFeatures;
+ final private String RESULT_KEY_HEAD = "HEAD";
+ final private String SUPPORT_OBSERVER = "MultiWindowSupportObserver";
+ private static boolean mConfigLoaded = false;
+ private static boolean mSupportMultiWindow = true;
+
protected static String getAmStartCmd(final String activityName) {
return "am start -n " + getActivityComponentName(activityName);
@@ -218,6 +225,68 @@
|| PRETEND_DEVICE_SUPPORTS_FREEFORM;
}
+ protected boolean supportsMultiWindowMode() {
+ if (!mConfigLoaded) {
+ try {
+ executeShellCommand("am start -n " + "android.server.app/." + SUPPORT_OBSERVER);
+ waitForResume("android.server.app", SUPPORT_OBSERVER);
+ Map map = getLogResults(SUPPORT_OBSERVER);
+ String value = (String)map.get(RESULT_KEY_HEAD);
+ if (value != null && value.equals("OK")) {
+ mConfigLoaded = true;
+ mSupportMultiWindow = !map.get("config_supportsMultiWindow").equals("false");
+ }
+ executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+ clearLogs();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return mSupportMultiWindow;
+ }
+
+ private void clearLogs() throws DeviceNotAvailableException {
+ executeShellCommand("logcat -c");
+ }
+
+ private Map<String, String> getLogResults(String className) throws Exception {
+ int retryCount = 3;
+ Map<String, String> output = new HashMap<String, String>();
+ do {
+
+ String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S");
+ for (String line : logs.split("\\n")) {
+ if (line.startsWith("I/" + className)) {
+ String payload = line.split(":")[1].trim();
+ final String[] split = payload.split("=");
+ if (split.length > 1) {
+ output.put(split[0], split[1]);
+ }
+ }
+ }
+ if (output.containsKey(RESULT_KEY_HEAD)) {
+ return output;
+ }
+ } while (retryCount-- > 0);
+ return output;
+ }
+
+ private void waitForResume(String packageName, String activityName) throws Exception {
+ final String fullActivityName = packageName + "." + activityName;
+ int retryCount = 3;
+ do {
+ Thread.sleep(500);
+ String logs = executeShellCommand("logcat -d -b events");
+ for (String line : logs.split("\\n")) {
+ if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
+ return;
+ }
+ }
+ } while (retryCount-- > 0);
+
+ throw new Exception(fullActivityName + " has failed to start");
+ }
+
protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
if (mAvailableFeatures == null) {
// TODO: Move this logic to ITestDevice.
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml b/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
index 296a979..a45b62f 100644
--- a/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
+++ b/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
@@ -23,7 +23,8 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
-
+ <activity android:name=".MultiWindowSupportObserver"
+ android:exported="true"/>
<provider android:name="android.wm.cts.dndsourceapp.DragSourceContentProvider"
android:authorities="android.wm.cts.dndsource.contentprovider"
android:grantUriPermissions="true"/>
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java b/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java
new file mode 100644
index 0000000..b8bd3c6
--- /dev/null
+++ b/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java
@@ -0,0 +1,18 @@
+package android.wm.cts.dndsourceapp;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MultiWindowSupportObserver extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int id = Resources.getSystem().getIdentifier("config_supportsMultiWindow", "bool", "android");
+ boolean support = Resources.getSystem().getBoolean(id);
+ Log.i(getClass().getSimpleName(), "HEAD=OK");
+ Log.i(getClass().getSimpleName(), "DROP=OK");
+ Log.i(getClass().getSimpleName(), "config_supportsMultiWindow="+support);
+ }
+}
diff --git a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
index ccdba15..b63b633 100644
--- a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
+++ b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
@@ -52,7 +52,10 @@
private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
-
+ private static boolean mConfigLoaded = false;
+ private final String SUPPORT_OBSERVER = "MultiWindowSupportObserver";
+ private static boolean mSupportMultiWindow = true;
+ private final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.wm.cts.dndsourceapp";
private static final String SOURCE_ACTIVITY_NAME = "DragSource";
private static final String TARGET_ACTIVITY_NAME = "DropTarget";
@@ -393,4 +396,23 @@
public void testGrantWriteRequestWrite() throws Exception {
doTestDragAndDrop(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
}
+ protected boolean supportsMultiWindowMode() {
+ if (!mConfigLoaded) {
+ try {
+ executeShellCommand("am start -n " + "android.wm.cts.dndsourceapp/." + SUPPORT_OBSERVER);
+ waitForResume("android.wm.cts.dndsourceapp", SUPPORT_OBSERVER);
+ Map map = getLogResults(SUPPORT_OBSERVER);
+ String value = (String)map.get(RESULT_KEY_DROP_RESULT);
+ if (value != null && value.equals("OK")) {
+ mConfigLoaded = true;
+ mSupportMultiWindow = !map.get("config_supportsMultiWindow").equals("false");
+ }
+ executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+ clearLogs();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return mSupportMultiWindow;
+ }
}
diff --git a/hostsidetests/theme/assets/24/560dpi.zip b/hostsidetests/theme/assets/24/560dpi.zip
index a171f7c..9a65c38 100644
--- a/hostsidetests/theme/assets/24/560dpi.zip
+++ b/hostsidetests/theme/assets/24/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/mdpi.zip b/hostsidetests/theme/assets/24/mdpi.zip
new file mode 100755
index 0000000..6e4528f
--- /dev/null
+++ b/hostsidetests/theme/assets/24/mdpi.zip
Binary files differ
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 1a474c5..3af213e 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -1014,6 +1014,7 @@
updateLocationAndWait(TEST_MOCK_PROVIDER_NAME, realProviderToFool, latitude, longitude);
}
+ @UiThreadTest
public void testGpsStatusListener() {
MockGpsStatusListener listener = new MockGpsStatusListener();
mManager.addGpsStatusListener(listener);
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java b/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
index 4a40fad..2af3c6b 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
@@ -16,13 +16,18 @@
package android.mediastress.cts;
+import android.os.Bundle;
import android.os.Environment;
+import android.support.test.InstrumentationRegistry;
import java.io.File;
import junit.framework.Assert;
public class WorkDir {
+
+ private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
+
static final File getTopDir() {
Assert.assertEquals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
return Environment.getExternalStorageDirectory();
@@ -33,6 +38,13 @@
}
static final String getMediaDirString() {
- return (getTopDirString() + "test/");
+ Bundle bundle = InstrumentationRegistry.getArguments();
+ String mediaDirString = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
+ if (mediaDirString != null) {
+ // user has specified the mediaDirString via instrumentation-arg
+ return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
+ } else {
+ return (getTopDirString() + "test/");
+ }
}
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
index 94b2d14..6164936 100644
--- a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
@@ -157,6 +157,10 @@
if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
+ if (mPassthroughInputList.size() == 0) {
+ // The device does not have any passthrough inputs. Skipping the stress test.
+ return;
+ }
// Tuning should be completed within 3 seconds on average, therefore, we set 100 iterations
// here to fit the test case running time in 5 minutes limitation of CTS test cases.
final int ITERATIONS = 100;
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
index 3430576..e521ebd 100644
--- a/tools/cts-tradefed/res/config/cts-preconditions.xml
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -53,6 +53,7 @@
<option name="src-dir" value="/sdcard/device-info-files/"/>
<option name="dest-dir" value="device-info-files/"/>
<option name="temp-dir" value="temp-device-info-files/"/>
+ <option name="throw-error" value="false"/>
</target_preparer>
<!-- The following values are used in cts/common/device-side/util/DeviceReportLog.java,