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