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 var counter = 0 135 val result = withTimeoutOrNull(320.milliseconds) { 136 while (true) { 137 val inner = withTimeoutOrNull(150.milliseconds) { 138 while (true) { 139 yield() 140 } 141 } 142 assertNull(inner) 143 counter++ 144 } 145 } 146 assertNull(result) 147 check(counter in 1..2) {"Executed: $counter times"} 148 } 149 150 @Test <lambda>null151 fun testBadClass() = runTest { 152 val bad = BadClass() 153 val result = withTimeoutOrNull(100.milliseconds) { 154 bad 155 } 156 assertSame(bad, result) 157 } 158 159 class BadClass { equalsnull160 override fun equals(other: Any?): Boolean = error("Should not be called") 161 override fun hashCode(): Int = error("Should not be called") 162 override fun toString(): String = error("Should not be called") 163 } 164 165 @Test 166 fun testNullOnTimeout() = runTest { 167 expect(1) 168 val result = withTimeoutOrNull(100.milliseconds) { 169 expect(2) 170 delay(1000.milliseconds) 171 expectUnreached() 172 "OK" 173 } 174 assertNull(result) 175 finish(3) 176 } 177 178 @Test <lambda>null179 fun testSuppressExceptionWithResult() = runTest { 180 expect(1) 181 val result = withTimeoutOrNull(100.milliseconds) { 182 expect(2) 183 try { 184 delay(1000.milliseconds) 185 } catch (e: CancellationException) { 186 expect(3) 187 } 188 "OK" 189 } 190 assertNull(result) 191 finish(4) 192 } 193 194 @Test <lambda>null195 fun testSuppressExceptionWithAnotherException() = runTest { 196 expect(1) 197 try { 198 withTimeoutOrNull(100.milliseconds) { 199 expect(2) 200 try { 201 delay(1000.milliseconds) 202 } catch (e: CancellationException) { 203 expect(3) 204 throw TestException() 205 } 206 expectUnreached() 207 "OK" 208 } 209 expectUnreached() 210 } catch (e: TestException) { 211 // catches TestException 212 finish(4) 213 214 } 215 } 216 217 @Test <lambda>null218 fun testNegativeTimeout() = runTest { 219 expect(1) 220 var result = withTimeoutOrNull(-1.milliseconds) { 221 expectUnreached() 222 } 223 assertNull(result) 224 result = withTimeoutOrNull(0.milliseconds) { 225 expectUnreached() 226 } 227 assertNull(result) 228 finish(2) 229 } 230 231 @Test <lambda>null232 fun testExceptionFromWithinTimeout() = runTest { 233 expect(1) 234 try { 235 expect(2) 236 withTimeoutOrNull<Unit>(1000.milliseconds) { 237 expect(3) 238 throw TestException() 239 } 240 expectUnreached() 241 } catch (e: TestException) { 242 finish(4) 243 } 244 } 245 } 246