Revert "Rebase to master@ 6976336"
Revert submission 13068802-rebase_tf_173451928
Reason for revert: It leads to test flakiness and the coverage tests are not executed at all: https://b.corp.google.com/issues/173451928#comment47
Reverted Changes:
I249714c9e:Cherrypick from aosp/1478357.
I572e5c2ad:Cherrypick from https://android-review.googlesourc...
I56119259a:Cherrypicked from https://android-review.git.corp....
Id204d5385:Update build api library.
I9693182b7:Cherrypicked from https://android-review.googlesou...
I270764875:Cherrypicked from https://android-review.googlesou...
Iccd4e0e52:Cherrypick from: https://googleplex-android-review...
I69ef44a39:Cherrypick from: https://android-review.googlesour...
Ia9303dd1f:Cherrypick from: https://android-review.googlesour...
I569229605:Rebase to master@ 6976336
I58902ca81:Rebase to master@ 6976336 and copy the change in a...
I32e150372:Rebase to master@ 6976336
I3460d928c:Rebase to master@ 6976336
I63e2bedb6:Rebase to master@ 6976336
I82c2f9159:Rebase to master@ 6976336
Exempt-From-Owner-Approval: revert
Change-Id: I841c2207f7d89fafabfaa424e60d53c596b24294
Merged-In: I716ceacd3af80e26113e1520d33147f7844e4820
diff --git a/Android.bp b/Android.bp
index d70ae95..f7055c4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,6 +20,7 @@
libs: [
"guava",
+ "junit",
"json-prebuilt",
],
}
diff --git a/test_framework/com/android/tradefed/device/metric/LogcatTimingMetricCollector.java b/test_framework/com/android/tradefed/device/metric/LogcatTimingMetricCollector.java
new file mode 100644
index 0000000..648a3af
--- /dev/null
+++ b/test_framework/com/android/tradefed/device/metric/LogcatTimingMetricCollector.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tradefed.device.metric;
+
+import com.android.loganalysis.item.GenericTimingItem;
+import com.android.loganalysis.parser.TimingsLogParser;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.LogcatReceiver;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.metrics.proto.MetricMeasurement.DataType;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Measurements;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestDescription;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * A metric collector that collects timing information (e.g. user switch time) from logcat during
+ * one or multiple repeated tests by using given regex patterns to parse start and end signals of an
+ * event from logcat lines.
+ */
+@OptionClass(alias = "timing-metric-collector")
+public class LogcatTimingMetricCollector extends BaseDeviceMetricCollector {
+
+ private static final String LOGCAT_NAME_FORMAT = "device_%s_test_logcat";
+ // Use logcat -T 'count' to only print a few line before we start and not the full buffer
+ private static final String LOGCAT_CMD = "logcat *:D -T 150";
+
+ @Option(
+ name = "start-pattern",
+ description =
+ "Key-value pairs to specify the timing metric start patterns to capture from"
+ + " logcat. Key: metric name, value: regex pattern of logcat line"
+ + " indicating the start of the timing metric")
+ private final Map<String, String> mStartPatterns = new HashMap<>();
+
+ @Option(
+ name = "end-pattern",
+ description =
+ "Key-value pairs to specify the timing metric end patterns to capture from"
+ + " logcat. Key: metric name, value: regex pattern of logcat line"
+ + " indicating the end of the timing metric")
+ private final Map<String, String> mEndPatterns = new HashMap<>();
+
+ @Option(
+ name = "logcat-buffer",
+ description =
+ "Logcat buffers where the timing metrics are captured. Default buffers will be"
+ + " used if not specified.")
+ private final List<String> mLogcatBuffers = new ArrayList<>();
+
+ private final Map<ITestDevice, LogcatReceiver> mLogcatReceivers = new HashMap<>();
+ private final TimingsLogParser mParser = new TimingsLogParser();
+
+ @Override
+ public void onTestRunStart(DeviceMetricData testData) {
+ // Adding patterns
+ mParser.clearDurationPatterns();
+ for (Map.Entry<String, String> entry : mStartPatterns.entrySet()) {
+ String name = entry.getKey();
+ if (!mEndPatterns.containsKey(name)) {
+ CLog.w("Metric %s is missing end pattern, skipping.", name);
+ continue;
+ }
+ Pattern start = Pattern.compile(entry.getValue());
+ Pattern end = Pattern.compile(mEndPatterns.get(name));
+ CLog.d("Adding metric: %s", name);
+ mParser.addDurationPatternPair(name, start, end);
+ }
+ // Start receiving logcat
+ String logcatCmd = LOGCAT_CMD;
+ if (!mLogcatBuffers.isEmpty()) {
+ logcatCmd += " -b " + String.join(",", mLogcatBuffers);
+ }
+ for (ITestDevice device : getDevices()) {
+ CLog.d(
+ "Creating logcat receiver on device %s with command %s",
+ device.getSerialNumber(), logcatCmd);
+ mLogcatReceivers.put(device, createLogcatReceiver(device, logcatCmd));
+ try {
+ device.executeShellCommand("logcat -c");
+ } catch (DeviceNotAvailableException e) {
+ CLog.e(
+ "Device not available when clear logcat. Skip logcat collection on %s",
+ device.getSerialNumber());
+ continue;
+ }
+ mLogcatReceivers.get(device).start();
+ }
+ }
+
+ @Override
+ public void onTestRunEnd(
+ DeviceMetricData testData, final Map<String, Metric> currentTestCaseMetrics) {
+ boolean isMultiDevice = getDevices().size() > 1;
+ for (ITestDevice device : getDevices()) {
+ try (InputStreamSource logcatData = mLogcatReceivers.get(device).getLogcatData()) {
+ Map<String, List<Double>> metrics = parse(logcatData);
+ for (Map.Entry<String, List<Double>> entry : metrics.entrySet()) {
+ String name = entry.getKey();
+ List<Double> values = entry.getValue();
+ if (isMultiDevice) {
+ testData.addMetricForDevice(device, name, createMetric(values));
+ } else {
+ testData.addMetric(name, createMetric(values));
+ }
+ CLog.d(
+ "Metric: %s with value: %s, added to device %s",
+ name, values, device.getSerialNumber());
+ }
+ testLog(
+ String.format(LOGCAT_NAME_FORMAT, device.getSerialNumber()),
+ LogDataType.TEXT,
+ logcatData);
+ }
+ mLogcatReceivers.get(device).stop();
+ mLogcatReceivers.get(device).clear();
+ }
+ }
+
+ @Override
+ public void onTestFail(DeviceMetricData testData, TestDescription test) {
+ for (ITestDevice device : getDevices()) {
+ try (InputStreamSource logcatData = mLogcatReceivers.get(device).getLogcatData()) {
+ testLog(
+ String.format(LOGCAT_NAME_FORMAT, device.getSerialNumber()),
+ LogDataType.TEXT,
+ logcatData);
+ }
+ mLogcatReceivers.get(device).stop();
+ mLogcatReceivers.get(device).clear();
+ }
+ }
+
+ @VisibleForTesting
+ Map<String, List<Double>> parse(InputStreamSource logcatData) {
+ Map<String, List<Double>> metrics = new HashMap<>();
+ try (InputStream inputStream = logcatData.createInputStream();
+ InputStreamReader logcatReader = new InputStreamReader(inputStream);
+ BufferedReader br = new BufferedReader(logcatReader)) {
+ List<GenericTimingItem> items = mParser.parseGenericTimingItems(br);
+ for (GenericTimingItem item : items) {
+ String metricKey = item.getName();
+ if (!metrics.containsKey(metricKey)) {
+ metrics.put(metricKey, new ArrayList<>());
+ }
+ metrics.get(metricKey).add(item.getDuration());
+ }
+ } catch (IOException e) {
+ CLog.e("Failed to parse timing metrics from logcat %s", e);
+ }
+ return metrics;
+ }
+
+ @VisibleForTesting
+ LogcatReceiver createLogcatReceiver(ITestDevice device, String logcatCmd) {
+ return new LogcatReceiver(device, logcatCmd, device.getOptions().getMaxLogcatDataSize(), 0);
+ }
+
+ private Metric.Builder createMetric(List<Double> values) {
+ // TODO: Fix post processors to handle double values. For now use concatenated string as we
+ // prefer to use AggregatedPostProcessor
+ String stringValue =
+ values.stream()
+ .map(value -> Double.toString(value))
+ .collect(Collectors.joining(","));
+ return Metric.newBuilder()
+ .setType(DataType.RAW)
+ .setMeasurements(Measurements.newBuilder().setSingleString(stringValue));
+ }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 0547b23..623b4ab 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -22,7 +22,7 @@
libs: [
"loganalysis",
"json-prebuilt",
- "junit",
+ "junit-host",
"easymock",
],
}