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 import kotlin.time.Duration.Companion.milliseconds 14 import kotlin.time.Duration.Companion.seconds 15 16 class WithTimeoutOrNullDurationTest : TestBase() { 17 /** 18 * Tests a case of no timeout and no suspension inside. 19 */ 20 @Test <lambda>null21 fun testBasicNoSuspend() = runTest { 22 expect(1) 23 val result = withTimeoutOrNull(10.seconds) { 24 expect(2) 25 "OK" 26 } 27 assertEquals("OK", result) 28 finish(3) 29 } 30 31 /** 32 * Tests a case of no timeout and one suspension inside. 33 */ 34 @Test <lambda>null35 fun testBasicSuspend() = runTest { 36 expect(1) 37 val result = withTimeoutOrNull(10.seconds) { 38 expect(2) 39 yield() 40 expect(3) 41 "OK" 42 } 43 assertEquals("OK", result) 44 finish(4) 45 } 46 47 /** 48 * Tests property dispatching of `withTimeoutOrNull` blocks 49 */ 50 @Test <lambda>null51 fun testDispatch() = runTest { 52 expect(1) 53 launch { 54 expect(4) 55 yield() // back to main 56 expect(7) 57 } 58 expect(2) 59 // test that it does not yield to the above job when started 60 val result = withTimeoutOrNull(1.seconds) { 61 expect(3) 62 yield() // yield only now 63 expect(5) 64 "OK" 65 } 66 assertEquals("OK", result) 67 expect(6) 68 yield() // back to launch 69 finish(8) 70 } 71 72 /** 73 * Tests that a 100% CPU-consuming loop will react on timeout if it has yields. 74 */ 75 @Test <lambda>null76 fun testYieldBlockingWithTimeout() = runTest { 77 expect(1) 78 val result = withTimeoutOrNull(100.milliseconds) { 79 while (true) { 80 yield() 81 } 82 } 83 assertNull(result) 84 finish(2) 85 } 86 87 @Test testSmallTimeoutnull88 fun testSmallTimeout() = runTest { 89 val channel = Channel<Int>(1) 90 val value = withTimeoutOrNull(1.milliseconds) { 91 channel.receive() 92 } 93 assertNull(value) 94 } 95 96 @Test <lambda>null97 fun testThrowException() = runTest(expected = {it is AssertionError}) { <lambda>null98 withTimeoutOrNull<Unit>(Duration.INFINITE) { 99 throw AssertionError() 100 } 101 } 102 103 @Test testInnerTimeoutnull104 fun testInnerTimeout() = runTest( 105 expected = { it is CancellationException } <lambda>null106 ) { 107 withTimeoutOrNull(1000.milliseconds) { 108 withTimeout(10.milliseconds) { 109 while (true) { 110 yield() 111 } 112 } 113 @Suppress("UNREACHABLE_CODE") 114 expectUnreached() // will timeout 115 } 116 expectUnreached() // will timeout 117 } 118 119 @Test testNestedTimeoutnull120 fun testNestedTimeout() = runTest(expected = { it is TimeoutCancellationException }) { <lambda>null121 withTimeoutOrNull(Duration.INFINITE) { 122 // Exception from this withTimeout is not suppressed by withTimeoutOrNull 123 withTimeout(10.milliseconds) { 124 delay(Duration.INFINITE) 125 1 126 } 127 } 128 129 expectUnreached() 130 } 131 132 @Test <lambda>null133 fun testOuterTimeout() = runTest { 134 if (isJavaAndWindows) return@runTest 135 var counter = 0 136 val result = withTimeoutOrNull(320.milliseconds) { 137 while (true) { 138 val inner = withTimeoutOrNull(150.milliseconds) { 139 while (true) { 140 yield() 141 } 142 } 143 assertNull(inner) 144 counter++ 145 } 146 } 147 assertNull(result) 148 check(counter in 1..2) {"Executed: $counter times"} 149 } 150 151 @Test <lambda>null152 fun testBadClass() = runTest { 153 val bad = BadClass() 154 val result = withTimeoutOrNull(100.milliseconds) { 155 bad 156 } 157 assertSame(bad, result) 158 } 159 160 class BadClass { equalsnull161 override fun equals(other: Any?): Boolean = error("Should not be called") 162 override fun hashCode(): Int = error("Should not be called") 163 override fun toString(): String = error("Should not be called") 164 } 165 166 @Test 167 fun testNullOnTimeout() = runTest { 168 expect(1) 169 val result = withTimeoutOrNull(100.milliseconds) { 170 expect(2) 171 delay(1000.milliseconds) 172 expectUnreached() 173 "OK" 174 } 175 assertNull(result) 176 finish(3) 177 } 178 179 @Test <lambda>null180 fun testSuppressExceptionWithResult() = runTest { 181 expect(1) 182 val result = withTimeoutOrNull(100.milliseconds) { 183 expect(2) 184 try { 185 delay(1000.milliseconds) 186 } catch (e: CancellationException) { 187 expect(3) 188 } 189 "OK" 190 } 191 assertNull(result) 192 finish(4) 193 } 194 195 @Test <lambda>null196 fun testSuppressExceptionWithAnotherException() = runTest { 197 expect(1) 198 try { 199 withTimeoutOrNull(100.milliseconds) { 200 expect(2) 201 try { 202 delay(1000.milliseconds) 203 } catch (e: CancellationException) { 204 expect(3) 205 throw TestException() 206 } 207 expectUnreached() 208 "OK" 209 } 210 expectUnreached() 211 } catch (e: TestException) { 212 // catches TestException 213 finish(4) 214 215 } 216 } 217 218 @Test <lambda>null219 fun testNegativeTimeout() = runTest { 220 expect(1) 221 var result = withTimeoutOrNull(-1.milliseconds) { 222 expectUnreached() 223 } 224 assertNull(result) 225 result = withTimeoutOrNull(0.milliseconds) { 226 expectUnreached() 227 } 228 assertNull(result) 229 finish(2) 230 } 231 232 @Test <lambda>null233 fun testExceptionFromWithinTimeout() = runTest { 234 expect(1) 235 try { 236 expect(2) 237 withTimeoutOrNull<Unit>(1000.milliseconds) { 238 expect(3) 239 throw TestException() 240 } 241 expectUnreached() 242 } catch (e: TestException) { 243 finish(4) 244 } 245 } 246 } 247