| /* |
| * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
| */ |
| |
| package kotlinx.coroutines.test |
| |
| import kotlinx.coroutines.* |
| import kotlin.coroutines.* |
| import kotlin.test.* |
| |
| class TestRunBlockingTest { |
| |
| @Test |
| fun delay_advancesTimeAutomatically() = runBlockingTest { |
| assertRunsFast { |
| delay(SLOW) |
| } |
| } |
| |
| @Test |
| fun callingSuspendWithDelay_advancesAutomatically() = runBlockingTest { |
| suspend fun withDelay(): Int { |
| delay(SLOW) |
| return 3 |
| } |
| |
| assertRunsFast { |
| assertEquals(3, withDelay()) |
| } |
| } |
| |
| @Test |
| fun launch_advancesAutomatically() = runBlockingTest { |
| val job = launch { |
| delay(SLOW) |
| } |
| assertRunsFast { |
| job.join() |
| assertTrue(job.isCompleted) |
| } |
| } |
| |
| @Test |
| fun async_advancesAutomatically() = runBlockingTest { |
| val deferred = async { |
| delay(SLOW) |
| 3 |
| } |
| |
| assertRunsFast { |
| assertEquals(3, deferred.await()) |
| } |
| } |
| |
| @Test |
| fun incorrectlyCalledRunblocking_doesNotHaveSameInterceptor() = runBlockingTest { |
| // this code is an error as a production test, please do not use this as an example |
| |
| // this test exists to document this error condition, if it's possible to make this code work please update |
| val outerInterceptor = coroutineContext[ContinuationInterceptor] |
| // runBlocking always requires an argument to pass the context in tests |
| runBlocking { |
| assertNotSame(coroutineContext[ContinuationInterceptor], outerInterceptor) |
| } |
| } |
| |
| @Test(expected = TimeoutCancellationException::class) |
| fun whenUsingTimeout_triggersWhenDelayed() = runBlockingTest { |
| assertRunsFast { |
| withTimeout(SLOW) { |
| delay(SLOW) |
| } |
| } |
| } |
| |
| @Test |
| fun whenUsingTimeout_doesNotTriggerWhenFast() = runBlockingTest { |
| assertRunsFast { |
| withTimeout(SLOW) { |
| delay(0) |
| } |
| } |
| } |
| |
| @Test(expected = TimeoutCancellationException::class) |
| fun whenUsingTimeout_triggersWhenWaiting() = runBlockingTest { |
| val uncompleted = CompletableDeferred<Unit>() |
| assertRunsFast { |
| withTimeout(SLOW) { |
| uncompleted.await() |
| } |
| } |
| } |
| |
| @Test |
| fun whenUsingTimeout_doesNotTriggerWhenComplete() = runBlockingTest { |
| val completed = CompletableDeferred<Unit>() |
| assertRunsFast { |
| completed.complete(Unit) |
| withTimeout(SLOW) { |
| completed.await() |
| } |
| } |
| } |
| |
| @Test |
| fun testDelayInAsync_withAwait() = runBlockingTest { |
| assertRunsFast { |
| val deferred = async { |
| delay(SLOW) |
| 3 |
| } |
| assertEquals(3, deferred.await()) |
| } |
| } |
| |
| @Test(expected = TimeoutCancellationException::class) |
| fun whenUsingTimeout_inAsync_triggersWhenDelayed() = runBlockingTest { |
| val deferred = async { |
| withTimeout(SLOW) { |
| delay(SLOW) |
| } |
| } |
| |
| assertRunsFast { |
| deferred.await() |
| } |
| } |
| |
| @Test |
| fun whenUsingTimeout_inAsync_doesNotTriggerWhenNotDelayed() = runBlockingTest { |
| val testScope = this |
| val deferred = async { |
| withTimeout(SLOW) { |
| delay(0) |
| } |
| } |
| |
| assertRunsFast { |
| deferred.await() |
| } |
| } |
| |
| @Test(expected = TimeoutCancellationException::class) |
| fun whenUsingTimeout_inLaunch_triggersWhenDelayed() = runBlockingTest { |
| val job= launch { |
| withTimeout(1) { |
| delay(SLOW + 1) |
| 3 |
| } |
| } |
| |
| assertRunsFast { |
| job.join() |
| throw job.getCancellationException() |
| } |
| } |
| |
| @Test |
| fun whenUsingTimeout_inLaunch_doesNotTriggerWhenNotDelayed() = runBlockingTest { |
| val job = launch { |
| withTimeout(SLOW) { |
| delay(0) |
| } |
| } |
| |
| assertRunsFast { |
| job.join() |
| assertTrue(job.isCompleted) |
| } |
| } |
| |
| @Test(expected = IllegalArgumentException::class) |
| fun throwingException_throws() = runBlockingTest { |
| assertRunsFast { |
| delay(SLOW) |
| throw IllegalArgumentException("Test") |
| } |
| } |
| |
| @Test(expected = IllegalArgumentException::class) |
| fun throwingException_inLaunch_throws() = runBlockingTest { |
| val job = launch { |
| delay(SLOW) |
| throw IllegalArgumentException("Test") |
| } |
| |
| assertRunsFast { |
| job.join() |
| throw job.getCancellationException().cause ?: assertFails { "expected exception" } |
| } |
| } |
| |
| @Test(expected = IllegalArgumentException::class) |
| fun throwingException__inAsync_throws() = runBlockingTest { |
| val deferred = async { |
| delay(SLOW) |
| throw IllegalArgumentException("Test") |
| } |
| |
| assertRunsFast { |
| deferred.await() |
| } |
| } |
| |
| @Test |
| fun callingLaunchFunction_executesLaunchBlockImmediately() = runBlockingTest { |
| assertRunsFast { |
| var executed = false |
| launch { |
| delay(SLOW) |
| executed = true |
| } |
| |
| delay(SLOW) |
| assertTrue(executed) |
| } |
| } |
| |
| @Test |
| fun callingAsyncFunction_executesAsyncBlockImmediately() = runBlockingTest { |
| assertRunsFast { |
| var executed = false |
| async { |
| delay(SLOW) |
| executed = true |
| } |
| advanceTimeBy(SLOW) |
| |
| assertTrue(executed) |
| } |
| } |
| |
| @Test |
| fun nestingBuilders_executesSecondLevelImmediately() = runBlockingTest { |
| assertRunsFast { |
| var levels = 0 |
| launch { |
| delay(SLOW) |
| levels++ |
| launch { |
| delay(SLOW) |
| levels++ |
| } |
| } |
| advanceUntilIdle() |
| |
| assertEquals(2, levels) |
| } |
| } |
| |
| @Test |
| fun testCancellationException() = runBlockingTest { |
| var actual: CancellationException? = null |
| val uncompleted = CompletableDeferred<Unit>() |
| val job = launch { |
| actual = kotlin.runCatching { uncompleted.await() }.exceptionOrNull() as? CancellationException |
| } |
| |
| assertNull(actual) |
| job.cancel() |
| assertNotNull(actual) |
| } |
| |
| @Test |
| fun testCancellationException_notThrown() = runBlockingTest { |
| val uncompleted = CompletableDeferred<Unit>() |
| val job = launch { |
| uncompleted.await() |
| } |
| |
| job.cancel() |
| job.join() |
| } |
| |
| @Test(expected = UncompletedCoroutinesError::class) |
| fun whenACoroutineLeaks_errorIsThrown() = runBlockingTest { |
| val uncompleted = CompletableDeferred<Unit>() |
| launch { |
| uncompleted.await() |
| } |
| } |
| |
| @Test(expected = java.lang.IllegalArgumentException::class) |
| fun runBlockingTestBuilder_throwsOnBadDispatcher() { |
| runBlockingTest(newSingleThreadContext("name")) { |
| |
| } |
| } |
| |
| @Test(expected = java.lang.IllegalArgumentException::class) |
| fun runBlockingTestBuilder_throwsOnBadHandler() { |
| runBlockingTest(CoroutineExceptionHandler { _, _ -> Unit} ) { |
| |
| } |
| } |
| |
| @Test |
| fun pauseDispatcher_disablesAutoAdvance_forCurrent() = runBlockingTest { |
| var mutable = 0 |
| pauseDispatcher { |
| launch { |
| mutable++ |
| } |
| assertEquals(0, mutable) |
| runCurrent() |
| assertEquals(1, mutable) |
| } |
| } |
| |
| @Test |
| fun pauseDispatcher_disablesAutoAdvance_forDelay() = runBlockingTest { |
| var mutable = 0 |
| pauseDispatcher { |
| launch { |
| mutable++ |
| delay(SLOW) |
| mutable++ |
| } |
| assertEquals(0, mutable) |
| runCurrent() |
| assertEquals(1, mutable) |
| advanceTimeBy(SLOW) |
| assertEquals(2, mutable) |
| } |
| } |
| |
| @Test |
| fun pauseDispatcher_withDelay_resumesAfterPause() = runBlockingTest { |
| var mutable = 0 |
| assertRunsFast { |
| pauseDispatcher { |
| delay(1_000) |
| mutable++ |
| } |
| } |
| assertEquals(1, mutable) |
| } |
| |
| |
| @Test(expected = IllegalAccessError::class) |
| fun testWithTestContextThrowingAnAssertionError() = runBlockingTest { |
| val expectedError = IllegalAccessError("hello") |
| |
| val job = launch { |
| throw expectedError |
| } |
| |
| // don't rethrow or handle the exception |
| } |
| |
| @Test(expected = IllegalAccessError::class) |
| fun testExceptionHandlingWithLaunch() = runBlockingTest { |
| val expectedError = IllegalAccessError("hello") |
| |
| launch { |
| throw expectedError |
| } |
| } |
| |
| @Test(expected = IllegalAccessError::class) |
| fun testExceptions_notThrownImmediately() = runBlockingTest { |
| val expectedException = IllegalAccessError("hello") |
| val result = runCatching { |
| launch { |
| throw expectedException |
| } |
| } |
| runCurrent() |
| assertEquals(true, result.isSuccess) |
| } |
| |
| |
| private val exceptionHandler = TestCoroutineExceptionHandler() |
| |
| @Test |
| fun testPartialContextOverride() = runBlockingTest(CoroutineName("named")) { |
| assertEquals(CoroutineName("named"), coroutineContext[CoroutineName]) |
| assertNotNull(coroutineContext[CoroutineExceptionHandler]) |
| assertNotSame(coroutineContext[CoroutineExceptionHandler], exceptionHandler) |
| } |
| |
| @Test(expected = IllegalArgumentException::class) |
| fun testPartialDispatcherOverride() = runBlockingTest(Dispatchers.Unconfined) { |
| fail("Unreached") |
| } |
| |
| @Test |
| fun testOverrideExceptionHandler() = runBlockingTest(exceptionHandler) { |
| assertSame(coroutineContext[CoroutineExceptionHandler], exceptionHandler) |
| } |
| |
| @Test(expected = IllegalArgumentException::class) |
| fun testOverrideExceptionHandlerError() = runBlockingTest(CoroutineExceptionHandler { _, _ -> }) { |
| fail("Unreached") |
| } |
| } |