1 /*
2  * Copyright 2016-2020 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") // KT-21913
6 
7 package kotlinx.coroutines
8 
9 import kotlin.test.*
10 import kotlin.time.*
11 
12 @ExperimentalTime
13 class WithTimeoutDurationTest : TestBase() {
14     /**
15      * Tests a case of no timeout and no suspension inside.
16      */
17     @Test
<lambda>null18     fun testBasicNoSuspend() = runTest {
19         expect(1)
20         val result = withTimeout(10.seconds) {
21             expect(2)
22             "OK"
23         }
24         assertEquals("OK", result)
25         finish(3)
26     }
27 
28     /**
29      * Tests a case of no timeout and one suspension inside.
30      */
31     @Test
<lambda>null32     fun testBasicSuspend() = runTest {
33         expect(1)
34         val result = withTimeout(10.seconds) {
35             expect(2)
36             yield()
37             expect(3)
38             "OK"
39         }
40         assertEquals("OK", result)
41         finish(4)
42     }
43 
44     /**
45      * Tests proper dispatching of `withTimeout` blocks
46      */
47     @Test
<lambda>null48     fun testDispatch() = runTest {
49         expect(1)
50         launch {
51             expect(4)
52             yield() // back to main
53             expect(7)
54         }
55         expect(2)
56         // test that it does not yield to the above job when started
57         val result = withTimeout(1.seconds) {
58             expect(3)
59             yield() // yield only now
60             expect(5)
61             "OK"
62         }
63         assertEquals("OK", result)
64         expect(6)
65         yield() // back to launch
66         finish(8)
67     }
68 
69 
70     /**
71      * Tests that a 100% CPU-consuming loop will react on timeout if it has yields.
72      */
73     @Test
testYieldBlockingWithTimeoutnull74     fun testYieldBlockingWithTimeout() = runTest(
75             expected = { it is CancellationException }
<lambda>null76     ) {
77         withTimeout(100.milliseconds) {
78             while (true) {
79                 yield()
80             }
81         }
82     }
83 
84     /**
85      * Tests that [withTimeout] waits for children coroutines to complete.
86      */
87     @Test
<lambda>null88     fun testWithTimeoutChildWait() = runTest {
89         expect(1)
90         withTimeout(100.milliseconds) {
91             expect(2)
92             // launch child with timeout
93             launch {
94                 expect(4)
95             }
96             expect(3)
97             // now will wait for child before returning
98         }
99         finish(5)
100     }
101 
102     @Test
<lambda>null103     fun testBadClass() = runTest {
104         val bad = BadClass()
105         val result = withTimeout(100.milliseconds) {
106             bad
107         }
108         assertSame(bad, result)
109     }
110 
111     class BadClass {
equalsnull112         override fun equals(other: Any?): Boolean = error("Should not be called")
113         override fun hashCode(): Int = error("Should not be called")
114         override fun toString(): String = error("Should not be called")
115     }
116 
117     @Test
118     fun testExceptionOnTimeout() = runTest {
119         expect(1)
120         try {
121             withTimeout(100.milliseconds) {
122                 expect(2)
123                 delay(1000.milliseconds)
124                 expectUnreached()
125                 "OK"
126             }
127         } catch (e: CancellationException) {
128             assertEquals("Timed out waiting for 100 ms", e.message)
129             finish(3)
130         }
131     }
132 
133     @Test
testSuppressExceptionWithResultnull134     fun testSuppressExceptionWithResult() = runTest(
135             expected = { it is CancellationException }
<lambda>null136     ) {
137         expect(1)
138         withTimeout(100.milliseconds) {
139             expect(2)
140             try {
141                 delay(1000.milliseconds)
142             } catch (e: CancellationException) {
143                 finish(3)
144             }
145             "OK"
146         }
147         expectUnreached()
148     }
149 
150     @Test
<lambda>null151     fun testSuppressExceptionWithAnotherException() = runTest {
152         expect(1)
153         try {
154             withTimeout(100.milliseconds) {
155                 expect(2)
156                 try {
157                     delay(1000.milliseconds)
158                 } catch (e: CancellationException) {
159                     expect(3)
160                     throw TestException()
161                 }
162                 expectUnreached()
163                 "OK"
164             }
165             expectUnreached()
166         } catch (e: TestException) {
167             finish(4)
168         }
169     }
170 
171     @Test
<lambda>null172     fun testNegativeTimeout() = runTest {
173         expect(1)
174         try {
175             withTimeout(-1.milliseconds) {
176                 expectUnreached()
177                 "OK"
178             }
179         } catch (e: TimeoutCancellationException) {
180             assertEquals("Timed out immediately", e.message)
181             finish(2)
182         }
183     }
184 
185     @Test
<lambda>null186     fun testExceptionFromWithinTimeout() = runTest {
187         expect(1)
188         try {
189             expect(2)
190             withTimeout(1.seconds) {
191                 expect(3)
192                 throw TestException()
193             }
194             expectUnreached()
195         } catch (e: TestException) {
196             finish(4)
197         }
198     }
199 
200     @Test
<lambda>null201     fun testIncompleteWithTimeoutState() = runTest {
202         lateinit var timeoutJob: Job
203         val handle = withTimeout(Duration.INFINITE) {
204             timeoutJob = coroutineContext[Job]!!
205             timeoutJob.invokeOnCompletion { }
206         }
207 
208         handle.dispose()
209         timeoutJob.join()
210         assertTrue(timeoutJob.isCompleted)
211         assertFalse(timeoutJob.isActive)
212         assertFalse(timeoutJob.isCancelled)
213     }
214 }
215