blob: 5ebd129534e50ce6a612b4d7438d102c4c49a80d [file] [log] [blame]
Roman Elizarov8bff72b2017-12-20 12:55:38 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
18
19package kotlinx.coroutines.experimental
20
Roman Elizarov9fe5f462018-02-21 19:05:52 +030021import kotlin.coroutines.experimental.*
Roman Elizarov8bff72b2017-12-20 12:55:38 +030022import kotlin.test.*
23
Roman Elizarovaa461cf2018-04-11 13:20:29 +030024class CoroutinesTest : TestBase() {
Roman Elizarov8bff72b2017-12-20 12:55:38 +030025 @Test
26 fun testSimple() = runTest {
27 expect(1)
28 finish(2)
29 }
30
31 @Test
32 fun testYield() = runTest {
33 expect(1)
34 yield() // effectively does nothing, as we don't have other coroutines
35 finish(2)
36 }
37
38 @Test
39 fun testLaunchAndYieldJoin() = runTest {
40 expect(1)
41 val job = launch(coroutineContext) {
42 expect(3)
43 yield()
44 expect(4)
45 }
46 expect(2)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030047 assertTrue(job.isActive && !job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030048 job.join()
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030049 assertTrue(!job.isActive && job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030050 finish(5)
51 }
52
53 @Test
54 fun testLaunchUndispatched() = runTest {
55 expect(1)
56 val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
57 expect(2)
58 yield()
59 expect(4)
60 }
61 expect(3)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030062 assertTrue(job.isActive && !job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030063 job.join()
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030064 assertTrue(!job.isActive && job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030065 finish(5)
66 }
67
68 @Test
69 fun testNested() = runTest {
70 expect(1)
71 val j1 = launch(coroutineContext) {
72 expect(3)
73 val j2 = launch(coroutineContext) {
74 expect(5)
75 }
76 expect(4)
77 j2.join()
78 expect(6)
79 }
80 expect(2)
81 j1.join()
82 finish(7)
83 }
84
85 @Test
86 fun testWaitChild() = runTest {
87 expect(1)
88 launch(coroutineContext) {
89 expect(3)
90 yield() // to parent
91 finish(5)
92 }
93 expect(2)
94 yield()
95 expect(4)
96 // parent waits for child's completion
97 }
98
99 @Test
100 fun testCancelChildExplicit() = runTest {
101 expect(1)
102 val job = launch(coroutineContext) {
103 expect(3)
104 yield()
105 expectUnreached()
106 }
107 expect(2)
108 yield()
109 expect(4)
110 job.cancel()
111 finish(5)
112 }
113
114 @Test
115 fun testCancelChildWithFinally() = runTest {
116 expect(1)
117 val job = launch(coroutineContext) {
118 expect(3)
119 try {
120 yield()
121 } finally {
122 finish(6) // cancelled child will still execute finally
123 }
124 expectUnreached()
125 }
126 expect(2)
127 yield()
128 expect(4)
129 job.cancel()
130 expect(5)
131 }
132
133 @Test
134 fun testWaitNestedChild() = runTest {
135 expect(1)
136 launch(coroutineContext) {
137 expect(3)
138 launch(coroutineContext) {
139 expect(6)
140 yield() // to parent
141 expect(9)
142 }
143 expect(4)
144 yield()
145 expect(7)
146 yield() // to parent
147 finish(10) // the last one to complete
148 }
149 expect(2)
150 yield()
151 expect(5)
152 yield()
153 expect(8)
154 // parent waits for child
155 }
156
157 @Test
158 fun testExceptionPropagation() = runTest(
159 expected = { it is TestException }
160 ) {
161 finish(1)
162 throw TestException()
163 }
164
165 @Test
166 fun testCancelParentOnChildException() = runTest(
167 expected = { it is JobCancellationException && it.cause is TestException },
168 unhandled = listOf({ it -> it is TestException })
169 ) {
170 expect(1)
171 launch(coroutineContext) {
172 finish(3)
173 throwTestException() // does not propagate exception to launch, but cancels parent (!)
174 expectUnreached()
175 }
176 expect(2)
177 yield()
178 expectUnreached() // because of exception in child
179 }
180
181 @Test
182 fun testCancelParentOnNestedException() = runTest(
183 expected = { it is JobCancellationException && it.cause is TestException },
184 unhandled = listOf(
185 { it -> it is TestException },
186 { it -> it is TestException }
187 )
188 ) {
189 expect(1)
190 launch(coroutineContext) {
191 expect(3)
192 launch(coroutineContext) {
193 finish(6)
194 throwTestException() // unhandled exception kills all parents
195 expectUnreached()
196 }
197 expect(4)
198 yield()
199 expectUnreached() // because of exception in child
200 }
201 expect(2)
202 yield()
203 expect(5)
204 yield()
205 expectUnreached() // because of exception in child
206 }
207
208 @Test
209 fun testJoinWithFinally() = runTest {
210 expect(1)
211 val job = launch(coroutineContext) {
212 expect(3)
213 try {
214 yield() // to main, will cancel us
215 } finally {
216 expect(7) // join is waiting
217 }
218 }
219 expect(2)
220 yield() // to job
221 expect(4)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300222 assertTrue(job.isActive && !job.isCompleted && !job.isCancelled)
223 assertTrue(job.cancel()) // cancels job
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300224 expect(5) // still here
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300225 assertTrue(!job.isActive && !job.isCompleted && job.isCancelled)
226 assertTrue(!job.cancel()) // second attempt returns false
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300227 expect(6) // we're still here
228 job.join() // join the job, let job complete its "finally" section
229 expect(8)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300230 assertTrue(!job.isActive && job.isCompleted && job.isCancelled)
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300231 finish(9)
232 }
233
234 @Test
235 fun testCancelAndJoin() = runTest {
236 expect(1)
237 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
238 try {
239 expect(2)
240 yield()
241 expectUnreached() // will get cancelled
242 } finally {
243 expect(4)
244 }
245 }
246 expect(3)
247 job.cancelAndJoin()
248 finish(5)
249 }
250
251 @Test
252 fun testCancelAndJoinChildCrash() = runTest(
253 expected = { it is JobCancellationException && it.cause is TestException },
254 unhandled = listOf({it -> it is TestException })
255 ) {
256 expect(1)
257 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
258 expect(2)
259 throwTestException()
260 expectUnreached()
261 }
262 // now we have a failed job with TestException
263 finish(3)
264 try {
265 job.cancelAndJoin() // join should crash on child's exception but it will be wrapped into JobCancellationException
266 } catch (e: Throwable) {
267 e as JobCancellationException // type assertion
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300268 assertTrue(e.cause is TestException)
269 assertTrue(e.job === coroutineContext[Job])
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300270 throw e
271 }
272 expectUnreached()
273 }
274
275 @Test
276 fun testYieldInFinally() = runTest(
277 expected = { it is TestException }
278 ) {
279 expect(1)
280 try {
281 expect(2)
282 throwTestException()
283 } finally {
284 expect(3)
285 yield()
286 finish(4)
287 }
288 expectUnreached()
289 }
290
291 @Test
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300292 fun testCancelAndJoinChildren() = runTest {
293 expect(1)
294 val parent = Job()
295 launch(coroutineContext, CoroutineStart.UNDISPATCHED, parent = parent) {
296 expect(2)
297 try {
298 yield() // to be cancelled
299 } finally {
300 expect(5)
301 }
302 expectUnreached()
303 }
304 expect(3)
305 parent.cancelChildren()
306 expect(4)
307 parent.joinChildren() // will yield to child
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300308 assertTrue(parent.isActive) // make sure it did not cancel parent
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300309 finish(6)
310 }
311
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300312 @Test
313 fun testParentCrashCancelsChildren() = runTest(
314 unhandled = listOf({ it -> it is TestException })
315 ) {
316 expect(1)
317 val parent = launch(coroutineContext + Job()) {
318 expect(4)
319 throw TestException("Crashed")
320 }
321 launch(coroutineContext + parent, CoroutineStart.UNDISPATCHED) {
322 expect(2)
323 try {
324 yield() // to test
325 } finally {
326 expect(5)
327 withContext(NonCancellable) { yield() } // to test
328 expect(7)
329 }
330 expectUnreached() // will get cancelled, because parent crashes
331 }
332 expect(3)
333 yield() // to parent
334 expect(6)
335 parent.join() // make sure crashed parent still waits for its child
336 finish(8)
337 }
338
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300339 @Test
340 fun testNotCancellableChildWithExceptionCancelled() = runTest(
341 expected = { it is TestException }
342 ) {
343 expect(1)
344 // CoroutineStart.ATOMIC makes sure it will not get cancelled for it starts executing
345 val d = async(coroutineContext, start = CoroutineStart.ATOMIC) {
346 finish(4)
347 throwTestException() // will throw
348 expectUnreached()
349 }
350 expect(2)
351 // now cancel with some other exception
352 d.cancel(IllegalArgumentException())
353 // now await to see how it got crashed -- IAE should have been suppressed by TestException
354 expect(3)
355 d.await()
356 }
357
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300358 private fun throwTestException() { throw TestException() }
359
360 private class TestException : Exception {
361 constructor(message: String): super(message)
362 constructor(): super()
363 }
364}