Merge "After joinReceiver, we don't expect more events so stop parsing"
diff --git a/invocation_interfaces/com/android/tradefed/result/TestRunResult.java b/invocation_interfaces/com/android/tradefed/result/TestRunResult.java
index 91e934e..234ef2b 100644
--- a/invocation_interfaces/com/android/tradefed/result/TestRunResult.java
+++ b/invocation_interfaces/com/android/tradefed/result/TestRunResult.java
@@ -298,6 +298,10 @@
updateTestResult(test, TestStatus.FAILURE, FailureDescription.create(trace));
}
+ public void testFailed(TestDescription test, FailureDescription failure) {
+ updateTestResult(test, TestStatus.FAILURE, failure);
+ }
+
public void testAssumptionFailure(TestDescription test, String trace) {
updateTestResult(test, TestStatus.ASSUMPTION_FAILURE, FailureDescription.create(trace));
}
diff --git a/src/com/android/tradefed/result/CollectingTestListener.java b/src/com/android/tradefed/result/CollectingTestListener.java
index 7f46ee1..e12902a 100644
--- a/src/com/android/tradefed/result/CollectingTestListener.java
+++ b/src/com/android/tradefed/result/CollectingTestListener.java
@@ -298,6 +298,12 @@
}
@Override
+ public void testFailed(TestDescription test, FailureDescription failure) {
+ setCountDirty();
+ mCurrentTestRunResult.testFailed(test, failure);
+ }
+
+ @Override
public void testAssumptionFailure(TestDescription test, String trace) {
setCountDirty();
mCurrentTestRunResult.testAssumptionFailure(test, trace);
diff --git a/src/com/android/tradefed/result/ConsoleResultReporter.java b/src/com/android/tradefed/result/ConsoleResultReporter.java
index 6cedd57..1955b7d 100644
--- a/src/com/android/tradefed/result/ConsoleResultReporter.java
+++ b/src/com/android/tradefed/result/ConsoleResultReporter.java
@@ -125,28 +125,33 @@
public void invocationEnded(long elapsedTime) {
int[] results = mResultCountListener.getResultCounts();
StringBuilder sb = new StringBuilder();
+ sb.append("========== Result Summary ==========");
sb.append(String.format("\nResults summary for test-tag '%s': ", mTestTag));
sb.append(mResultCountListener.getTotalTests());
sb.append(" Tests [");
sb.append(results[TestStatus.PASSED.ordinal()]);
- sb.append(" Passed ");
+ sb.append(" Passed");
if (results[TestStatus.FAILURE.ordinal()] > 0) {
+ sb.append(" ");
sb.append(results[TestStatus.FAILURE.ordinal()]);
- sb.append(" Failed ");
+ sb.append(" Failed");
}
if (results[TestStatus.IGNORED.ordinal()] > 0) {
+ sb.append(" ");
sb.append(results[TestStatus.IGNORED.ordinal()]);
- sb.append(" Ignored ");
+ sb.append(" Ignored");
}
if (results[TestStatus.ASSUMPTION_FAILURE.ordinal()] > 0) {
+ sb.append(" ");
sb.append(results[TestStatus.ASSUMPTION_FAILURE.ordinal()]);
- sb.append(" Assumption failures ");
+ sb.append(" Assumption failures");
}
if (results[TestStatus.INCOMPLETE.ordinal()] > 0) {
+ sb.append(" ");
sb.append(results[TestStatus.INCOMPLETE.ordinal()]);
sb.append(" Incomplete");
}
- sb.append("\r\n");
+ sb.append("] \r\n");
print(sb.toString());
if (mDisplayFailureSummary) {
for (Entry<TestDescription, TestResult> entry : mFailures.entrySet()) {
diff --git a/src/com/android/tradefed/result/proto/ProtoResultParser.java b/src/com/android/tradefed/result/proto/ProtoResultParser.java
index bb07623..0f775f9 100644
--- a/src/com/android/tradefed/result/proto/ProtoResultParser.java
+++ b/src/com/android/tradefed/result/proto/ProtoResultParser.java
@@ -36,9 +36,12 @@
import com.android.tradefed.result.proto.LogFileProto.LogFileInfo;
import com.android.tradefed.result.proto.TestRecordProto.ChildReference;
import com.android.tradefed.result.proto.TestRecordProto.DebugInfo;
+import com.android.tradefed.result.proto.TestRecordProto.DebugInfoContext;
+import com.android.tradefed.result.proto.TestRecordProto.FailureStatus;
import com.android.tradefed.result.proto.TestRecordProto.TestRecord;
import com.android.tradefed.testtype.suite.ModuleDefinition;
import com.android.tradefed.util.MultiMap;
+import com.android.tradefed.util.SerializationUtil;
import com.android.tradefed.util.proto.TestRecordProtoUtil;
import com.google.common.base.Splitter;
@@ -75,6 +78,8 @@
private boolean mInvocationStarted = false;
private boolean mInvocationEnded = false;
private boolean mFirstModule = true;
+ /** Track the name of the module in progress. */
+ private String mModuleInProgress = null;
/** Ctor. */
public ProtoResultParser(
@@ -187,6 +192,26 @@
return mInvocationEnded;
}
+ /** Returns the id of the module in progress. Returns null if none in progress. */
+ public String getModuleInProgress() {
+ return mModuleInProgress;
+ }
+
+ /** If needed to ensure consistent reporting, complete the events of the module. */
+ public void completeModuleEvents() {
+ if (getModuleInProgress() == null) {
+ return;
+ }
+ mListener.testRunStarted(getModuleInProgress(), 0);
+ FailureDescription failure =
+ FailureDescription.create(
+ "Module was interrupted after starting, results are incomplete.",
+ FailureStatus.INFRA_FAILURE);
+ mListener.testRunFailed(failure);
+ mListener.testRunEnded(0L, new HashMap<String, Metric>());
+ mListener.testModuleEnded();
+ }
+
private void evalChildrenProto(List<ChildReference> children, boolean isInRun) {
for (ChildReference child : children) {
TestRecord childProto = child.getInlineTestRecord();
@@ -274,9 +299,24 @@
}
if (endInvocationProto.hasDebugInfo()) {
- // TODO: Re-interpret the exception with proper type.
String trace = endInvocationProto.getDebugInfo().getTrace();
- mListener.invocationFailed(new Throwable(trace));
+ Throwable invocationError = new Throwable(trace);
+ if (endInvocationProto.getDebugInfo().hasDebugInfoContext()) {
+ DebugInfoContext failureContext =
+ endInvocationProto.getDebugInfo().getDebugInfoContext();
+ if (!Strings.isNullOrEmpty(failureContext.getErrorType())) {
+ try {
+ invocationError =
+ (Throwable)
+ SerializationUtil.deserialize(
+ failureContext.getErrorType());
+ } catch (IOException e) {
+ CLog.e("Failed to deserialize the invocation exception:");
+ CLog.e(e);
+ }
+ }
+ }
+ mListener.invocationFailed(invocationError);
}
log("Invocation ended proto");
@@ -311,12 +351,13 @@
InvocationContext.fromProto(anyDescription.unpack(Context.class));
String message = "Test module started proto";
if (moduleContext.getAttributes().containsKey(ModuleDefinition.MODULE_ID)) {
- message +=
- (": "
- + moduleContext
- .getAttributes()
- .getUniqueMap()
- .get(ModuleDefinition.MODULE_ID));
+ String moduleId =
+ moduleContext
+ .getAttributes()
+ .getUniqueMap()
+ .get(ModuleDefinition.MODULE_ID);
+ message += (": " + moduleId);
+ mModuleInProgress = moduleId;
}
log(message);
mListener.testModuleStarted(moduleContext);
@@ -334,6 +375,7 @@
handleLogs(moduleProto);
log("Test module ended proto");
mListener.testModuleEnded();
+ mModuleInProgress = null;
}
/** Handles the test run level of the invocation. */
diff --git a/src/com/android/tradefed/result/proto/ProtoResultReporter.java b/src/com/android/tradefed/result/proto/ProtoResultReporter.java
index 6ba026f..4e77622 100644
--- a/src/com/android/tradefed/result/proto/ProtoResultReporter.java
+++ b/src/com/android/tradefed/result/proto/ProtoResultReporter.java
@@ -33,12 +33,14 @@
import com.android.tradefed.result.proto.TestRecordProto.TestStatus;
import com.android.tradefed.result.retry.ISupportGranularResults;
import com.android.tradefed.testtype.suite.ModuleDefinition;
+import com.android.tradefed.util.SerializationUtil;
import com.android.tradefed.util.StreamUtil;
import com.google.common.base.Strings;
import com.google.protobuf.Any;
import com.google.protobuf.Timestamp;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
@@ -192,6 +194,14 @@
debugBuilder.setErrorMessage(mInvocationFailure.getMessage());
}
debugBuilder.setTrace(StreamUtil.getStackTrace(mInvocationFailure));
+ DebugInfoContext.Builder debugContext = DebugInfoContext.newBuilder();
+ try {
+ debugContext.setErrorType(SerializationUtil.serializeToString(mInvocationFailure));
+ } catch (IOException e) {
+ CLog.e("Failed to serialize the invocation failure:");
+ CLog.e(e);
+ }
+ debugBuilder.setDebugInfoContext(debugContext);
mInvocationRecordBuilder.setDebugInfo(debugBuilder);
}
diff --git a/src/com/android/tradefed/result/proto/StreamProtoReceiver.java b/src/com/android/tradefed/result/proto/StreamProtoReceiver.java
index c38b07b..f674906 100644
--- a/src/com/android/tradefed/result/proto/StreamProtoReceiver.java
+++ b/src/com/android/tradefed/result/proto/StreamProtoReceiver.java
@@ -197,6 +197,11 @@
return true;
}
+ /** If needed to ensure consistent reporting, complete the events of the module. */
+ public void completeModuleEvents() {
+ mParser.completeModuleEvents();
+ }
+
private void parse(TestRecord receivedRecord) {
if (mStopParsing) {
CLog.i(
diff --git a/src/com/android/tradefed/sandbox/SandboxConfigDump.java b/src/com/android/tradefed/sandbox/SandboxConfigDump.java
index 6b6b7e8..2395bbb 100644
--- a/src/com/android/tradefed/sandbox/SandboxConfigDump.java
+++ b/src/com/android/tradefed/sandbox/SandboxConfigDump.java
@@ -106,10 +106,6 @@
// Ensure we get the stdout logging in FileLogger case.
((FileLogger) logger).setLogLevelDisplay(LogLevel.VERBOSE);
}
- // Turn off some of the invocation level options that would be duplicated in the
- // parent.
- config.getCommandOptions().setBugreportOnInvocationEnded(false);
- config.getCommandOptions().setBugreportzOnInvocationEnded(false);
// Ensure in special conditions (placeholder devices) we can still allocate.
secureDeviceAllocation(config);
diff --git a/src/com/android/tradefed/sandbox/TradefedSandbox.java b/src/com/android/tradefed/sandbox/TradefedSandbox.java
index 83572ca..9718642 100644
--- a/src/com/android/tradefed/sandbox/TradefedSandbox.java
+++ b/src/com/android/tradefed/sandbox/TradefedSandbox.java
@@ -169,6 +169,9 @@
result.setStderr(
String.format("Event receiver thread did not complete.:\n%s", stderrText));
}
+ if (mProtoReceiver != null) {
+ mProtoReceiver.completeModuleEvents();
+ }
PrettyPrintDelimiter.printStageDelimiter(
String.format(
"Execution of the tests occurred in the sandbox, you can find its logs "
@@ -378,6 +381,10 @@
}
throw e;
}
+ // Turn off some of the invocation level options that would be duplicated in the
+ // child sandbox subprocess.
+ config.getCommandOptions().setBugreportOnInvocationEnded(false);
+ config.getCommandOptions().setBugreportzOnInvocationEnded(false);
} catch (IOException | ConfigurationException e) {
StreamUtil.close(mEventParser);
StreamUtil.close(mProtoReceiver);
diff --git a/src/com/android/tradefed/util/SerializationUtil.java b/src/com/android/tradefed/util/SerializationUtil.java
index 0c991ff..0bd3fed 100644
--- a/src/com/android/tradefed/util/SerializationUtil.java
+++ b/src/com/android/tradefed/util/SerializationUtil.java
@@ -15,6 +15,8 @@
*/
package com.android.tradefed.util;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -22,6 +24,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.util.Base64;
/** Utility to serialize/deserialize an object that implements {@link Serializable}. */
public class SerializationUtil {
@@ -53,6 +56,49 @@
}
/**
+ * Serialize and object into a base64 encoded string.
+ *
+ * @param o the object to serialize.
+ * @return the {@link String} where the object was serialized.
+ * @throws IOException if serialization fails.
+ */
+ public static String serializeToString(Serializable o) throws IOException {
+ ByteArrayOutputStream byteOut = null;
+ ObjectOutputStream out = null;
+ try {
+ byteOut = new ByteArrayOutputStream();
+ out = new ObjectOutputStream(byteOut);
+ out.writeObject(o);
+ return Base64.getEncoder().encodeToString(byteOut.toByteArray());
+ } finally {
+ StreamUtil.close(out);
+ StreamUtil.close(byteOut);
+ }
+ }
+
+ /**
+ * Deserialize an object that was serialized using {@link #serializeToString(Serializable)}.
+ *
+ * @param serialized the base64 string where the object was serialized.
+ * @return the Object deserialized.
+ * @throws IOException if the deserialization fails.
+ */
+ public static Object deserialize(String serialized) throws IOException {
+ ByteArrayInputStream bais = null;
+ ObjectInputStream in = null;
+ try {
+ bais = new ByteArrayInputStream(Base64.getDecoder().decode(serialized));
+ in = new ObjectInputStream(bais);
+ return in.readObject();
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException(cnfe);
+ } finally {
+ StreamUtil.close(in);
+ StreamUtil.close(bais);
+ }
+ }
+
+ /**
* Deserialize an object that was serialized using {@link #serialize(Serializable)}.
*
* @param serializedFile the file where the object was serialized.
diff --git a/test_framework/com/android/tradefed/postprocessor/PerfettoGenericPostProcessor.java b/test_framework/com/android/tradefed/postprocessor/PerfettoGenericPostProcessor.java
index 14ec4ef..618b6bc 100644
--- a/test_framework/com/android/tradefed/postprocessor/PerfettoGenericPostProcessor.java
+++ b/test_framework/com/android/tradefed/postprocessor/PerfettoGenericPostProcessor.java
@@ -135,6 +135,12 @@
+ "keys parsed and value is be the replacement string.")
private Map<String, String> mReplacePrefixMap = new LinkedHashMap<String, String>();
+ @Option(
+ name = "perfetto-all-metric-prefix",
+ description = "Prefix to be used with the metrics collected from perfetto."
+ + "This will be applied before any other prefixes to metrics.")
+ private String mAllMetricPrefix = "perfetto";
+
// Matches 1.73, 1.73E+2
private Pattern mNumberWithExponentPattern =
Pattern.compile("[-+]?[0-9]*[\\.]?[0-9]+([eE][-+]?[0-9]+)?");
@@ -222,6 +228,9 @@
parsedMetrics.putAll(
filterMetrics(convertPerfettoProtoMessage(builder.build())));
replacePrefix(parsedMetrics);
+ // Generic prefix string is applied to all the metrics parsed from
+ // perfetto trace file.
+ replaceAllMetricPrefix(parsedMetrics);
break;
case binary:
TraceMetrics metricProto = null;
@@ -230,6 +239,9 @@
parsedMetrics
.putAll(filterMetrics(convertPerfettoProtoMessage(metricProto)));
replacePrefix(parsedMetrics);
+ // Generic prefix string is applied to all the metrics parsed from
+ // perfetto trace file.
+ replaceAllMetricPrefix(parsedMetrics);
break;
case json:
CLog.w("JSON perfetto metric file processing not supported.");
@@ -245,6 +257,7 @@
FileUtil.recursiveDelete(uncompressedDir);
}
}
+
return parsedMetrics;
}
@@ -279,6 +292,25 @@
}
/**
+ * Prefix all the metrics key with given string.
+ *
+ * @param processPerfettoMetrics metrics parsed from the perfetto proto file.
+ */
+ private void replaceAllMetricPrefix(Map<String, Metric.Builder> processPerfettoMetrics) {
+ if (mAllMetricPrefix == null || mAllMetricPrefix.isEmpty()) {
+ return;
+ }
+ Map<String, Metric.Builder> finalMetrics = new HashMap<String, Metric.Builder>();
+ for (Map.Entry<String, Metric.Builder> metric : processPerfettoMetrics.entrySet()) {
+ String newKey = String.format("%s_%s", mAllMetricPrefix, metric.getKey());
+ finalMetrics.put(newKey, metric.getValue());
+ CLog.d("Perfetto trace metric: key: %s value: %s", newKey, metric.getValue());
+ }
+ processPerfettoMetrics.clear();
+ processPerfettoMetrics.putAll(finalMetrics);
+ }
+
+ /**
* Expands the metric proto file as tree structure and converts it into key, value pairs by
* recursively constructing the key using the message name, proto fields with string values
* until the numeric proto field is encountered.
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationFileTest.java b/test_framework/com/android/tradefed/testtype/InstrumentationFileTest.java
index 5fbbd76..7c0d7b6 100644
--- a/test_framework/com/android/tradefed/testtype/InstrumentationFileTest.java
+++ b/test_framework/com/android/tradefed/testtype/InstrumentationFileTest.java
@@ -203,7 +203,7 @@
} finally {
deleteTestFileFromDevice(mFilePathOnDevice);
Collection<TestDescription> completedTests =
- testTracker.getCurrentRunResults().getCompletedTests();
+ InstrumentationTest.excludeNonExecuted(testTracker.getCurrentRunResults());
if (mTests.removeAll(completedTests) && !mTests.isEmpty()) {
// re-run remaining tests from file
writeTestsToFileAndRun(mTests, testInfo, listener);
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationListener.java b/test_framework/com/android/tradefed/testtype/InstrumentationListener.java
index 2a96294..baafb76 100644
--- a/test_framework/com/android/tradefed/testtype/InstrumentationListener.java
+++ b/test_framework/com/android/tradefed/testtype/InstrumentationListener.java
@@ -45,6 +45,7 @@
private Set<TestDescription> mDuplicateTests = new HashSet<>();
private final Collection<TestDescription> mExpectedTests;
private boolean mDisableDuplicateCheck = false;
+ private boolean mReportUnexecutedTests = false;
private ProcessInfo mSystemServerProcess = null;
/**
@@ -68,6 +69,10 @@
mSystemServerProcess = info;
}
+ public void setReportUnexecutedTests(boolean enable) {
+ mReportUnexecutedTests = enable;
+ }
+
@Override
public void testRunStarted(String runName, int testCount) {
// In case of crash, run will attempt to report with 0
@@ -123,6 +128,18 @@
mDuplicateTests));
error.setFailureStatus(FailureStatus.TEST_FAILURE);
super.testRunFailed(error);
+ } else if (mReportUnexecutedTests && mExpectedTests.size() > mTests.size()) {
+ Set<TestDescription> missingTests = new LinkedHashSet<>(mExpectedTests);
+ missingTests.removeAll(mTests);
+ for (TestDescription miss : missingTests) {
+ super.testStarted(miss);
+ FailureDescription failure =
+ FailureDescription.create(
+ "test did not run due to instrumentation issue.",
+ FailureStatus.NOT_EXECUTED);
+ super.testFailed(miss, failure);
+ super.testEnded(miss, new HashMap<String, Metric>());
+ }
}
super.testRunEnded(elapsedTime, runMetrics);
}
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
index 97ae0dd..108f920 100644
--- a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
+++ b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
@@ -45,8 +45,10 @@
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.result.TestResult;
import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner;
+import com.android.tradefed.result.proto.TestRecordProto.FailureStatus;
import com.android.tradefed.retry.IRetryDecision;
import com.android.tradefed.retry.RetryStrategy;
import com.android.tradefed.testtype.coverage.CoverageOptions;
@@ -68,6 +70,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -315,6 +318,12 @@
+ "a system_server restart.")
private boolean mEnableSoftRestartCheck = false;
+ @Option(
+ name = "report-unexecuted-tests",
+ description =
+ "Whether or not to enable reporting all unexecuted tests from instrumentation.")
+ private boolean mReportUnexecuted = true;
+
private IAbi mAbi = null;
private Collection<String> mInstallArgs = new ArrayList<>();
@@ -1053,13 +1062,14 @@
instrumentationListener.setOriginalSystemServer(
getDevice().getProcessByName("system_server"));
}
+ instrumentationListener.setReportUnexecutedTests(mReportUnexecuted);
mDevice.runInstrumentationTests(mRunner, instrumentationListener);
TestRunResult testRun = testTracker.getCurrentRunResults();
if (testRun.isRunFailure() || !testRun.getCompletedTests().containsAll(expectedTests)) {
// Don't re-run any completed tests, unless this is a coverage run.
if (mConfiguration != null
&& !mConfiguration.getCoverageOptions().isCoverageEnabled()) {
- expectedTests.removeAll(testTracker.getCurrentRunResults().getCompletedTests());
+ expectedTests.removeAll(excludeNonExecuted(testTracker.getCurrentRunResults()));
IRetryDecision decision = mConfiguration.getRetryDecision();
if (!RetryStrategy.NO_RETRY.equals(decision.getRetryStrategy())
&& decision.getMaxRetryCount() > 1) {
@@ -1073,6 +1083,20 @@
}
}
+ /** Filter out "NOT_EXECUTED" for the purpose of tracking what needs to be rerun. */
+ protected static Set<TestDescription> excludeNonExecuted(TestRunResult results) {
+ Set<TestDescription> completedTest = results.getCompletedTests();
+ for (Entry<TestDescription, TestResult> entry : results.getTestResults().entrySet()) {
+ if (completedTest.contains(entry.getKey()) && entry.getValue().getFailure() != null) {
+ if (FailureStatus.NOT_EXECUTED.equals(
+ entry.getValue().getFailure().getFailureStatus())) {
+ completedTest.remove(entry.getKey());
+ }
+ }
+ }
+ return completedTest;
+ }
+
/**
* Rerun any <var>mRemainingTests</var>
*
diff --git a/tests/src/com/android/tradefed/postprocessor/PerfettoGenericPostProcessorTest.java b/tests/src/com/android/tradefed/postprocessor/PerfettoGenericPostProcessorTest.java
index 71c02fd..d83c343 100644
--- a/tests/src/com/android/tradefed/postprocessor/PerfettoGenericPostProcessorTest.java
+++ b/tests/src/com/android/tradefed/postprocessor/PerfettoGenericPostProcessorTest.java
@@ -67,6 +67,7 @@
private static final String KEY_PREFIX_OPTION = "perfetto-prefix-key-field";
private static final String REGEX_OPTION_VALUE = "perfetto-metric-filter-regex";
private static final String ALL_METRICS_OPTION = "perfetto-include-all-metrics";
+ private static final String ALL_METRICS_PREFIX_OPTION = "perfetto-all-metric-prefix";
private static final String REPLACE_REGEX_OPTION = "perfetto-metric-replace-prefix";
private static final String FILE_FORMAT_OPTION = "trace-processor-output-format";
@@ -120,10 +121,10 @@
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
- assertMetricsContain(parsedMetrics, "android_startup-startup-1-startup_id", 1);
+ assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-1-startup_id", 1);
assertMetricsContain(
parsedMetrics,
- "android_startup-startup-1-package_name-com.google."
+ "perfetto_android_startup-startup-1-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
}
@@ -152,10 +153,10 @@
assertFalse("Metric key not expected but found",
parsedMetrics.containsKey("android_startup-startup-1-startup_id"));
- assertMetricsContain(parsedMetrics, "newprefix-startup_id", 1);
+ assertMetricsContain(parsedMetrics, "perfetto_newprefix-startup_id", 1);
assertMetricsContain(
parsedMetrics,
- "newprefix-package_name-com.google."
+ "perfetto_newprefix-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
}
@@ -180,7 +181,7 @@
// Test for non startup metrics exists.
assertMetricsContain(
parsedMetrics,
- "android_mem-process_metrics-process_name-"
+ "perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
@@ -201,7 +202,29 @@
new TestDescription("class", "test"), new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
- "android_mem-process_metrics-process_name-"
+ "perfetto_android_mem-process_metrics-process_name-"
+ + ".dataservices-total_counters-anon_rss-min",
+ 27938816);
+ }
+
+ /** Test custom all metric suffix is applied correctly. */
+ @Test
+ public void testParsingWithAllMetricsPrefix() throws ConfigurationException, IOException {
+ setupPerfettoMetricFile(METRIC_FILE_FORMAT.text, true);
+ mOptionSetter.setOptionValue(PREFIX_OPTION, PREFIX_OPTION_VALUE);
+ mOptionSetter.setOptionValue(ALL_METRICS_OPTION, "true");
+ mOptionSetter.setOptionValue(ALL_METRICS_PREFIX_OPTION, "custom_all_prefix");
+ Map<String, LogFile> testLogs = new HashMap<>();
+ testLogs.put(
+ PREFIX_OPTION_VALUE,
+ new LogFile(
+ perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
+ Map<String, Metric.Builder> parsedMetrics =
+ mProcessor.processTestMetricsAndLogs(
+ new TestDescription("class", "test"), new HashMap<>(), testLogs);
+ assertMetricsContain(
+ parsedMetrics,
+ "custom_all_prefix_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
@@ -221,7 +244,7 @@
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
- "android_mem-process_metrics-process_name-"
+ "perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
@@ -242,10 +265,10 @@
perfettoMetricProtoFile.getAbsolutePath(), "some.url", LogDataType.TEXTPB));
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
- assertMetricsContain(parsedMetrics, "android_startup-startup-startup_id", 2);
+ assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-startup_id", 2);
assertMetricsContain(
parsedMetrics,
- "android_startup-startup-package_name-com.google."
+ "perfetto_android_startup-startup-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
53102401);
}
@@ -268,16 +291,16 @@
Map<String, Metric.Builder> parsedMetrics =
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
- assertMetricsContain(parsedMetrics, "android_startup-startup-1-startup_id", 1);
+ assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-1-startup_id", 1);
assertMetricsContain(
parsedMetrics,
- "android_startup-startup-1-package_name-com.google."
+ "perfetto_android_startup-startup-1-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
36175473);
- assertMetricsContain(parsedMetrics, "android_startup-startup-2-startup_id", 2);
+ assertMetricsContain(parsedMetrics, "perfetto_android_startup-startup-2-startup_id", 2);
assertMetricsContain(
parsedMetrics,
- "android_startup-startup-2-package_name-com.google."
+ "perfetto_android_startup-startup-2-package_name-com.google."
+ "android.apps.nexuslauncher-to_first_frame-dur_ns",
53102401);
}
@@ -301,7 +324,7 @@
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(parsedMetrics,
- "android_hwui_metric-process_info-process_name-com.android.systemui-all_mem_min",
+ "perfetto_android_hwui_metric-process_info-process_name-com.android.systemui-all_mem_min",
15120269);
}
@@ -323,7 +346,7 @@
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
- "android_mem-process_metrics-process_name-"
+ "perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
@@ -366,7 +389,7 @@
mProcessor.processRunMetricsAndLogs(new HashMap<>(), testLogs);
assertMetricsContain(
parsedMetrics,
- "android_mem-process_metrics-process_name-"
+ "perfetto_android_mem-process_metrics-process_name-"
+ ".dataservices-total_counters-anon_rss-min",
27938816);
}
diff --git a/tests/src/com/android/tradefed/result/ConsoleResultReporterTest.java b/tests/src/com/android/tradefed/result/ConsoleResultReporterTest.java
index f1290f8..fd0a169 100644
--- a/tests/src/com/android/tradefed/result/ConsoleResultReporterTest.java
+++ b/tests/src/com/android/tradefed/result/ConsoleResultReporterTest.java
@@ -88,7 +88,7 @@
public void testSummary() {
mResultReporter.testResult(mTest, createTestResult(TestStatus.PASSED));
mResultReporter.invocationEnded(0);
- Truth.assertThat(mOutput.toString()).contains("1 Tests [1 Passed ");
+ Truth.assertThat(mOutput.toString()).contains("1 Tests [1 Passed]");
}
@Test
diff --git a/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java b/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
index 517c0a0..eabcab2 100644
--- a/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
+++ b/tests/src/com/android/tradefed/result/proto/ProtoResultParserTest.java
@@ -17,6 +17,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.config.ConfigurationDef;
@@ -166,7 +167,8 @@
mMockListener.logAssociation(
EasyMock.eq("subprocess-invocation_log1"), EasyMock.anyObject());
// Invocation failure is replayed
- mMockListener.invocationFailed(EasyMock.anyObject());
+ Capture<Throwable> captureInvocFailure = new Capture<>();
+ mMockListener.invocationFailed(EasyMock.capture(captureInvocFailure));
mMockListener.invocationEnded(500L);
EasyMock.replay(mMockListener);
@@ -225,6 +227,9 @@
assertEquals(logFile.getType(), capturedFile.getType());
assertEquals(logFile.getSize(), capturedFile.getSize());
+ Throwable invocFailureCaptured = captureInvocFailure.getValue();
+ assertTrue(invocFailureCaptured instanceof RuntimeException);
+
// Check Context at the end
assertEquals(
"build_value", context.getBuildInfos().get(0).getBuildAttributes().get(TEST_KEY));
diff --git a/tests/src/com/android/tradefed/result/proto/StreamProtoResultReporterTest.java b/tests/src/com/android/tradefed/result/proto/StreamProtoResultReporterTest.java
index 41346e0..64f2eac 100644
--- a/tests/src/com/android/tradefed/result/proto/StreamProtoResultReporterTest.java
+++ b/tests/src/com/android/tradefed/result/proto/StreamProtoResultReporterTest.java
@@ -15,6 +15,7 @@
*/
package com.android.tradefed.result.proto;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import com.android.tradefed.config.ConfigurationDescriptor;
@@ -30,6 +31,7 @@
import com.android.tradefed.testtype.suite.ModuleDefinition;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
+import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
@@ -211,6 +213,42 @@
assertNull(receiver.getError());
}
+ @Test
+ public void testStream_incompleteModule() throws Exception {
+ StreamProtoReceiver receiver =
+ new StreamProtoReceiver(mMockListener, mMainInvocationContext, true);
+ OptionSetter setter = new OptionSetter(mReporter);
+ Capture<FailureDescription> capture = new Capture<>();
+ try {
+ setter.setOptionValue(
+ "proto-report-port", Integer.toString(receiver.getSocketServerPort()));
+ // Verify mocks
+ mMockListener.invocationStarted(EasyMock.anyObject());
+
+ mMockListener.testModuleStarted(EasyMock.anyObject());
+ mMockListener.testRunStarted(EasyMock.eq("arm64 module1"), EasyMock.eq(0));
+ mMockListener.testRunFailed(EasyMock.capture(capture));
+ mMockListener.testRunEnded(
+ EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
+ mMockListener.testModuleEnded();
+
+ EasyMock.replay(mMockListener);
+ mReporter.invocationStarted(mInvocationContext);
+ // Run modules
+ mReporter.testModuleStarted(createModuleContext("arm64 module1"));
+ // It stops unexpectedly
+ } finally {
+ receiver.joinReceiver(2000);
+ receiver.close();
+ receiver.completeModuleEvents();
+ }
+ EasyMock.verify(mMockListener);
+ assertNull(receiver.getError());
+ assertEquals(
+ "Module was interrupted after starting, results are incomplete.",
+ capture.getValue().getErrorMessage());
+ }
+
/** Helper to create a module context. */
private IInvocationContext createModuleContext(String moduleId) {
IInvocationContext context = new InvocationContext();
diff --git a/tests/src/com/android/tradefed/testtype/InstrumentationFileTestTest.java b/tests/src/com/android/tradefed/testtype/InstrumentationFileTestTest.java
index 09c5ef4..46e4b7b 100644
--- a/tests/src/com/android/tradefed/testtype/InstrumentationFileTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/InstrumentationFileTestTest.java
@@ -31,6 +31,7 @@
import com.android.tradefed.invoker.InvocationContext;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.FailureDescription;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ITestLifeCycleReceiver;
import com.android.tradefed.result.TestDescription;
@@ -262,6 +263,7 @@
@Test
public void testRun_serialReRunOfTwoFailedToCompleteTests()
throws DeviceNotAvailableException, ConfigurationException {
+ mMockListener = EasyMock.createStrictMock(ITestInvocationListener.class);
final Collection<TestDescription> testsList = new ArrayList<>(1);
final TestDescription test1 = new TestDescription("ClassFoo1", "methodBar1");
final TestDescription test2 = new TestDescription("ClassFoo2", "methodBar2");
@@ -351,6 +353,10 @@
mMockListener.testStarted(EasyMock.eq(test1), EasyMock.anyLong());
mMockListener.testEnded(
EasyMock.eq(test1), EasyMock.anyLong(), EasyMock.eq(new HashMap<String, Metric>()));
+ mMockListener.testStarted(EasyMock.eq(test2), EasyMock.anyLong());
+ mMockListener.testFailed(EasyMock.eq(test2), EasyMock.<FailureDescription>anyObject());
+ mMockListener.testEnded(
+ EasyMock.eq(test2), EasyMock.anyLong(), EasyMock.eq(new HashMap<String, Metric>()));
mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.eq(new HashMap<String, Metric>()));
// first serial re-run:
mMockListener.testRunStarted(TEST_PACKAGE_VALUE, 0, 1);
diff --git a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
index c633824..1c5e61a 100644
--- a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
@@ -465,6 +465,9 @@
inOrder.verify(mMockListener)
.testRunFailed(
FailureDescription.create(RUN_ERROR_MSG, FailureStatus.TEST_FAILURE));
+ inOrder.verify(mMockListener).testStarted(eq(TEST2), anyLong());
+ inOrder.verify(mMockListener).testFailed(eq(TEST2), (FailureDescription) any());
+ inOrder.verify(mMockListener).testEnded(eq(TEST2), anyLong(), eq(EMPTY_STRING_MAP));
inOrder.verify(mMockListener).testRunEnded(1, EMPTY_STRING_MAP);
inOrder.verify(mMockListener).testRunStarted(TEST_PACKAGE_VALUE, 0, 1);
diff --git a/tests/src/com/android/tradefed/util/SerializationUtilTest.java b/tests/src/com/android/tradefed/util/SerializationUtilTest.java
index aab84cb..08a2f4f 100644
--- a/tests/src/com/android/tradefed/util/SerializationUtilTest.java
+++ b/tests/src/com/android/tradefed/util/SerializationUtilTest.java
@@ -15,7 +15,9 @@
*/
package com.android.tradefed.util;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.build.BuildSerializedVersion;
@@ -48,6 +50,15 @@
}
}
+ @Test
+ public void testSerialize_DeserializeString() throws Exception {
+ RuntimeException e = new RuntimeException("test");
+ String serializedException = SerializationUtil.serializeToString(e);
+ Object o = SerializationUtil.deserialize(serializedException);
+ assertTrue(o instanceof RuntimeException);
+ assertEquals("test", ((RuntimeException) o).getMessage());
+ }
+
/** Tests that serialization and deserialization creates a similar object from the original. */
@Test
public void testSerialize_Deserialize() throws Exception {