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