Merge "Moved profile to RunListener interface."
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/ProcessShowmapHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/ProcessShowmapHelper.java
index cc57382..6584363 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/ProcessShowmapHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/ProcessShowmapHelper.java
@@ -22,6 +22,8 @@
import android.support.test.uiautomator.UiDevice;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import java.io.IOException;
@@ -42,7 +44,6 @@
* metrics = processShowmapHelper.getMetrics();
* processShowmapHelper.stopCollecting();
*
- * TODO(b/119675321) Take in multiple processes and output metrics for each
* TODO(b/119684651) Add support for writing showmap output to file
*/
public class ProcessShowmapHelper implements ICollectorHelper<Long> {
@@ -56,9 +57,9 @@
private static final String VSS = "vss";
private static final String DELTA = "delta";
- private String mProcessName;
- private ShowmapMetrics mTestStartMetrics;
- private ShowmapMetrics mTestEndMetrics;
+ private String[] mProcessNames;
+ private ShowmapMetrics[] mTestStartMetrics;
+ private ShowmapMetrics[] mTestEndMetrics;
private UiDevice mUiDevice;
private static final class ShowmapMetrics {
@@ -70,16 +71,16 @@
/**
* Sets up the helper before it starts sampling.
*
- * @param processName process name to sample
+ * @param processNames process names to sample
*/
- public void setUp(String processName) {
- mProcessName = processName;
+ public void setUp(String... processNames) {
+ mProcessNames = processNames;
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
@Override
public boolean startCollecting() {
- mTestStartMetrics = sampleMemory(mProcessName);
+ mTestStartMetrics = sampleMemoryOfProcesses(mProcessNames);
return mTestStartMetrics != null;
}
@@ -87,28 +88,37 @@
public Map<String, Long> getMetrics() {
// Collect end sample.
HashMap<String, Long> showmapFinalMap = new HashMap<>();
- mTestEndMetrics = sampleMemory(mProcessName);
+ mTestEndMetrics = sampleMemoryOfProcesses(mProcessNames);
if (mTestEndMetrics == null) {
- Log.e(TAG, "Unable to collect showmap memory at test end.");
+ Log.e(TAG, "Unable to collect any showmap metrics at end. Returning empty metrics");
return showmapFinalMap;
}
- // Calculate and determine final metrics.
- showmapFinalMap.put(constructKey(mProcessName, PSS), mTestEndMetrics.pss);
- showmapFinalMap.put(constructKey(mProcessName, RSS), mTestEndMetrics.rss);
- showmapFinalMap.put(constructKey(mProcessName, VSS), mTestEndMetrics.vss);
+ // Iterate over each process and collate start and end sample to build final metrics.
+ for (int i = 0; i < mTestEndMetrics.length; i++) {
+ String processName = mProcessNames[i];
+ ShowmapMetrics endMetrics = mTestEndMetrics[i];
+ if (endMetrics == null) {
+ // Failed to get end metrics for this process. Continue.
+ continue;
+ }
+ // Calculate and determine final metrics.
+ showmapFinalMap.put(constructKey(processName, PSS), endMetrics.pss);
+ showmapFinalMap.put(constructKey(processName, RSS), endMetrics.rss);
+ showmapFinalMap.put(constructKey(processName, VSS), endMetrics.vss);
- if (mTestStartMetrics == null) {
- Log.i(TAG, "Unable to get deltas because we were unable to sample memory when the "
- + "test began");
- return showmapFinalMap;
+ if (mTestStartMetrics == null || mTestStartMetrics[i] == null) {
+ // Failed to get start metrics for this process. Continue.
+ continue;
+ }
+ ShowmapMetrics startMetrics = mTestStartMetrics[i];
+ showmapFinalMap.put(
+ constructKey(processName, PSS, DELTA), endMetrics.pss - startMetrics.pss);
+ showmapFinalMap.put(
+ constructKey(processName, RSS, DELTA), endMetrics.rss - startMetrics.rss);
+ showmapFinalMap.put(
+ constructKey(processName, VSS, DELTA), endMetrics.vss - startMetrics.vss);
}
- showmapFinalMap.put(constructKey(mProcessName, PSS, DELTA),
- mTestEndMetrics.pss - mTestStartMetrics.pss);
- showmapFinalMap.put(constructKey(mProcessName, RSS, DELTA),
- mTestEndMetrics.rss - mTestStartMetrics.rss);
- showmapFinalMap.put(constructKey(mProcessName, VSS, DELTA),
- mTestEndMetrics.vss - mTestStartMetrics.vss);
return showmapFinalMap;
}
@@ -119,16 +129,30 @@
}
/**
+ * Sample the current memory for a set of processes using showmap.
+ *
+ * @param processNames the process names to sample
+ * @return a list of showmap metrics for each process given in order. May be null if it is not
+ * properly set up.
+ */
+ private @Nullable ShowmapMetrics[] sampleMemoryOfProcesses(String... processNames) {
+ if (processNames == null || mUiDevice == null) {
+ Log.e(TAG, "Process names or UI device is null. Make sure you've called setup.");
+ return null;
+ }
+ ShowmapMetrics[] metrics = new ShowmapMetrics[processNames.length];
+ for (int i = 0; i < processNames.length; i++) {
+ metrics[i] = sampleMemory(processNames[i]);
+ }
+ return metrics;
+ }
+
+ /**
* Samples the current memory use of the process using showmap. Gets PSS, RSS, and VSS.
*
* @return metrics object with pss, rss, and vss
*/
- private ShowmapMetrics sampleMemory(String processName) {
- if (processName == null || mUiDevice == null) {
- Log.e(TAG,"Process name or UI device is null. Make sure you've called setup.");
- return null;
- }
-
+ private @Nullable ShowmapMetrics sampleMemory(@NonNull String processName) {
// Get pid
int pid;
try {
@@ -137,7 +161,7 @@
String.format(PIDOF_CMD, processName));
pid = NumberFormat.getInstance().parse(pidofOutput).intValue();
} catch (IOException | ParseException e) {
- Log.e(TAG, "Unable to get pid of process", e);
+ Log.e(TAG, String.format("Unable to get pid of %s ", processName), e);
return null;
}
@@ -168,7 +192,7 @@
metrics.rss = sc.nextLong();
metrics.pss = sc.nextLong();
} catch (IndexOutOfBoundsException | InputMismatchException e) {
- Log.e(TAG, "Unexpected showmap format", e);
+ Log.e(TAG, String.format("Unexpected showmap format for %s ", processName), e);
return null;
}
return metrics;
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ProcessShowmapHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ProcessShowmapHelperTest.java
index a45c80c..9bf48be 100644
--- a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ProcessShowmapHelperTest.java
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/ProcessShowmapHelperTest.java
@@ -42,6 +42,8 @@
// Process name used for testing
private static final String TEST_PROCESS_NAME = "com.android.systemui";
+ // Second process name used for testing
+ private static final String TEST_PROCESS_NAME_2 = "system_server";
// Pss string in key
private static final String PSS = "pss";
// Delta string in keys
@@ -63,13 +65,12 @@
assertFalse(mShowmapHelper.startCollecting());
}
- /**
- * Test start collecting returns false if the process name is empty.
- */
+ /** Test no metrics are sampled if process name is empty. */
@Test
public void testEmptyProcessName() {
mShowmapHelper.setUp("");
- assertFalse(mShowmapHelper.startCollecting());
+ Map<String, Long> showmapMetrics = mShowmapHelper.getMetrics();
+ assertTrue(showmapMetrics.isEmpty());
}
/**
@@ -81,16 +82,26 @@
assertTrue(mShowmapHelper.startCollecting());
}
- /**
- * Test getting metrics based off sampled memory.
- */
+ /** Test getting metrics based off sampled memory. */
@Test
- public void testGetMetrics() {
+ public void testGetMetrics_OneProcess() {
mShowmapHelper.setUp(TEST_PROCESS_NAME);
assertTrue(mShowmapHelper.startCollecting());
Map<String, Long> showmapMetrics = mShowmapHelper.getMetrics();
- assertTrue(!showmapMetrics.isEmpty());
+ assertFalse(showmapMetrics.isEmpty());
assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME, PSS)));
assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME, PSS, DELTA)));
}
+
+ @Test
+ public void testGetMetrics_MultipleProcesses() {
+ mShowmapHelper.setUp(TEST_PROCESS_NAME, TEST_PROCESS_NAME_2);
+ assertTrue(mShowmapHelper.startCollecting());
+ Map<String, Long> showmapMetrics = mShowmapHelper.getMetrics();
+ assertFalse(showmapMetrics.isEmpty());
+ assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME, PSS)));
+ assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME, PSS, DELTA)));
+ assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME_2, PSS)));
+ assertTrue(showmapMetrics.containsKey(constructKey(TEST_PROCESS_NAME_2, PSS, DELTA)));
+ }
}
diff --git a/libraries/rule/src/android/platform/test/rule/StopwatchRule.java b/libraries/rule/src/android/platform/test/rule/StopwatchRule.java
new file mode 100644
index 0000000..eed7071
--- /dev/null
+++ b/libraries/rule/src/android/platform/test/rule/StopwatchRule.java
@@ -0,0 +1,79 @@
+/*
+ * 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.platform.test.rule;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.Stopwatch;
+import org.junit.runner.Description;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The rule measures the test execution time by extending {@link org.junit.rules.Stopwatch}. It will
+ * report the test time as key value pair to the instrumentation. For now, the rule will only report
+ * metric when the test succeed.
+ *
+ * <p>TODO: Consider implementing generic metric reporting library or tight listeners to report
+ * metric.
+ */
+public class StopwatchRule extends Stopwatch {
+ /**
+ * Metrics will be reported under the "status in progress" for test cases to be associated with
+ * the running use cases.
+ */
+ @VisibleForTesting static final int INST_STATUS_IN_PROGRESS = 2;
+
+ @VisibleForTesting static final String METRIC_FORMAT = "duration_ms_%s#%s";
+
+ private Bundle mResult = new Bundle();
+ private Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+ /**
+ * The method will report test time as milliseconds to instrumentation.
+ *
+ * @param nanos Test time input as nano seconds
+ * @param description Description for for the test
+ */
+ private void reportMetric(long nanos, Description description) {
+ String metricKey =
+ String.format(
+ METRIC_FORMAT, description.getClassName(), description.getMethodName());
+ long millis = TimeUnit.NANOSECONDS.toMillis(nanos);
+ mResult.putLong(metricKey, millis);
+ mInstrumentation.sendStatus(INST_STATUS_IN_PROGRESS, mResult);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void succeeded(long nanos, Description description) {
+ reportMetric(nanos, description);
+ }
+
+ @VisibleForTesting
+ Bundle getMetric() {
+ return mResult;
+ }
+
+ @VisibleForTesting
+ void setInstrumentation(Instrumentation instrumentation) {
+ mInstrumentation = instrumentation;
+ }
+}
diff --git a/libraries/rule/tests/src/android/platform/test/rule/StopwatchRuleTest.java b/libraries/rule/tests/src/android/platform/test/rule/StopwatchRuleTest.java
new file mode 100644
index 0000000..fea19e8
--- /dev/null
+++ b/libraries/rule/tests/src/android/platform/test/rule/StopwatchRuleTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.platform.test.rule;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.runners.model.Statement;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class StopwatchRuleTest {
+
+ private static final int SLEEP_TIME_MS = 1000;
+ private static final int TIME_DELTA_MS = 20;
+
+ @Test
+ public void testMeasurementIsCorrect() throws Throwable {
+ StopwatchRule rule = new StopwatchRule();
+ rule.apply(
+ new Statement() {
+ //Mock a test that will take 1000ms long
+ @Override
+ public void evaluate() throws Throwable {
+ Thread.sleep(SLEEP_TIME_MS);
+ }
+ },
+ Description.createTestDescription("clzz", "method"))
+ .evaluate();
+
+ Bundle metric = rule.getMetric();
+ String metricKey = String.format(StopwatchRule.METRIC_FORMAT, "clzz", "method");
+ long value = metric.getLong(metricKey);
+ // Assert if StopwatchRule correctly measures the test time.
+ assertEquals(SLEEP_TIME_MS, value, TIME_DELTA_MS);
+ }
+
+ @Test
+ public void testMetricSendToInstr() throws Throwable {
+ StopwatchRule rule = new StopwatchRule();
+ Instrumentation instr = Mockito.mock(Instrumentation.class);
+ rule.setInstrumentation(instr);
+ rule.apply(
+ new Statement() {
+ @Override
+ public void evaluate() throws Throwable {}
+ },
+ Description.EMPTY)
+ .evaluate();
+ verify(instr).sendStatus(StopwatchRule.INST_STATUS_IN_PROGRESS, rule.getMetric());
+ }
+}