1 /*
<lambda>null2  * 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 
17 package com.android.testutils
18 
19 import android.os.Handler
20 import android.os.HandlerThread
21 import com.android.testutils.FunctionalUtils.ThrowingSupplier
22 import kotlin.test.assertEquals
23 import kotlin.test.assertFailsWith
24 import kotlin.test.assertNull
25 import org.junit.Test
26 import org.junit.runner.RunWith
27 import org.junit.runners.JUnit4
28 
29 private const val ATTEMPTS = 50 // Causes testWaitForIdle to take about 150ms on aosp_crosshatch-eng
30 private const val TIMEOUT_MS = 1000
31 
32 @RunWith(JUnit4::class)
33 class HandlerUtilsTest {
34     @Test
35     fun testWaitForIdle() {
36         val handlerThread = HandlerThread("testHandler").apply { start() }
37 
38         // Tests that waitForIdle can be called many times without ill impact if the service is
39         // already idle.
40         repeat(ATTEMPTS) {
41             handlerThread.waitForIdle(TIMEOUT_MS)
42         }
43 
44         // Tests that calling waitForIdle waits for messages to be processed. Use both an
45         // inline runnable that's instantiated at each loop run and a runnable that's instantiated
46         // once for all.
47         val tempRunnable = object : Runnable {
48             // Use StringBuilder preferentially to StringBuffer because StringBuilder is NOT
49             // thread-safe. It's part of the point that both runnables run on the same thread
50             // so if anything is wrong in that space it's better to opportunistically use a class
51             // where things might go wrong, even if there is no guarantee of failure.
52             var memory = StringBuilder()
53             override fun run() {
54                 memory.append("b")
55             }
56         }
57         repeat(ATTEMPTS) { i ->
58             handlerThread.threadHandler.post { tempRunnable.memory.append("a"); }
59             handlerThread.threadHandler.post(tempRunnable)
60             handlerThread.waitForIdle(TIMEOUT_MS)
61             assertEquals(tempRunnable.memory.toString(), "ab".repeat(i + 1))
62         }
63     }
64 
65     // Statistical test : even if visibleOnHandlerThread doesn't work this is likely to succeed,
66     // but it will be at least flaky.
67     @Test
68     fun testVisibleOnHandlerThread() {
69         val handlerThread = HandlerThread("testHandler").apply { start() }
70         val handler = Handler(handlerThread.looper)
71 
72         repeat(ATTEMPTS) { attempt ->
73             var x = -10
74             var y = -11
75             y = visibleOnHandlerThread(handler, ThrowingSupplier<Int> { x = attempt; attempt })
76             assertEquals(attempt, x)
77             assertEquals(attempt, y)
78             handler.post { assertEquals(attempt, x) }
79         }
80 
81         assertFailsWith<IllegalArgumentException> {
82             visibleOnHandlerThread(handler) { throw IllegalArgumentException() }
83         }
84 
85         // Null values may be returned by the supplier
86         assertNull(visibleOnHandlerThread(handler, ThrowingSupplier<Nothing?> { null }))
87     }
88 }
89