blob: 2d27ee35c2f6e661df340c7a3a14a2b19abb950a [file] [log] [blame]
Roman Elizarov8bff72b2017-12-20 12:55:38 +03001/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03002 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarov8bff72b2017-12-20 12:55:38 +03003 */
4
5@file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913
6
7package kotlinx.coroutines.experimental
8
Roman Elizarov9fe5f462018-02-21 19:05:52 +03009import kotlin.coroutines.experimental.*
Roman Elizarov8bff72b2017-12-20 12:55:38 +030010import kotlin.test.*
11
Roman Elizarovaa461cf2018-04-11 13:20:29 +030012class CoroutinesTest : TestBase() {
Roman Elizarov8bff72b2017-12-20 12:55:38 +030013 @Test
14 fun testSimple() = runTest {
15 expect(1)
16 finish(2)
17 }
18
19 @Test
20 fun testYield() = runTest {
21 expect(1)
22 yield() // effectively does nothing, as we don't have other coroutines
23 finish(2)
24 }
25
26 @Test
27 fun testLaunchAndYieldJoin() = runTest {
28 expect(1)
29 val job = launch(coroutineContext) {
30 expect(3)
31 yield()
32 expect(4)
33 }
34 expect(2)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030035 assertTrue(job.isActive && !job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030036 job.join()
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030037 assertTrue(!job.isActive && job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030038 finish(5)
39 }
40
41 @Test
42 fun testLaunchUndispatched() = runTest {
43 expect(1)
44 val job = launch(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
45 expect(2)
46 yield()
47 expect(4)
48 }
49 expect(3)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030050 assertTrue(job.isActive && !job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030051 job.join()
Roman Elizarovc0d71dc2017-12-21 22:12:43 +030052 assertTrue(!job.isActive && job.isCompleted)
Roman Elizarov8bff72b2017-12-20 12:55:38 +030053 finish(5)
54 }
55
56 @Test
57 fun testNested() = runTest {
58 expect(1)
59 val j1 = launch(coroutineContext) {
60 expect(3)
61 val j2 = launch(coroutineContext) {
62 expect(5)
63 }
64 expect(4)
65 j2.join()
66 expect(6)
67 }
68 expect(2)
69 j1.join()
70 finish(7)
71 }
72
73 @Test
74 fun testWaitChild() = runTest {
75 expect(1)
76 launch(coroutineContext) {
77 expect(3)
78 yield() // to parent
79 finish(5)
80 }
81 expect(2)
82 yield()
83 expect(4)
84 // parent waits for child's completion
85 }
86
87 @Test
88 fun testCancelChildExplicit() = runTest {
89 expect(1)
90 val job = launch(coroutineContext) {
91 expect(3)
92 yield()
93 expectUnreached()
94 }
95 expect(2)
96 yield()
97 expect(4)
98 job.cancel()
99 finish(5)
100 }
101
102 @Test
103 fun testCancelChildWithFinally() = runTest {
104 expect(1)
105 val job = launch(coroutineContext) {
106 expect(3)
107 try {
108 yield()
109 } finally {
110 finish(6) // cancelled child will still execute finally
111 }
112 expectUnreached()
113 }
114 expect(2)
115 yield()
116 expect(4)
117 job.cancel()
118 expect(5)
119 }
120
121 @Test
122 fun testWaitNestedChild() = runTest {
123 expect(1)
124 launch(coroutineContext) {
125 expect(3)
126 launch(coroutineContext) {
127 expect(6)
128 yield() // to parent
129 expect(9)
130 }
131 expect(4)
132 yield()
133 expect(7)
134 yield() // to parent
135 finish(10) // the last one to complete
136 }
137 expect(2)
138 yield()
139 expect(5)
140 yield()
141 expect(8)
142 // parent waits for child
143 }
144
145 @Test
146 fun testExceptionPropagation() = runTest(
147 expected = { it is TestException }
148 ) {
149 finish(1)
150 throw TestException()
151 }
152
153 @Test
154 fun testCancelParentOnChildException() = runTest(
155 expected = { it is JobCancellationException && it.cause is TestException },
156 unhandled = listOf({ it -> it is TestException })
157 ) {
158 expect(1)
159 launch(coroutineContext) {
160 finish(3)
161 throwTestException() // does not propagate exception to launch, but cancels parent (!)
162 expectUnreached()
163 }
164 expect(2)
165 yield()
166 expectUnreached() // because of exception in child
167 }
168
169 @Test
170 fun testCancelParentOnNestedException() = runTest(
171 expected = { it is JobCancellationException && it.cause is TestException },
172 unhandled = listOf(
173 { it -> it is TestException },
174 { it -> it is TestException }
175 )
176 ) {
177 expect(1)
178 launch(coroutineContext) {
179 expect(3)
180 launch(coroutineContext) {
181 finish(6)
182 throwTestException() // unhandled exception kills all parents
183 expectUnreached()
184 }
185 expect(4)
186 yield()
187 expectUnreached() // because of exception in child
188 }
189 expect(2)
190 yield()
191 expect(5)
192 yield()
193 expectUnreached() // because of exception in child
194 }
195
196 @Test
197 fun testJoinWithFinally() = runTest {
198 expect(1)
199 val job = launch(coroutineContext) {
200 expect(3)
201 try {
202 yield() // to main, will cancel us
203 } finally {
204 expect(7) // join is waiting
205 }
206 }
207 expect(2)
208 yield() // to job
209 expect(4)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300210 assertTrue(job.isActive && !job.isCompleted && !job.isCancelled)
211 assertTrue(job.cancel()) // cancels job
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300212 expect(5) // still here
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300213 assertTrue(!job.isActive && !job.isCompleted && job.isCancelled)
214 assertTrue(!job.cancel()) // second attempt returns false
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300215 expect(6) // we're still here
216 job.join() // join the job, let job complete its "finally" section
217 expect(8)
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300218 assertTrue(!job.isActive && job.isCompleted && job.isCancelled)
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300219 finish(9)
220 }
221
222 @Test
223 fun testCancelAndJoin() = runTest {
224 expect(1)
225 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
226 try {
227 expect(2)
228 yield()
229 expectUnreached() // will get cancelled
230 } finally {
231 expect(4)
232 }
233 }
234 expect(3)
235 job.cancelAndJoin()
236 finish(5)
237 }
238
239 @Test
240 fun testCancelAndJoinChildCrash() = runTest(
241 expected = { it is JobCancellationException && it.cause is TestException },
242 unhandled = listOf({it -> it is TestException })
243 ) {
244 expect(1)
245 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
246 expect(2)
247 throwTestException()
248 expectUnreached()
249 }
250 // now we have a failed job with TestException
251 finish(3)
252 try {
253 job.cancelAndJoin() // join should crash on child's exception but it will be wrapped into JobCancellationException
254 } catch (e: Throwable) {
255 e as JobCancellationException // type assertion
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300256 assertTrue(e.cause is TestException)
257 assertTrue(e.job === coroutineContext[Job])
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300258 throw e
259 }
260 expectUnreached()
261 }
262
263 @Test
264 fun testYieldInFinally() = runTest(
265 expected = { it is TestException }
266 ) {
267 expect(1)
268 try {
269 expect(2)
270 throwTestException()
271 } finally {
272 expect(3)
273 yield()
274 finish(4)
275 }
276 expectUnreached()
277 }
278
279 @Test
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300280 fun testCancelAndJoinChildren() = runTest {
281 expect(1)
282 val parent = Job()
283 launch(coroutineContext, CoroutineStart.UNDISPATCHED, parent = parent) {
284 expect(2)
285 try {
286 yield() // to be cancelled
287 } finally {
288 expect(5)
289 }
290 expectUnreached()
291 }
292 expect(3)
293 parent.cancelChildren()
294 expect(4)
295 parent.joinChildren() // will yield to child
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300296 assertTrue(parent.isActive) // make sure it did not cancel parent
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300297 finish(6)
298 }
299
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300300 @Test
301 fun testParentCrashCancelsChildren() = runTest(
302 unhandled = listOf({ it -> it is TestException })
303 ) {
304 expect(1)
305 val parent = launch(coroutineContext + Job()) {
306 expect(4)
307 throw TestException("Crashed")
308 }
309 launch(coroutineContext + parent, CoroutineStart.UNDISPATCHED) {
310 expect(2)
311 try {
312 yield() // to test
313 } finally {
314 expect(5)
315 withContext(NonCancellable) { yield() } // to test
316 expect(7)
317 }
318 expectUnreached() // will get cancelled, because parent crashes
319 }
320 expect(3)
321 yield() // to parent
322 expect(6)
323 parent.join() // make sure crashed parent still waits for its child
324 finish(8)
325 }
326
Roman Elizarovc0d71dc2017-12-21 22:12:43 +0300327 @Test
328 fun testNotCancellableChildWithExceptionCancelled() = runTest(
329 expected = { it is TestException }
330 ) {
331 expect(1)
332 // CoroutineStart.ATOMIC makes sure it will not get cancelled for it starts executing
333 val d = async(coroutineContext, start = CoroutineStart.ATOMIC) {
334 finish(4)
335 throwTestException() // will throw
336 expectUnreached()
337 }
338 expect(2)
339 // now cancel with some other exception
340 d.cancel(IllegalArgumentException())
341 // now await to see how it got crashed -- IAE should have been suppressed by TestException
342 expect(3)
343 d.await()
344 }
345
Roman Elizarov8bff72b2017-12-20 12:55:38 +0300346 private fun throwTestException() { throw TestException() }
347
348 private class TestException : Exception {
349 constructor(message: String): super(message)
350 constructor(): super()
351 }
352}