• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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