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(5) 113 } 114 expect(2) 115 // Run choreographer detection 116 mainLooper.runOneTask() 117 expect(3) 118 mainLooper.scheduler.advanceBy(50, TimeUnit.MILLISECONDS) 119 expect(4) 120 mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS) 121 finish(6) 122 } 123 CoroutineScopenull124 private fun CoroutineScope.doTestAwaitWithDetectedChoreographer() { 125 ShadowChoreographer.setPostFrameCallbackDelay(100) 126 val mainLooper = Shadows.shadowOf(Looper.getMainLooper()) 127 launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) { 128 expect(1) 129 awaitFrame() 130 expect(4) 131 } 132 // Run choreographer detection 133 expect(2) 134 mainLooper.scheduler.advanceBy(50, TimeUnit.MILLISECONDS) 135 expect(3) 136 mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS) 137 finish(5) 138 } 139 } 140