1 /* 2 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.exceptions 6 7 import kotlinx.coroutines.* 8 import org.junit.* 9 import org.junit.Test 10 import java.util.concurrent.* 11 import kotlin.test.* 12 13 class JobExceptionsStressTest : TestBase() { 14 15 private val executor = newFixedThreadPoolContext(5, "JobExceptionsStressTest") 16 17 @After tearDownnull18 fun tearDown() { 19 executor.close() 20 } 21 22 @Test testMultipleChildrenThrowsnull23 fun testMultipleChildrenThrows() { 24 /* 25 * Root parent: launched job 26 * Owner: launch 3 children, every of it throws an exception, and then call delay() 27 * Result: one of the exceptions with the rest two as suppressed 28 */ 29 repeat(1000 * stressTestMultiplier) { 30 val exception = captureExceptionsRun(executor) { 31 val barrier = CyclicBarrier(4) 32 val job = launch(NonCancellable) { 33 launch(start = CoroutineStart.ATOMIC) { 34 barrier.await() 35 throw TestException1() 36 } 37 launch(start = CoroutineStart.ATOMIC) { 38 barrier.await() 39 throw TestException2() 40 } 41 launch(start = CoroutineStart.ATOMIC) { 42 barrier.await() 43 throw TestException3() 44 } 45 delay(1000) // to avoid OutOfMemory errors.... 46 } 47 barrier.await() 48 job.join() 49 } 50 val classes = mutableSetOf( 51 TestException1::class, 52 TestException2::class, 53 TestException3::class 54 ) 55 val suppressedExceptions = exception.suppressed.toSet() 56 assertTrue(classes.remove(exception::class), 57 "Failed to remove ${exception::class} from $suppressedExceptions" 58 ) 59 for (throwable in suppressedExceptions.toSet()) { // defensive copy 60 assertTrue(classes.remove(throwable::class), 61 "Failed to remove ${throwable::class} from $suppressedExceptions") 62 } 63 assertTrue(classes.isEmpty(), "Expected all exception to be present, but following exceptions are missing: $classes") 64 } 65 } 66 } 67