1 /* 2 * Copyright 2016-2022 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.annotation.* 13 import org.robolectric.shadows.* 14 import java.util.concurrent.* 15 import kotlin.test.* 16 17 @RunWith(RobolectricTestRunner::class) 18 @Config(manifest = Config.NONE, sdk = [28]) 19 @LooperMode(LooperMode.Mode.LEGACY) 20 class HandlerDispatcherTest : TestBase() { 21 @Test <lambda>null22 fun testImmediateDispatcherYield() = runBlocking(Dispatchers.Main) { 23 expect(1) 24 // launch in the immediate dispatcher 25 launch(Dispatchers.Main.immediate) { 26 expect(2) 27 yield() 28 expect(4) 29 } 30 expect(3) // after yield 31 yield() // yield back 32 finish(5) 33 } 34 35 @Test testMainDispatcherToStringnull36 fun testMainDispatcherToString() { 37 assertEquals("Dispatchers.Main", Dispatchers.Main.toString()) 38 assertEquals("Dispatchers.Main.immediate", Dispatchers.Main.immediate.toString()) 39 } 40 41 @Test <lambda>null42 fun testDefaultDelayIsNotDelegatedToMain() = runTest { 43 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 44 mainLooper.pause() 45 assertFalse { mainLooper.scheduler.areAnyRunnable() } 46 47 val job = launch(Dispatchers.Default, start = CoroutineStart.UNDISPATCHED) { 48 expect(1) 49 delay(Long.MAX_VALUE) 50 expectUnreached() 51 } 52 expect(2) 53 assertEquals(0, mainLooper.scheduler.size()) 54 job.cancelAndJoin() 55 finish(3) 56 } 57 58 @Test <lambda>null59 fun testWithTimeoutIsDelegatedToMain() = runTest { 60 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 61 mainLooper.pause() 62 assertFalse { mainLooper.scheduler.areAnyRunnable() } 63 val job = launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) { 64 withTimeout(1) { 65 expect(1) 66 hang { expect(3) } 67 } 68 expectUnreached() 69 } 70 expect(2) 71 assertEquals(1, mainLooper.scheduler.size()) 72 // Schedule cancellation 73 mainLooper.runToEndOfTasks() 74 job.join() 75 finish(4) 76 } 77 78 @Test <lambda>null79 fun testDelayDelegatedToMain() = runTest { 80 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 81 mainLooper.pause() 82 val job = launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) { 83 expect(1) 84 delay(1) 85 expect(3) 86 } 87 expect(2) 88 assertEquals(1, mainLooper.scheduler.size()) 89 // Schedule cancellation 90 mainLooper.runToEndOfTasks() 91 job.join() 92 finish(4) 93 } 94 95 @Test <lambda>null96 fun testAwaitFrame() = runTest { 97 doTestAwaitFrame() 98 99 reset() 100 101 // Now the second test: we cannot test it separately because we're caching choreographer in HandlerDispatcher 102 doTestAwaitWithDetectedChoreographer() 103 } 104 CoroutineScopenull105 private fun CoroutineScope.doTestAwaitFrame() { 106 ShadowChoreographer.setPostFrameCallbackDelay(100) 107 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 108 mainLooper.pause() 109 launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) { 110 expect(1) 111 awaitFrame() 112 expect(3) 113 } 114 expect(2) 115 // Run choreographer detection 116 mainLooper.runOneTask() 117 finish(4) 118 } 119 CoroutineScopenull120 private fun CoroutineScope.doTestAwaitWithDetectedChoreographer() { 121 ShadowChoreographer.setPostFrameCallbackDelay(100) 122 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 123 launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) { 124 expect(1) 125 awaitFrame() 126 expect(4) 127 } 128 // Run choreographer detection 129 expect(2) 130 mainLooper.scheduler.advanceBy(50, TimeUnit.MILLISECONDS) 131 expect(3) 132 mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS) 133 finish(5) 134 } 135 } 136