• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED", "UNREACHABLE_CODE", "USELESS_IS_CHECK") // KT-21913
6 
7 package kotlinx.coroutines
8 
9 import kotlin.test.*
10 
11 @Suppress("DEPRECATION") // cancel(cause)
12 class AsyncTest : TestBase() {
13 
14     @Test
<lambda>null15     fun testSimple() = runTest {
16         expect(1)
17         val d = async {
18             expect(3)
19             42
20         }
21         expect(2)
22         assertTrue(d.isActive)
23         assertEquals(d.await(), 42)
24         assertTrue(!d.isActive)
25         expect(4)
26         assertEquals(d.await(), 42) // second await -- same result
27         finish(5)
28     }
29 
30     @Test
testUndispatchednull31     fun testUndispatched() = runTest {
32         expect(1)
33         val d = async(start = CoroutineStart.UNDISPATCHED) {
34             expect(2)
35             42
36         }
37         expect(3)
38         assertTrue(!d.isActive)
39         assertEquals(d.await(), 42)
40         finish(4)
41     }
42 
43     @Test
<lambda>null44     fun testSimpleException() = runTest(expected = { it is TestException }) {
45         expect(1)
<lambda>null46         val d = async<Unit> {
47             finish(3)
48             throw TestException()
49         }
50         expect(2)
51         d.await() // will throw TestException
52     }
53 
54     @Test
<lambda>null55     fun testCancellationWithCause() = runTest {
56         expect(1)
57         val d = async(NonCancellable, start = CoroutineStart.ATOMIC) {
58             expect(3)
59             yield()
60         }
61         expect(2)
62         d.cancel(TestCancellationException("TEST"))
63         try {
64             d.await()
65         } catch (e: TestCancellationException) {
66             finish(4)
67             assertEquals("TEST", e.message)
68         }
69     }
70 
71     @Test
<lambda>null72     fun testLostException() = runTest {
73         expect(1)
74         val deferred = async(Job()) {
75             expect(2)
76             throw Exception()
77         }
78 
79         // Exception is not consumed -> nothing is reported
80         deferred.join()
81         finish(3)
82     }
83 
84     @Test
<lambda>null85     fun testParallelDecompositionCaughtException() = runTest {
86         val deferred = async(NonCancellable) {
87             val decomposed = async(NonCancellable) {
88                 throw TestException()
89                 1
90             }
91             try {
92                 decomposed.await()
93             } catch (e: TestException) {
94                 42
95             }
96         }
97         assertEquals(42, deferred.await())
98     }
99 
100     @Test
<lambda>null101     fun testParallelDecompositionCaughtExceptionWithInheritedParent() = runTest {
102         expect(1)
103         val deferred = async(NonCancellable) {
104             expect(2)
105             val decomposed = async { // inherits parent job!
106                 expect(3)
107                 throw TestException()
108                 1
109             }
110             try {
111                 decomposed.await()
112             } catch (e: TestException) {
113                 expect(4) // Should catch this exception, but parent is already cancelled
114                 42
115             }
116         }
117         try {
118             // This will fail
119             assertEquals(42, deferred.await())
120         } catch (e: TestException) {
121             finish(5)
122         }
123     }
124 
125     @Test
<lambda>null126     fun testParallelDecompositionUncaughtExceptionWithInheritedParent() = runTest(expected = { it is TestException }) {
<lambda>null127         val deferred = async(NonCancellable) {
128             val decomposed = async {
129                 throw TestException()
130                 1
131             }
132 
133             decomposed.await()
134         }
135 
136         deferred.await()
137         expectUnreached()
138     }
139 
140     @Test
<lambda>null141     fun testParallelDecompositionUncaughtException() = runTest(expected = { it is TestException }) {
<lambda>null142         val deferred = async(NonCancellable) {
143             val decomposed = async {
144                 throw TestException()
145                 1
146             }
147 
148             decomposed.await()
149         }
150 
151         deferred.await()
152         expectUnreached()
153     }
154 
155     @Test
<lambda>null156     fun testCancellationTransparency() = runTest {
157         val deferred = async(NonCancellable, start = CoroutineStart.ATOMIC) {
158             expect(2)
159             throw TestException()
160         }
161         expect(1)
162         deferred.cancel()
163         try {
164             deferred.await()
165         } catch (e: TestException) {
166             finish(3)
167         }
168     }
169 
170     @Test
<lambda>null171     fun testDeferAndYieldException() = runTest(expected = { it is TestException }) {
172         expect(1)
<lambda>null173         val d = async<Unit> {
174             expect(3)
175             yield() // no effect, parent waiting
176             finish(4)
177             throw TestException()
178         }
179         expect(2)
180         d.await() // will throw IOException
181     }
182 
183     @Test
<lambda>null184     fun testDeferWithTwoWaiters() = runTest {
185         expect(1)
186         val d = async {
187             expect(5)
188             yield()
189             expect(9)
190             42
191         }
192         expect(2)
193         launch {
194             expect(6)
195             assertEquals(d.await(), 42)
196             expect(11)
197         }
198         expect(3)
199         launch {
200             expect(7)
201             assertEquals(d.await(), 42)
202             expect(12)
203         }
204         expect(4)
205         yield() // this actually yields control to async, which produces results and resumes both waiters (in order)
206         expect(8)
207         yield() // yield again to "d", which completes
208         expect(10)
209         yield() // yield to both waiters
210         finish(13)
211     }
212 
213     @Test
<lambda>null214     fun testDeferBadClass() = runTest {
215         val bad = BadClass()
216         val d = async {
217             expect(1)
218             bad
219         }
220         assertSame(d.await(), bad)
221         finish(2)
222     }
223 
224     @Test
<lambda>null225     fun testOverriddenParent() = runTest {
226         val parent = Job()
227         val deferred = async(parent, CoroutineStart.ATOMIC) {
228             expect(2)
229             delay(Long.MAX_VALUE)
230         }
231 
232         parent.cancel()
233         try {
234             expect(1)
235             deferred.await()
236         } catch (e: CancellationException) {
237             finish(3)
238         }
239     }
240 
241     @Test
<lambda>null242     fun testIncompleteAsyncState() = runTest {
243         val deferred = async {
244             coroutineContext[Job]!!.invokeOnCompletion {  }
245         }
246 
247         deferred.await().dispose()
248         assertTrue(deferred.getCompleted() is DisposableHandle)
249         assertNull(deferred.getCompletionExceptionOrNull())
250         assertTrue(deferred.isCompleted)
251         assertFalse(deferred.isActive)
252         assertFalse(deferred.isCancelled)
253     }
254 
255     @Test
<lambda>null256     fun testIncompleteAsyncFastPath() = runTest {
257         val deferred = async(Dispatchers.Unconfined) {
258             coroutineContext[Job]!!.invokeOnCompletion {  }
259         }
260 
261         deferred.await().dispose()
262         assertTrue(deferred.getCompleted() is DisposableHandle)
263         assertNull(deferred.getCompletionExceptionOrNull())
264         assertTrue(deferred.isCompleted)
265         assertFalse(deferred.isActive)
266         assertFalse(deferred.isCancelled)
267     }
268 
269     @Test
<lambda>null270     fun testAsyncWithFinally() = runTest {
271         expect(1)
272 
273         @Suppress("UNREACHABLE_CODE")
274         val d = async {
275             expect(3)
276             try {
277                 yield() // to main, will cancel
278             } finally {
279                 expect(6) // will go there on await
280                 return@async "Fail" // result will not override cancellation
281             }
282             expectUnreached()
283             "Fail2"
284         }
285         expect(2)
286         yield() // to async
287         expect(4)
288         check(d.isActive && !d.isCompleted && !d.isCancelled)
289         d.cancel()
290         check(!d.isActive && !d.isCompleted && d.isCancelled)
291         check(!d.isActive && !d.isCompleted && d.isCancelled)
292         expect(5)
293         try {
294             d.await() // awaits
295             expectUnreached() // does not complete normally
296         } catch (e: Throwable) {
297             expect(7)
298             check(e is CancellationException)
299         }
300         check(!d.isActive && d.isCompleted && d.isCancelled)
301         finish(8)
302     }
303 }
304