blob: 0510764a0c19f8a0b9145e9391dbf7c923cdf158 [file] [log] [blame]
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.debug.junit4
import kotlinx.coroutines.debug.*
import org.junit.rules.*
import org.junit.runner.*
import org.junit.runners.model.*
import java.util.concurrent.*
/**
* Coroutines timeout rule for JUnit4 that is applied to all methods in the class.
* This rule is very similar to [Timeout] rule: it runs tests in a separate thread,
* fails tests after the given timeout and interrupts test thread.
*
* Additionally, this rule installs [DebugProbes] and dumps all coroutines at the moment of the timeout.
* It may cancel coroutines on timeout if [cancelOnTimeout] set to `true`.
* [enableCoroutineCreationStackTraces] controls the corresponding [DebugProbes.enableCreationStackTraces] property
* and can be optionally disabled to speed-up tests if creation stack traces are not needed.
*
* Example of usage:
* ```
* class HangingTest {
* @get:Rule
* val timeout = CoroutinesTimeout.seconds(5)
*
* @Test
* fun testThatHangs() = runBlocking {
* ...
* delay(Long.MAX_VALUE) // somewhere deep in the stack
* ...
* }
* }
* ```
*/
public class CoroutinesTimeout(
private val testTimeoutMs: Long,
private val cancelOnTimeout: Boolean = false,
private val enableCoroutineCreationStackTraces: Boolean = true
) : TestRule {
@Suppress("UNUSED") // Binary compatibility
public constructor(testTimeoutMs: Long, cancelOnTimeout: Boolean = false) : this(
testTimeoutMs,
cancelOnTimeout,
true
)
init {
require(testTimeoutMs > 0) { "Expected positive test timeout, but had $testTimeoutMs" }
/*
* Install probes in the constructor, so all the coroutines launched from within
* target test constructor will be captured
*/
// Do not preserve previous state for unit-test environment
DebugProbes.enableCreationStackTraces = enableCoroutineCreationStackTraces
DebugProbes.install()
}
public companion object {
/**
* Creates [CoroutinesTimeout] rule with the given timeout in seconds.
*/
@JvmOverloads
public fun seconds(
seconds: Int,
cancelOnTimeout: Boolean = false,
enableCoroutineCreationStackTraces: Boolean = true
): CoroutinesTimeout =
seconds(seconds.toLong(), cancelOnTimeout, enableCoroutineCreationStackTraces)
/**
* Creates [CoroutinesTimeout] rule with the given timeout in seconds.
*/
@JvmOverloads
public fun seconds(
seconds: Long,
cancelOnTimeout: Boolean = false,
enableCoroutineCreationStackTraces: Boolean = true
): CoroutinesTimeout =
CoroutinesTimeout(
TimeUnit.SECONDS.toMillis(seconds), // Overflow is properly handled by TimeUnit
cancelOnTimeout,
enableCoroutineCreationStackTraces
)
}
/**
* @suppress suppress from Dokka
*/
override fun apply(base: Statement, description: Description): Statement =
CoroutinesTimeoutStatement(base, description, testTimeoutMs, cancelOnTimeout)
}