1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.os.Process;
8 import android.support.test.filters.SmallTest;
9 
10 import org.junit.Assert;
11 import org.junit.Before;
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 
15 import org.chromium.base.EarlyTraceEvent.AsyncEvent;
16 import org.chromium.base.EarlyTraceEvent.Event;
17 import org.chromium.base.library_loader.LibraryLoader;
18 import org.chromium.base.test.BaseJUnit4ClassRunner;
19 import org.chromium.base.test.util.Feature;
20 
21 import java.util.ArrayList;
22 import java.util.List;
23 
24 /**
25  * Tests for {@link EarlyTraceEvent}.
26  *
27  * TODO(lizeb): Move to roboelectric tests.
28  */
29 @RunWith(BaseJUnit4ClassRunner.class)
30 public class EarlyTraceEventTest {
31     private static final String EVENT_NAME = "MyEvent";
32     private static final String EVENT_NAME2 = "MyOtherEvent";
33     private static final long EVENT_ID = 1;
34     private static final long EVENT_ID2 = 2;
35 
getMatchingCompletedEventCount(String eventName)36     int getMatchingCompletedEventCount(String eventName) {
37         int count = 0;
38         for (Event evt : EarlyTraceEvent.sCompletedEvents) {
39             if (evt.mName.equals(eventName)) {
40                 count++;
41             }
42         }
43         return count;
44     }
45 
getMatchingCompletedEvent(int idx, String eventName)46     Event getMatchingCompletedEvent(int idx, String eventName) {
47         int currIdx = idx;
48         for (Event evt : EarlyTraceEvent.sCompletedEvents) {
49             if (currIdx == 0 && evt.mName.equals(eventName)) {
50                 return evt;
51             }
52             currIdx--;
53         }
54         Assert.fail("No event " + eventName + " at index " + idx);
55         return null;
56     }
57 
pendingEventsContain(String eventName)58     boolean pendingEventsContain(String eventName) {
59         return EarlyTraceEvent.sPendingEventByKey.containsKey(
60                 EarlyTraceEvent.makeEventKeyForCurrentThread(eventName));
61     }
62 
63     @Before
setUp()64     public void setUp() {
65         LibraryLoader.getInstance().ensureInitialized();
66         EarlyTraceEvent.resetForTesting();
67     }
68 
69     @Test
70     @SmallTest
71     @Feature({"Android-AppBase"})
testCanRecordEvent()72     public void testCanRecordEvent() {
73         EarlyTraceEvent.enable();
74         long myThreadId = Process.myTid();
75         long beforeNanos = Event.elapsedRealtimeNanos();
76         EarlyTraceEvent.begin(EVENT_NAME);
77         EarlyTraceEvent.end(EVENT_NAME);
78         long afterNanos = Event.elapsedRealtimeNanos();
79 
80         Assert.assertEquals(1, getMatchingCompletedEventCount(EVENT_NAME));
81         Assert.assertFalse(pendingEventsContain(EVENT_NAME));
82         Event event = getMatchingCompletedEvent(0, EVENT_NAME);
83         Assert.assertEquals(EVENT_NAME, event.mName);
84         Assert.assertEquals(myThreadId, event.mThreadId);
85         Assert.assertTrue(
86                 beforeNanos <= event.mBeginTimeNanos && event.mBeginTimeNanos <= afterNanos);
87         Assert.assertTrue(event.mBeginTimeNanos <= event.mEndTimeNanos);
88         Assert.assertTrue(beforeNanos <= event.mEndTimeNanos && event.mEndTimeNanos <= afterNanos);
89     }
90 
91     @Test
92     @SmallTest
93     @Feature({"Android-AppBase"})
testCanRecordAsyncEvent()94     public void testCanRecordAsyncEvent() {
95         EarlyTraceEvent.enable();
96         long beforeNanos = Event.elapsedRealtimeNanos();
97         EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID);
98         EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID);
99         long afterNanos = Event.elapsedRealtimeNanos();
100 
101         List<AsyncEvent> matchingEvents = new ArrayList<AsyncEvent>();
102         for (AsyncEvent evt : EarlyTraceEvent.sAsyncEvents) {
103             if (evt.mName.equals(EVENT_NAME)) {
104                 matchingEvents.add(evt);
105             }
106         }
107         Assert.assertEquals(2, matchingEvents.size());
108         Assert.assertFalse(pendingEventsContain(EVENT_NAME));
109         AsyncEvent eventStart = matchingEvents.get(0);
110         AsyncEvent eventEnd = matchingEvents.get(1);
111         Assert.assertEquals(EVENT_NAME, eventStart.mName);
112         Assert.assertEquals(EVENT_ID, eventStart.mId);
113         Assert.assertEquals(EVENT_NAME, eventEnd.mName);
114         Assert.assertEquals(EVENT_ID, eventEnd.mId);
115         Assert.assertTrue(beforeNanos <= eventStart.mTimestampNanos
116                 && eventEnd.mTimestampNanos <= afterNanos);
117         Assert.assertTrue(eventStart.mTimestampNanos <= eventEnd.mTimestampNanos);
118     }
119 
120     @Test
121     @SmallTest
122     @Feature({"Android-AppBase"})
testRecordAsyncFinishEventWhenFinishing()123     public void testRecordAsyncFinishEventWhenFinishing() {
124         EarlyTraceEvent.enable();
125         EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID);
126         EarlyTraceEvent.disable();
127 
128         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState);
129         for (AsyncEvent evt : EarlyTraceEvent.sAsyncEvents) {
130             Assert.assertNotEquals(EVENT_NAME, evt.mName);
131         }
132         int pendingCount = 0;
133         for (String name : EarlyTraceEvent.sPendingAsyncEvents) {
134             if (name.equals(EVENT_NAME)) {
135                 ++pendingCount;
136             }
137         }
138         Assert.assertEquals(1, pendingCount);
139         EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID);
140         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState);
141     }
142 
143     @Test
144     @SmallTest
145     @Feature({"Android-AppBase"})
testCanRecordEventUsingTryWith()146     public void testCanRecordEventUsingTryWith() {
147         EarlyTraceEvent.enable();
148         long myThreadId = Process.myTid();
149         long beforeNanos = Event.elapsedRealtimeNanos();
150         try (TraceEvent e = TraceEvent.scoped(EVENT_NAME)) {
151             // Required comment to pass presubmit checks.
152         }
153         long afterNanos = Event.elapsedRealtimeNanos();
154 
155         Assert.assertEquals(1, getMatchingCompletedEventCount(EVENT_NAME));
156         Assert.assertFalse(pendingEventsContain(EVENT_NAME));
157         Event event = getMatchingCompletedEvent(0, EVENT_NAME);
158         Assert.assertEquals(EVENT_NAME, event.mName);
159         Assert.assertEquals(myThreadId, event.mThreadId);
160         Assert.assertTrue(
161                 beforeNanos <= event.mBeginTimeNanos && event.mBeginTimeNanos <= afterNanos);
162         Assert.assertTrue(event.mBeginTimeNanos <= event.mEndTimeNanos);
163         Assert.assertTrue(beforeNanos <= event.mEndTimeNanos && event.mEndTimeNanos <= afterNanos);
164     }
165 
166     @Test
167     @SmallTest
168     @Feature({"Android-AppBase"})
testIncompleteEvent()169     public void testIncompleteEvent() {
170         EarlyTraceEvent.enable();
171         EarlyTraceEvent.begin(EVENT_NAME);
172 
173         Assert.assertEquals(0, getMatchingCompletedEventCount(EVENT_NAME));
174         Assert.assertTrue(pendingEventsContain(EVENT_NAME));
175         EarlyTraceEvent.Event event = EarlyTraceEvent.sPendingEventByKey.get(
176                 EarlyTraceEvent.makeEventKeyForCurrentThread(EVENT_NAME));
177         Assert.assertEquals(EVENT_NAME, event.mName);
178     }
179 
180     @Test
181     @SmallTest
182     @Feature({"Android-AppBase"})
testNoDuplicatePendingEventsFromSameThread()183     public void testNoDuplicatePendingEventsFromSameThread() {
184         EarlyTraceEvent.enable();
185         EarlyTraceEvent.begin(EVENT_NAME);
186         try {
187             EarlyTraceEvent.begin(EVENT_NAME);
188         } catch (IllegalArgumentException e) {
189             // Expected.
190             return;
191         }
192         Assert.fail();
193     }
194 
195     @Test
196     @SmallTest
197     @Feature({"Android-AppBase"})
testDuplicatePendingEventsFromDifferentThreads()198     public void testDuplicatePendingEventsFromDifferentThreads() throws Exception {
199         EarlyTraceEvent.enable();
200 
201         Thread otherThread = new Thread(() -> { EarlyTraceEvent.begin(EVENT_NAME); });
202         otherThread.start();
203         otherThread.join();
204 
205         // At this point we have a pending event with EVENT_NAME name. But events are per
206         // thread, so we should be able to start EVENT_NAME event in a different thread.
207         EarlyTraceEvent.begin(EVENT_NAME);
208     }
209 
210     @Test
211     @SmallTest
212     @Feature({"Android-AppBase"})
testIgnoreEventsWhenDisabled()213     public void testIgnoreEventsWhenDisabled() {
214         EarlyTraceEvent.begin(EVENT_NAME);
215         EarlyTraceEvent.end(EVENT_NAME);
216         try (TraceEvent e = TraceEvent.scoped(EVENT_NAME2)) {
217             // Required comment to pass presubmit checks.
218         }
219         Assert.assertNull(EarlyTraceEvent.sCompletedEvents);
220     }
221 
222     @Test
223     @SmallTest
224     @Feature({"Android-AppBase"})
testIgnoreAsyncEventsWhenDisabled()225     public void testIgnoreAsyncEventsWhenDisabled() {
226         EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID);
227         EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID);
228         Assert.assertNull(EarlyTraceEvent.sAsyncEvents);
229     }
230 
231     @Test
232     @SmallTest
233     @Feature({"Android-AppBase"})
testIgnoreNewEventsWhenFinishing()234     public void testIgnoreNewEventsWhenFinishing() {
235         EarlyTraceEvent.enable();
236         EarlyTraceEvent.begin(EVENT_NAME);
237         EarlyTraceEvent.disable();
238 
239         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState);
240         EarlyTraceEvent.begin(EVENT_NAME2);
241         EarlyTraceEvent.end(EVENT_NAME2);
242 
243         Assert.assertTrue(pendingEventsContain(EVENT_NAME));
244         Assert.assertFalse(pendingEventsContain(EVENT_NAME2));
245         Assert.assertEquals(0, getMatchingCompletedEventCount(EVENT_NAME));
246         Assert.assertEquals(0, getMatchingCompletedEventCount(EVENT_NAME2));
247     }
248 
249     @Test
250     @SmallTest
251     @Feature({"Android-AppBase"})
testIgnoreNewAsyncEventsWhenFinishing()252     public void testIgnoreNewAsyncEventsWhenFinishing() {
253         EarlyTraceEvent.enable();
254         EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID);
255         EarlyTraceEvent.disable();
256 
257         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState);
258         EarlyTraceEvent.startAsync(EVENT_NAME2, EVENT_ID2);
259 
260         int pendingCount = 0;
261         for (String name : EarlyTraceEvent.sPendingAsyncEvents) {
262             if (name.equals(EVENT_NAME)) {
263                 ++pendingCount;
264             }
265         }
266         Assert.assertEquals(1, pendingCount);
267 
268         for (AsyncEvent evt : EarlyTraceEvent.sAsyncEvents) {
269             Assert.assertNotEquals(EVENT_NAME, evt.mName);
270         }
271     }
272 
273     @Test
274     @SmallTest
275     @Feature({"Android-AppBase"})
testFinishingToFinished()276     public void testFinishingToFinished() {
277         EarlyTraceEvent.enable();
278         EarlyTraceEvent.begin(EVENT_NAME);
279         EarlyTraceEvent.disable();
280 
281         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState);
282         EarlyTraceEvent.begin(EVENT_NAME2);
283         EarlyTraceEvent.end(EVENT_NAME2);
284         EarlyTraceEvent.end(EVENT_NAME);
285 
286         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState);
287     }
288 
289     @Test
290     @SmallTest
291     @Feature({"Android-AppBase"})
testCannotBeReenabledOnceFinished()292     public void testCannotBeReenabledOnceFinished() {
293         EarlyTraceEvent.enable();
294         EarlyTraceEvent.begin(EVENT_NAME);
295         EarlyTraceEvent.end(EVENT_NAME);
296         EarlyTraceEvent.disable();
297         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState);
298 
299         EarlyTraceEvent.enable();
300         Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState);
301     }
302 
303     @Test
304     @SmallTest
305     @Feature({"Android-AppBase"})
testThreadIdIsRecorded()306     public void testThreadIdIsRecorded() throws Exception {
307         EarlyTraceEvent.enable();
308         final long[] threadId = {0};
309 
310         Thread thread = new Thread() {
311             @Override
312             public void run() {
313                 TraceEvent.begin(EVENT_NAME);
314                 threadId[0] = Process.myTid();
315                 TraceEvent.end(EVENT_NAME);
316             }
317         };
318         thread.start();
319         thread.join();
320 
321         Assert.assertEquals(1, getMatchingCompletedEventCount(EVENT_NAME));
322         EarlyTraceEvent.Event event = getMatchingCompletedEvent(0, EVENT_NAME);
323         Assert.assertEquals(threadId[0], event.mThreadId);
324     }
325 
326     @Test
327     @SmallTest
328     @Feature({"Android-AppBase"})
testEnableAtStartup()329     public void testEnableAtStartup() {
330         ThreadUtils.setThreadAssertsDisabledForTesting(true);
331         EarlyTraceEvent.maybeEnable();
332         Assert.assertFalse(EarlyTraceEvent.enabled());
333         EarlyTraceEvent.setBackgroundStartupTracingFlag(false);
334         Assert.assertFalse(EarlyTraceEvent.enabled());
335 
336         EarlyTraceEvent.setBackgroundStartupTracingFlag(true);
337         EarlyTraceEvent.maybeEnable();
338         Assert.assertTrue(EarlyTraceEvent.getBackgroundStartupTracingFlag());
339         Assert.assertTrue(EarlyTraceEvent.enabled());
340         EarlyTraceEvent.disable();
341         EarlyTraceEvent.setBackgroundStartupTracingFlag(false);
342     }
343 
344     @Test
345     @SmallTest
346     @Feature({"Android-AppBase"})
testUserOverrideBackgroundTracing()347     public void testUserOverrideBackgroundTracing() {
348         ThreadUtils.setThreadAssertsDisabledForTesting(true);
349         // Setting command line should disable the background tracing flag.
350         CommandLine.getInstance().appendSwitch("trace-startup");
351         EarlyTraceEvent.setBackgroundStartupTracingFlag(true);
352         EarlyTraceEvent.maybeEnable();
353         Assert.assertFalse(EarlyTraceEvent.getBackgroundStartupTracingFlag());
354         Assert.assertTrue(EarlyTraceEvent.enabled());
355         EarlyTraceEvent.disable();
356         EarlyTraceEvent.setBackgroundStartupTracingFlag(false);
357     }
358 }
359