blob: fe97ae8d2775d370195eef20a5b8bad512b282f7 [file] [log] [blame]
/*
* Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.android
import android.os.*
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.runner.*
import org.robolectric.*
import org.robolectric.annotation.*
import org.robolectric.shadows.*
import java.util.concurrent.*
import kotlin.test.*
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, sdk = [28])
@LooperMode(LooperMode.Mode.LEGACY)
class HandlerDispatcherTest : TestBase() {
@Test
fun testImmediateDispatcherYield() = runBlocking(Dispatchers.Main) {
expect(1)
// launch in the immediate dispatcher
launch(Dispatchers.Main.immediate) {
expect(2)
yield()
expect(4)
}
expect(3) // after yield
yield() // yield back
finish(5)
}
@Test
fun testMainDispatcherToString() {
assertEquals("Dispatchers.Main", Dispatchers.Main.toString())
assertEquals("Dispatchers.Main.immediate", Dispatchers.Main.immediate.toString())
}
@Test
fun testDefaultDelayIsNotDelegatedToMain() = runTest {
val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
mainLooper.pause()
assertFalse { mainLooper.scheduler.areAnyRunnable() }
val job = launch(Dispatchers.Default, start = CoroutineStart.UNDISPATCHED) {
expect(1)
delay(Long.MAX_VALUE)
expectUnreached()
}
expect(2)
assertEquals(0, mainLooper.scheduler.size())
job.cancelAndJoin()
finish(3)
}
@Test
fun testWithTimeoutIsDelegatedToMain() = runTest {
val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
mainLooper.pause()
assertFalse { mainLooper.scheduler.areAnyRunnable() }
val job = launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) {
withTimeout(1) {
expect(1)
hang { expect(3) }
}
expectUnreached()
}
expect(2)
assertEquals(1, mainLooper.scheduler.size())
// Schedule cancellation
mainLooper.runToEndOfTasks()
job.join()
finish(4)
}
@Test
fun testDelayDelegatedToMain() = runTest {
val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
mainLooper.pause()
val job = launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) {
expect(1)
delay(1)
expect(3)
}
expect(2)
assertEquals(1, mainLooper.scheduler.size())
// Schedule cancellation
mainLooper.runToEndOfTasks()
job.join()
finish(4)
}
@Test
fun testAwaitFrame() = runTest {
doTestAwaitFrame()
reset()
// Now the second test: we cannot test it separately because we're caching choreographer in HandlerDispatcher
doTestAwaitWithDetectedChoreographer()
}
private fun CoroutineScope.doTestAwaitFrame() {
ShadowChoreographer.setPostFrameCallbackDelay(100)
val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
mainLooper.pause()
launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) {
expect(1)
awaitFrame()
expect(5)
}
expect(2)
// Run choreographer detection
mainLooper.runOneTask()
expect(3)
mainLooper.scheduler.advanceBy(50, TimeUnit.MILLISECONDS)
expect(4)
mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS)
finish(6)
}
private fun CoroutineScope.doTestAwaitWithDetectedChoreographer() {
ShadowChoreographer.setPostFrameCallbackDelay(100)
val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
launch(Dispatchers.Main, start = CoroutineStart.UNDISPATCHED) {
expect(1)
awaitFrame()
expect(4)
}
// Run choreographer detection
expect(2)
mainLooper.scheduler.advanceBy(50, TimeUnit.MILLISECONDS)
expect(3)
mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS)
finish(5)
}
}