1 /* <lambda>null2 * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.coroutines.test 6 7 import kotlinx.coroutines.* 8 import kotlinx.coroutines.flow.* 9 import kotlinx.coroutines.internal.* 10 import kotlin.coroutines.* 11 import kotlin.test.* 12 13 /** Copy of [RunTestTest], but for [TestCoroutineScope] */ 14 @Suppress("DEPRECATION") 15 class RunTestLegacyScopeTest { 16 17 @Test 18 fun testWithContextDispatching() = runTestWithLegacyScope { 19 var counter = 0 20 withContext(Dispatchers.Default) { 21 counter += 1 22 } 23 assertEquals(counter, 1) 24 } 25 26 @Test 27 fun testJoiningForkedJob() = runTestWithLegacyScope { 28 var counter = 0 29 val job = GlobalScope.launch { 30 counter += 1 31 } 32 job.join() 33 assertEquals(counter, 1) 34 } 35 36 @Test 37 fun testSuspendCoroutine() = runTestWithLegacyScope { 38 val answer = suspendCoroutine<Int> { 39 it.resume(42) 40 } 41 assertEquals(42, answer) 42 } 43 44 @Test 45 fun testNestedRunTestForbidden() = runTestWithLegacyScope { 46 assertFailsWith<IllegalStateException> { 47 runTest { } 48 } 49 } 50 51 @Test 52 fun testRunTestWithZeroTimeoutWithControlledDispatches() = runTestWithLegacyScope(dispatchTimeoutMs = 0) { 53 // below is some arbitrary concurrent code where all dispatches go through the same scheduler. 54 launch { 55 delay(2000) 56 } 57 val deferred = async { 58 val job = launch(StandardTestDispatcher(testScheduler)) { 59 launch { 60 delay(500) 61 } 62 delay(1000) 63 } 64 job.join() 65 } 66 deferred.await() 67 } 68 69 @Test 70 fun testRunTestWithZeroTimeoutWithUncontrolledDispatches() = testResultMap({ fn -> 71 assertFailsWith<UncompletedCoroutinesError> { fn() } 72 }) { 73 runTestWithLegacyScope(dispatchTimeoutMs = 0) { 74 withContext(Dispatchers.Default) { 75 delay(10) 76 3 77 } 78 fail("shouldn't be reached") 79 } 80 } 81 82 @Test 83 fun testRunTestWithSmallTimeout() = testResultMap({ fn -> 84 assertFailsWith<UncompletedCoroutinesError> { fn() } 85 }) { 86 runTestWithLegacyScope(dispatchTimeoutMs = 100) { 87 withContext(Dispatchers.Default) { 88 delay(10000) 89 3 90 } 91 fail("shouldn't be reached") 92 } 93 } 94 95 @Test 96 fun testRunTestWithLargeTimeout() = runTestWithLegacyScope(dispatchTimeoutMs = 5000) { 97 withContext(Dispatchers.Default) { 98 delay(50) 99 } 100 } 101 102 @Test 103 fun testRunTestTimingOutAndThrowing() = testResultMap({ fn -> 104 try { 105 fn() 106 fail("unreached") 107 } catch (e: UncompletedCoroutinesError) { 108 @Suppress("INVISIBLE_MEMBER") 109 val suppressed = unwrap(e).suppressedExceptions 110 assertEquals(1, suppressed.size) 111 assertIs<TestException>(suppressed[0]).also { 112 assertEquals("A", it.message) 113 } 114 } 115 }) { 116 runTestWithLegacyScope(dispatchTimeoutMs = 1) { 117 coroutineContext[CoroutineExceptionHandler]!!.handleException(coroutineContext, TestException("A")) 118 withContext(Dispatchers.Default) { 119 delay(10000) 120 3 121 } 122 fail("shouldn't be reached") 123 } 124 } 125 126 @Test 127 fun testRunTestWithIllegalContext() { 128 for (ctx in TestScopeTest.invalidContexts) { 129 assertFailsWith<IllegalArgumentException> { 130 runTestWithLegacyScope(ctx) { } 131 } 132 } 133 } 134 135 @Test 136 fun testThrowingInRunTestBody() = testResultMap({ 137 assertFailsWith<RuntimeException> { it() } 138 }) { 139 runTestWithLegacyScope { 140 throw RuntimeException() 141 } 142 } 143 144 @Test 145 fun testThrowingInRunTestPendingTask() = testResultMap({ 146 assertFailsWith<RuntimeException> { it() } 147 }) { 148 runTestWithLegacyScope { 149 launch { 150 delay(SLOW) 151 throw RuntimeException() 152 } 153 } 154 } 155 156 @Test 157 fun reproducer2405() = runTestWithLegacyScope { 158 val dispatcher = StandardTestDispatcher(testScheduler) 159 var collectedError = false 160 withContext(dispatcher) { 161 flow { emit(1) } 162 .combine( 163 flow<String> { throw IllegalArgumentException() } 164 ) { int, string -> int.toString() + string } 165 .catch { emit("error") } 166 .collect { 167 assertEquals("error", it) 168 collectedError = true 169 } 170 } 171 assertTrue(collectedError) 172 } 173 174 @Test 175 fun testChildrenCancellationOnTestBodyFailure(): TestResult { 176 var job: Job? = null 177 return testResultMap({ 178 assertFailsWith<AssertionError> { it() } 179 assertTrue(job!!.isCancelled) 180 }) { 181 runTestWithLegacyScope { 182 job = launch { 183 while (true) { 184 delay(1000) 185 } 186 } 187 throw AssertionError() 188 } 189 } 190 } 191 192 @Test 193 fun testTimeout() = testResultMap({ 194 assertFailsWith<TimeoutCancellationException> { it() } 195 }) { 196 runTestWithLegacyScope { 197 withTimeout(50) { 198 launch { 199 delay(1000) 200 } 201 } 202 } 203 } 204 205 @Test 206 fun testRunTestThrowsRootCause() = testResultMap({ 207 assertFailsWith<TestException> { it() } 208 }) { 209 runTestWithLegacyScope { 210 launch { 211 throw TestException() 212 } 213 } 214 } 215 216 @Test 217 fun testCompletesOwnJob(): TestResult { 218 var handlerCalled = false 219 return testResultMap({ 220 it() 221 assertTrue(handlerCalled) 222 }) { 223 runTestWithLegacyScope { 224 coroutineContext.job.invokeOnCompletion { 225 handlerCalled = true 226 } 227 } 228 } 229 } 230 231 @Test 232 fun testDoesNotCompleteGivenJob(): TestResult { 233 var handlerCalled = false 234 val job = Job() 235 job.invokeOnCompletion { 236 handlerCalled = true 237 } 238 return testResultMap({ 239 it() 240 assertFalse(handlerCalled) 241 assertEquals(0, job.children.filter { it.isActive }.count()) 242 }) { 243 runTestWithLegacyScope(job) { 244 assertTrue(coroutineContext.job in job.children) 245 } 246 } 247 } 248 249 @Test 250 fun testSuppressedExceptions() = testResultMap({ 251 try { 252 it() 253 fail("should not be reached") 254 } catch (e: TestException) { 255 assertEquals("w", e.message) 256 val suppressed = e.suppressedExceptions + 257 (e.suppressedExceptions.firstOrNull()?.suppressedExceptions ?: emptyList()) 258 assertEquals(3, suppressed.size) 259 assertEquals("x", suppressed[0].message) 260 assertEquals("y", suppressed[1].message) 261 assertEquals("z", suppressed[2].message) 262 } 263 }) { 264 runTestWithLegacyScope { 265 launch(SupervisorJob()) { throw TestException("x") } 266 launch(SupervisorJob()) { throw TestException("y") } 267 launch(SupervisorJob()) { throw TestException("z") } 268 throw TestException("w") 269 } 270 } 271 272 @Test 273 fun testScopeRunTestExceptionHandler(): TestResult { 274 val scope = TestCoroutineScope() 275 return testResultMap({ 276 try { 277 it() 278 fail("should not be reached") 279 } catch (e: TestException) { 280 // expected 281 } 282 }) { 283 scope.runTest { 284 launch(SupervisorJob()) { throw TestException("x") } 285 } 286 } 287 } 288 } 289