Skip to content

Commit 73e055d

Browse files
committed
Generalize more tests from Darwin and Swing
1 parent 113a645 commit 73e055d

File tree

6 files changed

+159
-131
lines changed

6 files changed

+159
-131
lines changed

kotlinx-coroutines-core/common/test/MainDispatcherTestBase.kt

+126-6
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ abstract class MainDispatcherTestBase: TestBase() {
1010

1111
open fun shouldSkipTesting(): Boolean = false
1212

13-
open fun checkIsMainThread() {}
13+
abstract fun isMainThread(): Boolean?
1414

15-
open fun checkNotMainThread() {}
15+
abstract fun scheduleOnMainQueue(block: () -> Unit)
1616

1717
/** Runs the given block as a test, unless [shouldSkipTesting] indicates that the environment is not suitable. */
1818
fun runTestOrSkip(block: suspend CoroutineScope.() -> Unit): TestResult {
@@ -57,14 +57,18 @@ abstract class MainDispatcherTestBase: TestBase() {
5757
@Ignore // TODO: hangs on Android
5858
fun testDelay() = runTestOrSkip {
5959
expect(1)
60+
checkNotMainThread()
61+
scheduleOnMainQueue { expect(2) }
6062
withContext(Dispatchers.Main) {
6163
checkIsMainThread()
62-
expect(2)
64+
expect(3)
65+
scheduleOnMainQueue { expect(4) }
6366
delay(100)
6467
checkIsMainThread()
65-
expect(3)
68+
expect(5)
6669
}
67-
finish(4)
70+
checkNotMainThread()
71+
finish(6)
6872
}
6973

7074
/** Tests that [Dispatchers.Main] shares its queue with [MainCoroutineDispatcher.immediate]. */
@@ -81,7 +85,120 @@ abstract class MainDispatcherTestBase: TestBase() {
8185
}
8286
expect(3) // after yield
8387
yield() // yield back
84-
finish(5)
88+
expect(5)
89+
}
90+
finish(6)
91+
}
92+
93+
/** Tests that [Dispatchers.Main] is in agreement with the default time source: it's not much slower. */
94+
@Test
95+
@Ignore // TODO: hangs on Android
96+
fun testWithTimeoutContextDelayNoTimeout() = runTestOrSkip {
97+
expect(1)
98+
withTimeout(1000) {
99+
withContext(Dispatchers.Main) {
100+
checkIsMainThread()
101+
expect(2)
102+
delay(100)
103+
checkIsMainThread()
104+
expect(3)
105+
}
106+
}
107+
checkNotMainThread()
108+
finish(4)
109+
}
110+
111+
/** Tests that [Dispatchers.Main] is in agreement with the default time source: it's not much faster. */
112+
@Test
113+
@Ignore // TODO: hangs on Android
114+
fun testWithTimeoutContextDelayTimeout() = runTestOrSkip {
115+
expect(1)
116+
assertFailsWith<TimeoutCancellationException> {
117+
withTimeout(300) {
118+
withContext(Dispatchers.Main) {
119+
checkIsMainThread()
120+
expect(2)
121+
delay(1000)
122+
expectUnreached()
123+
}
124+
}
125+
expectUnreached()
126+
}
127+
checkNotMainThread()
128+
finish(3)
129+
}
130+
131+
/** Tests that the timeout of [Dispatchers.Main] is in agreement with its [delay]: it's not much faster. */
132+
@Test
133+
@Ignore // TODO: hangs on Android
134+
fun testWithContextTimeoutDelayNoTimeout() = runTestOrSkip {
135+
expect(1)
136+
withContext(Dispatchers.Main) {
137+
withTimeout(1000) {
138+
checkIsMainThread()
139+
expect(2)
140+
delay(100)
141+
checkIsMainThread()
142+
expect(3)
143+
}
144+
}
145+
checkNotMainThread()
146+
finish(4)
147+
}
148+
149+
/** Tests that the timeout of [Dispatchers.Main] is in agreement with its [delay]: it's not much slower. */
150+
@Test
151+
@Ignore // TODO: hangs on Android
152+
fun testWithContextTimeoutDelayTimeout() = runTestOrSkip {
153+
expect(1)
154+
assertFailsWith<TimeoutCancellationException> {
155+
withContext(Dispatchers.Main) {
156+
withTimeout(100) {
157+
checkIsMainThread()
158+
expect(2)
159+
delay(1000)
160+
expectUnreached()
161+
}
162+
}
163+
expectUnreached()
164+
}
165+
checkNotMainThread()
166+
finish(3)
167+
}
168+
169+
/** Tests that entering [MainCoroutineDispatcher.immediate] from [Dispatchers.Main] happens immediately. */
170+
@Test
171+
fun testEnteringImmediateFromMain() = runTestOrSkip {
172+
withContext(Dispatchers.Main) {
173+
expect(1)
174+
val job = launch { expect(3) }
175+
withContext(Dispatchers.Main.immediate) {
176+
expect(2)
177+
}
178+
job.join()
179+
}
180+
finish(4)
181+
}
182+
183+
/** Tests that dispatching to [MainCoroutineDispatcher.immediate] is required from and only from dispatchers
184+
* other than the main dispatchers and that it's always required for [Dispatchers.Main] itself. */
185+
@Test
186+
@Ignore // TODO: hangs on Android
187+
fun testDispatchRequirements() = runTestOrSkip {
188+
withContext(Dispatchers.Default) {
189+
assertTrue(Dispatchers.Main.immediate.isDispatchNeeded(currentCoroutineContext()))
190+
assertTrue(Dispatchers.Main.isDispatchNeeded(currentCoroutineContext()))
191+
assertTrue(Dispatchers.Default.isDispatchNeeded(currentCoroutineContext()))
192+
withContext(Dispatchers.Main) {
193+
assertFalse(Dispatchers.Main.immediate.isDispatchNeeded(currentCoroutineContext()))
194+
assertTrue(Dispatchers.Main.isDispatchNeeded(currentCoroutineContext()))
195+
assertTrue(Dispatchers.Default.isDispatchNeeded(currentCoroutineContext()))
196+
withContext(Dispatchers.Main.immediate) {
197+
assertFalse(Dispatchers.Main.immediate.isDispatchNeeded(currentCoroutineContext()))
198+
assertTrue(Dispatchers.Main.isDispatchNeeded(currentCoroutineContext()))
199+
assertTrue(Dispatchers.Default.isDispatchNeeded(currentCoroutineContext()))
200+
}
201+
}
85202
}
86203
}
87204

@@ -140,4 +257,7 @@ abstract class MainDispatcherTestBase: TestBase() {
140257
}
141258
}
142259
}
260+
261+
fun checkIsMainThread() { isMainThread()?.let { check(it) } }
262+
fun checkNotMainThread() { isMainThread()?.let { check(!it) } }
143263
}

