1 2 @file:Suppress("NAMED_ARGUMENTS_NOT_ALLOWED") // KT-21913 3 4 package kotlinx.coroutines 5 6 import kotlinx.coroutines.testing.* 7 import kotlinx.coroutines.channels.* 8 import kotlin.test.* 9 import kotlin.time.* 10 import kotlin.time.Duration.Companion.milliseconds 11 import kotlin.time.Duration.Companion.seconds 12 13 class WithTimeoutOrNullDurationTest : 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 = withTimeoutOrNull(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 = withTimeoutOrNull(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 property dispatching of `withTimeoutOrNull` 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 = withTimeoutOrNull(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 * Tests that a 100% CPU-consuming loop will react on timeout if it has yields. 71 */ 72 @Test <lambda>null73 fun testYieldBlockingWithTimeout() = runTest { 74 expect(1) 75 val result = withTimeoutOrNull(100.milliseconds) { 76 while (true) { 77 yield() 78 } 79 } 80 assertNull(result) 81 finish(2) 82 } 83 84 @Test testSmallTimeoutnull85 fun testSmallTimeout() = runTest { 86 val channel = Channel<Int>(1) 87 val value = withTimeoutOrNull(1.milliseconds) { 88 channel.receive() 89 } 90 assertNull(value) 91 } 92 93 @Test <lambda>null94 fun testThrowException() = runTest(expected = {it is AssertionError}) { <lambda>null95 withTimeoutOrNull<Unit>(Duration.INFINITE) { 96 throw AssertionError() 97 } 98 } 99 100 @Test testInnerTimeoutnull101 fun testInnerTimeout() = runTest( 102 expected = { it is CancellationException } <lambda>null103 ) { 104 withTimeoutOrNull(1000.milliseconds) { 105 withTimeout(10.milliseconds) { 106 while (true) { 107 yield() 108 } 109 } 110 @Suppress("UNREACHABLE_CODE") 111 expectUnreached() // will timeout 112 } 113 expectUnreached() // will timeout 114 } 115 116 @Test testNestedTimeoutnull117 fun testNestedTimeout() = runTest(expected = { it is TimeoutCancellationException }) { <lambda>null118 withTimeoutOrNull(Duration.INFINITE) { 119 // Exception from this withTimeout is not suppressed by withTimeoutOrNull 120 withTimeout(10.milliseconds) { 121 delay(Duration.INFINITE) 122 1 123 } 124 } 125 126 expectUnreached() 127 } 128 129 @Test <lambda>null130 fun testOuterTimeout() = runTest { 131 if (isJavaAndWindows) return@runTest 132 var counter = 0 133 val result = withTimeoutOrNull(320.milliseconds) { 134 while (true) { 135 val inner = withTimeoutOrNull(150.milliseconds) { 136 while (true) { 137 yield() 138 } 139 } 140 assertNull(inner) 141 counter++ 142 } 143 } 144 assertNull(result) 145 check(counter in 1..2) {"Executed: $counter times"} 146 } 147 148 @Test <lambda>null149 fun testBadClass() = runTest { 150 val bad = BadClass() 151 val result = withTimeoutOrNull(100.milliseconds) { 152 bad 153 } 154 assertSame(bad, result) 155 } 156 157 class BadClass { equalsnull158 override fun equals(other: Any?): Boolean = error("Should not be called") 159 override fun hashCode(): Int = error("Should not be called") 160 override fun toString(): String = error("Should not be called") 161 } 162 163 @Test 164 fun testNullOnTimeout() = runTest { 165 expect(1) 166 val result = withTimeoutOrNull(100.milliseconds) { 167 expect(2) 168 delay(1000.milliseconds) 169 expectUnreached() 170 "OK" 171 } 172 assertNull(result) 173 finish(3) 174 } 175 176 @Test <lambda>null177 fun testSuppressExceptionWithResult() = runTest { 178 expect(1) 179 val result = withTimeoutOrNull(100.milliseconds) { 180 expect(2) 181 try { 182 delay(1000.milliseconds) 183 } catch (e: CancellationException) { 184 expect(3) 185 } 186 "OK" 187 } 188 assertNull(result) 189 finish(4) 190 } 191 192 @Test <lambda>null193 fun testSuppressExceptionWithAnotherException() = runTest { 194 expect(1) 195 try { 196 withTimeoutOrNull(100.milliseconds) { 197 expect(2) 198 try { 199 delay(1000.milliseconds) 200 } catch (e: CancellationException) { 201 expect(3) 202 throw TestException() 203 } 204 expectUnreached() 205 "OK" 206 } 207 expectUnreached() 208 } catch (e: TestException) { 209 // catches TestException 210 finish(4) 211 212 } 213 } 214 215 @Test <lambda>null216 fun testNegativeTimeout() = runTest { 217 expect(1) 218 var result = withTimeoutOrNull(-1.milliseconds) { 219 expectUnreached() 220 } 221 assertNull(result) 222 result = withTimeoutOrNull(0.milliseconds) { 223 expectUnreached() 224 } 225 assertNull(result) 226 finish(2) 227 } 228 229 @Test <lambda>null230 fun testExceptionFromWithinTimeout() = runTest { 231 expect(1) 232 try { 233 expect(2) 234 withTimeoutOrNull<Unit>(1000.milliseconds) { 235 expect(3) 236 throw TestException() 237 } 238 expectUnreached() 239 } catch (e: TestException) { 240 finish(4) 241 } 242 } 243 } 244