-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathTestCoroutineScopeTest.kt
159 lines (147 loc) · 5.3 KB
/
TestCoroutineScopeTest.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
* Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.coroutines.test
import kotlinx.coroutines.*
import kotlin.coroutines.*
import kotlin.test.*
class TestCoroutineScopeTest {
/** Tests failing to create a [TestCoroutineScope] with incorrect contexts. */
@Test
fun testCreateThrowsOnInvalidArguments() {
for (ctx in invalidContexts) {
assertFailsWith<IllegalArgumentException> {
TestCoroutineScope(ctx)
}
}
}
/** Tests that a newly-created [TestCoroutineScope] provides the correct scheduler. */
@Test
fun testCreateProvidesScheduler() {
// Creates a new scheduler.
run {
val scope = TestCoroutineScope()
assertNotNull(scope.coroutineContext[TestCoroutineScheduler])
}
// Reuses the scheduler that the dispatcher is linked to.
run {
val dispatcher = TestCoroutineDispatcher()
val scope = TestCoroutineScope(dispatcher)
assertSame(dispatcher.scheduler, scope.coroutineContext[TestCoroutineScheduler])
}
// Uses the scheduler passed to it.
run {
val scheduler = TestCoroutineScheduler()
val scope = TestCoroutineScope(scheduler)
assertSame(scheduler, scope.coroutineContext[TestCoroutineScheduler])
assertSame(scheduler, (scope.coroutineContext[ContinuationInterceptor] as TestDispatcher).scheduler)
}
// Doesn't touch the passed dispatcher and the scheduler if they match.
run {
val scheduler = TestCoroutineScheduler()
val dispatcher = TestCoroutineDispatcher(scheduler)
val scope = TestCoroutineScope(scheduler + dispatcher)
assertSame(scheduler, scope.coroutineContext[TestCoroutineScheduler])
assertSame(dispatcher, scope.coroutineContext[ContinuationInterceptor])
}
}
/** Tests that the cleanup procedure throws if there were uncompleted delays by the end. */
@Test
fun testPresentDelaysThrowing() {
val scope = TestCoroutineScope()
var result = false
scope.launch {
delay(5)
result = true
}
assertFalse(result)
assertFailsWith<AssertionError> { scope.cleanupTestCoroutines() }
assertFalse(result)
}
/** Tests that the cleanup procedure throws if there were active jobs by the end. */
@Test
fun testActiveJobsThrowing() {
val scope = TestCoroutineScope()
var result = false
val deferred = CompletableDeferred<String>()
scope.launch {
deferred.await()
result = true
}
assertFalse(result)
assertFailsWith<AssertionError> { scope.cleanupTestCoroutines() }
assertFalse(result)
}
/** Tests that the cleanup procedure doesn't throw if it detects that the job is already cancelled. */
@Test
fun testCancelledDelaysNotThrowing() {
val scope = TestCoroutineScope()
var result = false
val deferred = CompletableDeferred<String>()
val job = scope.launch {
deferred.await()
result = true
}
job.cancel()
assertFalse(result)
scope.cleanupTestCoroutines()
assertFalse(result)
}
/** Tests that the coroutine scope completes its job if the job was not passed to it as an argument. */
@Test
fun testCompletesOwnJob() {
val scope = TestCoroutineScope()
var handlerCalled = false
scope.coroutineContext.job.invokeOnCompletion {
handlerCalled = true
}
scope.cleanupTestCoroutines()
assertTrue(handlerCalled)
}
/** Tests that the coroutine scope completes its job if the job was not passed to it as an argument. */
@Test
fun testDoesNotCompleteGivenJob() {
var handlerCalled = false
val job = Job()
job.invokeOnCompletion {
handlerCalled = true
}
val scope = TestCoroutineScope(job)
scope.cleanupTestCoroutines()
assertFalse(handlerCalled)
}
/** Tests that uncaught exceptions are thrown at the cleanup. */
@Test
fun testThrowsUncaughtExceptionsOnCleanup() {
val scope = TestCoroutineScope()
val exception = TestException("test")
scope.launch {
throw exception
}
assertFailsWith<TestException> {
scope.cleanupTestCoroutines()
}
}
/** Tests that uncaught exceptions take priority over uncompleted jobs when throwing on cleanup. */
@Test
fun testUncaughtExceptionsPrioritizedOnCleanup() {
val scope = TestCoroutineScope()
val exception = TestException("test")
scope.launch {
throw exception
}
scope.launch {
delay(1000)
}
assertFailsWith<TestException> {
scope.cleanupTestCoroutines()
}
}
companion object {
internal val invalidContexts = listOf(
Dispatchers.Default, // not a [TestDispatcher]
TestCoroutineDispatcher() + TestCoroutineScheduler(), // the dispatcher is not linked to the scheduler
CoroutineExceptionHandler { _, _ -> }, // not an `UncaughtExceptionCaptor`
)
}
}