summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-08-15 04:47:14 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-08-15 04:47:14 +0000
commit94a7e58e30f64e95601e04f6caccb1822446cfa7 (patch)
tree32c55b354bcf11934cc484d2f0848ee4fbd1d95b
parent2e6635e0493f504122a8c059abd3669f41016f10 (diff)
parenta1c78ee85e6b6f4e2eabe5b9d28d674945890bc6 (diff)
downloadplatform_test_suite_harness-android10-mainline-a-release.tar.gz
platform_test_suite_harness-android10-mainline-a-release.tar.bz2
platform_test_suite_harness-android10-mainline-a-release.zip
Snap for 5803298 from a1c78ee85e6b6f4e2eabe5b9d28d674945890bc6 to qt-aml-releaseandroid-mainline-10.0.0_r2android10-mainline-a-release
Change-Id: I146c55ab69fbc3a713e88273513ab0ad5af0efc1
-rw-r--r--common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java33
-rw-r--r--common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousSessionFileCopier.java3
-rw-r--r--common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BuildFingerPrintPreparer.java9
-rw-r--r--common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java2
-rw-r--r--common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java71
-rw-r--r--common/util/src/com/android/compatibility/common/util/CrashUtils.java130
-rw-r--r--common/util/src/com/android/compatibility/common/util/InvocationResult.java13
-rw-r--r--common/util/src/com/android/compatibility/common/util/ResultHandler.java47
-rw-r--r--common/util/src/com/android/compatibility/common/util/ResultUnit.java4
-rw-r--r--common/util/tests/assets/logcat.txt64
-rw-r--r--common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java63
-rw-r--r--common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java110
-rwxr-xr-xtools/cts-tradefed/res/config/cts-known-failures.xml4
13 files changed, 481 insertions, 72 deletions
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 44939f3f..e62c2719 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
@@ -27,6 +27,7 @@ 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.InvocationResult.RunHistory;
import com.android.compatibility.common.util.MetricsStore;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultHandler;
@@ -64,6 +65,7 @@ import com.android.tradefed.util.proto.TfMetricProtoUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.xml.XmlEscapers;
+import com.google.gson.Gson;
import org.xmlpull.v1.XmlPullParserException;
@@ -75,6 +77,7 @@ import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -97,6 +100,8 @@ public class ResultReporter implements ILogSaverListener, ITestInvocationListene
private static final String CTS_PREFIX = "cts:";
private static final String BUILD_INFO = CTS_PREFIX + "build_";
private static final String LATEST_LINK_NAME = "latest";
+ /** Used to get run history from the test result of last run. */
+ private static final String RUN_HISTORY_KEY = "run_history";
public static final String BUILD_BRAND = "build_brand";
public static final String BUILD_DEVICE = "build_device";
@@ -533,6 +538,15 @@ public class ResultReporter implements ILogSaverListener, ITestInvocationListene
}
}
+ /**
+ * Returns whether a report creation should be skipped.
+ */
+ protected boolean shouldSkipReportCreation() {
+ // This value is always false here for backwards compatibility.
+ // Extended classes have the option to override this.
+ return false;
+ }
+
private void finalizeResults() {
if (mFingerprintFailure) {
CLog.w("Failed the fingerprint check. Skip result reporting.");
@@ -565,6 +579,25 @@ public class ResultReporter implements ILogSaverListener, ITestInvocationListene
String moduleProgress = String.format("%d of %d",
mResult.getModuleCompleteCount(), mResult.getModules().size());
+ // Get run history from the test result of last run and add the run history of the current
+ // run to it.
+ // TODO(b/137973382): avoid casting by move the method to interface level.
+ Collection<RunHistory> runHistories = ((InvocationResult) mResult).getRunHistories();
+ String runHistoryJSON = mResult.getInvocationInfo().get(RUN_HISTORY_KEY);
+ Gson gson = new Gson();
+ if (runHistoryJSON != null) {
+ RunHistory[] runHistoryArray = gson.fromJson(runHistoryJSON, RunHistory[].class);
+ Collections.addAll(runHistories, runHistoryArray);
+ }
+ RunHistory newRun = new RunHistory();
+ newRun.startTime = mResult.getStartTime();
+ newRun.endTime = newRun.startTime + mElapsedTime;
+ runHistories.add(newRun);
+ mResult.addInvocationInfo(RUN_HISTORY_KEY, gson.toJson(runHistories));
+
+ if (shouldSkipReportCreation()) {
+ return;
+ }
try {
// Zip the full test results directory.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousSessionFileCopier.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousSessionFileCopier.java
index 87a667b4..d0c70436 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousSessionFileCopier.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/suite/PreviousSessionFileCopier.java
@@ -42,6 +42,9 @@ public class PreviousSessionFileCopier implements ITestInvocationListener {
ChecksumReporter.NAME,
ChecksumReporter.PREV_NAME,
ResultHandler.FAILURE_REPORT_NAME,
+ CertificationSuiteResultReporter.FAILURE_REPORT_NAME,
+ CertificationSuiteResultReporter.SUMMARY_FILE,
+ CertificationChecksumHelper.NAME,
"diffs");
private CompatibilityBuildHelper mBuildHelper;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BuildFingerPrintPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BuildFingerPrintPreparer.java
index a96b76a3..30e303b0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BuildFingerPrintPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BuildFingerPrintPreparer.java
@@ -54,17 +54,22 @@ public final class BuildFingerPrintPreparer extends BaseTargetPreparer {
if (!compare.equals(currentBuildFingerprint)) {
throw new TargetSetupError(
String.format(
- "Device build fingerprint must match %s. Found '%s' instead.",
+ "Device build fingerprint must match '%s'. Found '%s' instead.",
compare, currentBuildFingerprint),
device.getDeviceDescriptor());
}
if (mExpectedVendorFingerprint != null) {
String currentBuildVendorFingerprint =
device.getProperty(mVendorFingerprintProperty);
+ // Replace by empty string if null to do a proper comparison.
+ if (currentBuildVendorFingerprint == null) {
+ currentBuildVendorFingerprint = "";
+ }
if (!mExpectedVendorFingerprint.equals(currentBuildVendorFingerprint)) {
throw new TargetSetupError(
String.format(
- "Device vendor build fingerprint must match %s - found %s instead.",
+ "Device vendor build fingerprint must match '%s'. Found '%s' "
+ + "instead.",
mExpectedVendorFingerprint, currentBuildVendorFingerprint),
device.getDeviceDescriptor());
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
index 48709fc3..d59f0b6b 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
@@ -386,7 +386,7 @@ public class BusinessLogicPreparer implements IAbiReceiver, IInvocationContextRe
extendedDeviceInfo
.add(String.format("%s:%s:%s", fileAndKey[0], fileAndKey[1], value));
}
- }catch(JSONException | IOException e){
+ }catch(JSONException | IOException | RuntimeException e){
CLog.e("Failed to read or parse Extended DeviceInfo JSON file: %s. Error: %s",
ediFile.getAbsolutePath(), e);
return new ArrayList<>();
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
index a179978e..82c4d8fe 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
@@ -549,4 +549,75 @@ public class ResultReporterTest extends TestCase {
TestStatus.PASS,
result1.getResultStatus());
}
+
+ /** Ensure that the run history of the current run is added to previous run history. */
+ public void testRetryWithRunHistory() throws Exception {
+ mReporter.invocationStarted(mContext);
+
+ // Set up IInvocationResult with existing results from previous session
+ mReporter.testRunStarted(ID, 2);
+ IInvocationResult invocationResult = mReporter.getResult();
+ IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+ ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+ ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+ testResult1.setResultStatus(TestStatus.PASS);
+ testResult1.setRetry(true);
+ ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+ testResult2.setResultStatus(TestStatus.FAIL);
+ testResult2.setStackTrace(STACK_TRACE);
+ testResult2.setRetry(true);
+ // Set up IInvocationResult with the run history of previous runs.
+ invocationResult.addInvocationInfo(
+ "run_history", "[{\"startTime\":1,\"endTime\":2},{\"startTime\":3,\"endTime\":4}]");
+
+ // Flip results for the current session
+ TestDescription test1 = new TestDescription(CLASS, METHOD_1);
+ mReporter.testStarted(test1);
+ mReporter.testFailed(test1, STACK_TRACE);
+ mReporter.testEnded(test1, new HashMap<String, Metric>());
+ TestDescription test2 = new TestDescription(CLASS, METHOD_2);
+ mReporter.testStarted(test2);
+ mReporter.testEnded(test2, new HashMap<String, Metric>());
+
+ mReporter.testRunEnded(10, new HashMap<String, Metric>());
+ mReporter.invocationEnded(10);
+
+ // Verification that results have been overwritten.
+ IInvocationResult result = mReporter.getResult();
+ assertEquals("Expected 1 pass", 1, result.countResults(TestStatus.PASS));
+ assertEquals("Expected 1 failure", 1, result.countResults(TestStatus.FAIL));
+ List<IModuleResult> modules = result.getModules();
+ assertEquals("Expected 1 module", 1, modules.size());
+ IModuleResult module = modules.get(0);
+ List<ICaseResult> cases = module.getResults();
+ assertEquals("Expected 1 test case", 1, cases.size());
+ ICaseResult case1 = cases.get(0);
+ List<ITestResult> testResults = case1.getResults();
+ assertEquals("Expected 2 tests", 2, testResults.size());
+
+ long startTime = mReporter.getResult().getStartTime();
+ String expectedRunHistory =
+ String.format(
+ "[{\"startTime\":1,\"endTime\":2},"
+ + "{\"startTime\":3,\"endTime\":4},{\"startTime\":%d,\"endTime\":%d}]",
+ startTime, startTime + 10);
+ assertEquals(expectedRunHistory, invocationResult.getInvocationInfo().get("run_history"));
+
+ // Test 1 details
+ ITestResult finalTestResult1 = case1.getResult(METHOD_1);
+ assertNotNull(String.format("Expected result for %s", TEST_1), finalTestResult1);
+ assertEquals(
+ String.format("Expected fail for %s", TEST_1),
+ TestStatus.FAIL,
+ finalTestResult1.getResultStatus());
+ assertEquals(finalTestResult1.getStackTrace(), STACK_TRACE);
+
+ // Test 2 details
+ ITestResult finalTestResult2 = case1.getResult(METHOD_2);
+ assertNotNull(String.format("Expected result for %s", TEST_2), finalTestResult2);
+ assertEquals(
+ String.format("Expected pass for %s", TEST_2),
+ TestStatus.PASS,
+ finalTestResult2.getResultStatus());
+ }
}
diff --git a/common/util/src/com/android/compatibility/common/util/CrashUtils.java b/common/util/src/com/android/compatibility/common/util/CrashUtils.java
index 08375ad3..430fafb9 100644
--- a/common/util/src/com/android/compatibility/common/util/CrashUtils.java
+++ b/common/util/src/com/android/compatibility/common/util/CrashUtils.java
@@ -16,8 +16,10 @@
package com.android.compatibility.common.util;
+import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.math.BigInteger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -25,7 +27,7 @@ import org.json.JSONObject;
/** Contains helper functions and shared constants for crash parsing. */
public class CrashUtils {
// used to only detect actual addresses instead of nullptr and other unlikely values
- public static final long MIN_CRASH_ADDR = 0x8000;
+ public static final BigInteger MIN_CRASH_ADDR = new BigInteger("8000", 16);
// Matches the end of a crash
public static final Pattern sEndofCrashPattern =
Pattern.compile("DEBUG\\s+?:\\s+?backtrace:");
@@ -37,11 +39,12 @@ public class CrashUtils {
public static final String NEW_TEST_ALERT = "New test starting with name: ";
public static final Pattern sNewTestPattern =
Pattern.compile(NEW_TEST_ALERT + "(\\w+?)\\(.*?\\)");
- public static final String SIGNAL = "signal",
- NAME = "name",
- PID = "pid",
- TID = "tid",
- FAULT_ADDRESS = "faultaddress";
+ public static final String SIGNAL = "signal";
+ public static final String NAME = "name";
+ public static final String PROCESS = "process";
+ public static final String PID = "pid";
+ public static final String TID = "tid";
+ public static final String FAULT_ADDRESS = "faultaddress";
// Matches the smallest blob that has the appropriate header and footer
private static final Pattern sCrashBlobPattern =
Pattern.compile("DEBUG\\s+?:( [*]{3})+?.*?DEBUG\\s+?:\\s+?backtrace:", Pattern.DOTALL);
@@ -58,47 +61,87 @@ public class CrashUtils {
Pattern.compile("(?i)Abort message.*?CHECK_");
/**
+ * returns true if the signal is a segmentation fault or bus error.
+ */
+ public static boolean isSecuritySignal(JSONObject crash) throws JSONException {
+ return crash.getString(SIGNAL).toLowerCase().matches("sig(segv|bus)");
+ }
+
+ /**
+ * returns the filename of the process.
+ * e.g. "/system/bin/mediaserver" returns "mediaserver"
+ */
+ public static String getProcessFileName(JSONObject crash) throws JSONException {
+ return new File(crash.getString(PROCESS)).getName();
+ }
+
+ /**
* Determines if the given input has a {@link com.android.compatibility.common.util.Crash} that
* should fail an sts test
*
- * @param processNames list of applicable process names
+ * @param processPatterns list of patterns that match applicable process names
* @param checkMinAddr if the minimum fault address should be respected
* @param crashes list of crashes to check
* @return if a crash is serious enough to fail an sts test
*/
- public static boolean detectCrash(
- String[] processNames, boolean checkMinAddr, JSONArray crashes) {
+ public static boolean securityCrashDetected(
+ JSONArray crashes, boolean checkMinAddr, Pattern... processPatterns) {
+ return matchSecurityCrashes(crashes, checkMinAddr, processPatterns).length() > 0;
+ }
+
+ public static BigInteger getBigInteger(JSONObject source, String name) throws JSONException {
+ if (source.isNull(name)) {
+ return null;
+ }
+ String intString = source.getString(name);
+ BigInteger value = null;
+ try {
+ value = new BigInteger(intString, 16);
+ } catch (NumberFormatException e) {}
+ return value;
+ }
+
+ /**
+ * Determines which given inputs have a {@link com.android.compatibility.common.util.Crash} that
+ * should fail an sts test
+ *
+ * @param processPatterns list of patterns that match applicable process names
+ * @param checkMinAddr if the minimum fault address should be respected
+ * @param crashes list of crashes to check
+ * @return the list of crashes serious enough to fail an sts test
+ */
+ public static JSONArray matchSecurityCrashes(
+ JSONArray crashes, boolean checkMinAddr, Pattern... processPatterns) {
+ JSONArray securityCrashes = new JSONArray();
for (int i = 0; i < crashes.length(); i++) {
try {
JSONObject crash = crashes.getJSONObject(i);
- if (!crash.getString(SIGNAL).toLowerCase().matches("sig(segv|bus)")) {
+ if (!matchesAny(getProcessFileName(crash), processPatterns)) {
continue;
}
-
- if (checkMinAddr && !crash.isNull(FAULT_ADDRESS)) {
- if (crash.getLong(FAULT_ADDRESS) < MIN_CRASH_ADDR) {
- continue;
- }
- }
-
- boolean foundProcess = false;
- String name = crash.getString(NAME);
- for (String process : processNames) {
- if (name.equals(process)) {
- foundProcess = true;
- break;
- }
+ if (!isSecuritySignal(crash)) {
+ continue;
}
-
- if (!foundProcess) {
+ BigInteger faultAddress = getBigInteger(crash, FAULT_ADDRESS);
+ if (checkMinAddr && faultAddress != null
+ && faultAddress.compareTo(MIN_CRASH_ADDR) < 0) {
continue;
}
+ securityCrashes.put(crash);
+ } catch (JSONException | NullPointerException e) {}
+ }
+ return securityCrashes;
+ }
- return true; // crash detected
- } catch (JSONException | NullPointerException e) {
+ /**
+ * returns true if the input matches any of the patterns.
+ */
+ private static boolean matchesAny(String input, Pattern... patterns) {
+ for (Pattern p : patterns) {
+ if (p.matcher(input).matches()) {
+ return true;
}
}
-
return false;
}
@@ -107,21 +150,23 @@ public class CrashUtils {
Matcher crashBlobFinder = sCrashBlobPattern.matcher(input);
while (crashBlobFinder.find()) {
String crashStr = crashBlobFinder.group(0);
- int tid = 0, pid = 0;
- Long faultAddress = null;
- String name = null, signal = null;
+ int tid = 0;
+ int pid = 0;
+ BigInteger faultAddress = null;
+ String name = null;
+ String process = null;
+ String signal = null;
Matcher pidtidNameMatcher = sPidtidNamePattern.matcher(crashStr);
if (pidtidNameMatcher.find()) {
try {
pid = Integer.parseInt(pidtidNameMatcher.group(1));
- } catch (NumberFormatException e) {
- }
+ } catch (NumberFormatException e) {}
try {
tid = Integer.parseInt(pidtidNameMatcher.group(2));
- } catch (NumberFormatException e) {
- }
+ } catch (NumberFormatException e) {}
name = pidtidNameMatcher.group(3).trim();
+ process = pidtidNameMatcher.group(4).trim();
}
Matcher faultLineMatcher = sFaultLinePattern.matcher(crashStr);
@@ -130,9 +175,8 @@ public class CrashUtils {
String faultAddrMatch = faultLineMatcher.group(2);
if (faultAddrMatch != null) {
try {
- faultAddress = Long.parseLong(faultAddrMatch, 16);
- } catch (NumberFormatException e) {
- }
+ faultAddress = new BigInteger(faultAddrMatch, 16);
+ } catch (NumberFormatException e) {}
}
}
if (!sAbortMessageCheckPattern.matcher(crashStr).find()) {
@@ -141,12 +185,12 @@ public class CrashUtils {
crash.put(PID, pid);
crash.put(TID, tid);
crash.put(NAME, name);
- crash.put(FAULT_ADDRESS, faultAddress);
+ crash.put(PROCESS, process);
+ crash.put(FAULT_ADDRESS,
+ faultAddress == null ? null : faultAddress.toString(16));
crash.put(SIGNAL, signal);
crashes.put(crash);
- } catch (JSONException e) {
-
- }
+ } catch (JSONException e) {}
}
}
return crashes;
diff --git a/common/util/src/com/android/compatibility/common/util/InvocationResult.java b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
index 45780a99..969e188f 100644
--- a/common/util/src/com/android/compatibility/common/util/InvocationResult.java
+++ b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
@@ -17,6 +17,7 @@ package com.android.compatibility.common.util;
import java.io.File;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -30,6 +31,13 @@ import java.util.Set;
*/
public class InvocationResult implements IInvocationResult {
+ /** Helper object for JSON conversion. */
+ public static final class RunHistory {
+ public long startTime;
+ public long endTime;
+ }
+
+ private Collection<RunHistory> mRunHistories = new ArrayList<>();
private long mTimestamp;
private Map<String, IModuleResult> mModuleResults = new LinkedHashMap<>();
private Map<String, String> mInvocationInfo = new HashMap<>();
@@ -40,6 +48,11 @@ public class InvocationResult implements IInvocationResult {
private RetryChecksumStatus mRetryChecksumStatus = RetryChecksumStatus.NotRetry;
private File mRetryDirectory = null;
+ /** @return a collection of the run history of previous runs. */
+ public Collection<RunHistory> getRunHistories() {
+ return mRunHistories;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 1be36242..53f73d3a 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -37,6 +37,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -104,6 +105,9 @@ public class ResultHandler {
private static final String RESULT_ATTR = "result";
private static final String RESULT_TAG = "Result";
private static final String RUNTIME_ATTR = "runtime";
+ private static final String RUN_HISTORY_ATTR = "run_history";
+ private static final String RUN_HISTORY_TAG = "RunHistory";
+ private static final String RUN_TAG = "Run";
private static final String SCREENSHOT_TAG = "Screenshot";
private static final String SKIPPED_ATTR = "skipped";
private static final String STACK_TAG = "StackTrace";
@@ -198,6 +202,10 @@ public class ResultHandler {
invocation.addInvocationInfo(BUILD_ID, parser.getAttributeValue(NS, BUILD_ID));
invocation.addInvocationInfo(BUILD_PRODUCT, parser.getAttributeValue(NS,
BUILD_PRODUCT));
+ String runHistoryValue = parser.getAttributeValue(NS, RUN_HISTORY_ATTR);
+ if (runHistoryValue != null) {
+ invocation.addInvocationInfo(RUN_HISTORY_ATTR, runHistoryValue);
+ }
// The build fingerprint needs to reflect the true fingerprint of the device under test,
// ignoring potential overrides made by test suites (namely STS) for APFE build
@@ -212,7 +220,19 @@ public class ResultHandler {
// --skip-device-info flag
parser.nextTag();
parser.require(XmlPullParser.END_TAG, NS, BUILD_TAG);
+
+ // Parse RunHistory tag.
parser.nextTag();
+ boolean hasRunHistoryTag = true;
+ try {
+ parser.require(parser.START_TAG, NS, RUN_HISTORY_TAG);
+ } catch (XmlPullParserException e) {
+ hasRunHistoryTag = false;
+ }
+ if (hasRunHistoryTag) {
+ parseRunHistory(parser);
+ }
+
parser.require(XmlPullParser.START_TAG, NS, SUMMARY_TAG);
parser.nextTag();
parser.require(XmlPullParser.END_TAG, NS, SUMMARY_TAG);
@@ -308,6 +328,18 @@ public class ResultHandler {
}
}
+ /** Parse and replay all run history information. */
+ private static void parseRunHistory(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ parser.require(XmlPullParser.START_TAG, NS, RUN_TAG);
+ parser.nextTag();
+ parser.require(XmlPullParser.END_TAG, NS, RUN_TAG);
+ }
+ parser.require(XmlPullParser.END_TAG, NS, RUN_HISTORY_TAG);
+ parser.nextTag();
+ }
+
/**
* @param result
* @param resultDir
@@ -392,6 +424,21 @@ public class ResultHandler {
}
serializer.endTag(NS, BUILD_TAG);
+ // Run history - this contains a list of start and end times of previous runs. More
+ // information may be added in the future.
+ Collection<InvocationResult.RunHistory> runHistories =
+ ((InvocationResult) result).getRunHistories();
+ if (!runHistories.isEmpty()) {
+ serializer.startTag(NS, RUN_HISTORY_TAG);
+ for (InvocationResult.RunHistory runHistory : runHistories) {
+ serializer.startTag(NS, RUN_TAG);
+ serializer.attribute(NS, START_TIME_ATTR, String.valueOf(runHistory.startTime));
+ serializer.attribute(NS, END_TIME_ATTR, String.valueOf(runHistory.endTime));
+ serializer.endTag(NS, RUN_TAG);
+ }
+ serializer.endTag(NS, RUN_HISTORY_TAG);
+ }
+
// Summary
serializer.startTag(NS, SUMMARY_TAG);
serializer.attribute(NS, PASS_ATTR, Integer.toString(passed));
diff --git a/common/util/src/com/android/compatibility/common/util/ResultUnit.java b/common/util/src/com/android/compatibility/common/util/ResultUnit.java
index 31aecbbe..131ba8f1 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultUnit.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultUnit.java
@@ -39,7 +39,9 @@ public enum ResultUnit {
/** unit for benchmarking with generic score. */
SCORE,
/** radian */
- RADIAN;
+ RADIAN,
+ /** Audio or Video frames count, dropped, repeated, etc... */
+ FRAMES;
/**
* @return a string to be used in the report.
diff --git a/common/util/tests/assets/logcat.txt b/common/util/tests/assets/logcat.txt
index ad778c76..b9d10d05 100644
--- a/common/util/tests/assets/logcat.txt
+++ b/common/util/tests/assets/logcat.txt
@@ -271,4 +271,66 @@
11-25 19:47:35.597 940 940 F DEBUG : #24 pc 0003fa3b /system/lib/libc.so (_ZL15__pthread_startPv+30)
11-25 19:47:35.597 940 940 F DEBUG : #25 pc 0001a085 /system/lib/libc.so (__start_thread+6)
11-25 19:47:35.837 940 940 F DEBUG :
-11-25 19:47:35.837 940 940 F DEBUG : Tombstone written to: /data/tombstones/tombstone_01 \ No newline at end of file
+11-25 19:47:35.837 940 940 F DEBUG : Tombstone written to: /data/tombstones/tombstone_01
+--------- beginning of crash
+09-03 17:48:05.627 11071 11189 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0xe9380000 in tid 11189 (synthetic_thread)
+09-03 17:48:05.707 359 359 W : debuggerd: handling request: pid=11071 uid=1041 gid=1005 tid=11189
+09-03 17:48:05.796 7072 7072 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
+09-03 17:48:05.796 7072 7072 F DEBUG : Build fingerprint: 'google/angler/angler:7.1.1/N4F26T/3687331:userdebug/dev-keys'
+09-03 17:48:05.796 7072 7072 F DEBUG : Revision: '0'
+09-03 17:48:05.796 7072 7072 F DEBUG : ABI: 'arm'
+09-03 17:48:05.796 7072 7072 F DEBUG : pid: 11071, tid: 11189, name: synthetic_thread >>> synthetic_process_0 <<<
+09-03 17:48:05.797 7072 7072 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xe9380000
+09-03 17:48:05.797 7072 7072 F DEBUG : r0 e9e7a240 r1 e9380000 r2 00000170 r3 00000000
+09-03 17:48:05.797 7072 7072 F DEBUG : r4 00000002 r5 00000000 r6 ec1e1f25 r7 eb6f8000
+09-03 17:48:05.797 7072 7072 F DEBUG : r8 00000000 r9 eb105204 sl 00000000 fp 000003c0
+09-03 17:48:05.797 7072 7072 F DEBUG : ip ebd3df18 sp eaf80688 lr ec1e1f41 pc ebd38dd6 cpsr 20000030
+09-03 17:48:05.805 7072 7072 F DEBUG :
+09-03 17:48:05.805 7072 7072 F DEBUG : backtrace:
+09-03 17:48:05.806 7072 7072 F DEBUG : #00 pc 00002dd6 /system/lib/libaudioutils.so (memcpy_to_float_from_i16+5)
+09-03 17:48:05.806 7072 7072 F DEBUG : #01 pc 00040f3d /system/lib/libaudioflinger.so
+09-03 17:48:05.806 7072 7072 F DEBUG : #02 pc 00040799 /system/lib/libaudioflinger.so
+09-03 17:48:05.806 7072 7072 F DEBUG : #03 pc 00011178 /system/lib/libaudioflinger.so
+09-03 17:48:05.806 7072 7072 F DEBUG : #04 pc 0003180b /system/lib/libaudioflinger.so
+09-03 17:48:05.806 7072 7072 F DEBUG : #05 pc 0002fe57 /system/lib/libaudioflinger.so
+09-03 17:48:05.806 7072 7072 F DEBUG : #06 pc 0000e345 /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+140)
+09-03 17:48:05.806 7072 7072 F DEBUG : #07 pc 000470b3 /system/lib/libc.so (_ZL15__pthread_startPv+22)
+09-03 17:48:05.806 7072 7072 F DEBUG : #08 pc 00019e3d /system/lib/libc.so (__start_thread+6)
+09-03 17:48:05.967 11272 11568 W NativeCrashListener: Couldn't find ProcessRecord for pid 11071
+09-03 17:48:05.969 359 359 W : debuggerd: resuming target 11071
+09-03 17:48:05.981 11272 11307 I BootReceiver: Copying /data/tombstones/tombstone_01 to DropBox (SYSTEM_TOMBSTONE)
+09-03 17:48:06.067 394 394 I ServiceManager: service 'media.sound_trigger_hw' died
+06-15 19:57:33.607 12736 12761 D PermissionCache: checking android.permission.MODIFY_AUDIO_SETTINGS for uid=10197 => granted (698 us)
+--------- beginning of crash
+06-15 19:57:33.607 12736 12761 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 12761 (synthetic_thread)
+06-15 19:57:33.608 379 379 W : debuggerd: handling request: pid=12736 uid=1041 gid=1005 tid=12761
+06-15 19:57:33.670 26192 26192 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
+06-15 19:57:33.670 26192 26192 F DEBUG : Build fingerprint: 'google/bullhead/bullhead:7.1.2/N2G48C/4104010:userdebug/dev-keys'
+06-15 19:57:33.670 26192 26192 F DEBUG : Revision: 'rev_1.0'
+06-15 19:57:33.670 26192 26192 F DEBUG : ABI: 'arm'
+06-15 19:57:33.670 26192 26192 F DEBUG : pid: 12736, tid: 12761, name: synthetic_thread >>> synthetic_process_1 <<<
+06-15 19:57:33.670 26192 26192 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
+06-15 19:57:33.670 26192 26192 F DEBUG : r0 00000000 r1 00000000 r2 0000005f r3 00000000
+06-15 19:57:33.670 26192 26192 F DEBUG : r4 ffffffff r5 00000000 r6 f14f9000 r7 00000001
+06-15 19:57:33.670 26192 26192 F DEBUG : r8 00000004 r9 f3353114 sl f3313900 fp 00000000
+06-15 19:57:33.670 26192 26192 F DEBUG : ip f3bd4d88 sp f127d9c8 lr f3b9cbc5 pc f3b65af4 cpsr 60000030
+06-15 19:57:33.676 26192 26192 F DEBUG :
+06-15 19:57:33.676 26192 26192 F DEBUG : backtrace:
+06-15 19:57:33.677 26192 26192 F DEBUG : #00 pc 00018af4 /system/lib/libc.so (strlen+71)
+06-15 19:57:33.677 26192 26192 F DEBUG : #01 pc 0004fbc1 /system/lib/libc.so (__strlen_chk+4)
+06-15 19:57:33.677 26192 26192 F DEBUG : #02 pc 0000c599 /system/lib/libutils.so (_ZN7android7String8C2EPKc+12)
+06-15 19:57:33.677 26192 26192 F DEBUG : #03 pc 0002fdbf /system/lib/libaudiopolicymanagerdefault.so (_ZNK7android18HwModuleCollection19getDeviceDescriptorEjPKcS2_b+458)
+06-15 19:57:33.677 26192 26192 F DEBUG : #04 pc 0001de47 /system/lib/libaudiopolicymanagerdefault.so (_ZN7android18AudioPolicyManager27setDeviceConnectionStateIntEj24audio_policy_dev_state_tPKcS3_+178)
+06-15 19:57:33.677 26192 26192 F DEBUG : #05 pc 0000a009 /system/lib/libaudiopolicyservice.so
+06-15 19:57:33.677 26192 26192 F DEBUG : #06 pc 000a01a5 /system/lib/libmedia.so (_ZN7android20BnAudioPolicyService10onTransactEjRKNS_6ParcelEPS1_j+1256)
+06-15 19:57:33.677 26192 26192 F DEBUG : #07 pc 000359c3 /system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+70)
+06-15 19:57:33.677 26192 26192 F DEBUG : #08 pc 0003d1bb /system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+702)
+06-15 19:57:33.677 26192 26192 F DEBUG : #09 pc 0003ce07 /system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+114)
+06-15 19:57:33.677 26192 26192 F DEBUG : #10 pc 0003d31b /system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+46)
+06-15 19:57:33.678 26192 26192 F DEBUG : #11 pc 0004f8c5 /system/lib/libbinder.so
+06-15 19:57:33.678 26192 26192 F DEBUG : #12 pc 0000e345 /system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+140)
+06-15 19:57:33.678 26192 26192 F DEBUG : #13 pc 000470b3 /system/lib/libc.so (_ZL15__pthread_startPv+22)
+06-15 19:57:33.678 26192 26192 F DEBUG : #14 pc 00019e3d /system/lib/libc.so (__start_thread+6)
+06-15 19:57:33.839 934 2991 W NativeCrashListener: Couldn't find ProcessRecord for pid 12736
+06-15 19:57:33.846 934 952 I BootReceiver: Copying /data/tombstones/tombstone_01 to DropBox (SYSTEM_TOMBSTONE)
+
diff --git a/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java b/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
index 94f472e6..2138b7b3 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java
@@ -27,6 +27,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.regex.Pattern;
/** Unit tests for {@link CrashUtils}. */
@RunWith(JUnit4.class)
@@ -52,67 +53,89 @@ public class CrashUtilsTest {
@Test
public void testGetAllCrashes() throws Exception {
JSONArray expectedResults = new JSONArray();
- expectedResults.put(createCrashJson(11071, 11189, "AudioOut_D", 3912761344L, "SIGSEGV"));
- expectedResults.put(createCrashJson(12736, 12761, "Binder:12736_2", 0L, "SIGSEGV"));
- expectedResults.put(createCrashJson(26201, 26227, "Binder:26201_3", 0L, "SIGSEGV"));
- expectedResults.put(createCrashJson(26246, 26282, "Binder:26246_5", 0L, "SIGSEGV"));
- expectedResults.put(createCrashJson(245, 245, "installd", null, "SIGABRT"));
- expectedResults.put(createCrashJson(6371, 8072, "media.codec", 3976200192L, "SIGSEGV"));
- expectedResults.put(createCrashJson(8373, 8414, "loo", null, "SIGABRT"));
+ expectedResults.put(createCrashJson(
+ 11071, 11189, "AudioOut_D", "/system/bin/audioserver", "e9380000", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 12736, 12761, "Binder:12736_2", "/system/bin/audioserver", "0", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 26201, 26227, "Binder:26201_3", "/system/bin/audioserver", "0", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 26246, 26282, "Binder:26246_5", "/system/bin/audioserver", "0", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 245, 245, "installd", "/system/bin/installd", null, "SIGABRT"));
+ expectedResults.put(createCrashJson(
+ 6371, 8072, "media.codec", "omx@1.0-service", "ed000000", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 8373, 8414, "loo", "com.android.bluetooth", null, "SIGABRT"));
+ expectedResults.put(createCrashJson(
+ 11071, 11189, "synthetic_thread", "synthetic_process_0", "e9380000", "SIGSEGV"));
+ expectedResults.put(createCrashJson(
+ 12736, 12761, "synthetic_thread", "synthetic_process_1", "0", "SIGSEGV"));
Assert.assertEquals(mCrashes.toString(), expectedResults.toString());
}
public JSONObject createCrashJson(
- int pid, int tid, String name, Long faultaddress, String signal) {
+ int pid, int tid, String name, String process, String faultaddress, String signal) {
JSONObject json = new JSONObject();
try {
json.put(CrashUtils.PID, pid);
json.put(CrashUtils.TID, tid);
json.put(CrashUtils.NAME, name);
+ json.put(CrashUtils.PROCESS, process);
json.put(CrashUtils.FAULT_ADDRESS, faultaddress);
json.put(CrashUtils.SIGNAL, signal);
- } catch (JSONException e) {
-
- }
+ } catch (JSONException e) {}
return json;
}
@Test
public void testValidCrash() throws Exception {
- Assert.assertTrue(CrashUtils.detectCrash(new String[] {"AudioOut_D"}, true, mCrashes));
+ Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("synthetic_process_0")));
}
@Test
public void testMissingName() throws Exception {
- Assert.assertFalse(CrashUtils.detectCrash(new String[] {""}, true, mCrashes));
+ Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("")));
}
@Test
public void testSIGABRT() throws Exception {
- Assert.assertFalse(CrashUtils.detectCrash(new String[] {"installd"}, true, mCrashes));
+ Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("installd")));
}
@Test
public void testFaultAddressBelowMin() throws Exception {
- Assert.assertFalse(CrashUtils.detectCrash(new String[] {"Binder:12736_2"}, true, mCrashes));
+ Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("synthetic_process_1")));
}
@Test
public void testIgnoreMinAddressCheck() throws Exception {
- Assert.assertTrue(CrashUtils.detectCrash(new String[] {"Binder:12736_2"}, false, mCrashes));
+ Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, false,
+ Pattern.compile("synthetic_process_1")));
+ }
+
+ @Test
+ public void testBadAbortMessage() throws Exception {
+ Assert.assertFalse(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("generic")));
}
@Test
public void testGoodAndBadCrashes() throws Exception {
- Assert.assertTrue(
- CrashUtils.detectCrash(new String[] {"AudioOut_D", "generic"}, true, mCrashes));
+ Assert.assertTrue(CrashUtils.securityCrashDetected(mCrashes, true,
+ Pattern.compile("synthetic_process_0"), Pattern.compile("generic")));
}
@Test
public void testNullFaultAddress() throws Exception {
JSONArray crashes = new JSONArray();
- crashes.put(createCrashJson(8373, 8414, "loo", null, "SIGSEGV"));
- Assert.assertTrue(CrashUtils.detectCrash(new String[] {"loo"}, true, crashes));
+ crashes.put(createCrashJson(8373, 8414, "loo", "com.android.bluetooth", null, "SIGSEGV"));
+ Assert.assertTrue(CrashUtils.securityCrashDetected(crashes, true,
+ Pattern.compile("com\\.android\\.bluetooth")));
}
}
diff --git a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
index 50a00f07..11ca7222 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
@@ -18,18 +18,29 @@ package com.android.compatibility.common.util;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.AbiUtils;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
import junit.framework.TestCase;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
/**
* Unit tests for {@link ResultHandler}
*/
@@ -59,10 +70,14 @@ public class ResultHandlerTest extends TestCase {
private static final String BUILD_FINGERPRINT_UNALTERED = "build_fingerprint_unaltered";
private static final String BUILD_ID = "build_id";
private static final String BUILD_PRODUCT = "build_product";
+ private static final String RUN_HISTORY = "run_history";
private static final String EXAMPLE_BUILD_ID = "XYZ";
private static final String EXAMPLE_BUILD_PRODUCT = "wolverine";
private static final String EXAMPLE_BUILD_FINGERPRINT = "example_build_fingerprint";
private static final String EXAMPLE_BUILD_FINGERPRINT_UNALTERED = "example_build_fingerprint_unaltered";
+ private static final String EXAMPLE_RUN_HISTORY =
+ "[{\"startTime\":10000000000000,\"endTime\":10000000000001},"
+ + "{\"startTime\":10000000000002,\"endTime\":10000000000003}]";
private static final String DEVICE_A = "device123";
private static final String DEVICE_B = "device456";
@@ -176,6 +191,18 @@ public class ResultHandlerTest extends TestCase {
result.addInvocationInfo(BUILD_FINGERPRINT, EXAMPLE_BUILD_FINGERPRINT);
result.addInvocationInfo(BUILD_ID, EXAMPLE_BUILD_ID);
result.addInvocationInfo(BUILD_PRODUCT, EXAMPLE_BUILD_PRODUCT);
+ result.addInvocationInfo(RUN_HISTORY, EXAMPLE_RUN_HISTORY);
+ Collection<InvocationResult.RunHistory> runHistories =
+ ((InvocationResult) result).getRunHistories();
+ InvocationResult.RunHistory runHistory1 = new InvocationResult.RunHistory();
+ runHistory1.startTime = 10000000000000L;
+ runHistory1.endTime = 10000000000001L;
+ runHistories.add(runHistory1);
+ InvocationResult.RunHistory runHistory2 = new InvocationResult.RunHistory();
+ runHistory2.startTime = 10000000000002L;
+ runHistory2.endTime = 10000000000003L;
+ runHistories.add(runHistory2);
+
// Module A: test1 passes, test2 not executed
IModuleResult moduleA = result.getOrCreateModule(ID_A);
moduleA.setDone(false);
@@ -208,12 +235,31 @@ public class ResultHandlerTest extends TestCase {
moduleBTest5.skipped();
// Serialize to file
- ResultHandler.writeResults(SUITE_NAME, SUITE_VERSION, SUITE_PLAN, SUITE_BUILD,
- result, resultDir, START_MS, END_MS, REFERENCE_URL, LOG_URL,
- COMMAND_LINE_ARGS);
+ File res =
+ ResultHandler.writeResults(
+ SUITE_NAME,
+ SUITE_VERSION,
+ SUITE_PLAN,
+ SUITE_BUILD,
+ result,
+ resultDir,
+ START_MS,
+ END_MS,
+ REFERENCE_URL,
+ LOG_URL,
+ COMMAND_LINE_ARGS);
+ String content = FileUtil.readStringFromFile(res);
+ assertXmlContainsAttribute(content, "Result/Build", "run_history", EXAMPLE_RUN_HISTORY);
+ assertXmlContainsNode(content, "Result/RunHistory");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "start", "10000000000000");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "end", "10000000000001");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "start", "10000000000002");
+ assertXmlContainsAttribute(content, "Result/RunHistory/Run", "end", "10000000000003");
// Parse the results and assert correctness
- checkResult(ResultHandler.getResultFromDir(resultDir), false);
+ result = ResultHandler.getResultFromDir(resultDir);
+ checkResult(result, false);
+ checkRunHistory(result);
}
public void testParsing() throws Exception {
@@ -350,6 +396,11 @@ public class ResultHandlerTest extends TestCase {
checkResult(result, EXAMPLE_BUILD_FINGERPRINT, newTestFormat);
}
+ static void checkRunHistory(IInvocationResult result) {
+ Map<String, String> buildInfo = result.getInvocationInfo();
+ assertEquals("Incorrect run history", EXAMPLE_RUN_HISTORY, buildInfo.get(RUN_HISTORY));
+ }
+
static void checkResult(
IInvocationResult result, String expectedBuildFingerprint, boolean newTestFormat)
throws Exception {
@@ -450,4 +501,55 @@ public class ResultHandlerTest extends TestCase {
assertNull("Unexpected stack trace", moduleBTest5.getStackTrace());
assertNull("Unexpected report", moduleBTest5.getReportLog());
}
+
+ /** Return all XML nodes that match the given xPathExpression. */
+ private NodeList getXmlNodes(String xml, String xPathExpression)
+ throws XPathExpressionException {
+
+ InputSource inputSource = new InputSource(new StringReader(xml));
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ return (NodeList) xpath.evaluate(xPathExpression, inputSource, XPathConstants.NODESET);
+ }
+
+ /** Assert that the XML contains a node matching the given xPathExpression. */
+ private NodeList assertXmlContainsNode(String xml, String xPathExpression)
+ throws XPathExpressionException {
+ NodeList nodes = getXmlNodes(xml, xPathExpression);
+ assertNotNull(
+ String.format("XML '%s' returned null for xpath '%s'.", xml, xPathExpression),
+ nodes);
+ assertTrue(
+ String.format(
+ "XML '%s' should have returned at least 1 node for xpath '%s', "
+ + "but returned %s nodes instead.",
+ xml, xPathExpression, nodes.getLength()),
+ nodes.getLength() >= 1);
+ return nodes;
+ }
+
+ /**
+ * Assert that the XML contains a node matching the given xPathExpression and that the node has
+ * a given value.
+ */
+ private void assertXmlContainsAttribute(
+ String xml, String xPathExpression, String attributeName, String attributeValue)
+ throws XPathExpressionException {
+ NodeList nodes = assertXmlContainsNode(xml, xPathExpression);
+ boolean found = false;
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Element element = (Element) nodes.item(i);
+ String value = element.getAttribute(attributeName);
+ if (attributeValue.equals(value)) {
+ found = true;
+ break;
+ }
+ }
+
+ assertTrue(
+ String.format(
+ "xPath '%s' should contain attribute '%s' but does not. XML: '%s'",
+ xPathExpression, attributeName, xml),
+ found);
+ }
}
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 40b195b7..5530f1b2 100755
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -139,6 +139,10 @@
<!-- b/62481870 -->
<option name="compatibility:exclude-filter" value="CtsNativeMediaAAudioTestCases android.nativemedia.aaudio.AAudioOutputStreamCallbackTest#testPlayback" />
+ <!-- b/134654621 -->
+ <option name="compatibility:exclude-filter" value="CtsWindowManagerDeviceTestCases android.server.wm.AppConfigurationTests#testTaskCloseRestoreFreeOrientation" />
+ <option name="compatibility:exclude-filter" value="CtsWindowManagerDeviceTestCases android.server.wm.AppConfigurationTests#testAppOrientationRequestConfigClears" />
+
<!-- b/62976713 -->
<option name="compatibility:exclude-filter" value="arm64-v8a CtsMediaBitstreamsTestCases" />
<option name="compatibility:exclude-filter" value="x86_64 CtsMediaBitstreamsTestCases" />