Make CtsTest resumable.
Change-Id: I5552ba3a5633a193a4cdc1b54918de985e548912
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index 9b14dd3..a1b29b9 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -19,12 +19,14 @@
import com.android.cts.tradefed.device.DeviceInfoCollector;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.IResumableTest;
import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
import java.io.BufferedInputStream;
@@ -34,7 +36,9 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Set;
import junit.framework.Test;
@@ -44,7 +48,7 @@
* <p/>
* Supports running all the tests contained in a CTS plan, or individual test packages.
*/
-public class CtsTest implements IDeviceTest, IRemoteTest {
+public class CtsTest implements IDeviceTest, IResumableTest {
private static final String LOG_TAG = "PlanTest";
@@ -85,6 +89,33 @@
"flag to control whether to collect info from device. Default true")
private boolean mCollectDeviceInfo = true;
+ @Option(name = "resume", description =
+ "flag to attempt to automatically resume aborted test run on another connected device. " +
+ "Default false.")
+ private boolean mResume = false;
+
+ /** data structure for a {@link IRemoteTest} and its known tests */
+ private class KnownTests {
+ private final IRemoteTest mTestForPackage;
+ private final Collection<TestIdentifier> mKnownTests;
+
+ KnownTests(IRemoteTest testForPackage, Collection<TestIdentifier> knownTests) {
+ mTestForPackage = testForPackage;
+ mKnownTests = knownTests;
+ }
+
+ IRemoteTest getTestForPackage() {
+ return mTestForPackage;
+ }
+
+ Collection<TestIdentifier> getKnownTests() {
+ return mKnownTests;
+ }
+ }
+
+ /** list of remaining tests to execute */
+ private List<KnownTests> mRemainingTests = null;
+
/**
* {@inheritDoc}
*/
@@ -174,49 +205,107 @@
/**
* {@inheritDoc}
*/
+ @Override
+ public boolean isResumable() {
+ return mResume;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
checkFields();
- Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
+ if (mRemainingTests == null) {
+ mRemainingTests = buildTestsToRun();
+ }
+ // always collect the device info, even for resumed runs, since test will likely be running
+ // on a different device
+ collectDeviceInfo(getDevice(), mTestCaseDir, listener);
+ while (!mRemainingTests.isEmpty()) {
+ KnownTests testPair = mRemainingTests.get(0);
+ IRemoteTest test = testPair.getTestForPackage();
+ if (test instanceof IDeviceTest) {
+ ((IDeviceTest)test).setDevice(getDevice());
+ }
+ ResultFilter filter = new ResultFilter(listener, testPair.getKnownTests());
+ test.run(filter);
+ mRemainingTests.remove(0);
+ }
+ }
+
+ /**
+ * Build the list of test packages to run
+ *
+ * @return
+ */
+ private List<KnownTests> buildTestsToRun() {
+ List<KnownTests> testList = new LinkedList<KnownTests>();
try {
ITestCaseRepo testRepo = createTestCaseRepo();
- Collection<String> testUris = getTestsToRun(testRepo);
- collectDeviceInfo(getDevice(), mTestCaseDir, listener);
+ Collection<String> testUris = getTestPackageUrisToRun(testRepo);
+
for (String testUri : testUris) {
ITestPackageDef testPackage = testRepo.getTestPackage(testUri);
- if (testPackage != null) {
- runTest(listener, testPackage);
- } else {
- Log.e(LOG_TAG, String.format("Could not find test package uri %s", testUri));
- }
+ addTestPackage(testList, testUri, testPackage);
+ }
+ if (testList.isEmpty()) {
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "No tests to run");
}
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("failed to find CTS plan file", e);
} catch (ParseException e) {
throw new IllegalArgumentException("failed to parse CTS plan file", e);
}
+ return testList;
}
/**
- * Return the list of test uris to run
+ * Adds a test package to the list of packages to test
*
- * @return the list of test uris to run
+ * @param testList
+ * @param testUri
+ * @param testPackage
+ */
+ private void addTestPackage(List<KnownTests> testList, String testUri,
+ ITestPackageDef testPackage) {
+ if (testPackage != null) {
+ IRemoteTest testForPackage = testPackage.createTest(mTestCaseDir, mClassName,
+ mMethodName);
+ if (testForPackage != null) {
+ Collection<TestIdentifier> knownTests = testPackage.getTests();
+ testList.add(new KnownTests(testForPackage, knownTests));
+ }
+ } else {
+ Log.e(LOG_TAG, String.format("Could not find test package uri %s", testUri));
+ }
+ }
+
+ /**
+ * Return the list of test package uris to run
+ *
+ * @return the list of test package uris to run
* @throws ParseException
* @throws FileNotFoundException
*/
- private Collection<String> getTestsToRun(ITestCaseRepo testRepo) throws ParseException,
- FileNotFoundException {
- Set<String> testUris = new HashSet<String>();
+ private Collection<String> getTestPackageUrisToRun(ITestCaseRepo testRepo)
+ throws ParseException, FileNotFoundException {
+ // use LinkedHashSet to have predictable iteration order
+ Set<String> testUris = new LinkedHashSet<String>();
if (mPlanName != null) {
+ Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
String ctsPlanRelativePath = String.format("%s.xml", mPlanName);
File ctsPlanFile = new File(mTestPlanDir, ctsPlanRelativePath);
IPlanXmlParser parser = createXmlParser();
parser.parse(createXmlStream(ctsPlanFile));
testUris.addAll(parser.getTestUris());
} else if (mPackageNames.size() > 0){
+ Log.i(LOG_TAG, String.format("Executing CTS test packages %s", mPackageNames));
testUris.addAll(mPackageNames);
} else if (mClassName != null) {
+ Log.i(LOG_TAG, String.format("Executing CTS test class %s", mClassName));
// try to find package to run from class name
String packageUri = testRepo.findPackageForTest(mClassName);
if (packageUri != null) {
@@ -233,6 +322,49 @@
return testUris;
}
+ /**
+ * Runs the device info collector instrumentation on device, and forwards it to test listeners
+ * as run metrics.
+ * <p/>
+ * Exposed so unit tests can mock.
+ *
+ * @param listeners
+ * @throws DeviceNotAvailableException
+ */
+ void collectDeviceInfo(ITestDevice device, File testApkDir, ITestInvocationListener listener)
+ throws DeviceNotAvailableException {
+ if (mCollectDeviceInfo) {
+ DeviceInfoCollector.collectDeviceInfo(device, testApkDir, listener);
+ }
+ }
+
+ /**
+ * Factory method for creating a {@link ITestCaseRepo}.
+ * <p/>
+ * Exposed for unit testing
+ */
+ ITestCaseRepo createTestCaseRepo() {
+ return new TestCaseRepo(mTestCaseDir);
+ }
+
+ /**
+ * Factory method for creating a {@link PlanXmlParser}.
+ * <p/>
+ * Exposed for unit testing
+ */
+ IPlanXmlParser createXmlParser() {
+ return new PlanXmlParser();
+ }
+
+ /**
+ * Factory method for creating a {@link InputStream} from a plan xml file.
+ * <p/>
+ * Exposed for unit testing
+ */
+ InputStream createXmlStream(File xmlFile) throws FileNotFoundException {
+ return new BufferedInputStream(new FileInputStream(xmlFile));
+ }
+
private void checkFields() {
// for simplicity of command line usage, make --plan, --package, and --class mutually
// exclusive
@@ -278,66 +410,4 @@
}
return currentVal;
}
-
- /**
- * Runs the test.
- *
- * @param listeners
- * @param testPackage
- * @throws DeviceNotAvailableException
- */
- private void runTest(ITestInvocationListener listener, ITestPackageDef testPackage)
- throws DeviceNotAvailableException {
- IRemoteTest test = testPackage.createTest(mTestCaseDir, mClassName, mMethodName);
- if (test != null) {
- if (test instanceof IDeviceTest) {
- ((IDeviceTest)test).setDevice(getDevice());
- }
- ResultFilter filter = new ResultFilter(listener, testPackage);
- test.run(filter);
- }
- }
-
- /**
- * Runs the device info collector instrumentation on device, and forwards it to test listeners
- * as run metrics.
- * <p/>
- * Exposed so unit tests can mock.
- *
- * @param listeners
- * @throws DeviceNotAvailableException
- */
- void collectDeviceInfo(ITestDevice device, File testApkDir, ITestInvocationListener listener)
- throws DeviceNotAvailableException {
- if (mCollectDeviceInfo) {
- DeviceInfoCollector.collectDeviceInfo(device, testApkDir, listener);
- }
- }
-
- /**
- * Factory method for creating a {@link ITestCaseRepo}.
- * <p/>
- * Exposed for unit testing
- */
- ITestCaseRepo createTestCaseRepo() {
- return new TestCaseRepo(mTestCaseDir);
- }
-
- /**
- * Factory method for creating a {@link PlanXmlParser}.
- * <p/>
- * Exposed for unit testing
- */
- IPlanXmlParser createXmlParser() {
- return new PlanXmlParser();
- }
-
- /**
- * Factory method for creating a {@link InputStream} from a plan xml file.
- * <p/>
- * Exposed for unit testing
- */
- InputStream createXmlStream(File xmlFile) throws FileNotFoundException {
- return new BufferedInputStream(new FileInputStream(xmlFile));
- }
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index 057e803..e4f13b5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -20,6 +20,7 @@
import com.android.tradefed.testtype.IRemoteTest;
import java.io.File;
+import java.util.Collection;
/**
* Container for CTS test info.
@@ -63,4 +64,9 @@
*/
public boolean isKnownTestClass(String testClassName);
+ /**
+ * Get the collection of tests in this test package.
+ */
+ public Collection<TestIdentifier> getTests();
+
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
index ed26c5e..02595c0 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
@@ -20,6 +20,7 @@
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ResultForwarder;
+import java.util.Collection;
import java.util.Map;
/**
@@ -28,17 +29,17 @@
*/
class ResultFilter extends ResultForwarder {
- private final ITestPackageDef mTestPackage;
+ private final Collection<TestIdentifier> mKnownTests;
/**
* Create a {@link ResultFilter}.
*
* @param listener the real {@link ITestInvocationListener} to forward results to
- * @param testPackage the {@link ITestPackageDef} that defines the expected tests
+ * @param expectedTests the full collection of known tests to expect
*/
- ResultFilter(ITestInvocationListener listener, ITestPackageDef testPackage) {
+ ResultFilter(ITestInvocationListener listener, Collection<TestIdentifier> knownTests) {
super(listener);
- mTestPackage = testPackage;
+ mKnownTests = knownTests;
}
/**
@@ -88,6 +89,6 @@
* @return
*/
private boolean isKnownTest(TestIdentifier test) {
- return mTestPackage.isKnownTest(test);
+ return mKnownTests.contains(test);
}
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index 3b89926..fd896f2 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -252,10 +252,9 @@
/**
* Get the collection of tests in this test package.
- * <p/>
- * Exposed for unit testing.
*/
- Collection<TestIdentifier> getTests() {
+ @Override
+ public Collection<TestIdentifier> getTests() {
return mTests;
}
}