blob: 79b84775431150176fb818aa025678f1cb77d08f [file] [log] [blame]
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +01001/*
2 * Copyright (C) 2018 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
17package com.android.internal.os;
18
19import static com.google.common.truth.Truth.assertThat;
20
21import android.os.Handler;
22import android.os.HandlerThread;
23import android.os.Looper;
Marcin Oczeretkoec758722018-09-12 12:53:47 +010024import android.os.Message;
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010025import android.platform.test.annotations.Presubmit;
Tadashi G. Takaokab4470f22019-01-15 18:29:15 +090026
27import androidx.test.filters.SmallTest;
28import androidx.test.runner.AndroidJUnit4;
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010029
30import org.junit.After;
31import org.junit.Assert;
32import org.junit.Before;
33import org.junit.Test;
34import org.junit.runner.RunWith;
35
36import java.util.Comparator;
37import java.util.List;
38
39@SmallTest
40@RunWith(AndroidJUnit4.class)
41@Presubmit
42public final class LooperStatsTest {
43 private HandlerThread mThreadFirst;
44 private HandlerThread mThreadSecond;
45 private Handler mHandlerFirst;
46 private Handler mHandlerSecond;
47 private Handler mHandlerAnonymous;
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +010048 private CachedDeviceState mDeviceState;
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010049
50 @Before
51 public void setUp() {
52 // The tests are all single-threaded. HandlerThreads are created to allow creating Handlers
53 // and to test Thread name collection.
54 mThreadFirst = new HandlerThread("TestThread1");
55 mThreadSecond = new HandlerThread("TestThread2");
56 mThreadFirst.start();
57 mThreadSecond.start();
58
59 mHandlerFirst = new TestHandlerFirst(mThreadFirst.getLooper());
60 mHandlerSecond = new TestHandlerSecond(mThreadSecond.getLooper());
61 mHandlerAnonymous = new Handler(mThreadFirst.getLooper()) {
62 /* To create an anonymous subclass. */
63 };
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +010064 mDeviceState = new CachedDeviceState();
65 mDeviceState.setCharging(false);
66 mDeviceState.setScreenInteractive(true);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010067 }
68
69 @After
70 public void tearDown() {
71 mThreadFirst.quit();
72 mThreadSecond.quit();
73 }
74
75 @Test
76 public void testSingleMessageDispatched() {
77 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
78
Marcin Oczeretkoec758722018-09-12 12:53:47 +010079 Message message = mHandlerFirst.obtainMessage(1000);
80 message.workSourceUid = 1000;
Marcin Oczeretko44272722018-09-19 11:01:32 +010081 message.when = looperStats.getSystemUptimeMillis();
82
83 looperStats.tickUptime(30);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010084 Object token = looperStats.messageDispatchStarting();
85 looperStats.tickRealtime(100);
86 looperStats.tickThreadTime(10);
Marcin Oczeretko44272722018-09-19 11:01:32 +010087 looperStats.tickUptime(200);
Marcin Oczeretkoec758722018-09-12 12:53:47 +010088 looperStats.messageDispatched(token, message);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010089
90 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
91 assertThat(entries).hasSize(1);
92 LooperStats.ExportedEntry entry = entries.get(0);
Marcin Oczeretkoec758722018-09-12 12:53:47 +010093 assertThat(entry.workSourceUid).isEqualTo(1000);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010094 assertThat(entry.threadName).isEqualTo("TestThread1");
95 assertThat(entry.handlerClassName).isEqualTo(
96 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
97 assertThat(entry.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +010098 assertThat(entry.isInteractive).isEqualTo(true);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +010099 assertThat(entry.messageCount).isEqualTo(1);
100 assertThat(entry.recordedMessageCount).isEqualTo(1);
101 assertThat(entry.exceptionCount).isEqualTo(0);
102 assertThat(entry.totalLatencyMicros).isEqualTo(100);
103 assertThat(entry.maxLatencyMicros).isEqualTo(100);
104 assertThat(entry.cpuUsageMicros).isEqualTo(10);
105 assertThat(entry.maxCpuUsageMicros).isEqualTo(10);
Marcin Oczeretko44272722018-09-19 11:01:32 +0100106 assertThat(entry.recordedDelayMessageCount).isEqualTo(1);
107 assertThat(entry.delayMillis).isEqualTo(30);
108 assertThat(entry.maxDelayMillis).isEqualTo(30);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100109 }
110
111 @Test
112 public void testThrewException() {
113 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
114
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100115 Message message = mHandlerFirst.obtainMessage(7);
116 message.workSourceUid = 123;
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100117 Object token = looperStats.messageDispatchStarting();
118 looperStats.tickRealtime(100);
119 looperStats.tickThreadTime(10);
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100120 looperStats.dispatchingThrewException(token, message, new ArithmeticException());
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100121
122 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
123 assertThat(entries).hasSize(1);
124 LooperStats.ExportedEntry entry = entries.get(0);
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100125 assertThat(entry.workSourceUid).isEqualTo(123);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100126 assertThat(entry.threadName).isEqualTo("TestThread1");
127 assertThat(entry.handlerClassName).isEqualTo(
128 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
129 assertThat(entry.messageName).isEqualTo("0x7" /* 7 in hex */);
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100130 assertThat(entry.isInteractive).isEqualTo(true);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100131 assertThat(entry.messageCount).isEqualTo(0);
132 assertThat(entry.recordedMessageCount).isEqualTo(0);
133 assertThat(entry.exceptionCount).isEqualTo(1);
134 assertThat(entry.totalLatencyMicros).isEqualTo(0);
135 assertThat(entry.maxLatencyMicros).isEqualTo(0);
136 assertThat(entry.cpuUsageMicros).isEqualTo(0);
137 assertThat(entry.maxCpuUsageMicros).isEqualTo(0);
138 }
139
140 @Test
Olivier Gaillard99ab9532019-02-04 10:47:48 +0000141 public void testThrewException_notSampled() {
142 TestableLooperStats looperStats = new TestableLooperStats(2, 100);
143
144 Object token = looperStats.messageDispatchStarting();
145 looperStats.tickRealtime(10);
146 looperStats.tickThreadTime(10);
147 looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(0));
148 assertThat(looperStats.getEntries()).hasSize(1);
149
150 // Will not be sampled so does not contribute to any entries.
151 Object token2 = looperStats.messageDispatchStarting();
152 looperStats.tickRealtime(100);
153 looperStats.tickThreadTime(10);
154 looperStats.dispatchingThrewException(
155 token2, mHandlerSecond.obtainMessage(7), new ArithmeticException());
156 assertThat(looperStats.getEntries()).hasSize(1);
157 assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(1);
158 }
159
160 @Test
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100161 public void testMultipleMessagesDispatched() {
162 TestableLooperStats looperStats = new TestableLooperStats(2, 100);
163
164 // Contributes to entry2.
165 Object token1 = looperStats.messageDispatchStarting();
166 looperStats.tickRealtime(100);
167 looperStats.tickThreadTime(10);
168 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
169
170 // Contributes to entry2.
171 Object token2 = looperStats.messageDispatchStarting();
172 looperStats.tickRealtime(50);
173 looperStats.tickThreadTime(20);
174 looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1000));
175
176 // Contributes to entry3.
177 Object token3 = looperStats.messageDispatchStarting();
178 looperStats.tickRealtime(10);
179 looperStats.tickThreadTime(10);
180 looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
181 }));
182
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100183 // Will not be sampled so does not contribute to any entries.
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100184 Object token4 = looperStats.messageDispatchStarting();
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100185 looperStats.tickRealtime(10);
186 looperStats.tickThreadTime(10);
187 looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(0));
188
189 // Contributes to entry1.
190 Object token5 = looperStats.messageDispatchStarting();
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100191 looperStats.tickRealtime(100);
192 looperStats.tickThreadTime(100);
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100193 looperStats.messageDispatched(token5, mHandlerAnonymous.obtainMessage(1));
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100194
195 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
196 assertThat(entries).hasSize(3);
197 entries.sort(Comparator.comparing(e -> e.handlerClassName));
198
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100199 // Captures data for token5 call.
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100200 LooperStats.ExportedEntry entry1 = entries.get(0);
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100201 assertThat(entry1.workSourceUid).isEqualTo(-1);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100202 assertThat(entry1.threadName).isEqualTo("TestThread1");
203 assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
204 assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
205 assertThat(entry1.messageCount).isEqualTo(1);
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100206 assertThat(entry1.recordedMessageCount).isEqualTo(1);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100207 assertThat(entry1.exceptionCount).isEqualTo(0);
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100208 assertThat(entry1.totalLatencyMicros).isEqualTo(100);
209 assertThat(entry1.maxLatencyMicros).isEqualTo(100);
210 assertThat(entry1.cpuUsageMicros).isEqualTo(100);
211 assertThat(entry1.maxCpuUsageMicros).isEqualTo(100);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100212
213 // Captures data for token1 and token2 calls.
214 LooperStats.ExportedEntry entry2 = entries.get(1);
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100215 assertThat(entry2.workSourceUid).isEqualTo(-1);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100216 assertThat(entry2.threadName).isEqualTo("TestThread1");
217 assertThat(entry2.handlerClassName).isEqualTo(
218 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
219 assertThat(entry2.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
220 assertThat(entry2.messageCount).isEqualTo(2);
221 assertThat(entry2.recordedMessageCount).isEqualTo(1);
222 assertThat(entry2.exceptionCount).isEqualTo(0);
223 assertThat(entry2.totalLatencyMicros).isEqualTo(100);
224 assertThat(entry2.maxLatencyMicros).isEqualTo(100);
225 assertThat(entry2.cpuUsageMicros).isEqualTo(10);
226 assertThat(entry2.maxCpuUsageMicros).isEqualTo(10);
227
228 // Captures data for token3 call.
229 LooperStats.ExportedEntry entry3 = entries.get(2);
Marcin Oczeretkoec758722018-09-12 12:53:47 +0100230 assertThat(entry3.workSourceUid).isEqualTo(-1);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100231 assertThat(entry3.threadName).isEqualTo("TestThread2");
232 assertThat(entry3.handlerClassName).isEqualTo(
233 "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
234 assertThat(entry3.messageName).startsWith(
235 "com.android.internal.os.-$$Lambda$LooperStatsTest$");
236 assertThat(entry3.messageCount).isEqualTo(1);
237 assertThat(entry3.recordedMessageCount).isEqualTo(1);
238 assertThat(entry3.exceptionCount).isEqualTo(0);
239 assertThat(entry3.totalLatencyMicros).isEqualTo(10);
240 assertThat(entry3.maxLatencyMicros).isEqualTo(10);
241 assertThat(entry3.cpuUsageMicros).isEqualTo(10);
242 assertThat(entry3.maxCpuUsageMicros).isEqualTo(10);
243 }
244
245 @Test
Marcin Oczeretko44272722018-09-19 11:01:32 +0100246 public void testDispatchDelayIsRecorded() {
247 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
248
249 // Dispatched right on time.
250 Message message1 = mHandlerFirst.obtainMessage(1000);
251 message1.when = looperStats.getSystemUptimeMillis();
252 Object token1 = looperStats.messageDispatchStarting();
253 looperStats.tickUptime(10);
254 looperStats.messageDispatched(token1, message1);
255
256 // Dispatched 100ms late.
257 Message message2 = mHandlerFirst.obtainMessage(1000);
258 message2.when = looperStats.getSystemUptimeMillis() - 100;
259 Object token2 = looperStats.messageDispatchStarting();
260 looperStats.tickUptime(10);
261 looperStats.messageDispatched(token2, message2);
262
263 // No target dispatching time.
264 Message message3 = mHandlerFirst.obtainMessage(1000);
265 message3.when = 0;
266 Object token3 = looperStats.messageDispatchStarting();
267 looperStats.tickUptime(10);
268 looperStats.messageDispatched(token3, message3);
269
270 // Dispatched too soon (should never happen).
271 Message message4 = mHandlerFirst.obtainMessage(1000);
272 message4.when = looperStats.getSystemUptimeMillis() + 200;
273 Object token4 = looperStats.messageDispatchStarting();
274 looperStats.tickUptime(10);
275 looperStats.messageDispatched(token4, message4);
276
277 // Dispatched 300ms late.
278 Message message5 = mHandlerFirst.obtainMessage(1000);
279 message5.when = looperStats.getSystemUptimeMillis() - 300;
280 Object token5 = looperStats.messageDispatchStarting();
281 looperStats.tickUptime(10);
282 looperStats.messageDispatched(token5, message5);
283
284 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
285 assertThat(entries).hasSize(1);
286
287 LooperStats.ExportedEntry entry = entries.get(0);
288 assertThat(entry.messageCount).isEqualTo(5);
289 assertThat(entry.recordedMessageCount).isEqualTo(5);
290 assertThat(entry.recordedDelayMessageCount).isEqualTo(4);
291 assertThat(entry.delayMillis).isEqualTo(400);
292 assertThat(entry.maxDelayMillis).isEqualTo(300);
293 }
294
295 @Test
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100296 public void testDataNotCollectedBeforeDeviceStateSet() {
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000297 TestableLooperStats looperStats = new TestableLooperStats(1, 100, null);
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100298
299 Object token1 = looperStats.messageDispatchStarting();
300 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
301 Object token2 = looperStats.messageDispatchStarting();
302 looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
303 new IllegalArgumentException());
304
305 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
306 assertThat(entries).hasSize(0);
307 }
308
309 @Test
310 public void testDataNotCollectedOnCharger() {
311 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
312 mDeviceState.setCharging(true);
313
314 Object token1 = looperStats.messageDispatchStarting();
315 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
316 Object token2 = looperStats.messageDispatchStarting();
317 looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
318 new IllegalArgumentException());
319
320 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
321 assertThat(entries).hasSize(0);
322 }
323
324 @Test
325 public void testScreenStateCollected() {
326 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
327
328 mDeviceState.setScreenInteractive(true);
329 Object token1 = looperStats.messageDispatchStarting();
330 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
331 Object token2 = looperStats.messageDispatchStarting();
332 looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
333 new IllegalArgumentException());
334
335 Object token3 = looperStats.messageDispatchStarting();
336 // If screen state changed during the call, we take the final state into account.
337 mDeviceState.setScreenInteractive(false);
338 looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1000));
339 Object token4 = looperStats.messageDispatchStarting();
340 looperStats.dispatchingThrewException(token4, mHandlerFirst.obtainMessage(1000),
341 new IllegalArgumentException());
342
343 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
344 assertThat(entries).hasSize(2);
345 entries.sort(Comparator.comparing(e -> e.isInteractive));
346
347 LooperStats.ExportedEntry entry1 = entries.get(0);
348 assertThat(entry1.isInteractive).isEqualTo(false);
349 assertThat(entry1.messageCount).isEqualTo(1);
350 assertThat(entry1.exceptionCount).isEqualTo(1);
351
352 LooperStats.ExportedEntry entry2 = entries.get(1);
353 assertThat(entry2.isInteractive).isEqualTo(true);
354 assertThat(entry2.messageCount).isEqualTo(1);
355 assertThat(entry2.exceptionCount).isEqualTo(1);
356 }
357
358 @Test
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100359 public void testMessagesOverSizeCap() {
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100360 TestableLooperStats looperStats = new TestableLooperStats(1, 1 /* sizeCap */);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100361
362 Object token1 = looperStats.messageDispatchStarting();
363 looperStats.tickRealtime(100);
364 looperStats.tickThreadTime(10);
365 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
366
367 Object token2 = looperStats.messageDispatchStarting();
368 looperStats.tickRealtime(50);
369 looperStats.tickThreadTime(20);
370 looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1001));
371
372 Object token3 = looperStats.messageDispatchStarting();
373 looperStats.tickRealtime(10);
374 looperStats.tickThreadTime(10);
375 looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1002));
376
377 Object token4 = looperStats.messageDispatchStarting();
378 looperStats.tickRealtime(10);
379 looperStats.tickThreadTime(10);
380 looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(1003));
381
382 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
383 assertThat(entries).hasSize(2);
384 entries.sort(Comparator.comparing(e -> e.handlerClassName));
385
386 LooperStats.ExportedEntry entry1 = entries.get(0);
387 assertThat(entry1.threadName).isEqualTo("");
388 assertThat(entry1.handlerClassName).isEqualTo("");
389 assertThat(entry1.messageName).isEqualTo("OVERFLOW");
390 assertThat(entry1.messageCount).isEqualTo(3);
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100391 assertThat(entry1.recordedMessageCount).isEqualTo(3);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100392 assertThat(entry1.exceptionCount).isEqualTo(0);
Marcin Oczeretkoa674bc92018-09-13 18:20:21 +0100393 assertThat(entry1.totalLatencyMicros).isEqualTo(70);
394 assertThat(entry1.maxLatencyMicros).isEqualTo(50);
395 assertThat(entry1.cpuUsageMicros).isEqualTo(40);
396 assertThat(entry1.maxCpuUsageMicros).isEqualTo(20);
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100397
398 LooperStats.ExportedEntry entry2 = entries.get(1);
399 assertThat(entry2.threadName).isEqualTo("TestThread1");
400 assertThat(entry2.handlerClassName).isEqualTo(
401 "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
402 }
403
404 @Test
405 public void testInvalidTokensCauseException() {
406 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
407 assertThrows(ClassCastException.class,
408 () -> looperStats.dispatchingThrewException(new Object(),
409 mHandlerFirst.obtainMessage(),
410 new ArithmeticException()));
411 assertThrows(ClassCastException.class,
412 () -> looperStats.messageDispatched(new Object(), mHandlerFirst.obtainMessage()));
413 assertThrows(ClassCastException.class,
414 () -> looperStats.messageDispatched(123, mHandlerFirst.obtainMessage()));
415 assertThrows(ClassCastException.class,
416 () -> looperStats.messageDispatched(mHandlerFirst.obtainMessage(),
417 mHandlerFirst.obtainMessage()));
418
419 assertThat(looperStats.getEntries()).hasSize(0);
420 }
421
422 @Test
423 public void testTracksMultipleHandlerInstancesIfSameClass() {
424 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
425 Handler handlerFirstAnother = new TestHandlerFirst(mHandlerFirst.getLooper());
426
427 Object token1 = looperStats.messageDispatchStarting();
428 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
429
430 Object token2 = looperStats.messageDispatchStarting();
431 looperStats.messageDispatched(token2, handlerFirstAnother.obtainMessage(1000));
432
433 assertThat(looperStats.getEntries()).hasSize(1);
434 assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
435 }
436
Marcin Oczeretko3e6494e2018-09-10 18:06:52 +0100437 @Test
438 public void testReset() {
439 TestableLooperStats looperStats = new TestableLooperStats(1, 1);
440
441 Object token1 = looperStats.messageDispatchStarting();
442 looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
443 Object token2 = looperStats.messageDispatchStarting();
444 looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(2000));
445 looperStats.reset();
446
447 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
448 assertThat(entries).hasSize(0);
449 }
450
Marcin Oczeretkod32a51352018-11-20 18:11:37 +0000451 @Test
452 public void testAddsDebugEntries() {
453 TestableLooperStats looperStats = new TestableLooperStats(1, 100);
454 looperStats.setAddDebugEntries(true);
455
456 Message message = mHandlerFirst.obtainMessage(1000);
457 message.when = looperStats.getSystemUptimeMillis();
458 Object token = looperStats.messageDispatchStarting();
459 looperStats.messageDispatched(token, message);
460
461 List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000462 assertThat(entries).hasSize(4);
Marcin Oczeretkod32a51352018-11-20 18:11:37 +0000463 LooperStats.ExportedEntry debugEntry1 = entries.get(1);
464 assertThat(debugEntry1.handlerClassName).isEqualTo("");
465 assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
Olivier Gaillard28109b52018-12-14 15:14:14 +0000466 assertThat(debugEntry1.totalLatencyMicros).isEqualTo(
467 looperStats.getStartElapsedTimeMillis());
Marcin Oczeretkod32a51352018-11-20 18:11:37 +0000468 LooperStats.ExportedEntry debugEntry2 = entries.get(2);
469 assertThat(debugEntry2.handlerClassName).isEqualTo("");
470 assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
Olivier Gaillard28109b52018-12-14 15:14:14 +0000471 assertThat(debugEntry2.totalLatencyMicros).isAtLeast(
472 looperStats.getStartElapsedTimeMillis());
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000473 LooperStats.ExportedEntry debugEntry3 = entries.get(3);
474 assertThat(debugEntry3.handlerClassName).isEqualTo("");
475 assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
Marcin Oczeretko8d861742018-12-06 11:13:29 +0000476 assertThat(debugEntry3.totalLatencyMicros).isAtLeast(0L);
Marcin Oczeretkod32a51352018-11-20 18:11:37 +0000477 }
478
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100479 private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
480 try {
481 r.run();
482 Assert.fail("Expected " + exceptionClass + " to be thrown.");
483 } catch (Exception exception) {
484 assertThat(exception).isInstanceOf(exceptionClass);
485 }
486 }
487
Marcin Oczeretkoc80c81a2018-08-30 20:15:52 +0100488 private final class TestableLooperStats extends LooperStats {
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100489 private static final long INITIAL_MICROS = 10001000123L;
490 private int mCount;
491 private long mRealtimeMicros;
492 private long mThreadTimeMicros;
Marcin Oczeretko44272722018-09-19 11:01:32 +0100493 private long mUptimeMillis;
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100494 private int mSamplingInterval;
495
496 TestableLooperStats(int samplingInterval, int sizeCap) {
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000497 this(samplingInterval, sizeCap, mDeviceState);
498 }
499
500 TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) {
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100501 super(samplingInterval, sizeCap);
Marcin Oczeretko6a2e5242018-11-28 11:08:50 +0000502 mSamplingInterval = samplingInterval;
503 setAddDebugEntries(false);
504 if (deviceState != null) {
505 setDeviceState(deviceState.getReadonlyClient());
506 }
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100507 }
508
509 void tickRealtime(long micros) {
510 mRealtimeMicros += micros;
511 }
512
513 void tickThreadTime(long micros) {
514 mThreadTimeMicros += micros;
515 }
516
Marcin Oczeretko44272722018-09-19 11:01:32 +0100517 void tickUptime(long millis) {
518 mUptimeMillis += millis;
519 }
520
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100521 @Override
522 protected long getElapsedRealtimeMicro() {
523 return INITIAL_MICROS + mRealtimeMicros;
524 }
525
526 @Override
527 protected long getThreadTimeMicro() {
528 return INITIAL_MICROS + mThreadTimeMicros;
529 }
530
531 @Override
Marcin Oczeretko44272722018-09-19 11:01:32 +0100532 protected long getSystemUptimeMillis() {
533 return INITIAL_MICROS / 1000 + mUptimeMillis;
534 }
535
536 @Override
Marcin Oczeretkod8cc8592018-08-22 16:07:36 +0100537 protected boolean shouldCollectDetailedData() {
538 return mCount++ % mSamplingInterval == 0;
539 }
540 }
541
542 private static final class TestHandlerFirst extends Handler {
543 TestHandlerFirst(Looper looper) {
544 super(looper);
545 }
546 }
547
548 private static final class TestHandlerSecond extends Handler {
549 TestHandlerSecond(Looper looper) {
550 super(looper);
551 }
552 }
553}