blob: f148a3ae43169af9700c0d9d4f78c334d52d04df [file] [log] [blame]
/*
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.exceptions
import kotlinx.coroutines.*
import org.junit.*
import org.junit.Test
import java.util.concurrent.*
import kotlin.coroutines.*
import kotlin.test.*
import kotlin.time.Duration.Companion.minutes
class WithContextCancellationStressTest : TestBase() {
private val timeoutAfter = 1.minutes
private val pool = newFixedThreadPoolContext(3, "WithContextCancellationStressTest")
@After
fun tearDown() {
pool.close()
}
@Test
@Suppress("DEPRECATION")
fun testConcurrentFailure() = runBlocking {
var eCnt = 0
var e1Cnt = 0
var e2Cnt = 0
withTimeout(timeoutAfter) {
while (eCnt == 0 || e1Cnt == 0 || e2Cnt == 0) {
val barrier = CyclicBarrier(4)
val ctx = pool + NonCancellable
var e1 = false
var e2 = false
val jobWithContext = async(ctx) {
withContext(wrapperDispatcher(coroutineContext)) {
launch {
barrier.await()
e1 = true
throw TestException1()
}
launch {
barrier.await()
e2 = true
throw TestException2()
}
barrier.await()
throw TestException()
}
}
barrier.await()
try {
jobWithContext.await()
} catch (e: Throwable) {
when (e) {
is TestException -> {
eCnt++
e.checkSuppressed(e1 = e1, e2 = e2)
}
is TestException1 -> {
e1Cnt++
e.checkSuppressed(ex = true, e2 = e2)
}
is TestException2 -> {
e2Cnt++
e.checkSuppressed(ex = true, e1 = e1)
}
else -> error("Unexpected exception $e")
}
}
}
}
}
private fun wrapperDispatcher(context: CoroutineContext): CoroutineContext {
val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher
return object : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatcher.dispatch(context, block)
}
}
}
private fun Throwable.checkSuppressed(
ex: Boolean = false,
e1: Boolean = false,
e2: Boolean = false
) {
val suppressed: Array<Throwable> = suppressed
if (ex) {
assertTrue(suppressed.any { it is TestException }, "TestException should be present: $this")
}
if (e1) {
assertTrue(suppressed.any { it is TestException1 }, "TestException1 should be present: $this")
}
if (e2) {
assertTrue(suppressed.any { it is TestException2 }, "TestException2 should be present: $this")
}
}
}