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