/* * 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) }