1 /* 2 * Copyright (C) 2019 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 package android.device.collectors; 17 18 import static org.mockito.ArgumentMatchers.argThat; 19 import static org.mockito.Mockito.any; 20 import static org.mockito.Mockito.anyInt; 21 import static org.mockito.Mockito.anyLong; 22 import static org.mockito.Mockito.doAnswer; 23 import static org.mockito.Mockito.doNothing; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.eq; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.never; 28 import static org.mockito.Mockito.spy; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 32 import android.content.res.AssetManager; 33 import android.os.Bundle; 34 35 import com.android.internal.os.nano.StatsdConfigProto; 36 import com.android.os.nano.AtomsProto; 37 import com.android.os.nano.StatsLog; 38 39 import com.google.common.collect.ImmutableMap; 40 import com.google.protobuf.nano.CodedOutputByteBufferNano; 41 import com.google.protobuf.nano.ExtendableMessageNano; 42 43 import org.junit.Assert; 44 import org.junit.Before; 45 import org.junit.Rule; 46 import org.junit.Test; 47 import org.junit.rules.ExpectedException; 48 import org.junit.runner.Description; 49 import org.junit.runner.Result; 50 51 import java.io.ByteArrayInputStream; 52 import java.io.File; 53 import java.io.IOException; 54 import java.nio.file.Paths; 55 import java.util.Arrays; 56 import java.util.Map; 57 import java.util.stream.IntStream; 58 59 /** Unit tests for {@link StatsdListener}. */ 60 public class StatsdListenerTest { 61 private StatsdListener mListener; 62 63 private static final String CONFIG_NAME_1 = "config-1"; 64 private static final String CONFIG_NAME_2 = "config-2"; 65 66 private static final long CONFIG_ID_1 = 1; 67 private static final long CONFIG_ID_2 = 2; 68 69 private static class DummyTest {} 70 71 private static final Class<?> TEST_CLASS = DummyTest.class; 72 private static final String TEST_METHOD_NAME_1 = "testMethodOne"; 73 private static final String TEST_METHOD_NAME_2 = "testMethodTwo"; 74 75 private static final StatsdConfigProto.StatsdConfig CONFIG_1 = 76 new StatsdConfigProto.StatsdConfig(); 77 78 private static final StatsdConfigProto.StatsdConfig CONFIG_2 = 79 new StatsdConfigProto.StatsdConfig(); 80 81 private static final StatsLog.ConfigMetricsReportList REPORT_1 = 82 new StatsLog.ConfigMetricsReportList(); 83 84 private static final StatsLog.ConfigMetricsReportList REPORT_2 = 85 new StatsLog.ConfigMetricsReportList(); 86 87 private static final ImmutableMap<String, StatsdConfigProto.StatsdConfig> CONFIG_MAP = 88 ImmutableMap.of(CONFIG_NAME_1, CONFIG_1, CONFIG_NAME_2, CONFIG_2); 89 90 @Rule public ExpectedException mExpectedException = ExpectedException.none(); 91 92 @Before setUp()93 public void setUp() throws Exception { 94 CONFIG_1.id = CONFIG_ID_1; 95 CONFIG_2.id = CONFIG_ID_2; 96 97 // Config key for report 1 98 StatsLog.ConfigMetricsReportList.ConfigKey report1Key = 99 new StatsLog.ConfigMetricsReportList.ConfigKey(); 100 report1Key.uid = 0; 101 report1Key.id = CONFIG_ID_1; 102 REPORT_1.configKey = report1Key; 103 104 // Config key for report 2 105 StatsLog.ConfigMetricsReportList.ConfigKey report2Key = 106 new StatsLog.ConfigMetricsReportList.ConfigKey(); 107 report1Key.uid = 0; 108 report1Key.id = CONFIG_ID_2; 109 REPORT_2.configKey = report2Key; 110 111 mListener = spy(new StatsdListener()); 112 // Stub the report collection to isolate collector from StatsManager. 113 doNothing().when(mListener).addStatsConfig(anyLong(), any()); 114 doReturn(serialize(REPORT_1)).when(mListener).getStatsReports(eq(CONFIG_ID_1)); 115 doReturn(serialize(REPORT_2)).when(mListener).getStatsReports(eq(CONFIG_ID_2)); 116 doNothing().when(mListener).removeStatsConfig(anyLong()); 117 // Stub calls to permission APIs. 118 doNothing().when(mListener).adoptShellPermissionIdentity(); 119 doNothing().when(mListener).dropShellPermissionIdentity(); 120 // Stub calls to StatsLog APIs. 121 doReturn(true).when(mListener).logStart(anyInt()); 122 doReturn(true).when(mListener).logStop(anyInt()); 123 // Stub file I/O. 124 doAnswer(invocation -> invocation.getArgument(0)).when(mListener).writeToFile(any(), any()); 125 // Stub randome UUID generation. 126 doReturn(CONFIG_ID_1).when(mListener).getUniqueIdForConfig(eq(CONFIG_1)); 127 doReturn(CONFIG_ID_2).when(mListener).getUniqueIdForConfig(eq(CONFIG_2)); 128 } 129 130 /** Test that the collector has correct interactions with statsd for per-run collection. */ 131 @Test testRunLevelCollection_statsdInteraction()132 public void testRunLevelCollection_statsdInteraction() throws Exception { 133 doReturn(CONFIG_MAP) 134 .when(mListener) 135 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_RUN_LEVEL)); 136 137 DataRecord runData = new DataRecord(); 138 Description description = Description.createSuiteDescription("TestRun"); 139 140 mListener.onTestRunStart(runData, description); 141 verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_1), eq(serialize(CONFIG_1))); 142 verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_2), eq(serialize(CONFIG_2))); 143 verify(mListener, times(1)).logStart(eq(StatsdListener.RUN_EVENT_LABEL)); 144 145 mListener.onTestRunEnd(runData, new Result()); 146 verify(mListener, times(1)).logStop(eq(StatsdListener.RUN_EVENT_LABEL)); 147 verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_1)); 148 verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_2)); 149 verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_1)); 150 verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_2)); 151 } 152 153 /** Test that the collector dumps reports and report them as metrics. */ 154 @Test testRunLevelCollection_metrics()155 public void testRunLevelCollection_metrics() throws Exception { 156 doReturn(CONFIG_MAP) 157 .when(mListener) 158 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_RUN_LEVEL)); 159 160 // Mock the DataRecord class as its content is not directly visible. 161 DataRecord runData = mock(DataRecord.class); 162 Description description = Description.createSuiteDescription("TestRun"); 163 164 mListener.onTestRunStart(runData, description); 165 mListener.onTestRunEnd(runData, new Result()); 166 167 verify(mListener, times(1)) 168 .writeToFile( 169 getExactFileNameMatcher( 170 Paths.get( 171 StatsdListener.REPORT_PATH_ROOT, 172 StatsdListener.REPORT_PATH_RUN_LEVEL) 173 .toString(), 174 StatsdListener.REPORT_FILENAME_PREFIX 175 + CONFIG_NAME_1 176 + StatsdListener.PROTO_EXTENSION), 177 any()); 178 verify(runData, times(1)) 179 .addFileMetric( 180 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 181 getExactFileNameMatcher( 182 Paths.get( 183 StatsdListener.REPORT_PATH_ROOT, 184 StatsdListener.REPORT_PATH_RUN_LEVEL) 185 .toString(), 186 StatsdListener.REPORT_FILENAME_PREFIX 187 + CONFIG_NAME_1 188 + StatsdListener.PROTO_EXTENSION)); 189 verify(mListener, times(1)) 190 .writeToFile( 191 getExactFileNameMatcher( 192 Paths.get( 193 StatsdListener.REPORT_PATH_ROOT, 194 StatsdListener.REPORT_PATH_RUN_LEVEL) 195 .toString(), 196 StatsdListener.REPORT_FILENAME_PREFIX 197 + CONFIG_NAME_2 198 + StatsdListener.PROTO_EXTENSION), 199 any()); 200 verify(runData, times(1)) 201 .addFileMetric( 202 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_2), 203 getExactFileNameMatcher( 204 Paths.get( 205 StatsdListener.REPORT_PATH_ROOT, 206 StatsdListener.REPORT_PATH_RUN_LEVEL) 207 .toString(), 208 StatsdListener.REPORT_FILENAME_PREFIX 209 + CONFIG_NAME_2 210 + StatsdListener.PROTO_EXTENSION)); 211 } 212 213 /** Test that the collector has correct interactions with statsd for per-test collection. */ 214 @Test testTestLevelCollection_statsdInteraction()215 public void testTestLevelCollection_statsdInteraction() throws Exception { 216 doReturn(CONFIG_MAP) 217 .when(mListener) 218 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_LEVEL)); 219 220 DataRecord testData = new DataRecord(); 221 Description description = Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 222 223 // onTestRunStart(...) has to be called because the arguments are parsed here. 224 mListener.onTestRunStart( 225 new DataRecord(), Description.createSuiteDescription("Placeholder")); 226 227 mListener.onTestStart(testData, description); 228 verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_1), eq(serialize(CONFIG_1))); 229 verify(mListener, times(1)).addStatsConfig(eq(CONFIG_ID_2), eq(serialize(CONFIG_2))); 230 verify(mListener, times(1)).logStart(eq(StatsdListener.TEST_EVENT_LABEL)); 231 232 mListener.onTestEnd(testData, description); 233 verify(mListener, times(1)).logStop(eq(StatsdListener.TEST_EVENT_LABEL)); 234 verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_1)); 235 verify(mListener, times(1)).getStatsReports(eq(CONFIG_ID_2)); 236 verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_1)); 237 verify(mListener, times(1)).removeStatsConfig(eq(CONFIG_ID_2)); 238 239 mListener.onTestRunEnd(new DataRecord(), new Result()); 240 } 241 242 /** Test that the collector dumps report and reports them as metric per test. */ 243 @Test testTestLevelCollection_metrics()244 public void testTestLevelCollection_metrics() throws Exception { 245 doReturn(CONFIG_MAP) 246 .when(mListener) 247 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_LEVEL)); 248 249 DataRecord testData = mock(DataRecord.class); 250 Description description = Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 251 252 // onTestRunStart(...) has to be called because the arguments are parsed here. 253 mListener.onTestRunStart( 254 new DataRecord(), Description.createSuiteDescription("Placeholder")); 255 256 mListener.onTestStart(testData, description); 257 mListener.onTestEnd(testData, description); 258 259 verify(mListener, times(1)) 260 .writeToFile( 261 getPartialFileNameMatcher( 262 Paths.get( 263 StatsdListener.REPORT_PATH_ROOT, 264 StatsdListener.REPORT_PATH_TEST_LEVEL) 265 .toString(), 266 StatsdListener.REPORT_FILENAME_PREFIX, 267 description.getClassName(), 268 TEST_METHOD_NAME_1, 269 CONFIG_NAME_1, 270 StatsdListener.PROTO_EXTENSION), 271 any()); 272 verify(testData, times(1)) 273 .addFileMetric( 274 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 275 getPartialFileNameMatcher( 276 Paths.get( 277 StatsdListener.REPORT_PATH_ROOT, 278 StatsdListener.REPORT_PATH_TEST_LEVEL) 279 .toString(), 280 StatsdListener.REPORT_FILENAME_PREFIX, 281 description.getClassName(), 282 TEST_METHOD_NAME_1, 283 CONFIG_NAME_1, 284 StatsdListener.PROTO_EXTENSION)); 285 verify(mListener, times(1)) 286 .writeToFile( 287 getPartialFileNameMatcher( 288 Paths.get( 289 StatsdListener.REPORT_PATH_ROOT, 290 StatsdListener.REPORT_PATH_TEST_LEVEL) 291 .toString(), 292 StatsdListener.REPORT_FILENAME_PREFIX, 293 description.getClassName(), 294 TEST_METHOD_NAME_1, 295 CONFIG_NAME_2, 296 StatsdListener.PROTO_EXTENSION), 297 any()); 298 verify(testData, times(1)) 299 .addFileMetric( 300 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_2), 301 getPartialFileNameMatcher( 302 Paths.get( 303 StatsdListener.REPORT_PATH_ROOT, 304 StatsdListener.REPORT_PATH_TEST_LEVEL) 305 .toString(), 306 StatsdListener.REPORT_FILENAME_PREFIX, 307 description.getClassName(), 308 TEST_METHOD_NAME_1, 309 CONFIG_NAME_2, 310 StatsdListener.PROTO_EXTENSION)); 311 312 mListener.onTestRunEnd(new DataRecord(), new Result()); 313 } 314 315 /** Test that the collector handles multiple test correctly for per-test collection. */ 316 @Test testTestLevelCollection_multipleTests()317 public void testTestLevelCollection_multipleTests() throws Exception { 318 doReturn(CONFIG_MAP) 319 .when(mListener) 320 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_LEVEL)); 321 322 // onTestRunStart(...) has to be called because the arguments are parsed here. 323 mListener.onTestRunStart( 324 new DataRecord(), Description.createSuiteDescription("Placeholder")); 325 326 DataRecord testData1 = mock(DataRecord.class); 327 Description description1 = 328 Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 329 330 mListener.onTestStart(testData1, description1); 331 mListener.onTestEnd(testData1, description1); 332 333 verify(testData1, times(1)) 334 .addFileMetric( 335 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 336 getPartialFileNameMatcher( 337 Paths.get( 338 StatsdListener.REPORT_PATH_ROOT, 339 StatsdListener.REPORT_PATH_TEST_LEVEL) 340 .toString(), 341 description1.getClassName(), 342 TEST_METHOD_NAME_1, 343 String.valueOf(1))); 344 345 DataRecord testData2 = mock(DataRecord.class); 346 Description description2 = 347 Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_2); 348 349 mListener.onTestStart(testData2, description2); 350 mListener.onTestEnd(testData2, description2); 351 352 verify(testData2, times(1)) 353 .addFileMetric( 354 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 355 getPartialFileNameMatcher( 356 Paths.get( 357 StatsdListener.REPORT_PATH_ROOT, 358 StatsdListener.REPORT_PATH_TEST_LEVEL) 359 .toString(), 360 description2.getClassName(), 361 TEST_METHOD_NAME_2, 362 String.valueOf(1))); 363 364 mListener.onTestRunEnd(new DataRecord(), new Result()); 365 } 366 367 /** Test that the collector handles multiple iterations correctly for per-test collection. */ 368 @Test testTestLevelCollection_multipleIterations()369 public void testTestLevelCollection_multipleIterations() throws Exception { 370 doReturn(CONFIG_MAP) 371 .when(mListener) 372 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_LEVEL)); 373 374 // onTestRunStart(...) has to be called because the arguments are parsed here. 375 mListener.onTestRunStart( 376 new DataRecord(), Description.createSuiteDescription("Placeholder")); 377 378 DataRecord testData1 = mock(DataRecord.class); 379 Description description1 = 380 Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 381 382 mListener.onTestStart(testData1, description1); 383 mListener.onTestEnd(testData1, description1); 384 385 // The metric file name should contain the iteration number (1). 386 verify(testData1, times(1)) 387 .addFileMetric( 388 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 389 getPartialFileNameMatcher( 390 Paths.get( 391 StatsdListener.REPORT_PATH_ROOT, 392 StatsdListener.REPORT_PATH_TEST_LEVEL) 393 .toString(), 394 description1.getClassName(), 395 TEST_METHOD_NAME_1, 396 String.valueOf(1))); 397 398 DataRecord testData2 = mock(DataRecord.class); 399 Description description2 = 400 Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 401 402 mListener.onTestStart(testData2, description2); 403 mListener.onTestEnd(testData2, description2); 404 405 // The metric file name should contain the iteration number (2). 406 verify(testData2, times(1)) 407 .addFileMetric( 408 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 409 getPartialFileNameMatcher( 410 Paths.get( 411 StatsdListener.REPORT_PATH_ROOT, 412 StatsdListener.REPORT_PATH_TEST_LEVEL) 413 .toString(), 414 description2.getClassName(), 415 TEST_METHOD_NAME_1, 416 String.valueOf(2))); 417 418 mListener.onTestRunEnd(new DataRecord(), new Result()); 419 } 420 421 /** Test that the collector can perform both run- and test-level collection in the same run. */ 422 @Test testRunAndTestLevelCollection()423 public void testRunAndTestLevelCollection() throws Exception { 424 doReturn(CONFIG_MAP) 425 .when(mListener) 426 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_RUN_LEVEL)); 427 doReturn(CONFIG_MAP) 428 .when(mListener) 429 .getConfigsFromOption(eq(StatsdListener.OPTION_CONFIGS_TEST_LEVEL)); 430 431 DataRecord runData = mock(DataRecord.class); 432 Description runDescription = Description.createSuiteDescription("TestRun"); 433 434 mListener.onTestRunStart(runData, runDescription); 435 436 DataRecord testData = mock(DataRecord.class); 437 Description testDescription = 438 Description.createTestDescription(TEST_CLASS, TEST_METHOD_NAME_1); 439 440 mListener.onTestStart(testData, testDescription); 441 mListener.onTestEnd(testData, testDescription); 442 443 verify(testData, times(1)) 444 .addFileMetric( 445 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 446 getPartialFileNameMatcher( 447 Paths.get( 448 StatsdListener.REPORT_PATH_ROOT, 449 StatsdListener.REPORT_PATH_TEST_LEVEL) 450 .toString(), 451 testDescription.getClassName(), 452 TEST_METHOD_NAME_1, 453 String.valueOf(1))); 454 455 mListener.onTestRunEnd(runData, new Result()); 456 457 verify(runData, times(1)) 458 .addFileMetric( 459 eq(StatsdListener.REPORT_KEY_PREFIX + CONFIG_NAME_1), 460 getExactFileNameMatcher( 461 Paths.get( 462 StatsdListener.REPORT_PATH_ROOT, 463 StatsdListener.REPORT_PATH_RUN_LEVEL) 464 .toString(), 465 StatsdListener.REPORT_FILENAME_PREFIX 466 + CONFIG_NAME_1 467 + StatsdListener.PROTO_EXTENSION)); 468 } 469 470 /** Test that the collector parses the configs from arguments correctly for valid configs. */ 471 @Test testParsingConfigFromArguments_validConfig()472 public void testParsingConfigFromArguments_validConfig() throws Exception { 473 // Stub two configs for testing. 474 ByteArrayInputStream config1Stream = new ByteArrayInputStream(serialize(CONFIG_1)); 475 doReturn(config1Stream) 476 .when(mListener) 477 .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_1)); 478 479 ByteArrayInputStream config2Stream = new ByteArrayInputStream(serialize(CONFIG_2)); 480 doReturn(config2Stream) 481 .when(mListener) 482 .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_2)); 483 484 Bundle args = new Bundle(); 485 args.putString( 486 StatsdListener.OPTION_CONFIGS_RUN_LEVEL, 487 String.join(",", CONFIG_NAME_1, CONFIG_NAME_2)); 488 doReturn(args).when(mListener).getArguments(); 489 490 Map<String, StatsdConfigProto.StatsdConfig> configs = 491 mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_RUN_LEVEL); 492 Assert.assertTrue(configs.containsKey(CONFIG_NAME_1)); 493 Assert.assertEquals(configs.get(CONFIG_NAME_1).id, CONFIG_ID_1); 494 Assert.assertTrue(configs.containsKey(CONFIG_NAME_2)); 495 Assert.assertEquals(configs.get(CONFIG_NAME_2).id, CONFIG_ID_2); 496 Assert.assertEquals(configs.size(), 2); 497 } 498 499 /** Test that the colletor fails and throws the right exception for an invalid config. */ 500 @Test testParsingConfigFromArguments_malformedConfig()501 public void testParsingConfigFromArguments_malformedConfig() throws Exception { 502 // Set up an invalid config for testing. 503 ByteArrayInputStream configStream = new ByteArrayInputStream("not a config".getBytes()); 504 doReturn(configStream) 505 .when(mListener) 506 .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_1)); 507 508 Bundle args = new Bundle(); 509 args.putString(StatsdListener.OPTION_CONFIGS_RUN_LEVEL, CONFIG_NAME_1); 510 doReturn(args).when(mListener).getArguments(); 511 512 mExpectedException.expectMessage("Cannot parse"); 513 Map<String, StatsdConfigProto.StatsdConfig> configs = 514 mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_RUN_LEVEL); 515 } 516 517 /** Test that the collector fails and throws the right exception for a nonexistent config. */ 518 @Test testParsingConfigFromArguments_nonexistentConfig()519 public void testParsingConfigFromArguments_nonexistentConfig() { 520 Bundle args = new Bundle(); 521 args.putString(StatsdListener.OPTION_CONFIGS_RUN_LEVEL, "nah"); 522 doReturn(args).when(mListener).getArguments(); 523 524 mExpectedException.expectMessage("does not exist"); 525 Map<String, StatsdConfigProto.StatsdConfig> configs = 526 mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_RUN_LEVEL); 527 } 528 529 /** Test that the collector has no effect when no config arguments are supplied. */ 530 @Test testNoConfigArguments()531 public void testNoConfigArguments() throws Exception { 532 doReturn(new Bundle()).when(mListener).getArguments(); 533 534 // Mock the DataRecord class as its content is not directly visible. 535 DataRecord runData = mock(DataRecord.class); 536 Description description = Description.createSuiteDescription("TestRun"); 537 538 mListener.onTestRunStart(runData, description); 539 mListener.onTestRunEnd(runData, new Result()); 540 541 verify(runData, never()).addFileMetric(any(), any()); 542 verify(mListener, never()).addStatsConfig(anyLong(), any()); 543 verify(mListener, never()).getStatsReports(anyLong()); 544 verify(mListener, never()).removeStatsConfig(anyLong()); 545 } 546 547 /** 548 * Test that the collector can work with arbitrarily constructed test descriptions. 549 * 550 * <p>This test was created as some runners will create new descriptions on the fly. 551 */ 552 @Test testArbitraryTestDescription()553 public void testArbitraryTestDescription() { 554 // Creates a description with no test class and method. 555 Description arbitraryDescription = Description.createSuiteDescription("some_test"); 556 557 // The test passes if no exceptions are thrown in these callbacks. 558 mListener.onTestStart(mock(DataRecord.class), arbitraryDescription); 559 mListener.onTestEnd(mock(DataRecord.class), arbitraryDescription); 560 } 561 562 /** Returns a Mockito argument matcher that matches the exact file name. */ getExactFileNameMatcher(String parentName, String filename)563 private File getExactFileNameMatcher(String parentName, String filename) { 564 return argThat(f -> f.getParent().contains(parentName) && f.getName().equals(filename)); 565 } 566 567 /** Returns a Mockito argument matcher that matche a file name to one or more substrings. */ getPartialFileNameMatcher( String parentName, String component, String... moreComponents)568 private File getPartialFileNameMatcher( 569 String parentName, String component, String... moreComponents) { 570 return argThat( 571 f -> 572 f.getParent().contains(parentName) 573 && f.getName().contains(component) 574 && Arrays.stream(moreComponents) 575 .allMatch(c -> f.getName().contains(c))); 576 } 577 578 /** Test that configs are parsed and applied with correct permission fixes. */ 579 @Test testConfigsHavePermissionFixes()580 public void testConfigsHavePermissionFixes() throws Exception { 581 // Stub a config for testing. 582 ByteArrayInputStream configStream = new ByteArrayInputStream(serialize(CONFIG_1)); 583 doReturn(configStream) 584 .when(mListener) 585 .openConfigWithAssetManager(any(AssetManager.class), eq(CONFIG_NAME_1)); 586 587 Bundle args = new Bundle(); 588 args.putString(StatsdListener.OPTION_CONFIGS_RUN_LEVEL, CONFIG_NAME_1); 589 doReturn(args).when(mListener).getArguments(); 590 591 Map<String, StatsdConfigProto.StatsdConfig> configs = 592 mListener.getConfigsFromOption(StatsdListener.OPTION_CONFIGS_RUN_LEVEL); 593 Assert.assertTrue(configs.containsKey(CONFIG_NAME_1)); 594 int[] whitelistedAtomIds = configs.get(CONFIG_NAME_1).whitelistedAtomIds; 595 String[] defaultPullPackages = configs.get(CONFIG_NAME_1).defaultPullPackages; 596 Assert.assertTrue( 597 IntStream.of(whitelistedAtomIds) 598 .anyMatch( 599 id -> id == AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER)); 600 Assert.assertTrue( 601 Arrays.asList(defaultPullPackages) 602 .stream() 603 .anyMatch(name -> "AID_SYSTEM".equals(name))); 604 } 605 606 // Some utilities for Nano protos. 607 serialize( ExtendableMessageNano<T> message)608 private static <T extends ExtendableMessageNano<T>> byte[] serialize( 609 ExtendableMessageNano<T> message) throws IOException { 610 byte[] serialized = new byte[message.getSerializedSize()]; 611 CodedOutputByteBufferNano buffer = CodedOutputByteBufferNano.newInstance(serialized); 612 message.writeTo(buffer); 613 return serialized; 614 } 615 } 616