Merge "Added a RunnerBuilder to enable cmdline swapping to longevity runner." into qt-dev
diff --git a/libraries/collectors-helper/statsd/Android.bp b/libraries/collectors-helper/statsd/Android.bp
index e1060bb..ea586a3 100644
--- a/libraries/collectors-helper/statsd/Android.bp
+++ b/libraries/collectors-helper/statsd/Android.bp
@@ -27,6 +27,7 @@
"statsdprotolite",
"androidx.test.runner",
"guava",
+ "ub-uiautomator",
],
platform_apis: true,
diff --git a/libraries/collectors-helper/statsd/src/com/android/helpers/ThermalHelper.java b/libraries/collectors-helper/statsd/src/com/android/helpers/ThermalHelper.java
index b764959..567dd14 100644
--- a/libraries/collectors-helper/statsd/src/com/android/helpers/ThermalHelper.java
+++ b/libraries/collectors-helper/statsd/src/com/android/helpers/ThermalHelper.java
@@ -17,13 +17,17 @@
package com.android.helpers;
import android.os.TemperatureTypeEnum;
+import android.support.test.uiautomator.UiDevice;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.ThermalThrottlingSeverityStateChanged;
import com.android.os.StatsLog.EventMetricData;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -36,15 +40,45 @@
public class ThermalHelper implements ICollectorHelper<StringBuilder> {
private static final String LOG_TAG = ThermalHelper.class.getSimpleName();
+ private static final int UNDEFINED_SEVERITY = -1;
+ private static final Pattern SEVERITY_DUMPSYS_PATTERN =
+ Pattern.compile("Thermal Status: (\\d+)");
+
private StatsdHelper mStatsdHelper;
+ private UiDevice mDevice;
+ private int mInitialSeverity;
/** Set up the statsd config to track thermal events. */
@Override
public boolean startCollecting() {
- Log.i(LOG_TAG, "Registering thermal config to statsd.");
+ // Add an initial value because this only detects changes.
+ mInitialSeverity = UNDEFINED_SEVERITY;
+ try {
+ String[] output = getDevice().executeShellCommand("dumpsys thermalservice").split("\n");
+ for (String line : output) {
+ Matcher severityMatcher = SEVERITY_DUMPSYS_PATTERN.matcher(line);
+ if (severityMatcher.matches()) {
+ mInitialSeverity = Integer.parseInt(severityMatcher.group(1));
+ Log.v(LOG_TAG, String.format("Initial severity: %s.", mInitialSeverity));
+ }
+ }
+ } catch (NumberFormatException nfe) {
+ Log.w(LOG_TAG, String.format("Couldn't identify severity. Error parsing: %s", nfe));
+ return false;
+ } catch (IOException ioe) {
+ Log.w(LOG_TAG, String.format("Failed to query thermalservice. Error: %s", ioe));
+ return false;
+ }
+
+ // Skip this monitor if severity isn't identified.
+ if (mInitialSeverity == UNDEFINED_SEVERITY) {
+ Log.w(LOG_TAG, "Did not find an initial severity. Quitting.");
+ return false;
+ }
+
+ // Register the thermal event config to statsd.
List<Integer> atomIdList = new ArrayList<>();
atomIdList.add(Atom.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED_FIELD_NUMBER);
- // TODO(b/137793331): Add an initial value because this only detects changes.
return getStatsdHelper().addEventConfig(atomIdList);
}
@@ -53,6 +87,10 @@
public Map<String, StringBuilder> getMetrics() {
Map<String, StringBuilder> results = new HashMap<>();
+ // Add the initial severity value every time metrics are collected.
+ String severityKey = MetricUtility.constructKey("thermal", "throttling", "severity");
+ MetricUtility.addMetric(severityKey, mInitialSeverity, results);
+
List<EventMetricData> eventMetricData = getStatsdHelper().getEventMetrics();
Log.i(LOG_TAG, String.format("%d thermal data points found.", eventMetricData.size()));
// Collect all thermal throttling severity state change events.
@@ -60,14 +98,15 @@
if (dataItem.getAtom().hasThermalThrottlingSeverityStateChanged()) {
// TODO(b/137878503): Add elapsed_timestamp_nanos for timpestamp data.
// Get thermal throttling severity state change data point.
- ThermalThrottlingSeverityStateChanged stateChange =
- dataItem.getAtom().getThermalThrottlingSeverityStateChanged();
- String sensorType = getShorthandSensorType(stateChange.getSensorType());
- String sensorName = stateChange.getSensorName();
- int severity = stateChange.getSeverity().getNumber();
- // Store the severity state change by sensor type and name.
- String metricKey = MetricUtility.constructKey("thermal", sensorType, sensorName);
- MetricUtility.addMetric(metricKey, severity, results);
+ int severity =
+ dataItem.getAtom()
+ .getThermalThrottlingSeverityStateChanged()
+ .getSeverity()
+ .getNumber();
+ // Store the severity state change ignoring where the measurement came from.
+ MetricUtility.addMetric(severityKey, severity, results);
+ // Set the initial severity to the last value, in case #getMetrics is called again.
+ mInitialSeverity = severity;
}
}
@@ -119,4 +158,16 @@
void setStatsdHelper(StatsdHelper helper) {
mStatsdHelper = helper;
}
+
+ private UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+ return mDevice;
+ }
+
+ @VisibleForTesting
+ void setUiDevice(UiDevice device) {
+ mDevice = device;
+ }
}
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/ThermalHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/ThermalHelperTest.java
index 7620c04..bd32b29 100644
--- a/libraries/collectors-helper/statsd/test/src/com/android/helpers/ThermalHelperTest.java
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/ThermalHelperTest.java
@@ -19,6 +19,7 @@
import android.os.TemperatureTypeEnum;
import android.os.ThrottlingSeverityEnum;
+import android.support.test.uiautomator.UiDevice;
import androidx.test.runner.AndroidJUnit4;
import com.android.os.AtomsProto.Atom;
@@ -34,6 +35,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -46,15 +48,24 @@
*/
@RunWith(AndroidJUnit4.class)
public class ThermalHelperTest {
+ private static final String THROTTLING_KEY =
+ MetricUtility.constructKey("thermal", "throttling", "severity");
+ private static final String FAKE_SERVICE_DUMP = "F\nA\nK\nE\nThermal Status: 2\nO\nK";
private ThermalHelper mThermalHelper;
private StatsdHelper mStatsdHelper;
+ private UiDevice mDevice;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mThermalHelper = new ThermalHelper();
+ // Set up the statsd helper to mock statsd calls.
mStatsdHelper = Mockito.spy(new StatsdHelper());
mThermalHelper.setStatsdHelper(mStatsdHelper);
+ // Set up the fake device for mocking shell commands.
+ mDevice = Mockito.mock(UiDevice.class);
+ mThermalHelper.setUiDevice(mDevice);
+ when(mDevice.executeShellCommand("dumpsys thermalservice")).thenReturn(FAKE_SERVICE_DUMP);
}
/** Test registering and unregistering the thermal config. */
@@ -64,16 +75,25 @@
assertTrue(mThermalHelper.stopCollecting());
}
- /** Test that no metrics show up when there are no events. */
+ /** Test registering the thermal config fails when no initial throttling value is found. */
@Test
- public void testNoMetricsWithoutEvents() throws Exception {
+ public void testThermalConfigRegistration_noInitialValue() throws Exception {
+ when(mDevice.executeShellCommand("dumpsys thermalservice")).thenReturn("FAKE RESPONSE");
+ assertFalse(mThermalHelper.startCollecting());
+ }
+
+ /** Test that only the initial value shows up when there are no events. */
+ @Test
+ public void testInitialMetricsWithoutEvents() throws Exception {
when(mStatsdHelper.getEventMetrics()).thenReturn(new ArrayList<EventMetricData>());
assertTrue(mThermalHelper.startCollecting());
- assertTrue(mThermalHelper.getMetrics().isEmpty());
+ assertEquals(
+ mThermalHelper.getMetrics().get(THROTTLING_KEY).toString(),
+ String.valueOf(ThrottlingSeverityEnum.MODERATE.getNumber()));
assertTrue(mThermalHelper.stopCollecting());
}
- /** Test that a single event shows up as a single metric event. */
+ /** Test that the initial value and a single event show up from a single metric event. */
@Test
public void testSingleEvent() throws Exception {
when(mStatsdHelper.getEventMetrics())
@@ -82,52 +102,19 @@
getThermalThrottlingSeverityStateChangedEvent(
TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN,
"sensor_name",
- ThrottlingSeverityEnum.NONE)));
-
+ ThrottlingSeverityEnum.LIGHT)));
assertTrue(mThermalHelper.startCollecting());
Map<String, StringBuilder> metrics = mThermalHelper.getMetrics();
- String key = getMetricKey(TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN, "sensor_name");
- assertTrue(metrics.containsKey(key));
assertEquals(
- metrics.get(key).toString(),
- String.valueOf(ThrottlingSeverityEnum.NONE.getNumber()));
- assertTrue(mThermalHelper.stopCollecting());
- }
-
- /** Test that multiple, similar events shows up as a single metric with multiple values. */
- @Test
- public void testMultipleSimilarEvents() throws Exception {
- when(mStatsdHelper.getEventMetrics())
- .thenReturn(
- getFakeEventMetrics(
- getThermalThrottlingSeverityStateChangedEvent(
- TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN,
- "sensor_name",
- ThrottlingSeverityEnum.NONE),
- getThermalThrottlingSeverityStateChangedEvent(
- TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN,
- "sensor_name",
- ThrottlingSeverityEnum.LIGHT),
- getThermalThrottlingSeverityStateChangedEvent(
- TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN,
- "sensor_name",
- ThrottlingSeverityEnum.MODERATE)));
-
- assertTrue(mThermalHelper.startCollecting());
- Map<String, StringBuilder> metrics = mThermalHelper.getMetrics();
- String key = getMetricKey(TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN, "sensor_name");
- assertTrue(metrics.containsKey(key));
- assertEquals(
- metrics.get(key).toString(),
+ metrics.get(THROTTLING_KEY).toString(),
String.join(
",",
- String.valueOf(ThrottlingSeverityEnum.NONE.getNumber()),
- String.valueOf(ThrottlingSeverityEnum.LIGHT.getNumber()),
- String.valueOf(ThrottlingSeverityEnum.MODERATE.getNumber())));
+ String.valueOf(ThrottlingSeverityEnum.MODERATE.getNumber()),
+ String.valueOf(ThrottlingSeverityEnum.LIGHT.getNumber())));
assertTrue(mThermalHelper.stopCollecting());
}
- /** Test that multiple, different events shows up as a multiple metrics with a single value. */
+ /** Test that multiple throttling events with different sources show up together. */
@Test
public void testMultipleDifferentEvents() throws Exception {
when(mStatsdHelper.getEventMetrics())
@@ -140,7 +127,7 @@
getThermalThrottlingSeverityStateChangedEvent(
TemperatureTypeEnum.TEMPERATURE_TYPE_CPU,
"sensor2_name",
- ThrottlingSeverityEnum.LIGHT),
+ ThrottlingSeverityEnum.MODERATE),
getThermalThrottlingSeverityStateChangedEvent(
TemperatureTypeEnum.TEMPERATURE_TYPE_GPU,
"sensor3_name",
@@ -148,21 +135,14 @@
assertTrue(mThermalHelper.startCollecting());
Map<String, StringBuilder> metrics = mThermalHelper.getMetrics();
- String skinKey = getMetricKey(TemperatureTypeEnum.TEMPERATURE_TYPE_SKIN, "sensor1_name");
- String cpuKey = getMetricKey(TemperatureTypeEnum.TEMPERATURE_TYPE_CPU, "sensor2_name");
- String gpuKey = getMetricKey(TemperatureTypeEnum.TEMPERATURE_TYPE_GPU, "sensor3_name");
- assertTrue(metrics.containsKey(skinKey));
- assertTrue(metrics.containsKey(cpuKey));
- assertTrue(metrics.containsKey(gpuKey));
assertEquals(
- metrics.get(skinKey).toString(),
- String.valueOf(ThrottlingSeverityEnum.LIGHT.getNumber()));
- assertEquals(
- metrics.get(cpuKey).toString(),
- String.valueOf(ThrottlingSeverityEnum.LIGHT.getNumber()));
- assertEquals(
- metrics.get(gpuKey).toString(),
- String.valueOf(ThrottlingSeverityEnum.NONE.getNumber()));
+ metrics.get(THROTTLING_KEY).toString(),
+ String.join(
+ ",",
+ String.valueOf(ThrottlingSeverityEnum.MODERATE.getNumber()),
+ String.valueOf(ThrottlingSeverityEnum.LIGHT.getNumber()),
+ String.valueOf(ThrottlingSeverityEnum.MODERATE.getNumber()),
+ String.valueOf(ThrottlingSeverityEnum.NONE.getNumber())));
assertTrue(mThermalHelper.stopCollecting());
}
diff --git a/libraries/collectors-helper/system/Android.bp b/libraries/collectors-helper/system/Android.bp
new file mode 100644
index 0000000..9baeeed
--- /dev/null
+++ b/libraries/collectors-helper/system/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+// Used for collecting the any of the metrics from the system.
+java_library {
+ name: "system-metric-helper",
+ defaults: ["tradefed_errorprone_defaults"],
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.runner",
+ "collector-helper-utilities",
+ "guava",
+ "ub-uiautomator",
+ ],
+
+ sdk_version: "current",
+}
+
diff --git a/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java b/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java
new file mode 100644
index 0000000..025d53e
--- /dev/null
+++ b/libraries/collectors-helper/system/src/com/android/helpers/ProcLoadHelper.java
@@ -0,0 +1,158 @@
+/*
+ * 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.helpers;
+
+import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** An {@link ProcLoadHelper} to check for cpu load in last minute is lesser or equal
+ * to given threshold for a given timeout and collect the cpu load as metric.
+ */
+public class ProcLoadHelper implements ICollectorHelper<Double> {
+
+ private static final String LOG_TAG = ProcLoadHelper.class.getSimpleName();
+ private static final String LOAD_CMD = "cat /proc/loadavg";
+ public static final String LAST_MINUTE_LOAD_METRIC_KEY = "proc_loadavg_last_minute";
+
+ private static final Pattern LOAD_OUTPUT_PATTERN = Pattern.compile(
+ "(?<LASTMINUTELOAD>.*)\\s.*\\s.*\\s.*\\s.*");
+
+ private double mProcLoadThreshold = 0;
+ private long mProcLoadWaitTimeInMs = 0;
+ // Default to 500 msecs timeout.
+ private long mProcLoadIntervalInMs = 500;
+ private double mRecentLoad = 0;
+ private UiDevice mDevice;
+
+ /** Wait untill the proc/load reaches below the threshold or timeout expires */
+ @Override
+ public boolean startCollecting() {
+ mRecentLoad = 0;
+ long remainingWaitTime = mProcLoadWaitTimeInMs;
+ while (true) {
+ mRecentLoad = getProcLoadInLastMinute();
+ Log.i(LOG_TAG, String.format("Average cpu load in last minute is : %s", mRecentLoad));
+ if (mRecentLoad <= mProcLoadThreshold) {
+ break;
+ } else {
+ if (remainingWaitTime <= 0) {
+ Log.i(LOG_TAG, "Timeout because proc/loadavg never went below the threshold.");
+ return false;
+ }
+ long currWaitTime = (mProcLoadIntervalInMs < remainingWaitTime)
+ ? mProcLoadIntervalInMs : remainingWaitTime;
+ Log.d(LOG_TAG, String.format("Waiting for %s msecs", currWaitTime));
+ SystemClock.sleep(currWaitTime);
+ remainingWaitTime = remainingWaitTime - mProcLoadIntervalInMs;
+ }
+ }
+ return true;
+ }
+
+ /** Collect the proc/load_avg last minute cpu load average metric. */
+ @Override
+ public Map<String, Double> getMetrics() {
+ // Adding the last recorded load in the metric that will be reported.
+ Map<String, Double> result = new HashMap<>();
+ Log.i(LOG_TAG, String.format("proc/loadavg in last minute before test is : %s",
+ mRecentLoad));
+ result.put(LAST_MINUTE_LOAD_METRIC_KEY, mRecentLoad);
+ return result;
+ }
+
+ /** Do nothing, because nothing is needed to disable cpuy load avg. */
+ @Override
+ public boolean stopCollecting() {
+ return true;
+ }
+
+ /**
+ * Parse the last minute cpu load from proc/loadavg
+ *
+ * @return cpu load in last minute. Returns -1 in case if it is failed to parse.
+ */
+ private double getProcLoadInLastMinute() {
+ try {
+ String output = getDevice().executeShellCommand(LOAD_CMD);
+ Log.i(LOG_TAG, String.format("Output of proc_loadavg is : %s", output));
+ // Output of the load command
+ // 1.39 1.10 1.21 2/2679 6380
+ // 1.39 is the proc load in the last minute.
+
+ Matcher match = null;
+ if ((match = matches(LOAD_OUTPUT_PATTERN, output.trim())) != null) {
+ Log.i(LOG_TAG, String.format("Current load is : %s",
+ match.group("LASTMINUTELOAD")));
+ return Double.parseDouble(match.group("LASTMINUTELOAD"));
+ } else {
+ Log.w(LOG_TAG, "Not able to parse the proc/loadavg");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to get proc/loadavg.", e);
+ }
+
+ return -1;
+ }
+
+ /** Returns the {@link UiDevice} under test. */
+ private UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+ return mDevice;
+ }
+
+ /**
+ * Checks whether {@code line} matches the given {@link Pattern}.
+ *
+ * @return The resulting {@link Matcher} obtained by matching the {@code line} against
+ * {@code pattern}, or null if the {@code line} does not match.
+ */
+ private static Matcher matches(Pattern pattern, String line) {
+ Matcher ret = pattern.matcher(line);
+ return ret.matches() ? ret : null;
+ }
+
+ /**
+ * Sets the threshold value which the device cpu load average should be lesser than or equal.
+ */
+ public void setProcLoadThreshold(double procLoadThreshold) {
+ mProcLoadThreshold = procLoadThreshold;
+ }
+
+ /**
+ * Sets the timeout in msecs checking for threshold before proceeding with the testing.
+ */
+ public void setProcLoadWaitTimeInMs(long procLoadWaitTimeInMs) {
+ mProcLoadWaitTimeInMs = procLoadWaitTimeInMs;
+ }
+
+ /**
+ * Sets the interval time in msecs to check continuosly untill the timeout expires.
+ */
+ public void setProcLoadIntervalInMs(long procLoadIntervalInMs) {
+ mProcLoadIntervalInMs = procLoadIntervalInMs;
+ }
+}
diff --git a/libraries/collectors-helper/system/test/Android.bp b/libraries/collectors-helper/system/test/Android.bp
new file mode 100644
index 0000000..4d94e5a
--- /dev/null
+++ b/libraries/collectors-helper/system/test/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+java_library {
+ name: "system-helper-test",
+ defaults: ["tradefed_errorprone_defaults"],
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.test.runner",
+ "system-metric-helper",
+ "junit",
+ "mockito-target",
+ "truth-prebuilt",
+ ],
+
+ sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java b/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java
new file mode 100644
index 0000000..a66d8bc
--- /dev/null
+++ b/libraries/collectors-helper/system/test/src/com/android/helpers/tests/ProcLoadHelperTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.helpers.tests;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.ProcLoadHelper;
+import static com.android.helpers.ProcLoadHelper.LAST_MINUTE_LOAD_METRIC_KEY;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Android Unit tests for {@link ProcLoadHelperTest}.
+ *
+ * To run:
+ * atest CollectorsHelperTest:com.android.helpers.tests.ProcLoadHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ProcLoadHelperTest {
+
+ private ProcLoadHelper mLoadHelper;
+
+ @Before
+ public void setUp() {
+ mLoadHelper = new ProcLoadHelper();
+ }
+
+ /** Test to verify succesfull threshold flow. **/
+ @Test
+ public void testThresholdSuccess() {
+ // By setting the threshold higher(i.e 100) the current load should be always
+ // lesser than the the 100 which should proceed with add the current
+ // load avg during the last minute in the metrics.
+ mLoadHelper.setProcLoadThreshold(100);
+ mLoadHelper.setProcLoadWaitTimeInMs(1100L);
+ mLoadHelper.setProcLoadIntervalInMs(100L);
+ assertTrue(mLoadHelper.startCollecting());
+ Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+ assertTrue(procLoadMetric.containsKey(LAST_MINUTE_LOAD_METRIC_KEY));
+ assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+ }
+
+ /** Test to verify threshold not met workflow. */
+ @Test
+ public void testMetricAfterTimeout() {
+ // By setting the threshold lower(i.e 0) the cpu utilization will never be met
+ // which should result in timeout.
+ mLoadHelper.setProcLoadThreshold(0);
+ mLoadHelper.setProcLoadWaitTimeInMs(4000L);
+ mLoadHelper.setProcLoadIntervalInMs(200L);
+ assertFalse(mLoadHelper.startCollecting());
+ Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+ assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+ }
+
+ /** Test to verify the timeout if the threshold did not meet. */
+ @Test
+ public void testWaitForTimeout() {
+ // By setting the threshold lower(i.e 0) the cpu utilization will never be met
+ // which should result in timeout.
+ mLoadHelper.setProcLoadThreshold(0);
+ mLoadHelper.setProcLoadWaitTimeInMs(4000L);
+ mLoadHelper.setProcLoadIntervalInMs(200L);
+ long startTime = System.currentTimeMillis();
+ assertFalse(mLoadHelper.startCollecting());
+ long waitTime = System.currentTimeMillis() - startTime;
+ assertTrue((waitTime > 4000L && waitTime < 6000L));
+ Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+ assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+ }
+
+ /** Test to verify metric exist and exits with the default threshold, timeout and interval */
+ @Test
+ public void testDefaults() {
+ long startTime = System.currentTimeMillis();
+ assertFalse(mLoadHelper.startCollecting());
+ Map<String, Double> procLoadMetric = mLoadHelper.getMetrics();
+ assertTrue(procLoadMetric.containsKey(LAST_MINUTE_LOAD_METRIC_KEY));
+ assertTrue(procLoadMetric.get(LAST_MINUTE_LOAD_METRIC_KEY) > 0);
+ }
+}
diff --git a/libraries/device-collectors/src/main/Android.bp b/libraries/device-collectors/src/main/Android.bp
index 9e144ed..e78c906 100644
--- a/libraries/device-collectors/src/main/Android.bp
+++ b/libraries/device-collectors/src/main/Android.bp
@@ -25,6 +25,7 @@
"memory-helper",
"perfetto-helper",
"ub-uiautomator",
+ "system-metric-helper",
],
sdk_version: "current",
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java
new file mode 100644
index 0000000..adc4c7b
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/ProcLoadListener.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.ProcLoadHelper;
+
+/**
+ * A {@link ProcLoadListener} that waits until the proc/load threshold is met
+ * or timeout expires.
+ *
+ * Options:
+ * <p>-e proc-loadavg-threshold 1 : The threshold the system cpu load has
+ * to be less than or equal in last minute.
+ *
+ * <p>-e proc-loadavg-timeout 1000 :
+ * Timeout to wait before the threshold is met.
+ *
+ * <p>-e proc-loadavg-interval 100 :
+ * Interval frequency to check if the threshold is met or not.
+ *
+ */
+@OptionClass(alias = "procload-collector")
+public class ProcLoadListener extends BaseCollectionListener<Double> {
+
+ private static final String TAG = ProcLoadListener.class.getSimpleName();
+ @VisibleForTesting
+ static final String PROC_LOAD_THRESHOLD = "proc-loadavg-threshold";
+ @VisibleForTesting
+ static final String PROC_THRESHOLD_TIMEOUT = "proc-loadavg-timeout";
+ @VisibleForTesting
+ static final String PROC_LOAD_INTERVAL = "proc-loadavg-interval";
+
+ private ProcLoadHelper mProcLoadHelper = new ProcLoadHelper();
+
+ public ProcLoadListener() {
+ createHelperInstance(mProcLoadHelper);
+ }
+
+ /**
+ * Constructor to simulate receiving the instrumentation arguments. Should not be used except
+ * for testing.
+ */
+ @VisibleForTesting
+ public ProcLoadListener(Bundle args, ProcLoadHelper helper) {
+ super(args, helper);
+ mProcLoadHelper = helper;
+ createHelperInstance(mProcLoadHelper);
+ }
+
+ /**
+ * Adds the options for total pss collector.
+ */
+ @Override
+ public void setupAdditionalArgs() {
+ Bundle args = getArgsBundle();
+
+ if (args.getString(PROC_LOAD_THRESHOLD) != null) {
+ mProcLoadHelper.setProcLoadThreshold(Double.parseDouble(args
+ .getString(PROC_LOAD_THRESHOLD)));
+ }
+
+ if (args.getString(PROC_THRESHOLD_TIMEOUT) != null) {
+ mProcLoadHelper.setProcLoadWaitTimeInMs(Long.parseLong(args
+ .getString(PROC_THRESHOLD_TIMEOUT)));
+ }
+
+ if (args.getString(PROC_LOAD_INTERVAL) != null) {
+ mProcLoadHelper.setProcLoadIntervalInMs(Long.parseLong(args
+ .getString(PROC_LOAD_INTERVAL)));
+ }
+
+ }
+}
diff --git a/libraries/device-collectors/src/main/platform-collectors/Android.bp b/libraries/device-collectors/src/main/platform-collectors/Android.bp
index 719211c..89f3733 100644
--- a/libraries/device-collectors/src/main/platform-collectors/Android.bp
+++ b/libraries/device-collectors/src/main/platform-collectors/Android.bp
@@ -48,6 +48,7 @@
"libprotobuf-java-lite",
"statsd-config-protos",
"statsd-helper",
+ "ub-uiautomator",
],
platform_apis: true,
}
diff --git a/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java
index 8652525..a9799f5 100644
--- a/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java
+++ b/libraries/device-collectors/src/main/platform-collectors/src/android/device/collectors/StatsdListener.java
@@ -67,6 +67,9 @@
// Prefix template for test-level metric report files.
static final String TEST_PREFIX_TEMPLATE = "%s-%d_";
+ // Common prefix for the metric key pointing to the report path.
+ static final String REPORT_KEY_PREFIX = "statsd-";
+
// Configs used for the test run and each test, respectively.
private Map<String, StatsdConfig> mRunLevelConfigs = new HashMap<String, StatsdConfig>();
private Map<String, StatsdConfig> mTestLevelConfigs = new HashMap<String, StatsdConfig>();
@@ -103,7 +106,7 @@
pullReportsAndRemoveConfigs(
mRunLevelConfigIds, Paths.get(REPORT_PATH_ROOT, REPORT_PATH_RUN_LEVEL), "");
for (String configName : configReports.keySet()) {
- runData.addFileMetric(configName, configReports.get(configName));
+ runData.addFileMetric(REPORT_KEY_PREFIX + configName, configReports.get(configName));
}
}
@@ -128,7 +131,7 @@
Paths.get(REPORT_PATH_ROOT, REPORT_PATH_TEST_LEVEL),
getTestPrefix(description));
for (String configName : configReports.keySet()) {
- testData.addFileMetric(configName, configReports.get(configName));
+ testData.addFileMetric(REPORT_KEY_PREFIX + configName, configReports.get(configName));
}
}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java
new file mode 100644
index 0000000..f90470e
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/ProcLoadListenerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.device.collectors;
+
+import static android.device.collectors.ProcLoadListener.PROC_LOAD_THRESHOLD;
+import static android.device.collectors.ProcLoadListener.PROC_THRESHOLD_TIMEOUT;
+import static android.device.collectors.ProcLoadListener.PROC_LOAD_INTERVAL;
+
+
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.ProcLoadHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Android Unit tests for {@link ProcLoadListener}.
+ *
+ * To run:
+ * atest CollectorDeviceLibTest:android.device.collectors.ProcLoadListenerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ProcLoadListenerTest {
+
+ @Mock
+ private Instrumentation mInstrumentation;
+ @Mock
+ private ProcLoadHelper mProcLoadHelper;
+
+ private ProcLoadListener mListener;
+ private Description mRunDesc;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRunDesc = Description.createSuiteDescription("run");
+ }
+
+ private ProcLoadListener initListener(Bundle b) {
+ ProcLoadListener listener = new ProcLoadListener(b, mProcLoadHelper);
+ listener.setInstrumentation(mInstrumentation);
+ return listener;
+ }
+
+ @Test
+ public void testLoadProcAdditionalOptions() throws Exception {
+ Bundle b = new Bundle();
+ b.putString(PROC_LOAD_THRESHOLD, "1");
+ b.putString(PROC_THRESHOLD_TIMEOUT, "3");
+ b.putString(PROC_LOAD_INTERVAL, "2");
+ mListener = initListener(b);
+ mListener.testRunStarted(mRunDesc);
+
+ verify(mProcLoadHelper).setProcLoadThreshold(1);
+ verify(mProcLoadHelper).setProcLoadWaitTimeInMs(3L);
+ verify(mProcLoadHelper).setProcLoadIntervalInMs(2L);
+ }
+}
diff --git a/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java b/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java
index e704a53..170ded2 100644
--- a/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java
+++ b/libraries/device-collectors/src/test/platform/android/device/collectors/StatsdListenerTest.java
@@ -149,7 +149,7 @@
any());
verify(runData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getExactFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -167,7 +167,7 @@
any());
verify(runData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_2),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_2),
getExactFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -234,7 +234,7 @@
any());
verify(testData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -258,7 +258,7 @@
any());
verify(testData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_2),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_2),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -292,7 +292,7 @@
verify(testData1, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -311,7 +311,7 @@
verify(testData2, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -345,7 +345,7 @@
// The metric file name should contain the iteration number (1).
verify(testData1, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -365,7 +365,7 @@
// The metric file name should contain the iteration number (2).
verify(testData2, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -402,7 +402,7 @@
verify(testData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getPartialFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,
@@ -416,7 +416,7 @@
verify(runData, times(1))
.addFileMetric(
- eq(CONFIG_NAME_1),
+ eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1),
getExactFileNameMatcher(
Paths.get(
StatsdListener.REPORT_PATH_ROOT,