blob: 875f49dc868a049cb4eefbdefe6a5470543df20e [file] [log] [blame]
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +03001/*
2 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5package kotlinx.coroutines.experimental
6
Andrew Mikhaylov3de2be42018-09-13 16:30:55 +03007import kotlin.coroutines.experimental.*
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +03008import kotlin.test.*
9
10class CoroutineScopeTest : TestBase() {
11
12 @Test
13 fun testScope() = runTest {
14 suspend fun callJobScoped() = coroutineScope {
15 expect(2)
16
17 launch {
18 expect(4)
19 }
20
21 launch {
22 expect(5)
23
24 launch {
25 expect(7)
26 }
27
28 expect(6)
29
30 }
31
32 expect(3)
33 42
34 }
35
36
37 expect(1)
38 val result = callJobScoped()
39 assertEquals(42, result)
40 yield() // Check we're not cancelled
41 finish(8)
42 }
43
44 @Test
45 fun testScopeCancelledFromWithin() = runTest {
46 expect(1)
47 suspend fun callJobScoped() = coroutineScope {
48
49 launch {
50 expect(2)
51 delay(Long.MAX_VALUE)
52 }
53
54 launch {
55 expect(3)
56 throw IllegalArgumentException()
57 }
58 }
59
60 try {
61 callJobScoped()
62 expectUnreached()
63 } catch (e: IllegalArgumentException) {
64 expect(4)
65 }
66
67 yield() // Check we're not cancelled
68 finish(5)
69 }
70
71 @Test
72 fun testScopeBlockThrows() = runTest {
73 expect(1)
74 suspend fun callJobScoped(): Unit = coroutineScope {
75
76 launch {
77 expect(2)
78 delay(Long.MAX_VALUE)
79 }
80
81 yield() // let launch sleep
82 throw NotImplementedError()
83 }
84
85 try {
86 callJobScoped()
87 expectUnreached()
88 } catch (e: NotImplementedError) {
89 expect(3)
90 }
91
92 yield() // Check we're not cancelled
93 finish(4)
94 }
95
96 @Test
97 fun testOuterJobIsCancelled() = runTest {
98
99 suspend fun callJobScoped() = coroutineScope {
100
101 launch {
102 expect(3)
103 try {
104 delay(Long.MAX_VALUE)
105 } finally {
106 expect(4)
107 }
108 }
109
110 expect(2)
111 delay(Long.MAX_VALUE)
112 42
113 }
114
115
Roman Elizarov6e3ffb12018-09-14 13:46:58 +0300116 val outerJob = launch(NonCancellable) {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300117 expect(1)
118 try {
119 callJobScoped()
120 expectUnreached()
Vsevolod Tolstopyatova2d80882018-09-24 19:51:49 +0300121 } catch (e: CancellationException) {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300122 expect(5)
123 assertNull(e.cause)
124 }
125 }
126
127 repeat(3) { yield() } // let everything to start properly
128 outerJob.cancel()
129 outerJob.join()
130 finish(6)
131 }
132
133 @Test
Roman Elizarovc32579e2018-09-09 19:21:43 +0300134 fun testAsyncCancellation() = runTest {
135 try {
136 expect(1)
137 failedConcurrentSum()
138 expectUnreached()
139 } catch (e: IndexOutOfBoundsException) {
140 finish(5)
141 }
142 }
143
144 private suspend fun failedConcurrentSum(): Int = coroutineScope {
145 val one = async<Int> {
146 println("First child throws an exception")
147 expect(3)
148 throw IndexOutOfBoundsException()
149 }
150 val two = async<Int>(start = CoroutineStart.ATOMIC) {
151 try {
152 expect(4)
153 delay(Long.MAX_VALUE) // Emulates very long computation
154 42
155 } finally {
156 println("Second child was cancelled")
157 }
158 }
159
160 expect(2)
161 one.await() + two.await()
162 }
163
164 @Test
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300165 @Suppress("UNREACHABLE_CODE")
166 fun testDocumentationExample() = runTest {
167 suspend fun loadData() = coroutineScope {
168 expect(1)
169 val data = async {
170 try {
171 delay(Long.MAX_VALUE)
172 } finally {
173 expect(3)
174 }
175 }
176
177 yield()
178
179 // UI updater
180 withContext(coroutineContext) {
181 expect(2)
182 throw AssertionError()
183 data.await() // Actually unreached
184 expectUnreached()
185 }
186 }
187
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300188 try {
189 loadData()
190 expectUnreached()
191 } catch (e: AssertionError) {
192 finish(4)
193 }
194 }
Andrew Mikhaylov3de2be42018-09-13 16:30:55 +0300195
196 @Test
197 fun testScopePlusContext() {
198 assertSame(EmptyCoroutineContext, scopePlusContext(EmptyCoroutineContext, EmptyCoroutineContext))
199 assertSame(Dispatchers.Default, scopePlusContext(EmptyCoroutineContext, Dispatchers.Default))
200 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Default, EmptyCoroutineContext))
201 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Default, Dispatchers.Default))
202 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Unconfined, Dispatchers.Default))
203 assertSame(Dispatchers.Unconfined, scopePlusContext(Dispatchers.Default, Dispatchers.Unconfined))
204 assertSame(Dispatchers.Unconfined, scopePlusContext(Dispatchers.Unconfined, Dispatchers.Unconfined))
205 }
206
207 private fun scopePlusContext(c1: CoroutineContext, c2: CoroutineContext) =
208 (CoroutineScope(c1) + c2).coroutineContext
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300209}