1 /* 2 * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.debug.junit4 6 7 import kotlinx.coroutines.debug.* 8 import org.junit.rules.* 9 import org.junit.runner.* 10 import org.junit.runners.model.* 11 import java.util.concurrent.* 12 13 /** 14 * Coroutines timeout rule for JUnit4 that is applied to all methods in the class. 15 * This rule is very similar to [Timeout] rule: it runs tests in a separate thread, 16 * fails tests after the given timeout and interrupts test thread. 17 * 18 * Additionally, this rule installs [DebugProbes] and dumps all coroutines at the moment of the timeout. 19 * It may cancel coroutines on timeout if [cancelOnTimeout] set to `true`. 20 * [enableCoroutineCreationStackTraces] controls the corresponding [DebugProbes.enableCreationStackTraces] property 21 * and can be optionally disabled to speed-up tests if creation stack traces are not needed. 22 * 23 * Example of usage: 24 * ``` 25 * class HangingTest { 26 * @get:Rule 27 * val timeout = CoroutinesTimeout.seconds(5) 28 * 29 * @Test 30 * fun testThatHangs() = runBlocking { 31 * ... 32 * delay(Long.MAX_VALUE) // somewhere deep in the stack 33 * ... 34 * } 35 * } 36 * ``` 37 */ 38 public class CoroutinesTimeout( 39 private val testTimeoutMs: Long, 40 private val cancelOnTimeout: Boolean = false, 41 private val enableCoroutineCreationStackTraces: Boolean = true 42 ) : TestRule { 43 44 @Suppress("UNUSED") // Binary compatibility 45 public constructor(testTimeoutMs: Long, cancelOnTimeout: Boolean = false) : this( 46 testTimeoutMs, 47 cancelOnTimeout, 48 true 49 ) 50 51 init { <lambda>null52 require(testTimeoutMs > 0) { "Expected positive test timeout, but had $testTimeoutMs" } 53 /* 54 * Install probes in the constructor, so all the coroutines launched from within 55 * target test constructor will be captured 56 */ 57 // Do not preserve previous state for unit-test environment 58 DebugProbes.enableCreationStackTraces = enableCoroutineCreationStackTraces 59 DebugProbes.install() 60 } 61 62 public companion object { 63 /** 64 * Creates [CoroutinesTimeout] rule with the given timeout in seconds. 65 */ 66 @JvmOverloads secondsnull67 public fun seconds( 68 seconds: Int, 69 cancelOnTimeout: Boolean = false, 70 enableCoroutineCreationStackTraces: Boolean = true 71 ): CoroutinesTimeout = 72 seconds(seconds.toLong(), cancelOnTimeout, enableCoroutineCreationStackTraces) 73 74 /** 75 * Creates [CoroutinesTimeout] rule with the given timeout in seconds. 76 */ 77 @JvmOverloads 78 public fun seconds( 79 seconds: Long, 80 cancelOnTimeout: Boolean = false, 81 enableCoroutineCreationStackTraces: Boolean = true 82 ): CoroutinesTimeout = 83 CoroutinesTimeout( 84 TimeUnit.SECONDS.toMillis(seconds), // Overflow is properly handled by TimeUnit 85 cancelOnTimeout, 86 enableCoroutineCreationStackTraces 87 ) 88 } 89 90 /** 91 * @suppress suppress from Dokka 92 */ 93 override fun apply(base: Statement, description: Description): Statement = 94 CoroutinesTimeoutStatement(base, description, testTimeoutMs, cancelOnTimeout) 95 } 96