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