blob: b0e4ba2453520d3c5fd33dcc74fbc5d967baf2db [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
Roman Elizarov0950dfa2018-07-13 10:33:25 +03005package kotlinx.coroutines
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +03006
Roman Elizarov0950dfa2018-07-13 10:33:25 +03007import kotlinx.coroutines.internal.*
8import kotlin.coroutines.*
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +03009import kotlin.test.*
10
11class CoroutineScopeTest : TestBase() {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030012 @Test
13 fun testScope() = runTest {
14 suspend fun callJobScoped() = coroutineScope {
15 expect(2)
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030016 launch {
17 expect(4)
18 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030019 launch {
20 expect(5)
21
22 launch {
23 expect(7)
24 }
25
26 expect(6)
27
28 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030029 expect(3)
30 42
31 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030032 expect(1)
33 val result = callJobScoped()
34 assertEquals(42, result)
35 yield() // Check we're not cancelled
36 finish(8)
37 }
38
39 @Test
40 fun testScopeCancelledFromWithin() = runTest {
41 expect(1)
42 suspend fun callJobScoped() = coroutineScope {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030043 launch {
44 expect(2)
45 delay(Long.MAX_VALUE)
46 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030047 launch {
48 expect(3)
Roman Elizarov52bfdab2018-09-27 19:17:16 +030049 throw TestException2()
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030050 }
51 }
52
53 try {
54 callJobScoped()
55 expectUnreached()
Roman Elizarov52bfdab2018-09-27 19:17:16 +030056 } catch (e: TestException2) {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030057 expect(4)
58 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030059 yield() // Check we're not cancelled
60 finish(5)
61 }
62
63 @Test
Roman Elizarov52bfdab2018-09-27 19:17:16 +030064 fun testExceptionFromWithin() = runTest {
65 expect(1)
66 try {
67 expect(2)
68 coroutineScope {
69 expect(3)
70 throw TestException1()
71 }
72 expectUnreached()
73 } catch (e: TestException1) {
74 finish(4)
75 }
76 }
77
78 @Test
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030079 fun testScopeBlockThrows() = runTest {
80 expect(1)
81 suspend fun callJobScoped(): Unit = coroutineScope {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030082 launch {
83 expect(2)
84 delay(Long.MAX_VALUE)
85 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030086 yield() // let launch sleep
Roman Elizarov52bfdab2018-09-27 19:17:16 +030087 throw TestException1()
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030088 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030089 try {
90 callJobScoped()
91 expectUnreached()
Roman Elizarov52bfdab2018-09-27 19:17:16 +030092 } catch (e: TestException1) {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030093 expect(3)
94 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +030095 yield() // Check we're not cancelled
96 finish(4)
97 }
98
99 @Test
100 fun testOuterJobIsCancelled() = runTest {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300101 suspend fun callJobScoped() = coroutineScope {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300102 launch {
103 expect(3)
104 try {
105 delay(Long.MAX_VALUE)
106 } finally {
107 expect(4)
108 }
109 }
110
111 expect(2)
112 delay(Long.MAX_VALUE)
113 42
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 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300126 repeat(3) { yield() } // let everything to start properly
127 outerJob.cancel()
128 outerJob.join()
129 finish(6)
130 }
131
132 @Test
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300133 fun testAsyncCancellationFirst() = runTest {
Roman Elizarovc32579e2018-09-09 19:21:43 +0300134 try {
135 expect(1)
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300136 failedConcurrentSumFirst()
Roman Elizarovc32579e2018-09-09 19:21:43 +0300137 expectUnreached()
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300138 } catch (e: TestException1) {
139 finish(6)
Roman Elizarovc32579e2018-09-09 19:21:43 +0300140 }
141 }
142
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300143 // First async child fails -> second is cancelled
144 private suspend fun failedConcurrentSumFirst(): Int = coroutineScope {
Roman Elizarovc32579e2018-09-09 19:21:43 +0300145 val one = async<Int> {
Roman Elizarovc32579e2018-09-09 19:21:43 +0300146 expect(3)
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300147 throw TestException1()
Roman Elizarovc32579e2018-09-09 19:21:43 +0300148 }
149 val two = async<Int>(start = CoroutineStart.ATOMIC) {
150 try {
151 expect(4)
152 delay(Long.MAX_VALUE) // Emulates very long computation
153 42
154 } finally {
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300155 expect(5)
Roman Elizarovc32579e2018-09-09 19:21:43 +0300156 }
157 }
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300158 expect(2)
159 one.await() + two.await()
160 }
Roman Elizarovc32579e2018-09-09 19:21:43 +0300161
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300162 @Test
163 fun testAsyncCancellationSecond() = runTest {
164 try {
165 expect(1)
166 failedConcurrentSumSecond()
167 expectUnreached()
168 } catch (e: TestException1) {
169 finish(6)
170 }
171 }
172
173 // Second async child fails -> fist is cancelled
174 private suspend fun failedConcurrentSumSecond(): Int = coroutineScope {
175 val one = async<Int> {
176 try {
177 expect(3)
178 delay(Long.MAX_VALUE) // Emulates very long computation
179 42
180 } finally {
181 expect(5)
182 }
183 }
184 val two = async<Int>(start = CoroutineStart.ATOMIC) {
185 expect(4)
186 throw TestException1()
187 }
Roman Elizarovc32579e2018-09-09 19:21:43 +0300188 expect(2)
189 one.await() + two.await()
190 }
191
192 @Test
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300193 @Suppress("UNREACHABLE_CODE")
194 fun testDocumentationExample() = runTest {
195 suspend fun loadData() = coroutineScope {
196 expect(1)
197 val data = async {
198 try {
199 delay(Long.MAX_VALUE)
200 } finally {
201 expect(3)
202 }
203 }
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300204 yield()
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300205 // UI updater
206 withContext(coroutineContext) {
207 expect(2)
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300208 throw TestException1()
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300209 data.await() // Actually unreached
210 expectUnreached()
211 }
212 }
213
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300214 try {
215 loadData()
216 expectUnreached()
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300217 } catch (e: TestException1) {
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300218 finish(4)
219 }
220 }
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300221
Andrew Mikhaylov3de2be42018-09-13 16:30:55 +0300222 @Test
Roman Elizarov0f943042018-10-08 16:13:01 +0300223 fun testCoroutineScopeCancellationVsException() = runTest {
224 expect(1)
225 var job: Job? = null
226 job = launch(start = CoroutineStart.UNDISPATCHED) {
227 expect(2)
228 try {
229 coroutineScope {
230 expect(3)
231 yield() // must suspend
232 expect(5)
233 job!!.cancel() // cancel this job _before_ it throws
234 throw TestException1()
235 }
236 } catch (e: TestException1) {
237 // must have caught TextException
238 expect(6)
239 }
240 }
241 expect(4)
242 yield() // to coroutineScope
243 finish(7)
244 }
245
246 @Test
Andrew Mikhaylov3de2be42018-09-13 16:30:55 +0300247 fun testScopePlusContext() {
248 assertSame(EmptyCoroutineContext, scopePlusContext(EmptyCoroutineContext, EmptyCoroutineContext))
249 assertSame(Dispatchers.Default, scopePlusContext(EmptyCoroutineContext, Dispatchers.Default))
250 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Default, EmptyCoroutineContext))
251 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Default, Dispatchers.Default))
252 assertSame(Dispatchers.Default, scopePlusContext(Dispatchers.Unconfined, Dispatchers.Default))
253 assertSame(Dispatchers.Unconfined, scopePlusContext(Dispatchers.Default, Dispatchers.Unconfined))
254 assertSame(Dispatchers.Unconfined, scopePlusContext(Dispatchers.Unconfined, Dispatchers.Unconfined))
255 }
256
257 private fun scopePlusContext(c1: CoroutineContext, c2: CoroutineContext) =
Roman Elizarov5c44afb2018-09-25 14:53:08 +0300258 (ContextScope(c1) + c2).coroutineContext
Roman Elizarov52bfdab2018-09-27 19:17:16 +0300259
260 private class TestException1 : Exception()
261 private class TestException2 : Exception()
Vsevolod Tolstopyatov79414ec2018-08-30 16:50:56 +0300262}