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