Skip to content

Commit 5bc9442

Browse files
committed
TestBase documented
1 parent 3fa4bec commit 5bc9442

File tree

1 file changed

+46
-4
lines changed
  • kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental

1 file changed

+46
-4
lines changed

kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/TestBase.kt

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,76 @@ import java.util.concurrent.atomic.AtomicBoolean
2121
import java.util.concurrent.atomic.AtomicInteger
2222
import java.util.concurrent.atomic.AtomicReference
2323

24+
/**
25+
* Base class for tests, so that tests for predictable scheduling of actions in multiple coroutines sharing a single
26+
* thread can be written. Use it like this:
27+
*
28+
* ```
29+
* class MyTest {
30+
* @Test
31+
* fun testSomething() = runBlocking<Unit> { // run in the context of the main thread
32+
* expect(1) // initiate action counter
33+
* val job = launch(context) { // use the context of the main thread
34+
* expect(3) // the body of this coroutine in going to be executed in the 3rd step
35+
* }
36+
* expect(2) // launch just scheduled coroutine for exectuion later, so this line is executed second
37+
* yield() // yield main thread to the launched job
38+
* finish(4) // fourth step is the last one. `finish` must be invoked or test fails
39+
* }
40+
* }
41+
* ```
42+
*/
2443
open class TestBase {
44+
/**
45+
* Is `true` when nightly stress test is done.
46+
*/
2547
val isStressTest = System.getProperty("stressTest") != null
48+
49+
/**
50+
* Multiply various constants in stress tests by this factor, so that they run longer during nightly stress test.
51+
*/
2652
val stressTestMultiplier = if (isStressTest) 30 else 1
2753

28-
var actionIndex = AtomicInteger()
29-
var finished = AtomicBoolean()
30-
var error = AtomicReference<Throwable>()
54+
private var actionIndex = AtomicInteger()
55+
private var finished = AtomicBoolean()
56+
private var error = AtomicReference<Throwable>()
3157

58+
/**
59+
* Throws [IllegalStateException] like `error` in stdlib, but also ensures that the test will not
60+
* complete successfully even if this exception is consumed somewhere in the test.
61+
*/
3262
public fun error(message: Any): Nothing {
3363
val exception = IllegalStateException(message.toString())
3464
error.compareAndSet(null, exception)
3565
throw exception
3666
}
3767

68+
/**
69+
* Throws [IllegalStateException] when `value` is false like `check` in stdlib, but also ensures that the
70+
* test will not complete successfully even if this exception is consumed somewhere in the test.
71+
*/
3872
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
3973
if (!value) error(lazyMessage())
4074
}
4175

76+
/**
77+
* Asserts that this invocation is `index`-th in the execution sequence (counting from one).
78+
*/
4279
fun expect(index: Int) {
4380
val wasIndex = actionIndex.incrementAndGet()
4481
check(index == wasIndex) { "Expecting action index $index but it is actually $wasIndex" }
4582
}
4683

84+
/**
85+
* Asserts that this line is never executed.
86+
*/
4787
fun expectUnreached() {
4888
error("Should not be reached")
4989
}
5090

91+
/**
92+
* Asserts that this it the last action in the test. It must be invoked by any test that used [expect].
93+
*/
5194
fun finish(index: Int) {
5295
expect(index)
5396
check(!finished.getAndSet(true)) { "Should call 'finish(...)' at most once" }
@@ -58,5 +101,4 @@ open class TestBase {
58101
error.get()?.let { throw it }
59102
check(actionIndex.get() == 0 || finished.get()) { "Expecting that 'finish(...)' was invoked, but it was not" }
60103
}
61-
62104
}

0 commit comments

Comments
 (0)