• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.android
6 
7 import android.os.*
8 import kotlinx.coroutines.*
9 import org.junit.Test
10 import org.junit.runner.*
11 import org.robolectric.*
12 import org.robolectric.Shadows.*
13 import org.robolectric.annotation.*
14 import org.robolectric.shadows.*
15 import org.robolectric.util.*
16 import java.util.concurrent.*
17 import kotlin.test.*
18 
19 @RunWith(RobolectricTestRunner::class)
20 @Config(manifest = Config.NONE, sdk = [28])
21 @LooperMode(LooperMode.Mode.LEGACY)
22 class HandlerDispatcherAsyncTest : TestBase() {
23 
24     /**
25      * Because [Dispatchers.Main] is a singleton, we cannot vary its initialization behavior. As a
26      * result we only test its behavior on the newest API level and assert that it uses async
27      * messages. We rely on the other tests to exercise the variance of the mechanism that the main
28      * dispatcher uses to ensure it has correct behavior on all API levels.
29      */
30     @Test
<lambda>null31     fun mainIsAsync() = runTest {
32         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 28)
33 
34         val mainLooper = shadowOf(Looper.getMainLooper())
35         mainLooper.pause()
36         val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)
37 
38         val job = launch(Dispatchers.Main) {
39             expect(2)
40         }
41 
42         val message = mainMessageQueue.head
43         assertTrue(message.isAsynchronous)
44         job.join(mainLooper)
45     }
46 
47     @Test
<lambda>null48     fun asyncMessagesApi14() = runTest {
49         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 14)
50 
51         val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher()
52 
53         val mainLooper = shadowOf(Looper.getMainLooper())
54         mainLooper.pause()
55         val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)
56 
57         val job = launch(main) {
58             expect(2)
59         }
60 
61         val message = mainMessageQueue.head
62         assertFalse(message.isAsynchronous)
63         job.join(mainLooper)
64     }
65 
66     @Test
<lambda>null67     fun asyncMessagesApi16() = runTest {
68         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 16)
69 
70         val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher()
71 
72         val mainLooper = shadowOf(Looper.getMainLooper())
73         mainLooper.pause()
74         val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)
75 
76         val job = launch(main) {
77             expect(2)
78         }
79 
80         val message = mainMessageQueue.head
81         assertTrue(message.isAsynchronous)
82         job.join(mainLooper)
83     }
84 
85     @Test
<lambda>null86     fun asyncMessagesApi28() = runTest {
87         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 28)
88 
89         val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher()
90 
91         val mainLooper = shadowOf(Looper.getMainLooper())
92         mainLooper.pause()
93         val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)
94 
95         val job = launch(main) {
96             expect(2)
97         }
98 
99         val message = mainMessageQueue.head
100         assertTrue(message.isAsynchronous)
101         job.join(mainLooper)
102     }
103 
104     @Test
<lambda>null105     fun noAsyncMessagesIfNotRequested() = runTest {
106         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 28)
107 
108         val main = Looper.getMainLooper().asHandler(async = false).asCoroutineDispatcher()
109 
110         val mainLooper = shadowOf(Looper.getMainLooper())
111         mainLooper.pause()
112         val mainMessageQueue = shadowOf(Looper.getMainLooper().queue)
113 
114         val job = launch(main) {
115             expect(2)
116         }
117 
118         val message = mainMessageQueue.head
119         assertFalse(message.isAsynchronous)
120         job.join(mainLooper)
121     }
122 
123     @Test
testToStringnull124     fun testToString() {
125         ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 28)
126         val main = Looper.getMainLooper().asHandler(async = true).asCoroutineDispatcher("testName")
127         assertEquals("testName", main.toString())
128         assertEquals("testName.immediate", main.immediate.toString())
129         assertEquals("testName.immediate", main.immediate.immediate.toString())
130     }
131 
joinnull132     private suspend fun Job.join(mainLooper: ShadowLooper) {
133         expect(1)
134         mainLooper.unPause()
135         join()
136         finish(3)
137     }
138 
139     // TODO compile against API 23+ so this can be invoked without reflection.
140     private val Looper.queue: MessageQueue
141         get() = Looper::class.java.getDeclaredMethod("getQueue").invoke(this) as MessageQueue
142 
143     // TODO compile against API 22+ so this can be invoked without reflection.
144     private val Message.isAsynchronous: Boolean
145         get() = Message::class.java.getDeclaredMethod("isAsynchronous").invoke(this) as Boolean
146 }
147