diff options
author | Oliver Nguyen <olivernguyen@google.com> | 2020-08-06 12:55:33 -0700 |
---|---|---|
committer | Oliver Nguyen <olivernguyen@google.com> | 2020-10-07 20:46:04 +0000 |
commit | 8fe595ab36bbfee17715f80d8f23ec6fe5036407 (patch) | |
tree | 13367ef161d33c439ea9f3b8f533cb057ab80eb2 | |
parent | 126bb7dff3557a2e32bf8ee77abf201d4f9c10ca (diff) | |
download | tradefederation-8fe595ab36bbfee17715f80d8f23ec6fe5036407.tar.gz |
Reland "Change Java coverage listener to implement BaseDeviceMetricCollector."
Java coverage listener also handles the configuration options directly.
Bug: 147904124
Bug: 163063008
Test: Unit tests
Change-Id: I92e48650143e11270b18c8915ff44e4b42e8fb09
Merged-In: I92e48650143e11270b18c8915ff44e4b42e8fb09
4 files changed, 132 insertions, 90 deletions
diff --git a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java index 75e82d792..bd14d9393 100644 --- a/test_framework/com/android/tradefed/testtype/InstrumentationTest.java +++ b/test_framework/com/android/tradefed/testtype/InstrumentationTest.java @@ -911,7 +911,7 @@ public class InstrumentationTest // Reruns do not create new listeners or clear coverage measurements. if (!mIsRerun) { listener = addBugreportListenerIfEnabled(listener); - listener = addJavaCoverageListenerIfEnabled(listener); + listener = addJavaCoverageListenerIfEnabled(testInfo, listener); listener = addGcovCoverageListenerIfEnabled(listener); listener = addClangCoverageListenerIfEnabled(listener); @@ -994,17 +994,16 @@ public class InstrumentationTest * Returns a listener that will collect coverage measurements, or the original {@code listener} * if this feature is disabled. */ - ITestInvocationListener addJavaCoverageListenerIfEnabled(ITestInvocationListener listener) { + ITestInvocationListener addJavaCoverageListenerIfEnabled( + final TestInformation testInfo, ITestInvocationListener listener) { if (mConfiguration == null) { return listener; } if (mConfiguration.getCoverageOptions().isCoverageEnabled() && mConfiguration.getCoverageOptions().getCoverageToolchains().contains(JACOCO)) { - return new JavaCodeCoverageListener( - getDevice(), - mConfiguration.getCoverageOptions(), - mMergeCoverageMeasurements, - listener); + JavaCodeCoverageListener javaListener = new JavaCodeCoverageListener(); + javaListener.setConfiguration(mConfiguration); + return javaListener.init(testInfo.getContext(), listener); } return listener; } diff --git a/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java b/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java index 7053c1f82..7cb4df678 100644 --- a/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java +++ b/test_framework/com/android/tradefed/testtype/JavaCodeCoverageListener.java @@ -19,15 +19,18 @@ package com.android.tradefed.testtype; import static com.google.common.base.Verify.verifyNotNull; import static com.google.common.io.Files.getNameWithoutExtension; +import com.android.tradefed.config.IConfiguration; +import com.android.tradefed.config.IConfigurationReceiver; +import com.android.tradefed.config.Option; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.invoker.logger.CurrentInvocation; +import com.android.tradefed.device.metric.BaseDeviceMetricCollector; +import com.android.tradefed.device.metric.DeviceMetricData; import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; import com.android.tradefed.result.FailureDescription; import com.android.tradefed.result.FileInputStreamSource; -import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.result.LogDataType; -import com.android.tradefed.result.ResultForwarder; import com.android.tradefed.result.error.InfraErrorIdentifier; import com.android.tradefed.testtype.coverage.CoverageOptions; import com.android.tradefed.util.FileUtil; @@ -41,14 +44,15 @@ import org.jacoco.core.tools.ExecFileLoader; import java.io.File; import java.io.IOException; -import java.util.HashMap; +import java.util.Map; import java.util.List; /** - * A {@link ResultForwarder} that will pull Java coverage measurements off of the device and log - * them as test artifacts. + * A {@link BaseDeviceMetricCollector} that will pull Java coverage measurements off of the device + * and log them as test artifacts. */ -final class JavaCodeCoverageListener extends ResultForwarder { +final class JavaCodeCoverageListener extends BaseDeviceMetricCollector + implements IConfigurationReceiver { public static final String MERGE_COVERAGE_MEASUREMENTS_TEST_NAME = "mergeCoverageMeasurements"; public static final String COVERAGE_MEASUREMENT_KEY = "coverageFilePath"; @@ -56,27 +60,30 @@ final class JavaCodeCoverageListener extends ResultForwarder { public static final String FIND_COVERAGE_FILES = String.format("find %s -name '*.ec'", COVERAGE_DIRECTORY); - private final ITestDevice mDevice; - private final CoverageOptions mCoverageOptions; - - private final boolean mMergeCoverageMeasurements; + @Option( + name = "merge-coverage-measurements", + description = + "Merge coverage measurements after all tests are complete rather than logging individual measurements.") + private boolean mMergeCoverageMeasurements = false; private final ExecFileLoader mExecFileLoader = new ExecFileLoader(); private JavaCodeCoverageFlusher mFlusher; - private String mCurrentRunName; - - public JavaCodeCoverageListener( - ITestDevice device, - CoverageOptions options, - boolean mergeMeasurements, - ITestInvocationListener... listeners) { - super(listeners); - mDevice = device; - mCoverageOptions = options; - mMergeCoverageMeasurements = mergeMeasurements; - - mFlusher = new JavaCodeCoverageFlusher(device, options.getCoverageProcesses()); + private IConfiguration mConfiguration; + + @Override + public void setConfiguration(IConfiguration configuration) { + mConfiguration = configuration; + } + + private JavaCodeCoverageFlusher getCoverageFlusher() { + if (mFlusher == null) { + mFlusher = + new JavaCodeCoverageFlusher( + getRealDevices().get(0), + mConfiguration.getCoverageOptions().getCoverageProcesses()); + } + return mFlusher; } @VisibleForTesting @@ -84,15 +91,22 @@ final class JavaCodeCoverageListener extends ResultForwarder { mFlusher = flusher; } - @Override - public void testRunStarted(String runName, int testCount) { - super.testRunStarted(runName, testCount); - mCurrentRunName = runName; + @VisibleForTesting + public void setMergeMeasurements(boolean merge) { + mMergeCoverageMeasurements = merge; } @Override - public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) { - if (MERGE_COVERAGE_MEASUREMENTS_TEST_NAME.equals(mCurrentRunName)) { + public void onTestRunEnd(DeviceMetricData runData, final Map<String, Metric> runMetrics) { + if (mConfiguration == null + || !mConfiguration.getCoverageOptions().isCoverageEnabled() + || !mConfiguration + .getCoverageOptions() + .getCoverageToolchains() + .contains(CoverageOptions.Toolchain.JACOCO)) { + return; + } + if (MERGE_COVERAGE_MEASUREMENTS_TEST_NAME.equals(getRunName())) { // Log the merged runtime coverage measurement. try { File mergedMeasurements = @@ -106,8 +120,6 @@ final class JavaCodeCoverageListener extends ResultForwarder { logCoverageMeasurement("merged_runtime_coverage", mergedMeasurements); } catch (IOException e) { throw new RuntimeException(e); - } finally { - super.testRunEnded(elapsedTime, runMetrics); } } else { // Get the path of the coverage measurement on the device. @@ -115,35 +127,32 @@ final class JavaCodeCoverageListener extends ResultForwarder { if (devicePathMetric == null) { super.testRunFailed( createCodeCoverageFailure("No Java code coverage measurement.")); - super.testRunEnded(elapsedTime, runMetrics); return; } String testCoveragePath = devicePathMetric.getMeasurements().getSingleString(); if (testCoveragePath == null) { super.testRunFailed( createCodeCoverageFailure("No Java code coverage measurement.")); - super.testRunEnded(elapsedTime, runMetrics); return; } + ITestDevice device = getRealDevices().get(0); ImmutableList.Builder<String> devicePaths = ImmutableList.builder(); devicePaths.add(testCoveragePath); try { - if (mCoverageOptions.isCoverageFlushEnabled()) { - mFlusher.forceCoverageFlush(); + if (mConfiguration.getCoverageOptions().isCoverageFlushEnabled()) { + getCoverageFlusher().forceCoverageFlush(); } // Find all .ec files in /data/misc/trace and pull them from the device as well. - String fileList = mDevice.executeShellCommand(FIND_COVERAGE_FILES); + String fileList = device.executeShellCommand(FIND_COVERAGE_FILES); devicePaths.addAll(Splitter.on('\n').omitEmptyStrings().split(fileList)); - collectAndLogCoverageMeasurementsAsRoot(devicePaths.build()); + collectAndLogCoverageMeasurementsAsRoot(device, devicePaths.build()); } catch (DeviceNotAvailableException | IOException e) { throw new RuntimeException(e); - } finally { - super.testRunEnded(elapsedTime, runMetrics); } } } @@ -154,7 +163,8 @@ final class JavaCodeCoverageListener extends ResultForwarder { } } - private void collectAndLogCoverageMeasurementsAsRoot(List<String> devicePaths) + private void collectAndLogCoverageMeasurementsAsRoot( + ITestDevice device, List<String> devicePaths) throws IOException, DeviceNotAvailableException { // We enable root before pulling files off the device since the coverage file of the test @@ -162,31 +172,30 @@ final class JavaCodeCoverageListener extends ResultForwarder { // files of other processes should be accessible without root. Note that we also restore // root status to what it was after we're done to not interfere with subsequent tests that // run on the device. - boolean wasRoot = mDevice.isAdbRoot(); - if (!wasRoot && !mDevice.enableAdbRoot()) { + boolean wasRoot = device.isAdbRoot(); + if (!wasRoot && !device.enableAdbRoot()) { throw new RuntimeException( "Failed to enable root before pulling Java code coverage files off device"); } try { - collectAndLogCoverageMeasurements(devicePaths); + collectAndLogCoverageMeasurements(device, devicePaths); } finally { for (String devicePath : devicePaths) { - mDevice.deleteFile(devicePath); + device.deleteFile(devicePath); } - - if (!wasRoot && !mDevice.disableAdbRoot()) { + if (!wasRoot && !device.disableAdbRoot()) { throw new RuntimeException( "Failed to disable root after pulling Java code coverage files off device"); } } } - private void collectAndLogCoverageMeasurements(List<String> devicePaths) + private void collectAndLogCoverageMeasurements(ITestDevice device, List<String> devicePaths) throws IOException, DeviceNotAvailableException { for (String devicePath : devicePaths) { - File coverageFile = mDevice.pullFile(devicePath); + File coverageFile = device.pullFile(devicePath); verifyNotNull( coverageFile, "Failed to pull the Java code coverage file from %s", devicePath); @@ -197,7 +206,7 @@ final class JavaCodeCoverageListener extends ResultForwarder { mExecFileLoader.load(coverageFile); } else { logCoverageMeasurement( - mCurrentRunName + getRunName() + "_" + getNameWithoutExtension(devicePath) + "_runtime_coverage", diff --git a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java index a836fa3c8..8933fd203 100644 --- a/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java +++ b/tests/src/com/android/tradefed/testtype/InstrumentationTestTest.java @@ -106,6 +106,7 @@ public class InstrumentationTestTest { private TestInformation mTestInfo = null; private CoverageOptions mCoverageOptions = null; private OptionSetter mCoverageOptionsSetter = null; + private IInvocationContext mContext = null; // The mock objects. @Mock IDevice mMockIDevice; @@ -160,8 +161,8 @@ public class InstrumentationTestTest { mConfig.setCoverageOptions(mCoverageOptions); mInstrumentationTest.setConfiguration(mConfig); - IInvocationContext context = new InvocationContext(); - mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build(); + mContext = new InvocationContext(); + mTestInfo = TestInformation.newBuilder().setInvocationContext(mContext).build(); } /** Test normal run scenario. */ @@ -751,13 +752,15 @@ public class InstrumentationTestTest { mInstrumentationTest.run(mTestInfo, mMockListener); InOrder inOrder = Mockito.inOrder(mMockListener); - inOrder.verify(mMockListener).testRunStarted(TEST_PACKAGE_VALUE, 2); + 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()); + 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)); @@ -1093,7 +1096,7 @@ public class InstrumentationTestTest { mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "JACOCO"); ITestInvocationListener listener = - mInstrumentationTest.addJavaCoverageListenerIfEnabled(mMockListener); + mInstrumentationTest.addJavaCoverageListenerIfEnabled(mTestInfo, mMockListener); assertThat(listener).isInstanceOf(JavaCodeCoverageListener.class); listener = mInstrumentationTest.addGcovCoverageListenerIfEnabled(mMockListener); @@ -1105,7 +1108,7 @@ public class InstrumentationTestTest { mCoverageOptionsSetter.setOptionValue("coverage", "false"); ITestInvocationListener listener = - mInstrumentationTest.addJavaCoverageListenerIfEnabled(mMockListener); + mInstrumentationTest.addJavaCoverageListenerIfEnabled(mTestInfo, mMockListener); assertThat(listener).isSameAs(mMockListener); listener = mInstrumentationTest.addGcovCoverageListenerIfEnabled(mMockListener); diff --git a/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java b/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java index b074de584..014b6dc68 100644 --- a/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java +++ b/tests/src/com/android/tradefed/testtype/JavaCodeCoverageListenerTest.java @@ -20,7 +20,6 @@ import static com.android.tradefed.testtype.JavaCodeCoverageListener.MERGE_COVER import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -30,9 +29,12 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.android.tradefed.config.ConfigurationException; +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.invoker.IInvocationContext; import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.result.InputStreamSource; @@ -41,7 +43,6 @@ import com.android.tradefed.testtype.coverage.CoverageOptions; import com.android.tradefed.util.JavaCodeCoverageFlusher; import com.android.tradefed.util.proto.TfMetricProtoUtil; -import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.protobuf.ByteString; @@ -90,6 +91,8 @@ public class JavaCodeCoverageListenerTest { @Rule public TemporaryFolder folder = new TemporaryFolder(); + @Mock IConfiguration mMockConfiguration; + @Mock IInvocationContext mMockContext; @Mock ITestDevice mMockDevice; @Mock JavaCodeCoverageFlusher mMockFlusher; @@ -108,17 +111,38 @@ public class JavaCodeCoverageListenerTest { mCoverageOptions = new CoverageOptions(); mCoverageOptionsSetter = new OptionSetter(mCoverageOptions); + when(mMockConfiguration.getCoverageOptions()).thenReturn(mCoverageOptions); + + when(mMockContext.getDevices()).thenReturn(ImmutableList.of(mMockDevice)); + // Mock an unrooted device that has no issues enabling or disabling root. when(mMockDevice.isAdbRoot()).thenReturn(false); when(mMockDevice.enableAdbRoot()).thenReturn(true); when(mMockDevice.disableAdbRoot()).thenReturn(true); - mCodeCoverageListener = - new JavaCodeCoverageListener(mMockDevice, mCoverageOptions, false, mFakeListener); + mCodeCoverageListener = new JavaCodeCoverageListener(); + mCodeCoverageListener.setConfiguration(mMockConfiguration); + mCodeCoverageListener.init(mMockContext, mFakeListener); + } + + @Test + public void testRunEnded_noCoverageEnabled_noop() throws Exception { + // Setup mocks. + HashMap<String, Metric> runMetrics = new HashMap<>(); + + // Simulate a test run. + mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); + mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); + + // Verify testLog(..) was not called. + verify(mFakeListener, never()) + .testLog(anyString(), eq(LogDataType.COVERAGE), eq(COVERAGE_MEASUREMENT)); } @Test public void testRunEnded_rootEnabled_logsCoverageMeasurement() throws Exception { + enableJavaCoverage(); + // Setup mocks. HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); mockCoverageFileOnDevice(DEVICE_PATH); @@ -138,7 +162,9 @@ public class JavaCodeCoverageListenerTest { } @Test - public void testFailure_noCoverageMetric() { + public void testFailure_noCoverageMetric() throws Exception { + enableJavaCoverage(); + // Simulate a test run. mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); mCodeCoverageListener.testRunEnded(ELAPSED_TIME, new HashMap<String, Metric>()); @@ -152,26 +178,23 @@ public class JavaCodeCoverageListenerTest { } @Test - public void testFailure_unableToPullFile() throws DeviceNotAvailableException { + public void testFailure_unableToPullFile() throws Exception { + enableJavaCoverage(); HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); doReturn("").when(mMockDevice).executeShellCommand(anyString()); doReturn(null).when(mMockDevice).pullFile(DEVICE_PATH); // Simulate a test run. mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); - try { - mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); - fail("Exception not thrown"); - } catch (VerifyException expected) { - } + mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); - // Verify testLog(..) was not called. verify(mFakeListener, never()) .testLog(anyString(), eq(LogDataType.COVERAGE), any(InputStreamSource.class)); } @Test public void testRunEnded_rootDisabled_enablesRootBeforePullingFiles() throws Exception { + enableJavaCoverage(); HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); mockCoverageFileOnDevice(DEVICE_PATH); when(mMockDevice.isAdbRoot()).thenReturn(false); @@ -187,22 +210,23 @@ public class JavaCodeCoverageListenerTest { } @Test - public void testRunEnded_rootDisabled_throwsIfCannotEnableRoot() throws Exception { + public void testRunEnded_rootDisabled_noLogIfCannotEnableRoot() throws Exception { + enableJavaCoverage(); HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); mockCoverageFileOnDevice(DEVICE_PATH); when(mMockDevice.isAdbRoot()).thenReturn(false); when(mMockDevice.enableAdbRoot()).thenReturn(false); mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); - try { - mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); - fail("Exception not thrown"); - } catch (RuntimeException expected) { - } + mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); + + verify(mFakeListener, never()) + .testLog(anyString(), eq(LogDataType.COVERAGE), any(InputStreamSource.class)); } @Test public void testRunEnded_rootDisabled_disablesRootAfterPullingFiles() throws Exception { + enableJavaCoverage(); HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); mockCoverageFileOnDevice(DEVICE_PATH); when(mMockDevice.isAdbRoot()).thenReturn(false); @@ -218,23 +242,24 @@ public class JavaCodeCoverageListenerTest { } @Test - public void testRunEnded_rootDisabled_throwsIfCannotDisableRoot() throws Exception { + public void testRunEnded_rootDisabled_noLogIfCannotDisableRoot() throws Exception { + enableJavaCoverage(); HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); mockCoverageFileOnDevice(DEVICE_PATH); when(mMockDevice.isAdbRoot()).thenReturn(false); when(mMockDevice.disableAdbRoot()).thenReturn(false); mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); - try { - mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); - fail("Exception not thrown"); - } catch (RuntimeException expected) { - } + mCodeCoverageListener.testRunEnded(ELAPSED_TIME, runMetrics); + + verify(mFakeListener, never()) + .testLog(anyString(), eq(LogDataType.COVERAGE), any(InputStreamSource.class)); } @Test - public void testMerge_producesSingleMeasurement() - throws DeviceNotAvailableException, IOException { + public void testMerge_producesSingleMeasurement() throws Exception { + enableJavaCoverage(); + // Setup mocks. File coverageFile1 = folder.newFile("coverage1.ec"); try (OutputStream out = new FileOutputStream(coverageFile1)) { @@ -251,8 +276,7 @@ public class JavaCodeCoverageListenerTest { measurement.writeTo(out); } - mCodeCoverageListener = - new JavaCodeCoverageListener(mMockDevice, mCoverageOptions, true, mFakeListener); + mCodeCoverageListener.setMergeMeasurements(true); Map<String, String> metric = new HashMap<>(); metric.put("coverageFilePath", DEVICE_PATH); @@ -292,6 +316,8 @@ public class JavaCodeCoverageListenerTest { @Test public void testCoverageFlush_producesMultipleMeasurements() throws Exception { + enableJavaCoverage(); + List<String> coverageFileList = ImmutableList.of( "/data/misc/trace/com.android.test1.ec", @@ -376,6 +402,11 @@ public class JavaCodeCoverageListenerTest { return TfMetricProtoUtil.upgradeConvert(ImmutableMap.of("coverageFilePath", devicePath)); } + private void enableJavaCoverage() throws ConfigurationException { + mCoverageOptionsSetter.setOptionValue("coverage", "true"); + mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "JACOCO"); + } + /** An {@link ITestInvocationListener} which reads test log data streams for verification. */ private static class LogFileReader implements ITestInvocationListener { /** |