diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2020-10-21 17:49:44 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2020-10-21 17:49:44 +0000 |
commit | 27137e33cd8d6b7a1e5e7d696994506197caa3e7 (patch) | |
tree | a9b182f4517742413e5c9b8d6c6bf2d9f2b91689 | |
parent | 576d836d274afdd114608a8d8ab7a1d4e1ee80c6 (diff) | |
parent | 366800d3f4e4d1032091cb3ecaabb1f844ae7904 (diff) | |
download | tradefederation-27137e33cd8d6b7a1e5e7d696994506197caa3e7.tar.gz |
Merge cherrypicks of [12894601, 12894602, 12891329, 12891330, 12894603, 12894604, 12885923] into mainline-release
Change-Id: I0f53097421019f70b56ee337948afc4f426ba645
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. * |