1 /* <lambda>null2 * 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 6 7 public actual val isStressTest: Boolean = false 8 public actual val stressTestMultiplier: Int = 1 9 10 public actual open class TestBase actual constructor() { 11 private var actionIndex = 0 12 private var finished = false 13 private var error: Throwable? = null 14 15 /** 16 * Throws [IllegalStateException] like `error` in stdlib, but also ensures that the test will not 17 * complete successfully even if this exception is consumed somewhere in the test. 18 */ 19 @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") 20 public actual fun error(message: Any, cause: Throwable? = null): Nothing { 21 val exception = IllegalStateException(message.toString(), cause) 22 if (error == null) error = exception 23 throw exception 24 } 25 26 private fun printError(message: String, cause: Throwable) { 27 if (error == null) error = cause 28 println("$message: $cause") 29 } 30 31 /** 32 * Asserts that this invocation is `index`-th in the execution sequence (counting from one). 33 */ 34 public actual fun expect(index: Int) { 35 val wasIndex = ++actionIndex 36 check(index == wasIndex) { "Expecting action index $index but it is actually $wasIndex" } 37 } 38 39 /** 40 * Asserts that this line is never executed. 41 */ 42 public actual fun expectUnreached() { 43 error("Should not be reached") 44 } 45 46 /** 47 * Asserts that this it the last action in the test. It must be invoked by any test that used [expect]. 48 */ 49 public actual fun finish(index: Int) { 50 expect(index) 51 check(!finished) { "Should call 'finish(...)' at most once" } 52 finished = true 53 } 54 55 /** 56 * Asserts that [finish] was invoked 57 */ 58 public actual fun ensureFinished() { 59 require(finished) { "finish(...) should be caller prior to this check" } 60 } 61 62 public actual fun reset() { 63 check(actionIndex == 0 || finished) { "Expecting that 'finish(...)' was invoked, but it was not" } 64 actionIndex = 0 65 finished = false 66 } 67 68 @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") 69 public actual fun runTest( 70 expected: ((Throwable) -> Boolean)? = null, 71 unhandled: List<(Throwable) -> Boolean> = emptyList(), 72 block: suspend CoroutineScope.() -> Unit 73 ) { 74 var exCount = 0 75 var ex: Throwable? = null 76 try { 77 runBlocking(block = block, context = CoroutineExceptionHandler { context, e -> 78 if (e is CancellationException) return@CoroutineExceptionHandler // are ignored 79 exCount++ 80 when { 81 exCount > unhandled.size -> 82 printError("Too many unhandled exceptions $exCount, expected ${unhandled.size}, got: $e", e) 83 !unhandled[exCount - 1](e) -> 84 printError("Unhandled exception was unexpected: $e", e) 85 } 86 }) 87 } catch (e: Throwable) { 88 ex = e 89 if (expected != null) { 90 if (!expected(e)) 91 error("Unexpected exception: $e", e) 92 } else 93 throw e 94 } finally { 95 if (ex == null && expected != null) error("Exception was expected but none produced") 96 } 97 if (exCount < unhandled.size) 98 error("Too few unhandled exceptions $exCount, expected ${unhandled.size}") 99 } 100 } 101