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 testRunTestWithSmallTimeout() = testResultMap({ fn -> 71 assertFailsWith<UncompletedCoroutinesError> { fn() } 72 }) { 73 runTestWithLegacyScope(dispatchTimeoutMs = 100) { 74 withContext(Dispatchers.Default) { 75 delay(10000) 76 3 77 } 78 fail("shouldn't be reached") 79 } 80 } 81 82 @Test 83 fun testRunTestWithLargeTimeout() = runTestWithLegacyScope(dispatchTimeoutMs = 5000) { 84 withContext(Dispatchers.Default) { 85 delay(50) 86 } 87 } 88 89 @Test 90 fun testRunTestTimingOutAndThrowing() = testResultMap({ fn -> 91 try { 92 fn() 93 fail("unreached") 94 } catch (e: UncompletedCoroutinesError) { 95 @Suppress("INVISIBLE_MEMBER") 96 val suppressed = unwrap(e).suppressedExceptions 97 assertEquals(1, suppressed.size) 98 assertIs<TestException>(suppressed[0]).also { 99 assertEquals("A", it.message) 100 } 101 } 102 }) { 103 runTestWithLegacyScope(dispatchTimeoutMs = 1) { 104 coroutineContext[CoroutineExceptionHandler]!!.handleException(coroutineContext, TestException("A")) 105 withContext(Dispatchers.Default) { 106 delay(10000) 107 3 108 } 109 fail("shouldn't be reached") 110 } 111 } 112 113 @Test 114 fun testRunTestWithIllegalContext() { 115 for (ctx in TestScopeTest.invalidContexts) { 116 assertFailsWith<IllegalArgumentException> { 117 runTestWithLegacyScope(ctx) { } 118 } 119 } 120 } 121 122 @Test 123 fun testThrowingInRunTestBody() = testResultMap({ 124 assertFailsWith<RuntimeException> { it() } 125 }) { 126 runTestWithLegacyScope { 127 throw RuntimeException() 128 } 129 } 130 131 @Test 132 fun testThrowingInRunTestPendingTask() = testResultMap({ 133 assertFailsWith<RuntimeException> { it() } 134 }) { 135 runTestWithLegacyScope { 136 launch { 137 delay(SLOW) 138 throw RuntimeException() 139 } 140 } 141 } 142 143 @Test 144 fun reproducer2405() = runTestWithLegacyScope { 145 val dispatcher = StandardTestDispatcher(testScheduler) 146 var collectedError = false 147 withContext(dispatcher) { 148 flow { emit(1) } 149 .combine( 150 flow<String> { throw IllegalArgumentException() } 151 ) { int, string -> int.toString() + string } 152 .catch { emit("error") } 153 .collect { 154 assertEquals("error", it) 155 collectedError = true 156 } 157 } 158 assertTrue(collectedError) 159 } 160 161 @Test 162 fun testChildrenCancellationOnTestBodyFailure(): TestResult { 163 var job: Job? = null 164 return testResultMap({ 165 assertFailsWith<AssertionError> { it() } 166 assertTrue(job!!.isCancelled) 167 }) { 168 runTestWithLegacyScope { 169 job = launch { 170 while (true) { 171 delay(1000) 172 } 173 } 174 throw AssertionError() 175 } 176 } 177 } 178 179 @Test 180 fun testTimeout() = testResultMap({ 181 assertFailsWith<TimeoutCancellationException> { it() } 182 }) { 183 runTestWithLegacyScope { 184 withTimeout(50) { 185 launch { 186 delay(1000) 187 } 188 } 189 } 190 } 191 192 @Test 193 fun testRunTestThrowsRootCause() = testResultMap({ 194 assertFailsWith<TestException> { it() } 195 }) { 196 runTestWithLegacyScope { 197 launch { 198 throw TestException() 199 } 200 } 201 } 202 203 @Test 204 fun testCompletesOwnJob(): TestResult { 205 var handlerCalled = false 206 return testResultMap({ 207 it() 208 assertTrue(handlerCalled) 209 }) { 210 runTestWithLegacyScope { 211 coroutineContext.job.invokeOnCompletion { 212 handlerCalled = true 213 } 214 } 215 } 216 } 217 218 @Test 219 fun testDoesNotCompleteGivenJob(): TestResult { 220 var handlerCalled = false 221 val job = Job() 222 job.invokeOnCompletion { 223 handlerCalled = true 224 } 225 return testResultMap({ 226 it() 227 assertFalse(handlerCalled) 228 assertEquals(0, job.children.filter { it.isActive }.count()) 229 }) { 230 runTestWithLegacyScope(job) { 231 assertTrue(coroutineContext.job in job.children) 232 } 233 } 234 } 235 236 @Test 237 fun testSuppressedExceptions() = testResultMap({ 238 try { 239 it() 240 fail("should not be reached") 241 } catch (e: TestException) { 242 assertEquals("w", e.message) 243 val suppressed = e.suppressedExceptions + 244 (e.suppressedExceptions.firstOrNull()?.suppressedExceptions ?: emptyList()) 245 assertEquals(3, suppressed.size) 246 assertEquals("x", suppressed[0].message) 247 assertEquals("y", suppressed[1].message) 248 assertEquals("z", suppressed[2].message) 249 } 250 }) { 251 runTestWithLegacyScope { 252 launch(SupervisorJob()) { throw TestException("x") } 253 launch(SupervisorJob()) { throw TestException("y") } 254 launch(SupervisorJob()) { throw TestException("z") } 255 throw TestException("w") 256 } 257 } 258 259 @Test 260 fun testScopeRunTestExceptionHandler(): TestResult { 261 val scope = TestCoroutineScope() 262 return testResultMap({ 263 try { 264 it() 265 fail("should not be reached") 266 } catch (e: TestException) { 267 // expected 268 } 269 }) { 270 scope.runTest { 271 launch(SupervisorJob()) { throw TestException("x") } 272 } 273 } 274 } 275 } 276