kotlinx-coroutines-core/js/test/ImmediateDispatcherTest.kt

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,17 @@
44

55
package kotlinx.coroutines
66

7+
import kotlin.coroutines.*
78
import kotlin.test.*
89

9-
class ImmediateDispatcherTest : MainDispatcherTestBase(Dispatchers.Main) {
10+
class ImmediateDispatcherTest : MainDispatcherTestBase() {
1011

12+
/** Tests that entering [MainCoroutineDispatcher.immediate] takes priority even outside [Dispatchers.Main]. */
1113
@Test
1214
fun testImmediate() = runTest {
1315
expect(1)
1416
val job = launch { expect(3) }
17+
assertTrue(Dispatchers.Main.immediate.isDispatchNeeded(currentCoroutineContext()))
1518
withContext(Dispatchers.Main.immediate) {
1619
expect(2)
1720
}
@@ -29,4 +32,10 @@ class ImmediateDispatcherTest : MainDispatcherTestBase(Dispatchers.Main) {
2932
job.join()
3033
finish(4)
3134
}
35+
36+
override fun isMainThread(): Boolean? = null
37+
38+
override fun scheduleOnMainQueue(block: () -> Unit) {
39+
Dispatchers.Default.dispatch(EmptyCoroutineContext, Runnable { block() })
40+
}
3241
}

kotlinx-coroutines-core/nativeDarwin/test/MainDispatcherTest.kt

+6-104
Original file line numberDiff line numberDiff line change
@@ -11,119 +11,21 @@ import kotlin.test.*
1111

1212
class MainDispatcherTest : MainDispatcherTestBase() {
1313

14-
private fun isMainThread(): Boolean = CFRunLoopGetCurrent() == CFRunLoopGetMain()
15-
16-
override fun checkIsMainThread() = assertTrue(isMainThread())
17-
18-
override fun checkNotMainThread() = assertFalse(isMainThread())
14+
override fun isMainThread(): Boolean = CFRunLoopGetCurrent() == CFRunLoopGetMain()
1915

2016
// skip if already on the main thread, run blocking doesn't really work well with that
2117
override fun shouldSkipTesting(): Boolean = isMainThread()
2218

2319
@Test
2420
fun testDispatchNecessityCheckWithMainImmediateDispatcher() = runTestOrSkip {
25-
val main = Dispatchers.Main.immediate
26-
assertTrue(main.isDispatchNeeded(EmptyCoroutineContext))
21+
val immediate = Dispatchers.Main.immediate
22+
assertTrue(immediate.isDispatchNeeded(EmptyCoroutineContext))
2723
withContext(Dispatchers.Default) {
28-
assertTrue(main.isDispatchNeeded(EmptyCoroutineContext))
29-
withContext(Dispatchers.Main) {
30-
assertFalse(main.isDispatchNeeded(EmptyCoroutineContext))
31-
}
32-
assertTrue(main.isDispatchNeeded(EmptyCoroutineContext))
33-
}
34-
}
35-
36-
@Test
37-
fun testWithContext() = runTestOrSkip {
38-
expect(1)
39-
checkNotMainThread()
40-
withContext(Dispatchers.Main) {
41-
checkIsMainThread()
42-
expect(2)
43-
}
44-
checkNotMainThread()
45-
finish(3)
46-
}
47-
48-
@Test
49-
fun testWithContextDelay() = runTestOrSkip {
50-
expect(1)
51-
withContext(Dispatchers.Main) {
52-
checkIsMainThread()
53-
expect(2)
54-
delay(100)
55-
checkIsMainThread()
56-
expect(3)
57-
}
58-
checkNotMainThread()
59-
finish(4)
60-
}
61-
62-
@Test
63-
fun testWithTimeoutContextDelayNoTimeout() = runTestOrSkip {
64-
expect(1)
65-
withTimeout(1000) {
66-
withContext(Dispatchers.Main) {
67-
checkIsMainThread()
68-
expect(2)
69-
delay(100)
70-
checkIsMainThread()
71-
expect(3)
72-
}
73-
}
74-
checkNotMainThread()
75-
finish(4)
76-
}
77-
78-
@Test
79-
fun testWithTimeoutContextDelayTimeout() = runTestOrSkip {
80-
expect(1)
81-
assertFailsWith<TimeoutCancellationException> {
82-
withTimeout(100) {
83-
withContext(Dispatchers.Main) {
84-
checkIsMainThread()
85-
expect(2)
86-
delay(1000)
87-
expectUnreached()
88-
}
89-
}
90-
expectUnreached()
91-
}
92-
checkNotMainThread()
93-
finish(3)
94-
}
95-
96-
@Test
97-
fun testWithContextTimeoutDelayNoTimeout() = runTestOrSkip {
98-
expect(1)
99-
withContext(Dispatchers.Main) {
100-
withTimeout(1000) {
101-
checkIsMainThread()
102-
expect(2)
103-
delay(100)
104-
checkIsMainThread()
105-
expect(3)
106-
}
107-
}
108-
checkNotMainThread()
109-
finish(4)
110-
}
111-
112-
@Test
113-
fun testWithContextTimeoutDelayTimeout() = runTestOrSkip {
114-
expect(1)
115-
assertFailsWith<TimeoutCancellationException> {
24+
assertTrue(immediate.isDispatchNeeded(EmptyCoroutineContext))
11625
withContext(Dispatchers.Main) {
117-
withTimeout(100) {
118-
checkIsMainThread()
119-
expect(2)
120-
delay(1000)
121-
expectUnreached()
122-
}
26+
assertFalse(immediate.isDispatchNeeded(EmptyCoroutineContext))
12327
}
124-
expectUnreached()
28+
assertTrue(immediate.isDispatchNeeded(EmptyCoroutineContext))
12529
}
126-
checkNotMainThread()
127-
finish(3)
12830
}
12931
}

ui/kotlinx-coroutines-android/test/HandlerDispatcherTest.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import java.util.concurrent.*
1515
import kotlin.test.*
1616

1717
@RunWith(RobolectricTestRunner::class)
18-
@Config(manifest = Config.NONE, sdk = [28])
1918
@LooperMode(LooperMode.Mode.LEGACY)
19+
@Config(manifest = Config.NONE, sdk = [28])
2020
class HandlerDispatcherTest : MainDispatcherTestBase() {
2121
@Test
2222
fun testDefaultDelayIsNotDelegatedToMain() = runTest {
@@ -112,4 +112,10 @@ class HandlerDispatcherTest : MainDispatcherTestBase() {
112112
mainLooper.scheduler.advanceBy(51, TimeUnit.MILLISECONDS)
113113
finish(5)
114114
}
115+
116+
override fun isMainThread(): Boolean = Looper.getMainLooper().thread === Thread.currentThread()
117+
118+
override fun scheduleOnMainQueue(block: () -> Unit) {
119+
Handler(Looper.getMainLooper()).post(block)
120+
}
115121
}

ui/kotlinx-coroutines-javafx/test/JavaFxDispatcherTest.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import javafx.application.*
88
import kotlinx.coroutines.*
99
import org.junit.*
1010
import org.junit.Test
11+
import javax.swing.*
1112
import kotlin.test.*
1213

1314
class JavaFxDispatcherTest : MainDispatcherTestBase() {
@@ -24,7 +25,11 @@ class JavaFxDispatcherTest : MainDispatcherTestBase() {
2425
return false
2526
}
2627

27-
override fun checkIsMainThread() { check(Platform.isFxApplicationThread()) }
28+
override fun isMainThread() = Platform.isFxApplicationThread()
29+
30+
override fun scheduleOnMainQueue(block: () -> Unit) {
31+
Platform.runLater { block() }
32+
}
2833

2934
/** Tests that the Main dispatcher is in fact the JavaFx one. */
3035
@Test

ui/kotlinx-coroutines-swing/test/SwingTest.kt

+4-18
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,15 @@ class SwingTest : MainDispatcherTestBase() {
1717
ignoreLostThreads("AWT-EventQueue-")
1818
}
1919

20-
override fun checkIsMainThread() { check(SwingUtilities.isEventDispatchThread()) }
20+
override fun isMainThread() = SwingUtilities.isEventDispatchThread()
2121

22+
override fun scheduleOnMainQueue(block: () -> Unit) {
23+
SwingUtilities.invokeLater { block() }
24+
}
2225

2326
/** Tests that the Main dispatcher is in fact the JavaFx one. */
2427
@Test
2528
fun testMainIsJavaFx() {
2629
assertSame(Dispatchers.Swing, Dispatchers.Main)
2730
}
28-
29-
/** similar to [MainDispatcherTestBase.testDelay], but also uses `invokeLater` */
30-
@Test
31-
fun testDelayWithInvokeLater() = runBlocking {
32-
expect(1)
33-
SwingUtilities.invokeLater { expect(2) }
34-
val job = launch(Dispatchers.Main) {
35-
checkIsMainThread()
36-
expect(3)
37-
SwingUtilities.invokeLater { expect(4) }
38-
delay(100)
39-
checkIsMainThread()
40-
expect(5)
41-
}
42-
job.join()
43-
finish(6)
44-
}
4531
}

0 commit comments

Comments
 (0)