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