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") // KT-21913
6 
7 package kotlinx.coroutines
8 
9 import kotlin.test.*
10 
11 class AsyncLazyTest : TestBase() {
12 
13     @Test
14     fun testSimple() = runTest {
15         expect(1)
16         val d = async(start = CoroutineStart.LAZY) {
17             expect(3)
18             42
19         }
20         expect(2)
21         assertTrue(!d.isActive && !d.isCompleted)
22         assertEquals(d.await(), 42)
23         assertTrue(!d.isActive && d.isCompleted && !d.isCancelled)
24         expect(4)
25         assertEquals(d.await(), 42) // second await -- same result
26         finish(5)
27     }
28 
29     @Test
30     fun testLazyDeferAndYield() = runTest {
31         expect(1)
32         val d = async(start = CoroutineStart.LAZY) {
33             expect(3)
34             yield() // this has not effect, because parent coroutine is waiting
35             expect(4)
36             42
37         }
38         expect(2)
39         assertTrue(!d.isActive && !d.isCompleted)
40         assertEquals(d.await(), 42)
41         assertTrue(!d.isActive && d.isCompleted && !d.isCancelled)
42         expect(5)
43         assertEquals(d.await(), 42) // second await -- same result
44         finish(6)
45     }
46 
47     @Test
48     fun testLazyDeferAndYield2() = runTest {
49         expect(1)
50         val d = async(start = CoroutineStart.LAZY) {
51             expect(7)
52             42
53         }
54         expect(2)
55         assertTrue(!d.isActive && !d.isCompleted)
56         launch { // see how it looks from another coroutine
57             expect(4)
58             assertTrue(!d.isActive && !d.isCompleted)
59             yield() // yield back to main
60             expect(6)
61             assertTrue(d.isActive && !d.isCompleted) // implicitly started by main's await
62             yield() // yield to d
63         }
64         expect(3)
65         assertTrue(!d.isActive && !d.isCompleted)
66         yield() // yield to second child (lazy async is not computing yet)
67         expect(5)
68         assertTrue(!d.isActive && !d.isCompleted)
69         assertEquals(d.await(), 42) // starts computing
70         assertTrue(!d.isActive && d.isCompleted && !d.isCancelled)
71         finish(8)
72     }
73 
74     @Test
75     fun testSimpleException() = runTest(
76         expected = { it is TestException }
77     ) {
78         expect(1)
79         val d = async(start = CoroutineStart.LAZY) {
80             finish(3)
81             throw TestException()
82         }
83         expect(2)
84         assertTrue(!d.isActive && !d.isCompleted)
85         d.await() // will throw IOException
86     }
87 
88     @Test
89     fun testLazyDeferAndYieldException() =  runTest(
90         expected = { it is TestException }
91     ) {
92         expect(1)
93         val d = async(start = CoroutineStart.LAZY) {
94             expect(3)
95             yield() // this has not effect, because parent coroutine is waiting
96             finish(4)
97             throw TestException()
98         }
99         expect(2)
100         assertTrue(!d.isActive && !d.isCompleted)
101         d.await() // will throw IOException
102     }
103 
104     @Test
105     fun testCatchException() = runTest {
106         expect(1)
107         val d = async(NonCancellable, start = CoroutineStart.LAZY) {
108             expect(3)
109             throw TestException()
110         }
111         expect(2)
112         assertTrue(!d.isActive && !d.isCompleted)
113         try {
114             d.await() // will throw IOException
115         } catch (e: TestException) {
116             assertTrue(!d.isActive && d.isCompleted && d.isCancelled)
117             expect(4)
118         }
119         finish(5)
120     }
121 
122     @Test
123     fun testStart() = runTest {
124         expect(1)
125         val d = async(start = CoroutineStart.LAZY) {
126             expect(4)
127             42
128         }
129         expect(2)
130         assertTrue(!d.isActive && !d.isCompleted)
131         assertTrue(d.start())
132         assertTrue(d.isActive && !d.isCompleted)
133         expect(3)
134         assertTrue(!d.start())
135         yield() // yield to started coroutine
136         assertTrue(!d.isActive && d.isCompleted && !d.isCancelled) // and it finishes
137         expect(5)
138         assertEquals(d.await(), 42) // await sees result
139         finish(6)
140     }
141 
142     @Test
143     fun testCancelBeforeStart() = runTest(
144         expected = { it is CancellationException }
145     ) {
146         expect(1)
147         val d = async(start = CoroutineStart.LAZY) {
148             expectUnreached()
149             42
150         }
151         expect(2)
152         assertTrue(!d.isActive && !d.isCompleted)
153         d.cancel()
154         assertTrue(!d.isActive && d.isCompleted && d.isCancelled)
155         assertTrue(!d.start())
156         finish(3)
157         assertEquals(d.await(), 42) // await shall throw CancellationException
158         expectUnreached()
159     }
160 
161     @Test
162     fun testCancelWhileComputing() = runTest(
163         expected = { it is CancellationException }
164     ) {
165         expect(1)
166         val d = async(start = CoroutineStart.LAZY) {
167             expect(4)
168             yield() // yield to main, that is going to cancel us
169             expectUnreached()
170             42
171         }
172         expect(2)
173         assertTrue(!d.isActive && !d.isCompleted && !d.isCancelled)
174         assertTrue(d.start())
175         assertTrue(d.isActive && !d.isCompleted && !d.isCancelled)
176         expect(3)
177         yield() // yield to d
178         expect(5)
179         assertTrue(d.isActive && !d.isCompleted && !d.isCancelled)
180         d.cancel()
181         assertTrue(!d.isActive && d.isCancelled) // cancelling !
182         assertTrue(!d.isActive && d.isCancelled) // still cancelling
183         finish(6)
184         assertEquals(d.await(), 42) // await shall throw CancellationException
185         expectUnreached()
186     }
187 }