1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.device.metric; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.fail; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyInt; 24 import static org.mockito.ArgumentMatchers.anyLong; 25 import static org.mockito.ArgumentMatchers.anyString; 26 import static org.mockito.ArgumentMatchers.eq; 27 import static org.mockito.Mockito.doAnswer; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.inOrder; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.times; 32 import static org.mockito.Mockito.verify; 33 import static org.mockito.Mockito.verifyNoMoreInteractions; 34 import static org.mockito.Mockito.when; 35 36 import com.android.tradefed.config.ConfigurationException; 37 import com.android.tradefed.config.IConfiguration; 38 import com.android.tradefed.config.OptionSetter; 39 import com.android.tradefed.device.DeviceNotAvailableException; 40 import com.android.tradefed.device.ITestDevice; 41 import com.android.tradefed.invoker.IInvocationContext; 42 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 43 import com.android.tradefed.result.ITestInvocationListener; 44 import com.android.tradefed.result.InputStreamSource; 45 import com.android.tradefed.result.LogDataType; 46 import com.android.tradefed.testtype.coverage.CoverageOptions; 47 import com.android.tradefed.testtype.suite.ModuleDefinition; 48 import com.android.tradefed.util.CommandResult; 49 import com.android.tradefed.util.CommandStatus; 50 import com.android.tradefed.util.JavaCodeCoverageFlusher; 51 import com.android.tradefed.util.MultiMap; 52 import com.android.tradefed.util.TarUtil; 53 import com.android.tradefed.util.proto.TfMetricProtoUtil; 54 55 import com.google.common.collect.ImmutableList; 56 import com.google.common.collect.ImmutableMap; 57 import com.google.protobuf.ByteString; 58 59 import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 60 import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 61 import org.jacoco.core.tools.ExecFileLoader; 62 import org.jacoco.core.data.ExecutionData; 63 import org.jacoco.core.data.ExecutionDataStore; 64 import org.jacoco.core.data.ExecutionDataWriter; 65 import org.jacoco.core.internal.data.CRC64; 66 import org.junit.After; 67 import org.junit.Before; 68 import org.junit.Rule; 69 import org.junit.Test; 70 import org.junit.rules.TemporaryFolder; 71 import org.junit.runner.RunWith; 72 import org.junit.runners.JUnit4; 73 import org.mockito.ArgumentCaptor; 74 import org.mockito.InOrder; 75 import org.mockito.Mock; 76 import org.mockito.MockitoAnnotations; 77 import org.mockito.Spy; 78 79 import java.io.ByteArrayOutputStream; 80 import java.io.File; 81 import java.io.FileInputStream; 82 import java.io.FileOutputStream; 83 import java.io.IOException; 84 import java.io.InputStream; 85 import java.io.OutputStream; 86 import java.util.Arrays; 87 import java.util.ArrayList; 88 import java.util.HashMap; 89 import java.util.List; 90 import java.util.Map; 91 import java.util.concurrent.TimeUnit; 92 93 /** Unit tests for {@link JavaCodeCoverageCollector}. */ 94 @RunWith(JUnit4.class) 95 public class JavaCodeCoverageCollectorTest { 96 97 private static final int PROBE_COUNT = 10; 98 99 private static final String RUN_NAME = "SomeTest"; 100 private static final int TEST_COUNT = 5; 101 private static final long ELAPSED_TIME = 1000; 102 103 private static final String DEVICE_PATH = "/some/path/on/the/device.ec"; 104 private static final ByteString COVERAGE_MEASUREMENT = 105 ByteString.copyFromUtf8("Mi estas kovrado mezurado"); 106 107 @Rule public TemporaryFolder folder = new TemporaryFolder(); 108 109 @Mock IConfiguration mMockConfiguration; 110 @Mock IInvocationContext mMockContext; 111 @Mock ITestDevice mMockDevice; 112 @Mock JavaCodeCoverageFlusher mMockFlusher; 113 114 @Spy LogFileReader mFakeListener = new LogFileReader(); 115 116 /** Object under test. */ 117 JavaCodeCoverageCollector mCodeCoverageCollector; 118 119 CoverageOptions mCoverageOptions = null; 120 OptionSetter mCoverageOptionsSetter = null; 121 List<File> mFilesToClean; 122 123 @Before setUp()124 public void setUp() throws Exception { 125 MockitoAnnotations.initMocks(this); 126 127 mCoverageOptions = new CoverageOptions(); 128 mCoverageOptionsSetter = new OptionSetter(mCoverageOptions); 129 mFilesToClean = new ArrayList<>(); 130 131 when(mMockConfiguration.getCoverageOptions()).thenReturn(mCoverageOptions); 132 133 when(mMockContext.getDevices()).thenReturn(ImmutableList.of(mMockDevice)); 134 when(mMockContext.getAttributes()) 135 .thenReturn( 136 new MultiMap(ImmutableMap.of(ModuleDefinition.MODULE_NAME, "myModule"))); 137 138 // Mock an unrooted device that has no issues enabling or disabling root. 139 when(mMockDevice.isAdbRoot()).thenReturn(false); 140 when(mMockDevice.enableAdbRoot()).thenReturn(true); 141 when(mMockDevice.disableAdbRoot()).thenReturn(true); 142 143 mCodeCoverageCollector = new JavaCodeCoverageCollector(); 144 mCodeCoverageCollector.setConfiguration(mMockConfiguration); 145 } 146 147 @After cleanUp()148 public void cleanUp() throws IOException { 149 for (File file : mFilesToClean) { 150 file.delete(); 151 } 152 } 153 154 @Test testRunEnded_noCoverageEnabled_noop()155 public void testRunEnded_noCoverageEnabled_noop() throws Exception { 156 // Setup mocks. 157 HashMap<String, Metric> runMetrics = new HashMap<>(); 158 159 // Simulate a test run. 160 mCodeCoverageCollector.init(mMockContext, mFakeListener); 161 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 162 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 163 164 // Verify testLog(..) was not called. 165 verify(mFakeListener, never()) 166 .testLog(anyString(), eq(LogDataType.COVERAGE), eq(COVERAGE_MEASUREMENT)); 167 } 168 169 @Test testRunEnded_rootEnabled_logsCoverageMeasurement()170 public void testRunEnded_rootEnabled_logsCoverageMeasurement() throws Exception { 171 enableJavaCoverage(); 172 mCoverageOptionsSetter.setOptionValue("pull-timeout", "314159"); 173 174 // Setup mocks. 175 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 176 mockCoverageFileOnDevice(DEVICE_PATH); 177 when(mMockDevice.isAdbRoot()).thenReturn(true); 178 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 179 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 180 181 // Simulate a test run. 182 mCodeCoverageCollector.init(mMockContext, mFakeListener); 183 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 184 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 185 186 // Verify timeout is set. 187 verify(mMockDevice, times(1)) 188 .executeShellV2Command( 189 eq("find /data/misc/trace -name '*.ec' | tar -czf - -T - 2>/dev/null"), 190 any(), 191 any(), 192 eq(314159L), 193 eq(TimeUnit.MILLISECONDS), 194 eq(1)); 195 196 // Verify testLog(..) was called with the coverage file. 197 verify(mFakeListener) 198 .testLog(anyString(), eq(LogDataType.COVERAGE), eq(COVERAGE_MEASUREMENT)); 199 } 200 201 @Test testRunEnded_rootEnabled_noModuleName_logsCoverageMeasurement()202 public void testRunEnded_rootEnabled_noModuleName_logsCoverageMeasurement() throws Exception { 203 enableJavaCoverage(); 204 205 // Setup mocks. 206 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 207 mockCoverageFileOnDevice(DEVICE_PATH); 208 when(mMockDevice.isAdbRoot()).thenReturn(true); 209 when(mMockContext.getAttributes()).thenReturn(new MultiMap(ImmutableMap.of())); 210 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 211 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 212 213 // Simulate a test run. 214 mCodeCoverageCollector.init(mMockContext, mFakeListener); 215 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 216 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 217 218 // Verify testLog(..) was called with the coverage file. 219 verify(mFakeListener) 220 .testLog(anyString(), eq(LogDataType.COVERAGE), eq(COVERAGE_MEASUREMENT)); 221 } 222 223 @Test testFailure_unableToPullFile()224 public void testFailure_unableToPullFile() throws Exception { 225 enableJavaCoverage(); 226 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 227 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 228 doReturn(null).when(mMockDevice).pullFile(DEVICE_PATH); 229 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 230 231 // Simulate a test run. 232 mCodeCoverageCollector.init(mMockContext, mFakeListener); 233 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 234 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 235 236 verify(mFakeListener, never()) 237 .testLog(anyString(), eq(LogDataType.COVERAGE), any(InputStreamSource.class)); 238 } 239 240 @Test testRunEnded_rootDisabled_enablesRootBeforePullingFiles()241 public void testRunEnded_rootDisabled_enablesRootBeforePullingFiles() throws Exception { 242 enableJavaCoverage(); 243 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 244 mockCoverageFileOnDevice(DEVICE_PATH); 245 when(mMockDevice.isAdbRoot()).thenReturn(false); 246 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 247 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 248 249 // Simulate a test run. 250 mCodeCoverageCollector.init(mMockContext, mFakeListener); 251 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 252 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 253 254 InOrder inOrder = inOrder(mMockDevice); 255 inOrder.verify(mMockDevice).enableAdbRoot(); 256 inOrder.verify(mMockDevice).pullFile(anyString()); 257 } 258 259 @Test testRunEnded_rootDisabled_noLogIfCannotEnableRoot()260 public void testRunEnded_rootDisabled_noLogIfCannotEnableRoot() throws Exception { 261 enableJavaCoverage(); 262 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 263 mockCoverageFileOnDevice(DEVICE_PATH); 264 when(mMockDevice.isAdbRoot()).thenReturn(false); 265 when(mMockDevice.enableAdbRoot()).thenReturn(false); 266 267 // Simulate a test run. 268 try { 269 mCodeCoverageCollector.init(mMockContext, mFakeListener); 270 fail("An exception should have been thrown."); 271 } catch (RuntimeException e) { 272 // Expected. 273 } 274 275 verify(mFakeListener, never()) 276 .testLog(anyString(), eq(LogDataType.COVERAGE), any(InputStreamSource.class)); 277 } 278 279 @Test testRunEnded_rootDisabled_disablesRootAfterPullingFiles()280 public void testRunEnded_rootDisabled_disablesRootAfterPullingFiles() throws Exception { 281 enableJavaCoverage(); 282 HashMap<String, Metric> runMetrics = createMetricsWithCoverageMeasurement(DEVICE_PATH); 283 mockCoverageFileOnDevice(DEVICE_PATH); 284 when(mMockDevice.isAdbRoot()).thenReturn(false); 285 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 286 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 287 288 // Simulate a test run. 289 mCodeCoverageCollector.init(mMockContext, mFakeListener); 290 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 291 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, runMetrics); 292 293 InOrder inOrder = inOrder(mMockDevice); 294 inOrder.verify(mMockDevice).pullFile(anyString()); 295 inOrder.verify(mMockDevice).disableAdbRoot(); 296 } 297 298 @Test testCoverageFlush_producesMultipleMeasurements()299 public void testCoverageFlush_producesMultipleMeasurements() throws Exception { 300 enableJavaCoverage(); 301 302 Map<String, ByteString> coverageData = 303 ImmutableMap.of( 304 "/data/misc/trace/com.android.test1.ec", 305 ByteString.copyFromUtf8("com.android.test1.ec"), 306 "/data/misc/trace/com.android.test2.ec", 307 ByteString.copyFromUtf8("com.android.test2.ec"), 308 "/data/misc/trace/com.google.test3.ec", 309 ByteString.copyFromUtf8("com.google.test3.ec")); 310 311 mCoverageOptionsSetter.setOptionValue("coverage-flush", "true"); 312 313 // Setup mocks. 314 mockCoverageFileOnDevice(DEVICE_PATH); 315 316 doReturn("").when(mMockDevice).executeShellCommand("ps -e"); 317 doReturn("") 318 .when(mMockDevice) 319 .executeShellCommand(JavaCodeCoverageCollector.FIND_COVERAGE_FILES); 320 returnFileContentsOnShellCommand(mMockDevice, createTarGz(coverageData)); 321 322 mCodeCoverageCollector.setCoverageFlusher(mMockFlusher); 323 324 // Simulate a test run. 325 mCodeCoverageCollector.init(mMockContext, mFakeListener); 326 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 327 Map<String, String> metric = new HashMap<>(); 328 metric.put("coverageFilePath", DEVICE_PATH); 329 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric)); 330 331 // Verify the coverage data was logged. 332 for (ByteString contents : coverageData.values()) { 333 verify(mFakeListener).testLog(anyString(), eq(LogDataType.COVERAGE), eq(contents)); 334 } 335 } 336 337 @Test testRunningProcess_coverageFileNotDeleted()338 public void testRunningProcess_coverageFileNotDeleted() throws Exception { 339 enableJavaCoverage(); 340 341 List<String> coverageFileList = 342 ImmutableList.of( 343 "/data/misc/trace/coverage1.ec", 344 "/data/misc/trace/coverage2.ec", 345 "/data/misc/trace/jacoco-123.mm.ec", 346 "/data/misc/trace/jacoco-456.mm.ec"); 347 String psOutput = 348 "USER PID PPID VSZ RSS WCHAN PC S NAME\n" 349 + "bluetooth 123 1366 123 456 SyS_epoll+ 0 S" 350 + " com.android.bluetooth\n" 351 + "radio 890 1 7890 123 binder_io+ 0 S com.android.phone\n" 352 + "root 11 1234 567 890 binder_io+ 0 S not.a.java.package\n"; 353 354 // Setup mocks. 355 mockCoverageFileOnDevice(DEVICE_PATH); 356 357 for (String additionalFile : coverageFileList) { 358 mockCoverageFileOnDevice(additionalFile); 359 } 360 361 doReturn("").when(mMockDevice).executeShellCommand("pm list packages -a"); 362 doReturn(psOutput).when(mMockDevice).executeShellCommand("ps -e"); 363 doReturn(String.join("\n", coverageFileList)) 364 .when(mMockDevice) 365 .executeShellCommand("find /data/misc/trace -name '*.ec'"); 366 returnFileContentsOnShellCommand(mMockDevice, createTarGz(ImmutableMap.of())); 367 368 // Simulate a test run. 369 mCodeCoverageCollector.init(mMockContext, mFakeListener); 370 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 371 Map<String, String> metric = new HashMap<>(); 372 metric.put("coverageFilePath", DEVICE_PATH); 373 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric)); 374 375 // Verify the correct files were deleted and some files were not deleted. 376 verify(mMockDevice).deleteFile(coverageFileList.get(0)); 377 verify(mMockDevice).deleteFile(coverageFileList.get(1)); 378 verify(mMockDevice, never()).deleteFile(coverageFileList.get(2)); 379 verify(mMockDevice).deleteFile(coverageFileList.get(3)); 380 } 381 382 @Test testStreamingCoverage_logsReceived()383 public void testStreamingCoverage_logsReceived() throws Exception { 384 enableJavaCoverage(); 385 386 String path1 = "path/to/coverage1.ec"; 387 ByteString contents1 = ByteString.copyFromUtf8("File contents 1"); 388 String path2 = "path/to/coverage2.ec"; 389 ByteString contents2 = ByteString.copyFromUtf8("File contents 2"); 390 File tarGz = 391 createTarGz( 392 ImmutableMap.of( 393 path1, contents1, 394 path2, contents2)); 395 396 // Return the tar.gz file when running the stream-compress command. 397 returnFileContentsOnShellCommand(mMockDevice, tarGz); 398 399 // Return no data for the `ps -e` command. 400 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 401 402 // Simulate a test run. 403 mCodeCoverageCollector.init(mMockContext, mFakeListener); 404 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 405 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, new HashMap<String, Metric>()); 406 407 // Verify that the coverage data was logged. 408 verify(mFakeListener).testLog(anyString(), eq(LogDataType.COVERAGE), eq(contents1)); 409 verify(mFakeListener).testLog(anyString(), eq(LogDataType.COVERAGE), eq(contents2)); 410 } 411 412 @Test testInitNoResetCoverage_noop()413 public void testInitNoResetCoverage_noop() throws Exception { 414 enableJavaCoverage(); 415 mCoverageOptionsSetter.setOptionValue("reset-coverage-before-test", "false"); 416 417 // Run init(...). 418 mCodeCoverageCollector.init(mMockContext, mFakeListener); 419 420 // Verify that nothing was run on the device. 421 verifyNoMoreInteractions(mMockDevice); 422 } 423 424 @Test testMergeSingleMeasurement_logReceived()425 public void testMergeSingleMeasurement_logReceived() throws Exception { 426 enableJavaCoverage(); 427 mCoverageOptionsSetter.setOptionValue("merge-coverage", "true"); 428 429 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 430 431 ByteString measurement = measurement(firstHalfCovered(JavaCodeCoverageCollector.class)); 432 File tarGz = createTarGz(ImmutableMap.of("path/to/coverage.ec", measurement)); 433 returnFileContentsOnShellCommand(mMockDevice, tarGz); 434 435 // Simulate a test run. 436 mCodeCoverageCollector.init(mMockContext, mFakeListener); 437 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 438 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, new HashMap<String, Metric>()); 439 440 // Validate the logged coverage data. 441 ArgumentCaptor<ByteString> stream = ArgumentCaptor.forClass(ByteString.class); 442 verify(mFakeListener).testLog(anyString(), eq(LogDataType.COVERAGE), stream.capture()); 443 444 ExecFileLoader execFileLoader = new ExecFileLoader(); 445 execFileLoader.load(stream.getValue().newInput()); 446 447 ExecutionDataStore execData = execFileLoader.getExecutionDataStore(); 448 boolean[] firstHalf = new boolean[PROBE_COUNT]; 449 for (int i = 0; i < PROBE_COUNT / 2; i++) { 450 firstHalf[i] = true; 451 } 452 453 assertThat(execData.contains(vmName(JavaCodeCoverageCollector.class))).isTrue(); 454 assertThat(getProbes(JavaCodeCoverageCollector.class, execData)).isEqualTo(firstHalf); 455 } 456 457 @Test testMergeMultipleMeasurements_logContainsAllData()458 public void testMergeMultipleMeasurements_logContainsAllData() throws Exception { 459 enableJavaCoverage(); 460 mCoverageOptionsSetter.setOptionValue("merge-coverage", "true"); 461 462 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 463 464 ByteString firstHalfCollector = 465 measurement(firstHalfCovered(JavaCodeCoverageCollector.class)); 466 ByteString secondHalfCollector = 467 measurement(secondHalfCovered(JavaCodeCoverageCollector.class)); 468 ByteString partialCollectorTest = 469 measurement(partiallyCovered(JavaCodeCoverageCollectorTest.class)); 470 File tarGz = 471 createTarGz( 472 ImmutableMap.of( 473 "JavaCodeCoverageColletor1.ec", firstHalfCollector, 474 "JavaCodeCoverageCollector2.ec", secondHalfCollector, 475 "JavaCodeCoverageCollectorTest.ec", partialCollectorTest)); 476 returnFileContentsOnShellCommand(mMockDevice, tarGz); 477 478 // Simulate a test run. 479 mCodeCoverageCollector.init(mMockContext, mFakeListener); 480 mCodeCoverageCollector.testRunStarted(RUN_NAME, TEST_COUNT); 481 mCodeCoverageCollector.testRunEnded(ELAPSED_TIME, new HashMap<String, Metric>()); 482 483 // Validate the logged coverage data. 484 ArgumentCaptor<ByteString> stream = ArgumentCaptor.forClass(ByteString.class); 485 verify(mFakeListener).testLog(anyString(), eq(LogDataType.COVERAGE), stream.capture()); 486 487 ExecFileLoader execFileLoader = new ExecFileLoader(); 488 execFileLoader.load(stream.getValue().newInput()); 489 490 ExecutionDataStore execData = execFileLoader.getExecutionDataStore(); 491 492 // Check coverage data for JavaCodeCoverageCollector. All probes should be true if the data 493 // merged successfully. 494 boolean[] fullyCovered = new boolean[PROBE_COUNT]; 495 Arrays.fill(fullyCovered, Boolean.TRUE); 496 497 assertThat(execData.contains(vmName(JavaCodeCoverageCollector.class))).isTrue(); 498 assertThat(getProbes(JavaCodeCoverageCollector.class, execData)).isEqualTo(fullyCovered); 499 500 // Check coverage data for JavaCodeCoverageCollectorTest. Only the first probe should be 501 // true. 502 boolean[] partiallyCovered = new boolean[PROBE_COUNT]; 503 partiallyCovered[0] = true; 504 505 assertThat(execData.contains(vmName(JavaCodeCoverageCollectorTest.class))).isTrue(); 506 assertThat(getProbes(JavaCodeCoverageCollectorTest.class, execData)) 507 .isEqualTo(partiallyCovered); 508 } 509 mockCoverageFileOnDevice(String devicePath)510 private void mockCoverageFileOnDevice(String devicePath) 511 throws IOException, DeviceNotAvailableException { 512 File coverageFile = folder.newFile(new File(devicePath).getName()); 513 514 try (OutputStream out = new FileOutputStream(coverageFile)) { 515 COVERAGE_MEASUREMENT.writeTo(out); 516 } 517 518 doReturn(coverageFile).when(mMockDevice).pullFile(devicePath); 519 } 520 vmName(Class<T> clazz)521 private static <T> String vmName(Class<T> clazz) { 522 return clazz.getName().replace('.', '/'); 523 } 524 fullyCovered(Class<T> clazz)525 private static <T> ExecutionData fullyCovered(Class<T> clazz) throws IOException { 526 boolean[] probes = new boolean[PROBE_COUNT]; 527 Arrays.fill(probes, Boolean.TRUE); 528 return new ExecutionData(classId(clazz), vmName(clazz), probes); 529 } 530 partiallyCovered(Class<T> clazz)531 private static <T> ExecutionData partiallyCovered(Class<T> clazz) throws IOException { 532 boolean[] probes = new boolean[PROBE_COUNT]; 533 probes[0] = true; 534 return new ExecutionData(classId(clazz), vmName(clazz), probes); 535 } 536 firstHalfCovered(Class<T> clazz)537 private static <T> ExecutionData firstHalfCovered(Class<T> clazz) throws IOException { 538 boolean[] probes = new boolean[PROBE_COUNT]; 539 for (int i = 0; i < PROBE_COUNT / 2; i++) { 540 probes[i] = true; 541 } 542 return new ExecutionData(classId(clazz), vmName(clazz), probes); 543 } 544 secondHalfCovered(Class<T> clazz)545 private static <T> ExecutionData secondHalfCovered(Class<T> clazz) throws IOException { 546 boolean[] probes = new boolean[PROBE_COUNT]; 547 for (int i = PROBE_COUNT / 2; i < PROBE_COUNT; i++) { 548 probes[i] = true; 549 } 550 return new ExecutionData(classId(clazz), vmName(clazz), probes); 551 } 552 classId(Class<T> clazz)553 private static <T> long classId(Class<T> clazz) throws IOException { 554 return Long.valueOf(CRC64.classId(classBytes(clazz).toByteArray())); 555 } 556 classBytes(Class<T> clazz)557 private static <T> ByteString classBytes(Class<T> clazz) throws IOException { 558 return ByteString.readFrom( 559 clazz.getClassLoader().getResourceAsStream(vmName(clazz) + ".class")); 560 } 561 measurement(ExecutionData... data)562 private static ByteString measurement(ExecutionData... data) throws IOException { 563 ExecutionDataStore dataStore = new ExecutionDataStore(); 564 Arrays.stream(data).forEach(dataStore::put); 565 566 try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) { 567 dataStore.accept(new ExecutionDataWriter(bytes)); 568 return ByteString.copyFrom(bytes.toByteArray()); 569 } 570 } 571 getProbes(Class<T> clazz, ExecutionDataStore execData)572 private static <T> boolean[] getProbes(Class<T> clazz, ExecutionDataStore execData) 573 throws IOException { 574 return execData.get(classId(clazz), vmName(clazz), PROBE_COUNT).getProbesCopy(); 575 } 576 createMetricsWithCoverageMeasurement(String devicePath)577 private static HashMap<String, Metric> createMetricsWithCoverageMeasurement(String devicePath) { 578 return TfMetricProtoUtil.upgradeConvert(ImmutableMap.of("coverageFilePath", devicePath)); 579 } 580 returnFileContentsOnShellCommand(ITestDevice device, File file)581 private static void returnFileContentsOnShellCommand(ITestDevice device, File file) 582 throws DeviceNotAvailableException, IOException { 583 doAnswer( 584 invocation -> { 585 OutputStream out = (OutputStream) invocation.getArgument(2); 586 try (InputStream in = new FileInputStream(file)) { 587 in.transferTo(out); 588 } 589 return new CommandResult(CommandStatus.SUCCESS); 590 }) 591 .when(device) 592 .executeShellV2Command( 593 eq(JavaCodeCoverageCollector.COMPRESS_COVERAGE_FILES), 594 any(), 595 any(OutputStream.class), 596 anyLong(), 597 any(TimeUnit.class), 598 anyInt()); 599 } 600 createTarGz(Map<String, ByteString> fileContents)601 private File createTarGz(Map<String, ByteString> fileContents) throws IOException { 602 File tarFile = folder.newFile(); 603 try (TarArchiveOutputStream out = 604 new TarArchiveOutputStream(new FileOutputStream(tarFile))) { 605 for (Map.Entry<String, ByteString> file : fileContents.entrySet()) { 606 TarArchiveEntry entry = new TarArchiveEntry(file.getKey()); 607 entry.setSize(file.getValue().size()); 608 609 out.putArchiveEntry(entry); 610 file.getValue().writeTo(out); 611 out.closeArchiveEntry(); 612 } 613 File tarGz = TarUtil.gzip(tarFile); 614 mFilesToClean.add(tarGz); 615 return tarGz; 616 } finally { 617 tarFile.delete(); 618 } 619 } 620 enableJavaCoverage()621 private void enableJavaCoverage() throws ConfigurationException { 622 mCoverageOptionsSetter.setOptionValue("coverage", "true"); 623 mCoverageOptionsSetter.setOptionValue("coverage-toolchain", "JACOCO"); 624 } 625 626 /** An {@link ITestInvocationListener} which reads test log data streams for verification. */ 627 private static class LogFileReader implements ITestInvocationListener { 628 /** 629 * Reads the contents of the {@code dataStream} and forwards it to the {@link 630 * #testLog(String, LogDataType, ByteString)} method. 631 */ 632 @Override testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)633 public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) { 634 try (InputStream input = dataStream.createInputStream()) { 635 testLog(dataName, dataType, ByteString.readFrom(input)); 636 } catch (IOException e) { 637 throw new RuntimeException(e); 638 } 639 } 640 641 /** No-op method for {@link Spy} verification. */ testLog(String dataName, LogDataType dataType, ByteString data)642 public void testLog(String dataName, LogDataType dataType, ByteString data) {} 643 } 644 } 645 646