Support tracking not executed cts tests.
Also fix issue in InstrumentationApkTest where it attempts to communicate with
device after its already deemed not available.
Bug 5376439
Change-Id: I1619586629d27e80be7586ece37908dc0e50be1c
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 380f891..4770e92 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -16,6 +16,8 @@
package com.android.cts.tradefed.result;
+import android.tests.getinfo.DeviceInfoConstants;
+
import com.android.cts.tradefed.build.CtsBuildHelper;
import com.android.cts.tradefed.device.DeviceInfoCollector;
import com.android.cts.tradefed.testtype.CtsTest;
@@ -25,17 +27,17 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IFolderBuildInfo;
import com.android.tradefed.config.Option;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.TestResult;
import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.StreamUtil;
import org.kxml2.io.KXmlSerializer;
-import android.tests.getinfo.DeviceInfoConstants;
-
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -151,10 +153,11 @@
@Override
public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
super.testRunEnded(elapsedTime, runMetrics);
- Log.i(LOG_TAG, String.format("Test run %s complete. Tests passed %d, failed %d, error %d",
+ CLog.i("%s complete: Passed %d, Failed %d, Not Executed %d",
getCurrentRunResults().getName(), getCurrentRunResults().getNumPassedTests(),
- getCurrentRunResults().getNumFailedTests(),
- getCurrentRunResults().getNumErrorTests()));
+ getCurrentRunResults().getNumFailedTests() +
+ getCurrentRunResults().getNumErrorTests(),
+ getCurrentRunResults().getNumIncompleteTests());
}
/**
@@ -187,21 +190,16 @@
serializeResultsDoc(serializer, startTimestamp, endTime);
serializer.endDocument();
// TODO: output not executed timeout omitted counts
- String msg = String.format("XML test result file generated at %s. Total tests %d, " +
- "Failed %d, Error %d", getReportPath(), getNumTotalTests(),
- getNumFailedTests(), getNumErrorTests());
+ String msg = String.format("XML test result file generated at %s. Passed %d, " +
+ "Failed %d, Not Executed %d", getReportPath(), getNumPassedTests(),
+ getNumFailedTests() + getNumErrorTests(), getNumIncompleteTests());
Log.logAndDisplay(LogLevel.INFO, LOG_TAG, msg);
Log.logAndDisplay(LogLevel.INFO, LOG_TAG, String.format("Time: %s",
TimeUtil.formatElapsedTime(elapsedTime)));
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to generate report data");
} finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException ignored) {
- }
- }
+ StreamUtil.closeStream(stream);
}
}
@@ -414,7 +412,8 @@
serializer.attribute(ns, "failed", Integer.toString(getNumErrorTests() +
getNumFailedTests()));
// TODO: output notExecuted, timeout count
- serializer.attribute(ns, "notExecuted", "0");
+ serializer.attribute(ns, "notExecuted", Integer.toString(getNumIncompleteTests()));
+ // ignore timeouts - these are reported as errors
serializer.attribute(ns, "timeout", "0");
serializer.attribute(ns, "pass", Integer.toString(getNumPassedTests()));
serializer.endTag(ns, "Summary");
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestCase.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestCase.java
index 65e00ff..3ebb6e3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestCase.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestCase.java
@@ -15,6 +15,7 @@
*/
package com.android.cts.tradefed.result;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.TestResult;
import com.android.tradefed.result.TestResult.TestStatus;
@@ -105,8 +106,10 @@
return "fail";
case PASSED:
return "pass";
- // TODO add notExecuted
+ case INCOMPLETE:
+ return "notExecuted";
}
+ CLog.w("Unrecognized status %s", status);
return "fail";
}
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 cc5dd37..88f58f8 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
@@ -43,6 +43,7 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -111,12 +112,12 @@
private boolean mScreenshot = false;
/** data structure for a {@link IRemoteTest} and its known tests */
- private class KnownTests {
+ class TestPackage {
private final IRemoteTest mTestForPackage;
private final Collection<TestIdentifier> mKnownTests;
private final ITestPackageDef mPackageDef;
- KnownTests(ITestPackageDef packageDef, IRemoteTest testForPackage,
+ TestPackage(ITestPackageDef packageDef, IRemoteTest testForPackage,
Collection<TestIdentifier> knownTests) {
mPackageDef = packageDef;
mTestForPackage = testForPackage;
@@ -134,10 +135,18 @@
ITestPackageDef getPackageDef() {
return mPackageDef;
}
+
+ /**
+ * Return the test run name that should be used for the TestPackage
+ * @return
+ */
+ String getTestRunName() {
+ return mPackageDef.getUri();
+ }
}
/** list of remaining tests to execute */
- private List<KnownTests> mRemainingTests = null;
+ private List<TestPackage> mRemainingTestPkgs = null;
private CtsBuildHelper mCtsBuild = null;
private IBuildInfo mBuildInfo = null;
@@ -247,38 +256,44 @@
throw new IllegalArgumentException("missing device");
}
- if (mRemainingTests == null) {
+ if (mRemainingTestPkgs == null) {
checkFields();
- mRemainingTests = buildTestsToRun();
- }
- // always collect the device info, even for resumed runs, since test will likely be running
- // on a different device
- collectDeviceInfo(getDevice(), mCtsBuild, listener);
-
- while (!mRemainingTests.isEmpty()) {
- KnownTests knownTests = mRemainingTests.get(0);
-
- IRemoteTest test = knownTests.getTestForPackage();
- if (test instanceof IDeviceTest) {
- ((IDeviceTest)test).setDevice(getDevice());
- }
- if (test instanceof IBuildReceiver) {
- ((IBuildReceiver)test).setBuild(mBuildInfo);
- }
-
- ResultFilter filter = new ResultFilter(listener, knownTests.getKnownTests());
- test.run(filter);
- forwardPackageDetails(knownTests.getPackageDef(), listener);
- mRemainingTests.remove(0);
+ mRemainingTestPkgs = buildTestsToRun();
}
- if (mScreenshot) {
- InputStreamSource screenshotSource = getDevice().getScreenshot();
- try {
- listener.testLog("screenshot", LogDataType.PNG, screenshotSource);
- } finally {
- screenshotSource.cancel();
+ ResultFilter filter = new ResultFilter(listener, mRemainingTestPkgs);
+
+ try {
+ // always collect the device info, even for resumed runs, since test will likely be
+ // running on a different device
+ collectDeviceInfo(getDevice(), mCtsBuild, listener);
+
+ while (!mRemainingTestPkgs.isEmpty()) {
+ TestPackage knownTests = mRemainingTestPkgs.get(0);
+
+ IRemoteTest test = knownTests.getTestForPackage();
+ if (test instanceof IDeviceTest) {
+ ((IDeviceTest)test).setDevice(getDevice());
+ }
+ if (test instanceof IBuildReceiver) {
+ ((IBuildReceiver)test).setBuild(mBuildInfo);
+ }
+
+ forwardPackageDetails(knownTests.getPackageDef(), listener);
+ test.run(filter);
+ mRemainingTestPkgs.remove(0);
}
+
+ if (mScreenshot) {
+ InputStreamSource screenshotSource = getDevice().getScreenshot();
+ try {
+ listener.testLog("screenshot", LogDataType.PNG, screenshotSource);
+ } finally {
+ screenshotSource.cancel();
+ }
+ }
+ } finally {
+ filter.reportUnexecutedTests();
}
}
@@ -287,8 +302,8 @@
*
* @return
*/
- private List<KnownTests> buildTestsToRun() {
- List<KnownTests> testList = new LinkedList<KnownTests>();
+ private List<TestPackage> buildTestsToRun() {
+ List<TestPackage> testList = new LinkedList<TestPackage>();
try {
ITestCaseRepo testRepo = createTestCaseRepo();
Collection<String> testUris = getTestPackageUrisToRun(testRepo);
@@ -315,14 +330,14 @@
* @param testUri
* @param testPackage
*/
- private void addTestPackage(List<KnownTests> testList, String testUri,
+ private void addTestPackage(List<TestPackage> testList, String testUri,
ITestPackageDef testPackage) {
if (testPackage != null) {
IRemoteTest testForPackage = testPackage.createTest(mCtsBuild.getTestCasesDir(),
mClassName, mMethodName);
if (testForPackage != null) {
Collection<TestIdentifier> knownTests = testPackage.getTests();
- testList.add(new KnownTests(testPackage, testForPackage, knownTests));
+ testList.add(new TestPackage(testPackage, testForPackage, knownTests));
}
} else {
Log.e(LOG_TAG, String.format("Could not find test package uri %s", testUri));
@@ -377,7 +392,7 @@
return null;
}
checkFields();
- List<KnownTests> allTests = buildTestsToRun();
+ List<TestPackage> allTests = buildTestsToRun();
if (allTests.size() <= 1) {
Log.w(LOG_TAG, "no tests to shard!");
@@ -389,13 +404,13 @@
// don't create more shards than the number of tests we have!
for (int i = 0; i < mShards && i < allTests.size(); i++) {
CtsTest shard = new CtsTest();
- shard.mRemainingTests = new LinkedList<KnownTests>();
+ shard.mRemainingTestPkgs = new LinkedList<TestPackage>();
shardQueue.add(shard);
}
while (!allTests.isEmpty()) {
- KnownTests testPair = allTests.remove(0);
+ TestPackage testPair = allTests.remove(0);
CtsTest shard = (CtsTest)shardQueue.poll();
- shard.mRemainingTests.add(testPair);
+ shard.mRemainingTestPkgs.add(testPair);
shardQueue.add(shard);
}
return shardQueue;
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
index e1bb91e..e911403 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
@@ -71,23 +71,20 @@
Assert.assertNotNull("missing device", getDevice());
Assert.assertNotNull("missing build", mCtsBuild);
- try {
- for (String apkFileName : mInstallFileNames) {
- Log.d(LOG_TAG, String.format("Installing %s on %s", apkFileName,
- getDevice().getSerialNumber()));
- try {
- getDevice().installPackage(mCtsBuild.getTestApp(apkFileName), true);
- } catch (FileNotFoundException e) {
- Log.e(LOG_TAG, e);
- }
+ for (String apkFileName : mInstallFileNames) {
+ Log.d(LOG_TAG, String.format("Installing %s on %s", apkFileName,
+ getDevice().getSerialNumber()));
+ try {
+ getDevice().installPackage(mCtsBuild.getTestApp(apkFileName), true);
+ } catch (FileNotFoundException e) {
+ Log.e(LOG_TAG, e);
}
- super.run(listener);
- } finally {
- for (String packageName : mUninstallPackages) {
- Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName,
- getDevice().getSerialNumber()));
- getDevice().uninstallPackage(packageName);
- }
+ }
+ super.run(listener);
+ for (String packageName : mUninstallPackages) {
+ Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName,
+ getDevice().getSerialNumber()));
+ getDevice().uninstallPackage(packageName);
}
}
}
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 02595c0..68bb62e 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
@@ -15,41 +15,60 @@
*/
package com.android.cts.tradefed.testtype;
-import com.android.ddmlib.Log;
+import com.android.cts.tradefed.testtype.CtsTest.TestPackage;
import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ResultForwarder;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
/**
* A {@link ITestInvocationListener} that filters test results based on the set of expected tests
* in CTS test package xml files.
+ * <p/>
+ * It will only report test results for expected tests, and at end of invocation, will report the
+ * set of expected tests that were not executed.
*/
class ResultFilter extends ResultForwarder {
- private final Collection<TestIdentifier> mKnownTests;
+ private final Map<String, Collection<TestIdentifier>> mKnownTestsMap;
+ private final Map<String, Collection<TestIdentifier>> mRemainingTestsMap;
+ private String mCurrentTestRun = null;
/**
* Create a {@link ResultFilter}.
*
* @param listener the real {@link ITestInvocationListener} to forward results to
- * @param expectedTests the full collection of known tests to expect
*/
- ResultFilter(ITestInvocationListener listener, Collection<TestIdentifier> knownTests) {
+ ResultFilter(ITestInvocationListener listener, List<TestPackage> testPackages) {
super(listener);
- mKnownTests = knownTests;
+
+ mKnownTestsMap = new HashMap<String, Collection<TestIdentifier>>();
+ // use LinkedHashMap for predictable test order
+ mRemainingTestsMap = new LinkedHashMap<String, Collection<TestIdentifier>>();
+
+ for (TestPackage testPkg : testPackages) {
+ mKnownTestsMap.put(testPkg.getTestRunName(), new HashSet<TestIdentifier>(
+ testPkg.getKnownTests()));
+ mRemainingTestsMap.put(testPkg.getTestRunName(), new LinkedHashSet<TestIdentifier>(
+ testPkg.getKnownTests()));
+ }
}
/**
* {@inheritDoc}
*/
@Override
- public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
- super.testRunEnded(elapsedTime, runMetrics);
- // TODO: report all remaining tests in mTestPackage as failed tests with
- // notExecuted result
+ public void testRunStarted(String runName, int testCount) {
+ super.testRunStarted(runName, testCount);
+ mCurrentTestRun = runName;
}
/**
@@ -60,7 +79,7 @@
if (isKnownTest(test)) {
super.testStarted(test);
} else {
- Log.d("ResultFilter", String.format("Skipping reporting unknown test %s", test));
+ CLog.d("Skipping reporting unknown test %s", test);
}
}
@@ -81,6 +100,7 @@
public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
if (isKnownTest(test)) {
super.testEnded(test, testMetrics);
+ removeExecutedTest(test);
}
}
@@ -89,6 +109,35 @@
* @return
*/
private boolean isKnownTest(TestIdentifier test) {
- return mKnownTests.contains(test);
+ if (mCurrentTestRun != null && mKnownTestsMap.containsKey(mCurrentTestRun)) {
+ return mKnownTestsMap.get(mCurrentTestRun).contains(test);
+ }
+ return false;
+ }
+
+ /**
+ * Remove given test from the 'remaining tests' data structure.
+ * @param test
+ */
+ private void removeExecutedTest(TestIdentifier test) {
+ if (mCurrentTestRun != null && mRemainingTestsMap.containsKey(mCurrentTestRun)) {
+ mRemainingTestsMap.get(mCurrentTestRun).remove(test);
+ }
+ }
+
+ /**
+ * Report the set of expected tests that were not executed
+ */
+ public void reportUnexecutedTests() {
+ for (Map.Entry<String, Collection<TestIdentifier>> entry : mRemainingTestsMap.entrySet()) {
+ super.testRunStarted(entry.getKey(), entry.getValue().size());
+ for (TestIdentifier test : entry.getValue()) {
+ // an unexecuted test is currently reported as a 'testStarted' event without a
+ // 'testEnded'. TODO: consider adding an explict API for reporting an unexecuted
+ // test
+ super.testStarted(test);
+ }
+ super.testRunEnded(0, new HashMap<String,String>());
+ }
}
}
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 58f2c00..9d70a61 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
@@ -197,11 +197,11 @@
// a reference app test is just a InstrumentationTest with one extra apk to install
InstrumentationApkTest instrTest = new InstrumentationApkTest();
instrTest.addInstallApk(String.format("%s.apk", mApkToTestName), mPackageToTest);
- return setInstrumentationTest(className, methodName, instrTest, testCaseDir);
+ return setInstrumentationTest(className, methodName, instrTest, testCaseDir, mTests);
} else {
Log.d(LOG_TAG, String.format("Creating instrumentation test for %s", mName));
InstrumentationApkTest instrTest = new InstrumentationApkTest();
- return setInstrumentationTest(className, methodName, instrTest, testCaseDir);
+ return setInstrumentationTest(className, methodName, instrTest, testCaseDir, mTests);
}
}
@@ -217,13 +217,15 @@
* @return the populated {@link InstrumentationTest} or <code>null</code>
*/
private InstrumentationTest setInstrumentationTest(String className,
- String methodName, InstrumentationApkTest instrTest, File testCaseDir) {
+ String methodName, InstrumentationApkTest instrTest, File testCaseDir,
+ Collection<TestIdentifier> testsToRun) {
instrTest.setRunName(getUri());
instrTest.setPackageName(mAppNameSpace);
instrTest.setRunnerName(mRunner);
instrTest.setTestPackageName(mTestPackageName);
instrTest.setClassName(className);
instrTest.setMethodName(methodName);
+ instrTest.setTestsToRun(testsToRun, true /* force batch mode */);
// mName means 'apk file name' for instrumentation tests
instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));