junit.CoroutinesTimeoutImpl.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlinx-coroutines-debug Show documentation
Show all versions of kotlinx-coroutines-debug Show documentation
Coroutines support libraries for Kotlin
The newest version!
package kotlinx.coroutines.debug
import java.util.concurrent.*
/**
* Run [invocation] in a separate thread with the given timeout in ms, after which the coroutines info is dumped and, if
* [cancelOnTimeout] is set, the execution is interrupted.
*
* Assumes that [DebugProbes] are installed. Does not deinstall them.
*/
internal inline fun runWithTimeoutDumpingCoroutines(
methodName: String,
testTimeoutMs: Long,
cancelOnTimeout: Boolean,
initCancellationException: () -> Throwable,
crossinline invocation: () -> T
): T {
val testStartedLatch = CountDownLatch(1)
val testResult = FutureTask {
testStartedLatch.countDown()
invocation()
}
/*
* We are using hand-rolled thread instead of single thread executor
* in order to be able to safely interrupt thread in the end of a test
*/
val testThread = Thread(testResult, "Timeout test thread").apply { isDaemon = true }
try {
testThread.start()
// Await until test is started to take only test execution time into account
testStartedLatch.await()
return testResult.get(testTimeoutMs, TimeUnit.MILLISECONDS)
} catch (e: TimeoutException) {
handleTimeout(testThread, methodName, testTimeoutMs, cancelOnTimeout, initCancellationException())
} catch (e: ExecutionException) {
throw e.cause ?: e
}
}
private fun handleTimeout(testThread: Thread, methodName: String, testTimeoutMs: Long, cancelOnTimeout: Boolean,
cancellationException: Throwable): Nothing {
val units =
if (testTimeoutMs % 1000 == 0L)
"${testTimeoutMs / 1000} seconds"
else "$testTimeoutMs milliseconds"
System.err.println("\nTest $methodName timed out after $units\n")
System.err.flush()
DebugProbes.dumpCoroutines()
System.out.flush() // Synchronize serr/sout
/*
* Order is important:
* 1) Create exception with a stacktrace of hang test
* 2) Cancel all coroutines via debug agent API (changing system state!)
* 3) Throw created exception
*/
cancellationException.attachStacktraceFrom(testThread)
testThread.interrupt()
cancelIfNecessary(cancelOnTimeout)
// If timed out test throws an exception, we can't do much except ignoring it
throw cancellationException
}
private fun cancelIfNecessary(cancelOnTimeout: Boolean) {
if (cancelOnTimeout) {
DebugProbes.dumpCoroutinesInfo().forEach {
it.job?.cancel()
}
}
}
private fun Throwable.attachStacktraceFrom(thread: Thread) {
val stackTrace = thread.stackTrace
this.stackTrace = stackTrace
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy