Skip to content

Commit aee107d

Browse files
committed
Fix 'runCurrent' executing newly-added tasks
1 parent 3888fd4 commit aee107d

File tree

2 files changed

+86
-55
lines changed

2 files changed

+86
-55
lines changed

kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt

+3-11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class TestCoroutineScheduler: AbstractCoroutineContextElement(TestCorouti
4141
/** The current virtual time. */
4242
@ExperimentalCoroutinesApi
4343
public var currentTime: Long = 0
44+
get() = synchronized(lock) { field }
4445
private set
4546

4647
/**
@@ -96,19 +97,10 @@ public class TestCoroutineScheduler: AbstractCoroutineContextElement(TestCorouti
9697
*/
9798
@ExperimentalCoroutinesApi
9899
public fun runCurrent() {
100+
val timeMark = synchronized(lock) { currentTime }
99101
while (true) {
100102
val event = synchronized(lock) {
101-
val timeMark = currentTime
102-
val event = events.peek() ?: return
103-
when {
104-
timeMark > event.time -> currentTimeAheadOfEvents()
105-
timeMark < event.time -> return
106-
else -> {
107-
val event2 = events.removeFirstOrNull()
108-
if (event !== event2) concurrentModificationUnderLock()
109-
event2
110-
}
111-
}
103+
events.removeFirstIf { it.time <= timeMark } ?: return
112104
}
113105
event.dispatcher.processEvent(event.time, event.marker)
114106
}

kotlinx-coroutines-test/common/test/TestCoroutineSchedulerTest.kt

+83-44
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,26 @@ import kotlin.test.*
1010
class TestCoroutineSchedulerTest {
1111
/** Tests that `TestCoroutineScheduler` attempts to detect if there are several instances of it. */
1212
@Test
13-
fun testContextElement() {
14-
runBlockingTest {
15-
assertFailsWith<IllegalStateException> {
16-
withContext(TestCoroutineDispatcher()) {
17-
}
13+
fun testContextElement() = runBlockingTest {
14+
assertFailsWith<IllegalStateException> {
15+
withContext(TestCoroutineDispatcher()) {
1816
}
1917
}
2018
}
2119

2220
/** Tests that, as opposed to [DelayController.advanceTimeBy] or [TestCoroutineScope.advanceTimeBy],
2321
* [TestCoroutineScheduler.advanceTimeBy] doesn't run the tasks scheduled at the target moment. */
2422
@Test
25-
fun testAdvanceTimeByDoesNotRunCurrent() {
26-
val dispatcher = TestCoroutineDispatcher()
27-
dispatcher.runBlockingTest {
28-
dispatcher.pauseDispatcher {
29-
var entered = false
30-
launch {
31-
delay(15)
32-
entered = true
33-
}
34-
testScheduler.advanceTimeBy(15)
35-
assertFalse(entered)
36-
testScheduler.runCurrent()
37-
assertTrue(entered)
38-
}
23+
fun testAdvanceTimeByDoesNotRunCurrent() = runBlockingTest {
24+
var entered = false
25+
launch {
26+
delay(15)
27+
entered = true
3928
}
29+
testScheduler.advanceTimeBy(15)
30+
assertFalse(entered)
31+
testScheduler.runCurrent()
32+
assertTrue(entered)
4033
}
4134

4235
/** Tests that [TestCoroutineScheduler.advanceTimeBy] doesn't accept negative delays. */
@@ -51,32 +44,78 @@ class TestCoroutineSchedulerTest {
5144
/** Tests that if [TestCoroutineScheduler.advanceTimeBy] encounters an arithmetic overflow, all the tasks scheduled
5245
* until the moment [Long.MAX_VALUE] get run. */
5346
@Test
54-
fun testAdvanceTimeByEnormousDelays() {
55-
val dispatcher = TestCoroutineDispatcher()
56-
dispatcher.runBlockingTest {
57-
dispatcher.pauseDispatcher {
58-
val initialDelay = 10L
59-
delay(initialDelay)
60-
assertEquals(initialDelay, currentTime)
61-
var enteredInfinity = false
62-
launch {
63-
delay(Long.MAX_VALUE - 1) // delay(Long.MAX_VALUE) does nothing
64-
assertEquals(Long.MAX_VALUE, currentTime)
65-
enteredInfinity = true
66-
}
67-
var enteredNearInfinity = false
68-
launch {
69-
delay(Long.MAX_VALUE - initialDelay - 1)
70-
assertEquals(Long.MAX_VALUE - 1, currentTime)
71-
enteredNearInfinity = true
72-
}
73-
testScheduler.advanceTimeBy(Long.MAX_VALUE)
74-
assertFalse(enteredInfinity)
75-
assertTrue(enteredNearInfinity)
76-
assertEquals(Long.MAX_VALUE, currentTime)
77-
testScheduler.runCurrent()
78-
assertTrue(enteredInfinity)
47+
fun testAdvanceTimeByEnormousDelays() = runBlockingTest {
48+
val initialDelay = 10L
49+
delay(initialDelay)
50+
assertEquals(initialDelay, currentTime)
51+
var enteredInfinity = false
52+
launch {
53+
delay(Long.MAX_VALUE - 1) // delay(Long.MAX_VALUE) does nothing
54+
assertEquals(Long.MAX_VALUE, currentTime)
55+
enteredInfinity = true
56+
}
57+
var enteredNearInfinity = false
58+
launch {
59+
delay(Long.MAX_VALUE - initialDelay - 1)
60+
assertEquals(Long.MAX_VALUE - 1, currentTime)
61+
enteredNearInfinity = true
62+
}
63+
testScheduler.advanceTimeBy(Long.MAX_VALUE)
64+
assertFalse(enteredInfinity)
65+
assertTrue(enteredNearInfinity)
66+
assertEquals(Long.MAX_VALUE, currentTime)
67+
testScheduler.runCurrent()
68+
assertTrue(enteredInfinity)
69+
}
70+
71+
/** Tests the basic functionality of [TestCoroutineScheduler.advanceTimeBy]. */
72+
@Test
73+
fun testAdvanceTimeBy() {
74+
val scheduler = TestCoroutineScheduler()
75+
val scope = TestCoroutineScope(scheduler)
76+
var stage = 1
77+
scope.launch {
78+
delay(1_000)
79+
assertEquals(1_000, scheduler.currentTime)
80+
stage = 2
81+
delay(500)
82+
assertEquals(1_500, scheduler.currentTime)
83+
stage = 3
84+
delay(501)
85+
assertEquals(2_001, scheduler.currentTime)
86+
stage = 4
87+
}
88+
assertEquals(1, stage)
89+
assertEquals(0, scheduler.currentTime)
90+
scheduler.advanceTimeBy(2_000)
91+
assertEquals(3, stage)
92+
assertEquals(2_000, scheduler.currentTime)
93+
scheduler.advanceTimeBy(2)
94+
assertEquals(4, stage)
95+
assertEquals(2_002, scheduler.currentTime)
96+
scope.cleanupTestCoroutines()
97+
}
98+
99+
/** Tests that [TestCoroutineScheduler.runCurrent] will not run new tasks after the current time has advanced. */
100+
@Test
101+
fun testRunCurrentNotDrainingQueue() {
102+
val scheduler = TestCoroutineScheduler()
103+
val scope = TestCoroutineScope(scheduler)
104+
var stage = 1
105+
scope.launch {
106+
delay(1)
107+
launch {
108+
delay(1)
109+
stage = 3
79110
}
111+
scheduler.advanceTimeBy(1)
112+
stage = 2
80113
}
114+
scheduler.advanceTimeBy(1)
115+
assertEquals(1, stage)
116+
scheduler.runCurrent()
117+
assertEquals(2, stage)
118+
scheduler.runCurrent()
119+
assertEquals(3, stage)
81120
}
82121
}

0 commit comments

Comments
 (0)