• 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 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