7
7
package kotlinx.coroutines.experimental
8
8
9
9
import kotlinx.coroutines.experimental.channels.*
10
+ import kotlin.coroutines.experimental.*
10
11
import kotlin.test.*
11
12
12
13
/* *
13
14
* Systematically tests that various builders cancel parent on failure.
14
15
*/
15
16
class ParentCancellationTest : TestBase () {
16
17
@Test
17
- @Ignore // todo: shall be passing in Supervisor branch
18
18
fun testJobChild () = runTest {
19
- testParentCancellation { fail ->
19
+ testParentCancellation(expectUnhandled = true ) { fail ->
20
20
val child = Job (coroutineContext[Job ])
21
21
CoroutineScope (coroutineContext + child).fail()
22
22
}
23
23
}
24
24
25
+ @Test
26
+ fun testSupervisorJobChild () = runTest {
27
+ testParentCancellation(expectParentActive = true , expectUnhandled = true ) { fail ->
28
+ val child = SupervisorJob (coroutineContext[Job ])
29
+ CoroutineScope (coroutineContext + child).fail()
30
+ }
31
+ }
32
+
25
33
@Test
26
34
fun testCompletableDeferredChild () = runTest {
27
35
testParentCancellation { fail ->
@@ -58,37 +66,48 @@ class ParentCancellationTest : TestBase() {
58
66
}
59
67
}
60
68
69
+ @Test
70
+ fun testSupervisorChild () = runTest {
71
+ testParentCancellation(expectParentActive = true , expectUnhandled = true ) { fail ->
72
+ supervisorScope { fail() }
73
+ }
74
+ }
75
+
61
76
@Test
62
77
fun testCoroutineScopeChild () = runTest {
63
- testParentCancellation(expectRethrows = true ) { fail ->
78
+ testParentCancellation(expectParentActive = true , expectRethrows = true ) { fail ->
64
79
coroutineScope { fail() }
65
80
}
66
81
}
67
82
68
83
@Test
69
84
fun testWithContextChild () = runTest {
70
- testParentCancellation(expectRethrows = true ) { fail ->
85
+ testParentCancellation(expectParentActive = true , expectRethrows = true ) { fail ->
71
86
withContext(CoroutineName (" fail" )) { fail() }
72
87
}
73
88
}
74
89
75
90
@Test
76
91
fun testWithTimeoutChild () = runTest {
77
- testParentCancellation(expectRethrows = true ) { fail ->
92
+ testParentCancellation(expectParentActive = true , expectRethrows = true ) { fail ->
78
93
withTimeout(1000 ) { fail() }
79
94
}
80
95
}
81
96
82
97
private suspend fun CoroutineScope.testParentCancellation (
98
+ expectParentActive : Boolean = false,
83
99
expectRethrows : Boolean = false,
100
+ expectUnhandled : Boolean = false,
84
101
child : suspend CoroutineScope .(block: suspend CoroutineScope .() -> Unit ) -> Unit
85
102
) {
86
- testWithException(expectRethrows, TestException (), child)
87
- testWithException(expectRethrows, CancellationException (" Test" ), child)
103
+ testWithException(expectParentActive, expectRethrows, expectUnhandled , TestException (), child)
104
+ testWithException(true , expectRethrows, false , CancellationException (" Test" ), child)
88
105
}
89
106
90
107
private suspend fun CoroutineScope.testWithException (
108
+ expectParentActive : Boolean ,
91
109
expectRethrows : Boolean ,
110
+ expectUnhandled : Boolean ,
92
111
throwException : Throwable ,
93
112
child : suspend CoroutineScope .(block: suspend CoroutineScope .() -> Unit ) -> Unit
94
113
) {
@@ -99,10 +118,17 @@ class ParentCancellationTest : TestBase() {
99
118
try {
100
119
scope.child {
101
120
// launch failing grandchild
102
- val grandchild = launch {
121
+ var unhandledException: Throwable ? = null
122
+ val handler = CoroutineExceptionHandler { _, e -> unhandledException = e }
123
+ val grandchild = launch(handler) {
103
124
throw throwException
104
125
}
105
126
grandchild.join()
127
+ if (expectUnhandled) {
128
+ assertSame(throwException, unhandledException)
129
+ } else {
130
+ assertNull(unhandledException)
131
+ }
106
132
}
107
133
if (expectRethrows && throwException !is CancellationException ) {
108
134
expectUnreached()
@@ -117,8 +143,7 @@ class ParentCancellationTest : TestBase() {
117
143
expectUnreached()
118
144
}
119
145
}
120
- if (expectRethrows || throwException is CancellationException ) {
121
- // Note: parent is not cancelled on CancellationException or when primitive rethrows it
146
+ if (expectParentActive) {
122
147
assertTrue(parent.isActive)
123
148
} else {
124
149
parent.join()
0 commit comments