• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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