aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2020-10-21 17:49:44 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2020-10-21 17:49:44 +0000
commit27137e33cd8d6b7a1e5e7d696994506197caa3e7 (patch)
treea9b182f4517742413e5c9b8d6c6bf2d9f2b91689
parent576d836d274afdd114608a8d8ab7a1d4e1ee80c6 (diff)
parent366800d3f4e4d1032091cb3ecaabb1f844ae7904 (diff)
downloadtradefederation-27137e33cd8d6b7a1e5e7d696994506197caa3e7.tar.gz
Merge cherrypicks of [12894601, 12894602, 12891329, 12891330, 12894603, 12894604, 12885923] into mainline-release
Change-Id: I0f53097421019f70b56ee337948afc4f426ba645
-rw-r--r--test_framework/com/android/tradefed/testtype/GTest.java48
-rw-r--r--test_framework/com/android/tradefed/testtype/InstrumentationTest.java91
-rw-r--r--test_framework/com/android/tradefed/testtype/rust/RustBinaryTest.java39
-rw-r--r--tests/src/com/android/tradefed/testtype/GTestTest.java116
-rw-r--r--tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java85
-rw-r--r--tests/src/com/android/tradefed/testtype/rust/RustBinaryTestTest.java195
6 files changed, 568 insertions, 6 deletions
diff --git a/test_framework/com/android/tradefed/testtype/GTest.java b/test_framework/com/android/tradefed/testtype/GTest.java
index 3e9086f0e..71b326bc1 100644
--- a/test_framework/com/android/tradefed/testtype/GTest.java
+++ b/test_framework/com/android/tradefed/testtype/GTest.java
@@ -16,6 +16,9 @@
package com.android.tradefed.testtype;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.CLANG;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.GCOV;
+
import com.android.ddmlib.FileListingService;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.config.Option;
@@ -23,9 +26,13 @@ import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.metric.ClangCodeCoverageCollector;
+import com.android.tradefed.device.metric.GcovCodeCoverageCollector;
+import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.coverage.CoverageOptions;
import com.android.tradefed.util.AbiUtils;
import com.android.tradefed.util.FileUtil;
@@ -420,6 +427,9 @@ public class GTest extends GTestBase implements IDeviceTest {
if (mStopRuntime) {
mDevice.executeShellCommand("stop");
}
+ // Insert the coverage listener if code coverage collection is enabled.
+ listener = addGcovCoverageListenerIfEnabled(testInfo.getContext(), listener);
+ listener = addClangCoverageListenerIfEnabled(testInfo.getContext(), listener);
listener = getGTestListener(listener);
Throwable throwable = null;
@@ -437,4 +447,42 @@ public class GTest extends GTestBase implements IDeviceTest {
}
}
}
+
+ /**
+ * Adds a listener to pull native code coverage measurements from the device after the test is
+ * complete if coverage is enabled, otherwise returns the same listener.
+ *
+ * @param listener the current chain of listeners
+ * @return a native coverage listener if coverage is enabled, otherwise the original listener
+ */
+ private ITestInvocationListener addGcovCoverageListenerIfEnabled(
+ IInvocationContext context, ITestInvocationListener listener) {
+ CoverageOptions options = getConfiguration().getCoverageOptions();
+
+ if (options.isCoverageEnabled() && options.getCoverageToolchains().contains(GCOV)) {
+ GcovCodeCoverageCollector nativeListener = new GcovCodeCoverageCollector();
+ nativeListener.setConfiguration(getConfiguration());
+ listener = nativeListener.init(context, listener);
+ }
+ return listener;
+ }
+
+ /**
+ * Adds a listener to pull Clang code coverage measurements from the device after the test is
+ * complete if coverage is enabled, otherwise returns the same listener.
+ *
+ * @param listener the current chain of listeners
+ * @return a native coverage listener if coverage is enabled, otherwise the original listener
+ */
+ private ITestInvocationListener addClangCoverageListenerIfEnabled(
+ IInvocationContext context, ITestInvocationListener listener) {
+ CoverageOptions options = getConfiguration().getCoverageOptions();
+
+ if (options.isCoverageEnabled() && options.getCoverageToolchains().contains(CLANG)) {
+ ClangCodeCoverageCollector clangListener = new ClangCodeCoverageCollector();
+ clangListener.setConfiguration(getConfiguration());
+ listener = clangListener.init(context, listener);
+ }
+ return listener;
+ }
}
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
index 81ec26351..00040953e 100644
--- a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
+++ b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java
@@ -16,6 +16,9 @@
package com.android.tradefed.testtype;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.CLANG;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.GCOV;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.JACOCO;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
@@ -33,11 +36,14 @@ import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.metric.ClangCodeCoverageCollector;
import com.android.tradefed.device.metric.GcovCodeCoverageCollector;
import com.android.tradefed.device.metric.IMetricCollector;
import com.android.tradefed.device.metric.IMetricCollectorReceiver;
+import com.android.tradefed.device.metric.JavaCodeCoverageCollector;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.BugreportCollector;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.ITestInvocationListener;
@@ -91,6 +97,9 @@ public class InstrumentationTest
/** default timeout for tests collection */
static final long TEST_COLLECTION_TIMEOUT_MS = 2 * 60 * 1000;
+ /** test run name for merging coverage measurements */
+ static final String MERGE_COVERAGE_MEASUREMENTS_TEST_NAME = "mergeCoverageMeasurements";
+
@Option(
name = "package",
shortName = 'p',
@@ -250,12 +259,12 @@ public class InstrumentationTest
)
private boolean mCoverage = false;
- @Deprecated
@Option(
- name = "merge-coverage-measurements",
- description =
- "Merge coverage measurements from all test runs into a single measurement "
- + "before logging.")
+ name = "merge-coverage-measurements",
+ description =
+ "Merge coverage measurements from all test runs into a single measurement before "
+ + "logging."
+ )
private boolean mMergeCoverageMeasurements = false;
@Deprecated
@@ -639,6 +648,12 @@ public class InstrumentationTest
return mForceAbi;
}
+ /** Sets the --merge-coverage-measurements option for testing. */
+ @VisibleForTesting
+ void setMergeCoverageMeasurements(boolean merge) {
+ mMergeCoverageMeasurements = merge;
+ }
+
/** Sets the --rerun-from-file option. */
public void setReRunUsingTestFile(boolean reRunUsingTestFile) {
mReRunUsingTestFile = reRunUsingTestFile;
@@ -892,9 +907,12 @@ public class InstrumentationTest
mRunner.addInstrumentationArg("coverage", "true");
}
- // Reruns do not create new listeners.
+ // Reruns do not create new listeners or clear coverage measurements.
if (!mIsRerun) {
listener = addBugreportListenerIfEnabled(listener);
+ listener = addJavaCoverageListenerIfEnabled(testInfo, listener);
+ listener = addGcovCoverageListenerIfEnabled(testInfo, listener);
+ listener = addClangCoverageListenerIfEnabled(testInfo, listener);
// TODO: Convert to device-side collectors when possible.
for (IMetricCollector collector : mCollectors) {
@@ -925,6 +943,13 @@ public class InstrumentationTest
} else {
CLog.i("No tests expected for %s, skipping", mPackageName);
}
+
+ // Merge coverage measurements after all tests have been run, but not inside the rerun
+ // itself since the merging will be handled by the caller.
+ if (!mIsRerun && mMergeCoverageMeasurements) {
+ listener.testRunStarted(MERGE_COVERAGE_MEASUREMENTS_TEST_NAME, 0);
+ listener.testRunEnded(0, new HashMap<String, Metric>());
+ }
}
/**
@@ -946,6 +971,60 @@ public class InstrumentationTest
}
/**
+ * Returns a listener that will collect coverage measurements, or the original {@code listener}
+ * if this feature is disabled.
+ */
+ ITestInvocationListener addJavaCoverageListenerIfEnabled(
+ final TestInformation testInfo, ITestInvocationListener listener) {
+ if (mConfiguration == null) {
+ return listener;
+ }
+ if (mConfiguration.getCoverageOptions().isCoverageEnabled()
+ && mConfiguration.getCoverageOptions().getCoverageToolchains().contains(JACOCO)) {
+ JavaCodeCoverageCollector javaListener = new JavaCodeCoverageCollector();
+ javaListener.setConfiguration(mConfiguration);
+ return javaListener.init(testInfo.getContext(), listener);
+ }
+ return listener;
+ }
+
+ /**
+ * Returns a listener that will collect gcov coverage measurements, or the original {@code
+ * listener} if this feature is disabled.
+ */
+ ITestInvocationListener addGcovCoverageListenerIfEnabled(
+ final TestInformation testInfo, ITestInvocationListener listener) {
+ if (mConfiguration == null) {
+ return listener;
+ }
+ if (mConfiguration.getCoverageOptions().isCoverageEnabled()
+ && mConfiguration.getCoverageOptions().getCoverageToolchains().contains(GCOV)) {
+ mNativeCoverageListener = new GcovCodeCoverageCollector();
+ mNativeCoverageListener.setConfiguration(mConfiguration);
+ listener = mNativeCoverageListener.init(testInfo.getContext(), listener);
+ }
+ return listener;
+ }
+
+ /**
+ * Returns a listener that will collect Clang coverage measurements, or the original {@code
+ * listener} if this feature is disabled.
+ */
+ ITestInvocationListener addClangCoverageListenerIfEnabled(
+ TestInformation testInfo, ITestInvocationListener listener) {
+ if (mConfiguration == null) {
+ return listener;
+ }
+ if (mConfiguration.getCoverageOptions().isCoverageEnabled()
+ && mConfiguration.getCoverageOptions().getCoverageToolchains().contains(CLANG)) {
+ ClangCodeCoverageCollector clangListener = new ClangCodeCoverageCollector();
+ clangListener.setConfiguration(mConfiguration);
+ listener = clangListener.init(testInfo.getContext(), listener);
+ }
+ return listener;
+ }
+
+ /**
* Execute the test run, but re-run incomplete tests individually if run fails to complete.
*
* @param listener the {@link ITestInvocationListener}
diff --git a/test_framework/com/android/tradefed/testtype/rust/RustBinaryTest.java b/test_framework/com/android/tradefed/testtype/rust/RustBinaryTest.java
index 7ff34c31d..e15d5c446 100644
--- a/test_framework/com/android/tradefed/testtype/rust/RustBinaryTest.java
+++ b/test_framework/com/android/tradefed/testtype/rust/RustBinaryTest.java
@@ -16,6 +16,9 @@
package com.android.tradefed.testtype.rust;
+import static com.android.tradefed.testtype.coverage.CoverageOptions.Toolchain.GCOV;
+
+
import com.android.ddmlib.FileListingService;
import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.config.IConfiguration;
@@ -24,11 +27,14 @@ 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.metric.GcovCodeCoverageCollector;
+import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.coverage.CoverageOptions;
import java.io.File;
import java.util.HashMap;
@@ -214,10 +220,43 @@ public class RustBinaryTest extends RustTestBase implements IDeviceTest, IConfig
return;
}
+ // Insert the coverage listener if code coverage collection is enabled.
+ listener = addGcovCoverageListenerIfEnabled(testInfo.getContext(), listener);
+
CLog.d("To run tests in directory " + testPath);
if (!doRunAllTestsInSubdirectory(testPath, mDevice, listener)) {
wrongTestPath("No test found under ", testPath, listener);
}
}
+
+ /**
+ * Returns the {@link CoverageOptions} for this test, if it exists. Otherwise returns a default
+ * {@link CoverageOptions} object with all coverage disabled.
+ */
+ protected CoverageOptions getCoverageOptions() {
+ if (mConfiguration != null) {
+ return mConfiguration.getCoverageOptions();
+ }
+ return new CoverageOptions();
+ }
+
+ /**
+ * Adds a listener to pull native code coverage measurements from the device after the test is
+ * complete if coverage is enabled, otherwise returns the same listener.
+ *
+ * @param listener the current chain of listeners
+ * @return a native coverage listener if coverage is enabled, otherwise the original listener
+ */
+ private ITestInvocationListener addGcovCoverageListenerIfEnabled(
+ IInvocationContext context, ITestInvocationListener listener) {
+ CoverageOptions options = getCoverageOptions();
+
+ if (options.isCoverageEnabled() && options.getCoverageToolchains().contains(GCOV)) {
+ GcovCodeCoverageCollector nativeListener = new GcovCodeCoverageCollector();
+ nativeListener.setConfiguration(mConfiguration);
+ listener = nativeListener.init(context, listener);
+ }
+ return listener;
+ }
}
diff --git a/tests/src/com/android/tradefed/testtype/GTestTest.java b/tests/src/com/android/tradefed/testtype/GTestTest.java
index 7f05104a9..2d70915a7 100644
--- a/tests/src/com/android/tradefed/testtype/GTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/GTestTest.java
@@ -42,6 +42,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -494,6 +496,120 @@ public class GTestTest {
verifyMocks();
}
+ /** Test cross-process coverage dump for all native processes */
+ @Test
+ public void testNativeCoverageAllProcesses() throws Exception {
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "GCOV");
+ mCoverageOptionsSetter.setOptionValue("coverage-flush", "true");
+
+ final String nativeTestPath = GTest.DEFAULT_NATIVETEST_PATH;
+ final String test1 = "test1";
+ final String test2 = "test2";
+ final String testPath1 = String.format("%s/%s", nativeTestPath, test1);
+ final String testPath2 = String.format("%s/%s", nativeTestPath, test2);
+
+ MockFileUtil.setMockDirContents(mMockITestDevice, nativeTestPath, test1, test2);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 -1")).andReturn("");
+ // Wait up to 5 minutes for the device to be available after flushing coverage data.
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
+ .andReturn("");
+ EasyMock.expect(mMockITestDevice.doesFileExist(nativeTestPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(nativeTestPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath1)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath1)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath2)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath2)).andReturn(true);
+
+ String[] files = new String[] {"test1", "test2"};
+ EasyMock.expect(mMockITestDevice.getChildren(nativeTestPath)).andReturn(files);
+ mMockITestDevice.executeShellCommand(
+ EasyMock.contains(test1),
+ EasyMock.same(mMockReceiver),
+ EasyMock.anyLong(),
+ (TimeUnit) EasyMock.anyObject(),
+ EasyMock.anyInt());
+ mMockITestDevice.executeShellCommand(
+ EasyMock.contains(test2),
+ EasyMock.same(mMockReceiver),
+ EasyMock.anyLong(),
+ (TimeUnit) EasyMock.anyObject(),
+ EasyMock.anyInt());
+
+ replayMocks();
+
+ mGTest.run(mTestInfo, mMockInvocationListener);
+ verifyMocks();
+ }
+
+ /** Test cross-process coverage dump for specific processes */
+ @Test
+ public void testNativeCoverageSpecificProcesses() throws Exception {
+ final List<String> processNames = new ArrayList<>();
+ processNames.add("init");
+ processNames.add("surfaceflinger");
+
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "GCOV");
+ mCoverageOptionsSetter.setOptionValue("coverage-flush", "true");
+ for (String processName : processNames) {
+ mCoverageOptionsSetter.setOptionValue("coverage-processes", processName);
+ }
+
+ final String nativeTestPath = GTest.DEFAULT_NATIVETEST_PATH;
+ final String test1 = "test1";
+ final String test2 = "test2";
+ final String testPath1 = String.format("%s/%s", nativeTestPath, test1);
+ final String testPath2 = String.format("%s/%s", nativeTestPath, test2);
+
+ MockFileUtil.setMockDirContents(mMockITestDevice, nativeTestPath, test1, test2);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ // Get the pids to flush coverage data.
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(0))).andReturn("1");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(1))).andReturn("1000");
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 1 1000")).andReturn("");
+ // Wait up to 5 minutes for the device to be available after flushing coverage data.
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+
+ // Clear the coverage data.
+ EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
+ .andReturn("");
+ EasyMock.expect(mMockITestDevice.doesFileExist(nativeTestPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(nativeTestPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath1)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath1)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath2)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath2)).andReturn(true);
+
+ String[] files = new String[] {"test1", "test2"};
+ EasyMock.expect(mMockITestDevice.getChildren(nativeTestPath)).andReturn(files);
+ mMockITestDevice.executeShellCommand(
+ EasyMock.contains(test1),
+ EasyMock.same(mMockReceiver),
+ EasyMock.anyLong(),
+ (TimeUnit) EasyMock.anyObject(),
+ EasyMock.anyInt());
+ mMockITestDevice.executeShellCommand(
+ EasyMock.contains(test2),
+ EasyMock.same(mMockReceiver),
+ EasyMock.anyLong(),
+ (TimeUnit) EasyMock.anyObject(),
+ EasyMock.anyInt());
+
+ replayMocks();
+
+ mGTest.run(mTestInfo, mMockInvocationListener);
+ verifyMocks();
+ }
+
@Test
public void testGetFileName() {
String expected = "bar";
diff --git a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
index d76714682..682759bfc 100644
--- a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java
@@ -24,7 +24,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollectionOf;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -41,7 +43,9 @@ import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.device.metric.GcovCodeCoverageCollector;
import com.android.tradefed.device.metric.IMetricCollector;
+import com.android.tradefed.device.metric.JavaCodeCoverageCollector;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.invoker.InvocationContext;
import com.android.tradefed.invoker.TestInformation;
@@ -52,6 +56,7 @@ import com.android.tradefed.result.FailureDescription;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ITestLifeCycleReceiver;
import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.result.proto.TestRecordProto.FailureStatus;
import com.android.tradefed.testtype.coverage.CoverageOptions;
@@ -718,6 +723,53 @@ public class InstrumentationTestTest {
inOrder.verifyNoMoreInteractions();
}
+ /** Verify that all tests are re-run when there is a failure during a coverage run. */
+ @Test
+ public void testRun_mergedCoverage()
+ throws ConfigurationException, DeviceNotAvailableException {
+ mInstrumentationTest.setRerunMode(true);
+ mInstrumentationTest.setMergeCoverageMeasurements(true);
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "JACOCO");
+
+ // Mock collected tests
+ RunInstrumentationTestsAnswer runTests =
+ (runner, listener) -> {
+ // perform call back on listener to show run of two tests
+ listener.testRunStarted(TEST_PACKAGE_VALUE, 2);
+ listener.testStarted(TEST1);
+ listener.testEnded(TEST1, EMPTY_STRING_MAP);
+ listener.testStarted(TEST2);
+ listener.testEnded(TEST2, EMPTY_STRING_MAP);
+ listener.testRunEnded(1, EMPTY_STRING_MAP);
+ return true;
+ };
+
+ doAnswer(runTests)
+ .when(mMockTestDevice)
+ .runInstrumentationTests(
+ any(IRemoteAndroidTestRunner.class), any(ITestLifeCycleReceiver.class));
+ doReturn(true).when(mMockTestDevice).enableAdbRoot();
+ doReturn("").when(mMockTestDevice).executeShellCommand(anyString());
+
+ mInstrumentationTest.run(mTestInfo, mMockListener);
+
+ InOrder inOrder = Mockito.inOrder(mMockListener);
+ inOrder.verify(mMockListener)
+ .testRunStarted(eq(TEST_PACKAGE_VALUE), eq(2), anyInt(), anyLong());
+ inOrder.verify(mMockListener).testStarted(eq(TEST1), anyLong());
+ inOrder.verify(mMockListener).testEnded(eq(TEST1), anyLong(), eq(EMPTY_STRING_MAP));
+ inOrder.verify(mMockListener).testStarted(eq(TEST2), anyLong());
+ inOrder.verify(mMockListener).testEnded(eq(TEST2), anyLong(), eq(EMPTY_STRING_MAP));
+ inOrder.verify(mMockListener).testRunEnded(1, EMPTY_STRING_MAP);
+ inOrder.verify(mMockListener)
+ .testRunStarted(eq("mergeCoverageMeasurements"), anyInt(), anyInt(), anyLong());
+ inOrder.verify(mMockListener)
+ .testLog(eq("merged_runtime_coverage"), eq(LogDataType.COVERAGE), any());
+ inOrder.verify(mMockListener).testRunEnded(anyLong(), eq(EMPTY_STRING_MAP));
+ inOrder.verifyNoMoreInteractions();
+ }
+
/** Test the reboot before re-run option. */
@Test
public void testRun_rebootBeforeReRun() throws DeviceNotAvailableException {
@@ -1040,6 +1092,39 @@ public class InstrumentationTestTest {
inOrder.verify(mMockListener).testRunEnded(1, EMPTY_STRING_MAP);
}
+ @Test
+ public void testAddCoverageListener_enabledAndFlushes() throws Exception {
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "GCOV");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "JACOCO");
+
+ doReturn(true).when(mMockTestDevice).isAdbRoot();
+ doReturn("").when(mMockTestDevice).executeShellCommand("ps -e");
+ doReturn("").when(mMockTestDevice).executeShellCommand("pm list packages -a");
+
+ ITestInvocationListener listener =
+ mInstrumentationTest.addJavaCoverageListenerIfEnabled(mTestInfo, mMockListener);
+ assertThat(listener).isInstanceOf(JavaCodeCoverageCollector.class);
+
+ listener = mInstrumentationTest.addGcovCoverageListenerIfEnabled(mTestInfo, mMockListener);
+ assertThat(listener).isInstanceOf(GcovCodeCoverageCollector.class);
+
+ // Ensure that a native coverage flush was executed.
+ verify(mMockTestDevice).executeShellCommand("kill -37 -1");
+ }
+
+ @Test
+ public void testAddCoverageListener_disabled() throws ConfigurationException {
+ mCoverageOptionsSetter.setOptionValue("coverage", "false");
+
+ ITestInvocationListener listener =
+ mInstrumentationTest.addJavaCoverageListenerIfEnabled(mTestInfo, mMockListener);
+ assertThat(listener).isSameAs(mMockListener);
+
+ listener = mInstrumentationTest.addGcovCoverageListenerIfEnabled(mTestInfo, mMockListener);
+ assertThat(listener).isSameAs(mMockListener);
+ }
+
/** Test normal run scenario when {@link IMetricCollector} are specified. */
@Test
public void testRun_withCollectors() throws DeviceNotAvailableException {
diff --git a/tests/src/com/android/tradefed/testtype/rust/RustBinaryTestTest.java b/tests/src/com/android/tradefed/testtype/rust/RustBinaryTestTest.java
index 3d7d450a6..4dd90369e 100644
--- a/tests/src/com/android/tradefed/testtype/rust/RustBinaryTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/rust/RustBinaryTestTest.java
@@ -28,7 +28,9 @@ 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.ITestInvocationListener;
+import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.coverage.CoverageOptions;
+import com.android.tradefed.util.FileUtil;
import org.easymock.EasyMock;
import org.junit.Before;
@@ -36,7 +38,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.TimeUnit;
/** Unit tests for {@link RustBinaryTest}. */
@@ -291,6 +296,196 @@ public class RustBinaryTestTest {
callReplayRunVerify();
}
+ /** Test cross-process coverage dump for all native processes */
+ @Test
+ public void testNativeCoverageAllProcesses() throws Exception {
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "GCOV");
+ mCoverageOptionsSetter.setOptionValue("coverage-flush", "true");
+
+ final String testPath = RustBinaryTest.DEFAULT_TEST_PATH;
+ final String test1 = "test1";
+ final String test2 = "test2";
+ final String testPath1 = String.format("%s/%s", testPath, test1);
+ final String testPath2 = String.format("%s/%s", testPath, test2);
+ final String coverageTarPath = "/data/misc/trace/coverage.tar";
+
+ MockFileUtil.setMockDirContents(mMockITestDevice, testPath, test1, test2);
+ EasyMock.expect(mMockITestDevice.getIDevice()).andReturn(null);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 -1")).andReturn("");
+ // Wait up to 5 minutes for the device to be available after flushing coverage data.
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
+ .andReturn("");
+ EasyMock.expect(mMockITestDevice.doesFileExist(testPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath1)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath1)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath2)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath2)).andReturn(true);
+
+ // End of test1
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 -1")).andReturn("");
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' | tar -cvf"
+ + " /data/misc/trace/coverage.tar -T -"))
+ .andReturn("");
+ File tmpFile1 = FileUtil.createTempFile("coverage", ".tar");
+ EasyMock.expect(mMockITestDevice.pullFile(coverageTarPath)).andReturn(tmpFile1);
+ mMockITestDevice.deleteFile(coverageTarPath);
+ mMockInvocationListener.testLog(
+ EasyMock.eq("test1_native_runtime_coverage"),
+ EasyMock.eq(LogDataType.NATIVE_COVERAGE),
+ EasyMock.anyObject());
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' -delete"))
+ .andReturn("");
+
+ // End of test2
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 -1")).andReturn("");
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' | tar -cvf"
+ + " /data/misc/trace/coverage.tar -T -"))
+ .andReturn("");
+ File tmpFile2 = FileUtil.createTempFile("coverage", ".tar");
+ EasyMock.expect(mMockITestDevice.pullFile(coverageTarPath)).andReturn(tmpFile2);
+ mMockITestDevice.deleteFile(coverageTarPath);
+ mMockInvocationListener.testLog(
+ EasyMock.eq("test2_native_runtime_coverage"),
+ EasyMock.eq(LogDataType.NATIVE_COVERAGE),
+ EasyMock.anyObject());
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' -delete"))
+ .andReturn("");
+
+ String[] files = new String[] {"test1", "test2"};
+ EasyMock.expect(mMockITestDevice.getChildren(testPath)).andReturn(files);
+
+ mockCountTests(testPath1, runListOutput(1));
+ mockTestRunStarted("test1", 1);
+ mockShellCommand(test1);
+ mockTestRunEnded();
+ mockCountTests(testPath2, runListOutput(1));
+ mockTestRunStarted("test2", 1);
+ mockShellCommand(test2);
+ mockTestRunEnded();
+ callReplayRunVerify();
+ }
+
+ /** Test cross-process coverage dump for specific processes */
+ @Test
+ public void testNativeCoverageSpecificProcesses() throws Exception {
+ final List<String> processNames = new ArrayList<>();
+ processNames.add("init");
+ processNames.add("surfaceflinger");
+
+ mCoverageOptionsSetter.setOptionValue("coverage", "true");
+ mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "GCOV");
+ mCoverageOptionsSetter.setOptionValue("coverage-flush", "true");
+ for (String processName : processNames) {
+ mCoverageOptionsSetter.setOptionValue("coverage-processes", processName);
+ }
+
+ final String testPath = RustBinaryTest.DEFAULT_TEST_PATH;
+ final String test1 = "test1";
+ final String test2 = "test2";
+ final String testPath1 = String.format("%s/%s", testPath, test1);
+ final String testPath2 = String.format("%s/%s", testPath, test2);
+ final String coverageTarPath = "/data/misc/trace/coverage.tar";
+
+ MockFileUtil.setMockDirContents(mMockITestDevice, testPath, test1, test2);
+ EasyMock.expect(mMockITestDevice.getIDevice()).andReturn(null);
+ // Get the pids to flush coverage data.
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isAdbRoot()).andReturn(true);
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(0))).andReturn("1");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(0))).andReturn("1");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(0))).andReturn("1");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(1))).andReturn("1000");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(1))).andReturn("1000");
+ EasyMock.expect(mMockITestDevice.getProcessPid(processNames.get(1))).andReturn("1000");
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 1 1000")).andReturn("");
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 1 1000")).andReturn("");
+ EasyMock.expect(mMockITestDevice.executeShellCommand("kill -37 1 1000")).andReturn("");
+ // Wait up to 5 minutes for the device to be available after flushing coverage data.
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ mMockITestDevice.waitForDeviceAvailable(5 * 60 * 1000);
+ EasyMock.expect(mMockITestDevice.executeShellCommand("rm -rf /data/misc/trace/*"))
+ .andReturn("");
+ EasyMock.expect(mMockITestDevice.doesFileExist(testPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath1)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath1)).andReturn(true);
+ EasyMock.expect(mMockITestDevice.isDirectory(testPath2)).andReturn(false);
+ // report the file as executable
+ EasyMock.expect(mMockITestDevice.isExecutable(testPath2)).andReturn(true);
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' | tar -cvf"
+ + " /data/misc/trace/coverage.tar -T -"))
+ .andReturn("");
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' | tar -cvf"
+ + " /data/misc/trace/coverage.tar -T -"))
+ .andReturn("");
+ File tmpFile1 = FileUtil.createTempFile("coverage", ".tar");
+ EasyMock.expect(mMockITestDevice.pullFile(coverageTarPath)).andReturn(tmpFile1);
+ File tmpFile2 = FileUtil.createTempFile("coverage", ".tar");
+ EasyMock.expect(mMockITestDevice.pullFile(coverageTarPath)).andReturn(tmpFile2);
+ mMockITestDevice.deleteFile(coverageTarPath);
+ mMockITestDevice.deleteFile(coverageTarPath);
+ mMockInvocationListener.testLog(
+ EasyMock.eq("test1_native_runtime_coverage"),
+ EasyMock.eq(LogDataType.NATIVE_COVERAGE),
+ EasyMock.anyObject());
+ mMockInvocationListener.testLog(
+ EasyMock.eq("test2_native_runtime_coverage"),
+ EasyMock.eq(LogDataType.NATIVE_COVERAGE),
+ EasyMock.anyObject());
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' -delete"))
+ .andReturn("");
+ EasyMock.expect(
+ mMockITestDevice.executeShellCommand(
+ "find /data/misc/trace -name '*.gcda' -delete"))
+ .andReturn("");
+
+ String[] files = new String[] {"test1", "test2"};
+ EasyMock.expect(mMockITestDevice.getChildren(testPath)).andReturn(files);
+
+ mockCountTests(testPath1, runListOutput(1));
+ mockTestRunStarted("test1", 1);
+ mockShellCommand(test1);
+ mockTestRunEnded();
+ mockCountTests(testPath2, runListOutput(1));
+ mockTestRunStarted("test2", 1);
+ mockShellCommand(test2);
+ mockTestRunEnded();
+ callReplayRunVerify();
+ }
+
/**
* Helper function to do the actual filtering test.
*