1 /*
2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlin.test.*
8 
9 class AwaitTest : TestBase() {
10 
11     @Test
12     fun testAwaitAll() = runTest {
13         expect(1)
14         val d = async {
15             expect(3)
16             "OK"
17         }
18 
19         val d2 = async {
20             yield()
21             expect(4)
22             1L
23         }
24 
25         expect(2)
26         require(d2.isActive && !d2.isCompleted)
27 
28         assertEquals(listOf("OK", 1L), awaitAll(d, d2))
29         expect(5)
30 
31         require(d.isCompleted && d2.isCompleted)
32         require(!d.isCancelled && !d2.isCancelled)
33         finish(6)
34     }
35 
36     @Test
37     fun testAwaitAllLazy() = runTest {
38         expect(1)
39         val d = async(start = CoroutineStart.LAZY) {
40             expect(2)
41             1
42         }
43         val d2 = async(start = CoroutineStart.LAZY) {
44             expect(3)
45             2
46         }
47         assertEquals(listOf(1, 2), awaitAll(d, d2))
48         finish(4)
49     }
50 
51     @Test
52     fun testAwaitAllTyped() = runTest {
53         val d1 = async { 1L }
54         val d2 = async { "" }
55         val d3 = async { }
56 
57         assertEquals(listOf(1L, ""), listOf(d1, d2).awaitAll())
58         assertEquals(listOf(1L, Unit), listOf(d1, d3).awaitAll())
59         assertEquals(listOf("", Unit), listOf(d2, d3).awaitAll())
60     }
61 
62     @Test
63     fun testAwaitAllExceptionally() = runTest {
64         expect(1)
65         val d = async {
66             expect(3)
67             "OK"
68         }
69 
70         val d2 = async(NonCancellable) {
71             yield()
72             throw TestException()
73         }
74 
75         val d3 = async {
76             expect(4)
77             delay(Long.MAX_VALUE)
78             1
79         }
80 
81         expect(2)
82         try {
83             awaitAll(d, d2, d3)
84         } catch (e: TestException) {
85             expect(5)
86         }
87 
88         yield()
89         require(d.isCompleted && d2.isCancelled && d3.isActive)
90         d3.cancel()
91         finish(6)
92     }
93 
94     @Test
95     fun testAwaitAllMultipleExceptions() = runTest {
96         val d = async(NonCancellable) {
97             expect(2)
98             throw TestException()
99         }
100 
101         val d2 = async(NonCancellable) {
102             yield()
103             throw TestException()
104         }
105 
106         val d3 = async {
107             yield()
108         }
109 
110         expect(1)
111         try {
112             awaitAll(d, d2, d3)
113         } catch (e: TestException) {
114             expect(3)
115         }
116 
117         finish(4)
118     }
119 
120     @Test
121     fun testAwaitAllCancellation() = runTest {
122         val outer = async {
123 
124             expect(1)
125             val inner = async {
126                 expect(4)
127                 delay(Long.MAX_VALUE)
128             }
129 
130             expect(2)
131             awaitAll(inner)
132             expectUnreached()
133         }
134 
135         yield()
136         expect(3)
137         yield()
138         require(outer.isActive)
139         outer.cancel()
140         require(outer.isCancelled)
141         finish(5)
142     }
143 
144     @Test
145     fun testAwaitAllPartiallyCompleted() = runTest {
146         val d1 = async { expect(1); 1 }
147         d1.await()
148         val d2 = async { expect(3); 2 }
149         expect(2)
150         assertEquals(listOf(1, 2), awaitAll(d1, d2))
151         require(d1.isCompleted && d2.isCompleted)
152         finish(4)
153     }
154 
155     @Test
156     fun testAwaitAllPartiallyCompletedExceptionally() = runTest {
157         val d1 = async(NonCancellable) {
158             expect(1)
159             throw TestException()
160         }
161 
162         yield()
163 
164         // This job is called after exception propagation
165         val d2 = async { expect(4) }
166 
167         expect(2)
168         try {
169             awaitAll(d1, d2)
170             expectUnreached()
171         } catch (e: TestException) {
172             expect(3)
173         }
174 
175         require(d2.isActive)
176         d2.await()
177         require(d1.isCompleted && d2.isCompleted)
178         finish(5)
179     }
180 
181     @Test
182     fun testAwaitAllFullyCompleted() = runTest {
183         val d1 = CompletableDeferred(Unit)
184         val d2 = CompletableDeferred(Unit)
185         val job = async { expect(3) }
186         expect(1)
187         awaitAll(d1, d2)
188         expect(2)
189         job.await()
190         finish(4)
191     }
192 
193     @Test
194     fun testAwaitOnSet() = runTest {
195         val d1 = CompletableDeferred(Unit)
196         val d2 = CompletableDeferred(Unit)
197         val job = async { expect(2) }
198         expect(1)
199         listOf(d1, d2, job).awaitAll()
200         finish(3)
201     }
202 
203     @Test
204     fun testAwaitAllFullyCompletedExceptionally() = runTest {
205         val d1 = CompletableDeferred<Unit>(parent = null)
206             .apply { completeExceptionally(TestException()) }
207         val d2 = CompletableDeferred<Unit>(parent = null)
208             .apply { completeExceptionally(TestException()) }
209         val job = async { expect(3) }
210         expect(1)
211         try {
212             awaitAll(d1, d2)
213         } catch (e: TestException) {
214             expect(2)
215         }
216 
217         job.await()
218         finish(4)
219     }
220 
221     @Test
222     fun testAwaitAllSameJobMultipleTimes() = runTest {
223         val d = async { "OK" }
224         // Duplicates are allowed though kdoc doesn't guarantee that
225         assertEquals(listOf("OK", "OK", "OK"), awaitAll(d, d, d))
226     }
227 
228     @Test
229     fun testAwaitAllSameThrowingJobMultipleTimes() = runTest {
230         val d1 =
231             async(NonCancellable) { throw TestException() }
232         val d2 = async { } // do nothing
233 
234         try {
235             expect(1)
236             // Duplicates are allowed though kdoc doesn't guarantee that
237             awaitAll(d1, d2, d1, d2)
238             expectUnreached()
239         } catch (e: TestException) {
240             finish(2)
241         }
242     }
243 
244     @Test
245     fun testAwaitAllEmpty() = runTest {
246         expect(1)
247         assertEquals(emptyList(), awaitAll<Unit>())
248         assertEquals(emptyList(), emptyList<Deferred<Unit>>().awaitAll())
249         finish(2)
250     }
251 
252     // joinAll
253 
254     @Test
255     fun testJoinAll() = runTest {
256         val d1 = launch { expect(2) }
257         val d2 = async {
258             expect(3)
259             "OK"
260         }
261         val d3 = launch { expect(4) }
262 
263         expect(1)
264         joinAll(d1, d2, d3)
265         finish(5)
266     }
267 
268     @Test
269     fun testJoinAllLazy() = runTest {
270         expect(1)
271         val d = async(start = CoroutineStart.LAZY) {
272             expect(2)
273         }
274         val d2 = launch(start = CoroutineStart.LAZY) {
275             expect(3)
276         }
277         joinAll(d, d2)
278         finish(4)
279     }
280 
281     @Test
282     fun testJoinAllExceptionally() = runTest {
283         val d1 = launch {
284             expect(2)
285         }
286         val d2 = async(NonCancellable) {
287             expect(3)
288             throw TestException()
289         }
290         val d3 = async {
291             expect(4)
292         }
293 
294         expect(1)
295         joinAll(d1, d2, d3)
296         finish(5)
297     }
298 
299     @Test
300     fun testJoinAllCancellation() = runTest {
301         val outer = launch {
302             expect(2)
303             val inner = launch {
304                 expect(3)
305                 delay(Long.MAX_VALUE)
306             }
307 
308             joinAll(inner)
309             expectUnreached()
310         }
311 
312         expect(1)
313         yield()
314         require(outer.isActive)
315         yield()
316         outer.cancel()
317         outer.join()
318         finish(4)
319     }
320 
321     @Test
322     fun testJoinAllAlreadyCompleted() = runTest {
323         val job = launch {
324             expect(1)
325         }
326 
327         job.join()
328         expect(2)
329 
330         joinAll(job)
331         finish(3)
332     }
333 
334     @Test
335     fun testJoinAllEmpty() = runTest {
336         expect(1)
337         joinAll()
338         listOf<Job>().joinAll()
339         finish(2)
340     }
341 
342     @Test
343     fun testJoinAllSameJob() = runTest {
344         val job = launch { }
345         joinAll(job, job, job)
346     }
347 
348     @Test
349     fun testJoinAllSameJobExceptionally() = runTest {
350         val job =
351             async(NonCancellable) { throw TestException() }
352         joinAll(job, job, job)
353     }
354 }
355