1 /*
2  * Copyright (C) 2024 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.internal.protolog;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertThrows;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyLong;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.when;
30 
31 import static java.io.File.createTempFile;
32 import static java.nio.file.Files.createTempDirectory;
33 
34 import android.content.Context;
35 import android.os.SystemClock;
36 import android.platform.test.annotations.Presubmit;
37 import android.tools.ScenarioBuilder;
38 import android.tools.traces.TraceConfig;
39 import android.tools.traces.TraceConfigs;
40 import android.tools.traces.io.ResultReader;
41 import android.tools.traces.io.ResultWriter;
42 import android.tools.traces.monitors.PerfettoTraceMonitor;
43 import android.tools.traces.protolog.ProtoLogTrace;
44 import android.tracing.perfetto.DataSource;
45 import android.util.proto.ProtoInputStream;
46 
47 import androidx.test.filters.SmallTest;
48 
49 import com.android.internal.protolog.common.IProtoLogGroup;
50 import com.android.internal.protolog.common.LogDataType;
51 import com.android.internal.protolog.common.LogLevel;
52 
53 import com.google.common.truth.Truth;
54 
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Test;
58 import org.junit.runner.RunWith;
59 import org.junit.runners.JUnit4;
60 import org.mockito.Mockito;
61 import org.mockito.MockitoAnnotations;
62 
63 import java.io.File;
64 import java.io.IOException;
65 import java.util.List;
66 import java.util.Random;
67 import java.util.TreeMap;
68 import java.util.concurrent.atomic.AtomicInteger;
69 
70 import perfetto.protos.Protolog;
71 import perfetto.protos.ProtologCommon;
72 
73 /**
74  * Test class for {@link ProtoLogImpl}.
75  */
76 @SuppressWarnings("ConstantConditions")
77 @SmallTest
78 @Presubmit
79 @RunWith(JUnit4.class)
80 public class PerfettoProtoLogImplTest {
81     private final File mTracingDirectory = createTempDirectory("temp").toFile();
82 
83     private final ResultWriter mWriter = new ResultWriter()
84             .forScenario(new ScenarioBuilder()
85                     .forClass(createTempFile("temp", "").getName()).build())
86             .withOutputDir(mTracingDirectory)
87             .setRunComplete();
88 
89     private final TraceConfigs mTraceConfig = new TraceConfigs(
90             new TraceConfig(false, true, false),
91             new TraceConfig(false, true, false),
92             new TraceConfig(false, true, false),
93             new TraceConfig(false, true, false)
94     );
95 
96     private PerfettoProtoLogImpl mProtoLog;
97     private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
98     private File mFile;
99     private Runnable mCacheUpdater;
100 
101     private ProtoLogViewerConfigReader mReader;
102 
PerfettoProtoLogImplTest()103     public PerfettoProtoLogImplTest() throws IOException {
104     }
105 
106     @Before
setUp()107     public void setUp() throws Exception {
108         MockitoAnnotations.initMocks(this);
109         final Context testContext = getInstrumentation().getContext();
110         mFile = testContext.getFileStreamPath("tracing_test.dat");
111         //noinspection ResultOfMethodCallIgnored
112         mFile.delete();
113 
114         mViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
115                 .addGroups(
116                         Protolog.ProtoLogViewerConfig.Group.newBuilder()
117                                 .setId(1)
118                                 .setName(TestProtoLogGroup.TEST_GROUP.toString())
119                                 .setTag(TestProtoLogGroup.TEST_GROUP.getTag())
120                 ).addMessages(
121                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
122                                 .setMessageId(1)
123                                 .setMessage("My Test Debug Log Message %b")
124                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
125                                 .setGroupId(1)
126                 ).addMessages(
127                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
128                                 .setMessageId(2)
129                                 .setMessage("My Test Verbose Log Message %b")
130                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
131                                 .setGroupId(1)
132                 ).addMessages(
133                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
134                                 .setMessageId(3)
135                                 .setMessage("My Test Warn Log Message %b")
136                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN)
137                                 .setGroupId(1)
138                 ).addMessages(
139                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
140                                 .setMessageId(4)
141                                 .setMessage("My Test Error Log Message %b")
142                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR)
143                                 .setGroupId(1)
144                 ).addMessages(
145                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
146                                 .setMessageId(5)
147                                 .setMessage("My Test WTF Log Message %b")
148                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF)
149                                 .setGroupId(1)
150                 );
151 
152         ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
153                 ViewerConfigInputStreamProvider.class);
154         Mockito.when(viewerConfigInputStreamProvider.getInputStream())
155                 .thenAnswer(it -> new ProtoInputStream(mViewerConfigBuilder.build().toByteArray()));
156 
157         mCacheUpdater = () -> {};
158         mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
159         mProtoLog = new PerfettoProtoLogImpl(
160                 viewerConfigInputStreamProvider, mReader, new TreeMap<>(),
161                 () -> mCacheUpdater.run());
162     }
163 
164     @After
tearDown()165     public void tearDown() {
166         if (mFile != null) {
167             //noinspection ResultOfMethodCallIgnored
168             mFile.delete();
169         }
170         ProtoLogImpl.setSingleInstance(null);
171     }
172 
173     @Test
isEnabled_returnsFalseByDefault()174     public void isEnabled_returnsFalseByDefault() {
175         assertFalse(mProtoLog.isProtoEnabled());
176     }
177 
178     @Test
isEnabled_returnsTrueAfterStart()179     public void isEnabled_returnsTrueAfterStart() {
180         PerfettoTraceMonitor traceMonitor =
181                 PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
182         try {
183             traceMonitor.start();
184             assertTrue(mProtoLog.isProtoEnabled());
185         } finally {
186             traceMonitor.stop(mWriter);
187         }
188     }
189 
190     @Test
isEnabled_returnsFalseAfterStop()191     public void isEnabled_returnsFalseAfterStop() {
192         PerfettoTraceMonitor traceMonitor =
193                 PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
194         try {
195             traceMonitor.start();
196             assertTrue(mProtoLog.isProtoEnabled());
197         } finally {
198             traceMonitor.stop(mWriter);
199         }
200 
201         assertFalse(mProtoLog.isProtoEnabled());
202     }
203 
204     @Test
defaultMode()205     public void defaultMode() throws IOException {
206         PerfettoTraceMonitor traceMonitor =
207                 PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
208         try {
209             traceMonitor.start();
210             // Shouldn't be logging anything except WTF unless explicitly requested in the group
211             // override.
212             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
213                     LogDataType.BOOLEAN, null, new Object[]{true});
214             mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
215                     LogDataType.BOOLEAN, null, new Object[]{true});
216             mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
217                     LogDataType.BOOLEAN, null, new Object[]{true});
218             mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
219                     LogDataType.BOOLEAN, null, new Object[]{true});
220             mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
221                     LogDataType.BOOLEAN, null, new Object[]{true});
222         } finally {
223             traceMonitor.stop(mWriter);
224         }
225 
226         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
227         final ProtoLogTrace protolog = reader.readProtoLogTrace();
228 
229         Truth.assertThat(protolog.messages).hasSize(1);
230         Truth.assertThat(protolog.messages.getFirst().getLevel()).isEqualTo(LogLevel.WTF);
231     }
232 
233     @Test
respectsOverrideConfigs_defaultMode()234     public void respectsOverrideConfigs_defaultMode() throws IOException {
235         PerfettoTraceMonitor traceMonitor =
236                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
237                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
238                                 TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
239                         .build();
240         try {
241             traceMonitor.start();
242             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
243                     LogDataType.BOOLEAN, null, new Object[]{true});
244             mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
245                     LogDataType.BOOLEAN, null, new Object[]{true});
246             mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
247                     LogDataType.BOOLEAN, null, new Object[]{true});
248             mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
249                     LogDataType.BOOLEAN, null, new Object[]{true});
250             mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
251                     LogDataType.BOOLEAN, null, new Object[]{true});
252         } finally {
253             traceMonitor.stop(mWriter);
254         }
255 
256         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
257         final ProtoLogTrace protolog = reader.readProtoLogTrace();
258 
259         Truth.assertThat(protolog.messages).hasSize(5);
260         Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG);
261         Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE);
262         Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN);
263         Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR);
264         Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF);
265     }
266 
267     @Test
respectsOverrideConfigs_allEnabledMode()268     public void respectsOverrideConfigs_allEnabledMode() throws IOException {
269         PerfettoTraceMonitor traceMonitor =
270                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
271                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
272                                 TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)))
273                         .build();
274         try {
275             traceMonitor.start();
276             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
277                     LogDataType.BOOLEAN, null, new Object[]{true});
278             mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
279                     LogDataType.BOOLEAN, null, new Object[]{true});
280             mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
281                     LogDataType.BOOLEAN, null, new Object[]{true});
282             mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
283                     LogDataType.BOOLEAN, null, new Object[]{true});
284             mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
285                     LogDataType.BOOLEAN, null, new Object[]{true});
286         } finally {
287             traceMonitor.stop(mWriter);
288         }
289 
290         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
291         final ProtoLogTrace protolog = reader.readProtoLogTrace();
292 
293         Truth.assertThat(protolog.messages).hasSize(3);
294         Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.WARN);
295         Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.ERROR);
296         Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WTF);
297     }
298 
299     @Test
respectsAllEnabledMode()300     public void respectsAllEnabledMode() throws IOException {
301         PerfettoTraceMonitor traceMonitor =
302                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of())
303                         .build();
304         try {
305             traceMonitor.start();
306             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
307                     LogDataType.BOOLEAN, null, new Object[]{true});
308             mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
309                     LogDataType.BOOLEAN, null, new Object[]{true});
310             mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
311                     LogDataType.BOOLEAN, null, new Object[]{true});
312             mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
313                     LogDataType.BOOLEAN, null, new Object[]{true});
314             mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
315                     LogDataType.BOOLEAN, null, new Object[]{true});
316         } finally {
317             traceMonitor.stop(mWriter);
318         }
319 
320         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
321         final ProtoLogTrace protolog = reader.readProtoLogTrace();
322 
323         Truth.assertThat(protolog.messages).hasSize(5);
324         Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG);
325         Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE);
326         Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN);
327         Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR);
328         Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF);
329     }
330 
331     @Test
log_logcatEnabledExternalMessage()332     public void log_logcatEnabledExternalMessage() {
333         when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
334         PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
335         TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
336         TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
337 
338         implSpy.log(
339                 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
340                 new Object[]{true, 10000, 30000, "test", 0.000003});
341 
342         verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
343                 LogLevel.INFO),
344                 eq("test true 10000 % 0x7530 test 3.0E-6"));
345         verify(mReader).getViewerString(eq(1234L));
346     }
347 
348     @Test
log_logcatEnabledInvalidMessage()349     public void log_logcatEnabledInvalidMessage() {
350         when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
351         PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
352         TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
353         TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
354 
355         implSpy.log(
356                 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
357                 new Object[]{true, 10000, 0.0001, 0.00002, "test"});
358 
359         verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
360                 LogLevel.INFO),
361                 eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
362         verify(mReader).getViewerString(eq(1234L));
363     }
364 
365     @Test
log_logcatEnabledInlineMessage()366     public void log_logcatEnabledInlineMessage() {
367         when(mReader.getViewerString(anyLong())).thenReturn("test %d");
368         PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
369         TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
370         TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
371 
372         implSpy.log(
373                 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
374                 new Object[]{5});
375 
376         verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
377                 LogLevel.INFO), eq("test 5"));
378         verify(mReader, never()).getViewerString(anyLong());
379     }
380 
381     @Test
log_logcatEnabledNoMessage()382     public void log_logcatEnabledNoMessage() {
383         when(mReader.getViewerString(anyLong())).thenReturn(null);
384         PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
385         TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
386         TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
387 
388         implSpy.log(
389                 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
390                 new Object[]{5});
391 
392         verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
393                 LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
394         verify(mReader).getViewerString(eq(1234L));
395     }
396 
397     @Test
log_logcatDisabled()398     public void log_logcatDisabled() {
399         when(mReader.getViewerString(anyLong())).thenReturn("test %d");
400         PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
401         TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
402 
403         implSpy.log(
404                 LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
405                 new Object[]{5});
406 
407         verify(implSpy, never()).passToLogcat(any(), any(), any());
408         verify(mReader, never()).getViewerString(anyLong());
409     }
410 
411     @Test
log_protoEnabled()412     public void log_protoEnabled() throws Exception {
413         final long messageHash = addMessageToConfig(
414                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
415                 "My test message :: %s, %d, %o, %x, %f, %e, %g, %b");
416 
417         PerfettoTraceMonitor traceMonitor =
418                 PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
419         long before;
420         long after;
421         try {
422             traceMonitor.start();
423             assertTrue(mProtoLog.isProtoEnabled());
424 
425             before = SystemClock.elapsedRealtimeNanos();
426             mProtoLog.log(
427                     LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
428                     0b1110101001010100, null,
429                     new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
430             after = SystemClock.elapsedRealtimeNanos();
431         } finally {
432             traceMonitor.stop(mWriter);
433         }
434 
435         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
436         final ProtoLogTrace protolog = reader.readProtoLogTrace();
437 
438         Truth.assertThat(protolog.messages).hasSize(1);
439         Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
440                 .isAtLeast(before);
441         Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
442                 .isAtMost(after);
443         Truth.assertThat(protolog.messages.getFirst().getMessage())
444                 .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, 5.000000e-01, 0.6, true");
445     }
446 
addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message)447     private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
448         final long messageId = new Random().nextLong();
449         mViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
450                 .setMessageId(messageId)
451                 .setMessage(message)
452                 .setLevel(logLevel)
453                 .setGroupId(1)
454         );
455 
456         return messageId;
457     }
458 
459     @Test
log_invalidParamsMask()460     public void log_invalidParamsMask() {
461         final long messageHash = addMessageToConfig(
462                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
463                 "My test message :: %s, %d, %f, %b");
464         PerfettoTraceMonitor traceMonitor =
465                 PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
466         long before;
467         long after;
468         try {
469             traceMonitor.start();
470             before = SystemClock.elapsedRealtimeNanos();
471             mProtoLog.log(
472                     LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
473                     0b01100100, null,
474                     new Object[]{"test", 1, 0.1, true});
475             after = SystemClock.elapsedRealtimeNanos();
476         } finally {
477             traceMonitor.stop(mWriter);
478         }
479 
480         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
481         assertThrows(IllegalStateException.class, reader::readProtoLogTrace);
482     }
483 
484     @Test
log_protoDisabled()485     public void log_protoDisabled() throws Exception {
486         PerfettoTraceMonitor traceMonitor =
487                 PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
488         try {
489             traceMonitor.start();
490             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
491                     0b11, null, new Object[]{true});
492         } finally {
493             traceMonitor.stop(mWriter);
494         }
495 
496         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
497         final ProtoLogTrace protolog = reader.readProtoLogTrace();
498 
499         Truth.assertThat(protolog.messages).isEmpty();
500     }
501 
502     @Test
stackTraceTrimmed()503     public void stackTraceTrimmed() throws IOException {
504         PerfettoTraceMonitor traceMonitor =
505                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
506                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
507                                 TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
508                                 true)))
509                         .build();
510         try {
511             traceMonitor.start();
512 
513             ProtoLogImpl.setSingleInstance(mProtoLog);
514             ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1,
515                     0b11, null, true);
516         } finally {
517             traceMonitor.stop(mWriter);
518         }
519 
520         final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
521         final ProtoLogTrace protolog = reader.readProtoLogTrace();
522 
523         Truth.assertThat(protolog.messages).hasSize(1);
524         String stacktrace = protolog.messages.getFirst().getStacktrace();
525         Truth.assertThat(stacktrace)
526                 .doesNotContain(PerfettoProtoLogImpl.class.getSimpleName() + ".java");
527         Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java");
528         Truth.assertThat(stacktrace)
529                 .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java");
530         Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName());
531         Truth.assertThat(stacktrace).contains("stackTraceTrimmed");
532     }
533 
534     @Test
cacheIsUpdatedWhenTracesStartAndStop()535     public void cacheIsUpdatedWhenTracesStartAndStop() {
536         final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
537         mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
538 
539         PerfettoTraceMonitor traceMonitor1 =
540                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
541                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
542                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
543                                         false)))
544                         .build();
545 
546         PerfettoTraceMonitor traceMonitor2 =
547                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
548                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
549                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
550                                         false)))
551                         .build();
552 
553         Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0);
554 
555         try {
556             traceMonitor1.start();
557 
558             Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(1);
559 
560             try {
561                 traceMonitor2.start();
562 
563                 Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(2);
564             } finally {
565                 traceMonitor2.stop(mWriter);
566             }
567 
568             Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(3);
569 
570         } finally {
571             traceMonitor1.stop(mWriter);
572         }
573 
574         Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(4);
575     }
576 
577     @Test
isEnabledUpdatesBasedOnRunningTraces()578     public void isEnabledUpdatesBasedOnRunningTraces() {
579         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
580                 .isFalse();
581         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
582                 .isFalse();
583         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
584                 .isFalse();
585         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
586                 .isFalse();
587         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
588                 .isFalse();
589         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isTrue();
590 
591         PerfettoTraceMonitor traceMonitor1 =
592                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
593                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
594                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
595                                         false)))
596                         .build();
597 
598         PerfettoTraceMonitor traceMonitor2 =
599                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
600                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
601                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
602                                         false)))
603                         .build();
604 
605         try {
606             traceMonitor1.start();
607 
608             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
609                     .isFalse();
610             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
611                     .isFalse();
612             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
613                     .isFalse();
614             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
615                     .isTrue();
616             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
617                     .isTrue();
618             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
619                     .isTrue();
620 
621             try {
622                 traceMonitor2.start();
623 
624                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
625                         .isTrue();
626                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP,
627                         LogLevel.VERBOSE)).isTrue();
628                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
629                         .isTrue();
630                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
631                         .isTrue();
632                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
633                         .isTrue();
634                 Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
635                         .isTrue();
636             } finally {
637                 traceMonitor2.stop(mWriter);
638             }
639 
640             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
641                     .isFalse();
642             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
643                     .isFalse();
644             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
645                     .isFalse();
646             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
647                     .isTrue();
648             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
649                     .isTrue();
650             Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
651                     .isTrue();
652         } finally {
653             traceMonitor1.stop(mWriter);
654         }
655 
656         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
657                 .isFalse();
658         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
659                 .isFalse();
660         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
661                 .isFalse();
662         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
663                 .isFalse();
664         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
665                 .isFalse();
666         Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
667                 .isTrue();
668     }
669 
670     private enum TestProtoLogGroup implements IProtoLogGroup {
671         TEST_GROUP(true, true, false, "TEST_TAG");
672 
673         private final boolean mEnabled;
674         private volatile boolean mLogToProto;
675         private volatile boolean mLogToLogcat;
676         private final String mTag;
677 
678         /**
679          * @param enabled     set to false to exclude all log statements for this group from
680          *                    compilation,
681          *                    they will not be available in runtime.
682          * @param logToProto  enable binary logging for the group
683          * @param logToLogcat enable text logging for the group
684          * @param tag         name of the source of the logged message
685          */
TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag)686         TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
687             this.mEnabled = enabled;
688             this.mLogToProto = logToProto;
689             this.mLogToLogcat = logToLogcat;
690             this.mTag = tag;
691         }
692 
693         @Override
isEnabled()694         public boolean isEnabled() {
695             return mEnabled;
696         }
697 
698         @Override
isLogToProto()699         public boolean isLogToProto() {
700             return mLogToProto;
701         }
702 
703         @Override
isLogToLogcat()704         public boolean isLogToLogcat() {
705             return mLogToLogcat;
706         }
707 
708         @Override
isLogToAny()709         public boolean isLogToAny() {
710             return mLogToLogcat || mLogToProto;
711         }
712 
713         @Override
getTag()714         public String getTag() {
715             return mTag;
716         }
717 
718         @Override
setLogToProto(boolean logToProto)719         public void setLogToProto(boolean logToProto) {
720             this.mLogToProto = logToProto;
721         }
722 
723         @Override
setLogToLogcat(boolean logToLogcat)724         public void setLogToLogcat(boolean logToLogcat) {
725             this.mLogToLogcat = logToLogcat;
726         }
727 
728         @Override
getId()729         public int getId() {
730             return ordinal();
731         }
732 
733     }
734 }
735