1 2 /* 3 * Copyright 2016-2020 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") // KT-21913 7 8 package kotlinx.coroutines 9 10 import kotlinx.coroutines.channels.* 11 import kotlin.test.* 12 import kotlin.time.* 13 14 @ExperimentalTime 15 class WithTimeoutOrNullDurationTest : TestBase() { 16 /** 17 * Tests a case of no timeout and no suspension inside. 18 */ 19 @Test <lambda>null20 fun testBasicNoSuspend() = runTest { 21 expect(1) 22 val result = withTimeoutOrNull(10.seconds) { 23 expect(2) 24 "OK" 25 } 26 assertEquals("OK", result) 27 finish(3) 28 } 29 30 /** 31 * Tests a case of no timeout and one suspension inside. 32 */ 33 @Test <lambda>null34 fun testBasicSuspend() = runTest { 35 expect(1) 36 val result = withTimeoutOrNull(10.seconds) { 37 expect(2) 38 yield() 39 expect(3) 40 "OK" 41 } 42 assertEquals("OK", result) 43 finish(4) 44 } 45 46 /** 47 * Tests property dispatching of `withTimeoutOrNull` blocks 48 */ 49 @Test <lambda>null50 fun testDispatch() = runTest { 51 expect(1) 52 launch { 53 expect(4) 54 yield() // back to main 55 expect(7) 56 } 57 expect(2) 58 // test that it does not yield to the above job when started 59 val result = withTimeoutOrNull(1.seconds) { 60 expect(3) 61 yield() // yield only now 62 expect(5) 63 "OK" 64 } 65 assertEquals("OK", result) 66 expect(6) 67 yield() // back to launch 68 finish(8) 69 } 70 71 /** 72 * Tests that a 100% CPU-consuming loop will react on timeout if it has yields. 73 */ 74 @Test <lambda>null75 fun testYieldBlockingWithTimeout() = runTest { 76 expect(1) 77 val result = withTimeoutOrNull(100.milliseconds) { 78 while (true) { 79 yield() 80 } 81 } 82 assertNull(result) 83 finish(2) 84 } 85 86 @Test testSmallTimeoutnull87 fun testSmallTimeout() = runTest { 88 val channel = Channel<Int>(1) 89 val value = withTimeoutOrNull(1.milliseconds) { 90 channel.receive() 91 } 92 assertNull(value) 93 } 94 95 @Test <lambda>null96 fun testThrowException() = runTest(expected = {it is AssertionError}) { <lambda>null97 withTimeoutOrNull(Duration.INFINITE) { 98 throw AssertionError() 99 } 100 } 101 102 @Test testInnerTimeoutnull103 fun testInnerTimeout() = runTest( 104 expected = { it is CancellationException } <lambda>null105 ) { 106 withTimeoutOrNull(1000.milliseconds) { 107 withTimeout(10.milliseconds) { 108 while (true) { 109 yield() 110 } 111 } 112 expectUnreached() // will timeout 113 } 114 expectUnreached() // will timeout 115 } 116 117 @Test testNestedTimeoutnull118 fun testNestedTimeout() = runTest(expected = { it is TimeoutCancellationException }) { <lambda>null119 withTimeoutOrNull(Duration.INFINITE) { 120 // Exception from this withTimeout is not suppressed by withTimeoutOrNull 121 withTimeout(10.milliseconds) { 122 delay(Duration.INFINITE) 123 1 124 } 125 } 126 127 expectUnreached() 128 } 129 130 @Test <lambda>null131 fun testOuterTimeout() = runTest { 132 var counter = 0 133 val result = withTimeoutOrNull(250.milliseconds) { 134 while (true) { 135 val inner = withTimeoutOrNull(100.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(1000.milliseconds) { 235 expect(3) 236 throw TestException() 237 } 238 expectUnreached() 239 } catch (e: TestException) { 240 finish(4) 241 } 242 } 243 } 244