• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.tasks
6 
7 import com.google.android.gms.tasks.*
8 import kotlinx.coroutines.*
9 import org.junit.*
10 import org.junit.Test
11 import java.util.concurrent.locks.*
12 import kotlin.concurrent.*
13 import kotlin.test.*
14 
15 class TaskTest : TestBase() {
16     @Before
17     fun setup() {
18         ignoreLostThreads("ForkJoinPool.commonPool-worker-")
19     }
20 
21     @Test
22     fun testCompletedDeferredAsTask() = runTest {
23         expect(1)
24         val deferred = async(start = CoroutineStart.UNDISPATCHED) {
25             expect(2) // Completed immediately
26             "OK"
27         }
28         expect(3)
29         val task = deferred.asTask()
30         assertEquals("OK", task.await())
31         finish(4)
32     }
33 
34     @Test
35     fun testDeferredAsTask() = runTest {
36         expect(1)
37         val deferred = async {
38             expect(3) // Completed later
39             "OK"
40         }
41         expect(2)
42         val task = deferred.asTask()
43         assertEquals("OK", task.await())
44         finish(4)
45     }
46 
47     @Test
48     fun testCancelledAsTask() = runTest {
49         val deferred = async(Dispatchers.Default) {
50             delay(100)
51         }.apply { cancel() }
52 
53         val task = deferred.asTask()
54         try {
55             runTest { task.await() }
56         } catch (e: Exception) {
57             assertTrue(e is CancellationException)
58             assertTrue(task.isCanceled)
59         }
60     }
61 
62     @Test
63     fun testThrowingAsTask() = runTest({ e -> e is TestException }) {
64         val deferred = async<Int>(Dispatchers.Default) {
65             throw TestException("Fail")
66         }
67 
68         val task = deferred.asTask()
69         runTest(expected = { it is TestException }) {
70             task.await()
71         }
72     }
73 
74     @Test
75     fun testStateAsTask() = runTest {
76         val lock = ReentrantLock().apply { lock() }
77 
78         val deferred: Deferred<Int> = Tasks.call {
79             lock.withLock { 42 }
80         }.asDeferred()
81 
82         assertFalse(deferred.isCompleted)
83         lock.unlock()
84 
85         assertEquals(42, deferred.await())
86         assertTrue(deferred.isCompleted)
87     }
88 
89     @Test
90     fun testTaskAsDeferred() = runTest {
91         val deferred = Tasks.forResult(42).asDeferred()
92         assertEquals(42, deferred.await())
93     }
94 
95     @Test
96     fun testNullResultTaskAsDeferred() = runTest {
97         assertNull(Tasks.forResult(null).asDeferred().await())
98     }
99 
100     @Test
101     fun testCancelledTaskAsDeferred() = runTest {
102         val deferred = Tasks.forCanceled<Int>().asDeferred()
103 
104         assertTrue(deferred.isCancelled)
105         try {
106             deferred.await()
107             fail("deferred.await() should be cancelled")
108         } catch (e: Exception) {
109             assertTrue(e is CancellationException)
110         }
111     }
112 
113     @Test
114     fun testFailedTaskAsDeferred() = runTest {
115         val deferred = Tasks.forException<Int>(TestException("something went wrong")).asDeferred()
116 
117         assertTrue(deferred.isCancelled && deferred.isCompleted)
118         val completionException = deferred.getCompletionExceptionOrNull()!!
119         assertTrue(completionException is TestException)
120         assertEquals("something went wrong", completionException.message)
121 
122         try {
123             deferred.await()
124             fail("deferred.await() should throw an exception")
125         } catch (e: Exception) {
126             assertTrue(e is TestException)
127             assertEquals("something went wrong", e.message)
128         }
129     }
130 
131     @Test
132     fun testFailingTaskAsDeferred() = runTest {
133         val lock = ReentrantLock().apply { lock() }
134 
135         val deferred: Deferred<Int> = Tasks.call {
136             lock.withLock { throw TestException("something went wrong") }
137         }.asDeferred()
138 
139         assertFalse(deferred.isCompleted)
140         lock.unlock()
141 
142         try {
143             deferred.await()
144             fail("deferred.await() should throw an exception")
145         } catch (e: Exception) {
146             assertTrue(e is TestException)
147             assertEquals("something went wrong", e.message)
148             assertSame(e.cause, deferred.getCompletionExceptionOrNull()) // debug mode stack augmentation
149         }
150     }
151 
152     @Test
153     fun testCancellableTaskAsDeferred() = runTest {
154         val cancellationTokenSource = CancellationTokenSource()
155         val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource)
156         assertEquals(42, deferred.await())
157         assertTrue(cancellationTokenSource.token.isCancellationRequested)
158     }
159 
160     @Test
161     fun testNullResultCancellableTaskAsDeferred() = runTest {
162         val cancellationTokenSource = CancellationTokenSource()
163         assertNull(Tasks.forResult(null).asDeferred(cancellationTokenSource).await())
164         assertTrue(cancellationTokenSource.token.isCancellationRequested)
165     }
166 
167     @Test
168     fun testCancelledCancellableTaskAsDeferred() = runTest {
169         val cancellationTokenSource = CancellationTokenSource()
170         val deferred = Tasks.forCanceled<Int>().asDeferred(cancellationTokenSource)
171 
172         assertTrue(deferred.isCancelled)
173         try {
174             deferred.await()
175             fail("deferred.await() should be cancelled")
176         } catch (e: Exception) {
177             assertTrue(e is CancellationException)
178         }
179         assertTrue(cancellationTokenSource.token.isCancellationRequested)
180     }
181 
182     @Test
183     fun testCancellingCancellableTaskAsDeferred() = runTest {
184         val cancellationTokenSource = CancellationTokenSource()
185         val task = TaskCompletionSource<Int>(cancellationTokenSource.token).task
186         val deferred = task.asDeferred(cancellationTokenSource)
187 
188         deferred.cancel()
189         try {
190             deferred.await()
191             fail("deferred.await() should be cancelled")
192         } catch (e: Exception) {
193             assertTrue(e is CancellationException)
194         }
195         assertTrue(cancellationTokenSource.token.isCancellationRequested)
196     }
197 
198     @Test
199     fun testExternallyCancelledCancellableTaskAsDeferred() = runTest {
200         val cancellationTokenSource = CancellationTokenSource()
201         val task = TaskCompletionSource<Int>(cancellationTokenSource.token).task
202         val deferred = task.asDeferred(cancellationTokenSource)
203 
204         cancellationTokenSource.cancel()
205 
206         try {
207             deferred.await()
208             fail("deferred.await() should be cancelled")
209         } catch (e: Exception) {
210             assertTrue(e is CancellationException)
211         }
212         assertTrue(cancellationTokenSource.token.isCancellationRequested)
213     }
214 
215     @Test
216     fun testSeparatelyCancelledCancellableTaskAsDeferred() = runTest {
217         val cancellationTokenSource = CancellationTokenSource()
218         val task = TaskCompletionSource<Int>().task
219         task.asDeferred(cancellationTokenSource)
220 
221         cancellationTokenSource.cancel()
222 
223         assertTrue(cancellationTokenSource.token.isCancellationRequested)
224     }
225 
226     @Test
227     fun testFailedCancellableTaskAsDeferred() = runTest {
228         val cancellationTokenSource = CancellationTokenSource()
229         val deferred = Tasks.forException<Int>(TestException("something went wrong")).asDeferred(cancellationTokenSource)
230 
231         assertTrue(deferred.isCancelled && deferred.isCompleted)
232         val completionException = deferred.getCompletionExceptionOrNull()!!
233         assertTrue(completionException is TestException)
234         assertEquals("something went wrong", completionException.message)
235 
236         try {
237             deferred.await()
238             fail("deferred.await() should throw an exception")
239         } catch (e: Exception) {
240             assertTrue(e is TestException)
241             assertEquals("something went wrong", e.message)
242         }
243         assertTrue(cancellationTokenSource.token.isCancellationRequested)
244     }
245 
246     @Test
247     fun testFailingCancellableTaskAsDeferred() = runTest {
248         val cancellationTokenSource = CancellationTokenSource()
249         val lock = ReentrantLock().apply { lock() }
250 
251         val deferred: Deferred<Int> = Tasks.call {
252             lock.withLock { throw TestException("something went wrong") }
253         }.asDeferred(cancellationTokenSource)
254 
255         assertFalse(deferred.isCompleted)
256         lock.unlock()
257 
258         try {
259             deferred.await()
260             fail("deferred.await() should throw an exception")
261         } catch (e: Exception) {
262             assertTrue(e is TestException)
263             assertEquals("something went wrong", e.message)
264             assertSame(e.cause, deferred.getCompletionExceptionOrNull()) // debug mode stack augmentation
265         }
266         assertTrue(cancellationTokenSource.token.isCancellationRequested)
267     }
268 
269     @Test
270     fun testFastPathCompletedTaskWithCancelledTokenSourceAsDeferred() = runTest {
271         val cancellationTokenSource = CancellationTokenSource()
272         val deferred = Tasks.forResult(42).asDeferred(cancellationTokenSource)
273         cancellationTokenSource.cancel()
274         assertEquals(42, deferred.await())
275     }
276 
277     @Test
278     fun testAwaitCancellableTask() = runTest {
279         val cancellationTokenSource = CancellationTokenSource()
280         val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token)
281 
282         val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) {
283             taskCompletionSource.task.await(cancellationTokenSource)
284         }
285 
286         assertFalse(deferred.isCompleted)
287         taskCompletionSource.setResult(42)
288 
289         assertEquals(42, deferred.await())
290         assertTrue(deferred.isCompleted)
291     }
292 
293     @Test
294     fun testFailedAwaitTask() = runTest(expected = { it is TestException }) {
295         val cancellationTokenSource = CancellationTokenSource()
296         val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token)
297 
298         val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) {
299             taskCompletionSource.task.await(cancellationTokenSource)
300         }
301 
302         assertFalse(deferred.isCompleted)
303         taskCompletionSource.setException(TestException("something went wrong"))
304 
305         deferred.await()
306     }
307 
308     @Test
309     fun testCancelledAwaitCancellableTask() = runTest {
310         val cancellationTokenSource = CancellationTokenSource()
311         val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token)
312 
313         val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) {
314             taskCompletionSource.task.await(cancellationTokenSource)
315         }
316 
317         assertFalse(deferred.isCompleted)
318         // Cancel the deferred
319         deferred.cancel()
320 
321         try {
322             deferred.await()
323             fail("deferred.await() should be cancelled")
324         } catch (e: Exception) {
325             assertTrue(e is CancellationException)
326         }
327 
328         assertTrue(cancellationTokenSource.token.isCancellationRequested)
329     }
330 
331     @Test
332     fun testExternallyCancelledAwaitCancellableTask() = runTest {
333         val cancellationTokenSource = CancellationTokenSource()
334         val taskCompletionSource = TaskCompletionSource<Int>(cancellationTokenSource.token)
335 
336         val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) {
337             taskCompletionSource.task.await(cancellationTokenSource)
338         }
339 
340         assertFalse(deferred.isCompleted)
341         // Cancel the cancellation token source
342         cancellationTokenSource.cancel()
343 
344         try {
345             deferred.await()
346             fail("deferred.await() should be cancelled")
347         } catch (e: Exception) {
348             assertTrue(e is CancellationException)
349         }
350 
351         assertTrue(cancellationTokenSource.token.isCancellationRequested)
352     }
353 
354     @Test
355     fun testFastPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest {
356         val cancellationTokenSource = CancellationTokenSource()
357         // Construct a task without the cancellation token source
358         val taskCompletionSource = TaskCompletionSource<Int>()
359 
360         val deferred: Deferred<Int> = async(start = CoroutineStart.LAZY) {
361             taskCompletionSource.task.await(cancellationTokenSource)
362         }
363 
364         assertFalse(deferred.isCompleted)
365         cancellationTokenSource.cancel()
366 
367         // Cancelling the token doesn't cancel the deferred
368         assertTrue(cancellationTokenSource.token.isCancellationRequested)
369         assertFalse(deferred.isCompleted)
370 
371         // Cleanup
372         deferred.cancel()
373     }
374 
375     @Test
376     fun testSlowPathCancellationTokenSourceCancelledAwaitCancellableTask() = runTest {
377         val cancellationTokenSource = CancellationTokenSource()
378         // Construct a task without the cancellation token source
379         val taskCompletionSource = TaskCompletionSource<Int>()
380 
381         val deferred: Deferred<Int> = async(start = CoroutineStart.UNDISPATCHED) {
382             taskCompletionSource.task.await(cancellationTokenSource)
383         }
384 
385         assertFalse(deferred.isCompleted)
386         cancellationTokenSource.cancel()
387 
388         // Cancelling the token doesn't cancel the deferred
389         assertTrue(cancellationTokenSource.token.isCancellationRequested)
390         assertFalse(deferred.isCompleted)
391 
392         // Cleanup
393         deferred.cancel()
394     }
395 
396     @Test
397     fun testFastPathWithCompletedTaskAndCanceledTokenSourceAwaitTask() = runTest {
398         val firstCancellationTokenSource = CancellationTokenSource()
399         val secondCancellationTokenSource = CancellationTokenSource()
400         // Construct a task with a different cancellation token source
401         val taskCompletionSource = TaskCompletionSource<Int>(firstCancellationTokenSource.token)
402 
403         val deferred: Deferred<Int> = async(start = CoroutineStart.LAZY) {
404             taskCompletionSource.task.await(secondCancellationTokenSource)
405         }
406 
407         assertFalse(deferred.isCompleted)
408         secondCancellationTokenSource.cancel()
409 
410         assertFalse(deferred.isCompleted)
411         taskCompletionSource.setResult(42)
412 
413         assertEquals(42, deferred.await())
414         assertTrue(deferred.isCompleted)
415     }
416 
417     class TestException(message: String) : Exception(message)
418 }
419