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