-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathDelayController.kt
206 lines (189 loc) · 7.94 KB
/
DelayController.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
@file:Suppress("DEPRECATION_ERROR")
package kotlinx.coroutines.test
import kotlinx.coroutines.*
/**
* Control the virtual clock time of a [CoroutineDispatcher].
*
* Testing libraries may expose this interface to the tests instead of [TestCoroutineDispatcher].
*
* This interface is deprecated without replacement.
* Instead, [TestCoroutineScheduler] is supposed to be used to control the virtual time.
* Please see the
* [migration guide](https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md)
* for an instruction on how to update the code for the new API.
*/
@ExperimentalCoroutinesApi
@Deprecated(
"Use `TestCoroutineScheduler` to control virtual time.",
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
public interface DelayController {
/**
* Returns the current virtual clock-time as it is known to this Dispatcher.
*
* @return The virtual clock-time
*/
@ExperimentalCoroutinesApi
public val currentTime: Long
/**
* Moves the Dispatcher's virtual clock forward by a specified amount of time.
*
* The amount the clock is progressed may be larger than the requested `delayTimeMillis` if the code under test uses
* blocking coroutines.
*
* The virtual clock time will advance once for each delay resumed until the next delay exceeds the requested
* `delayTimeMills`. In the following test, the virtual time will progress by 2_000 then 1 to resume three different
* calls to delay.
*
* ```
* @Test
* fun advanceTimeTest() = runBlockingTest {
* foo()
* advanceTimeBy(2_000) // advanceTimeBy(2_000) will progress through the first two delays
* // virtual time is 2_000, next resume is at 2_001
* advanceTimeBy(2) // progress through the last delay of 501 (note 500ms were already advanced)
* // virtual time is 2_0002
* }
*
* fun CoroutineScope.foo() {
* launch {
* delay(1_000) // advanceTimeBy(2_000) will progress through this delay (resume @ virtual time 1_000)
* // virtual time is 1_000
* delay(500) // advanceTimeBy(2_000) will progress through this delay (resume @ virtual time 1_500)
* // virtual time is 1_500
* delay(501) // advanceTimeBy(2_000) will not progress through this delay (resume @ virtual time 2_001)
* // virtual time is 2_001
* }
* }
* ```
*
* @param delayTimeMillis The amount of time to move the CoroutineContext's clock forward.
* @return The amount of delay-time that this Dispatcher's clock has been forwarded.
*/
@ExperimentalCoroutinesApi
public fun advanceTimeBy(delayTimeMillis: Long): Long
/**
* Immediately execute all pending tasks and advance the virtual clock-time to the last delay.
*
* If new tasks are scheduled due to advancing virtual time, they will be executed before `advanceUntilIdle`
* returns.
*
* @return the amount of delay-time that this Dispatcher's clock has been forwarded in milliseconds.
*/
@ExperimentalCoroutinesApi
public fun advanceUntilIdle(): Long
/**
* Run any tasks that are pending at or before the current virtual clock-time.
*
* Calling this function will never advance the clock.
*/
@ExperimentalCoroutinesApi
public fun runCurrent()
/**
* Call after test code completes to ensure that the dispatcher is properly cleaned up.
*
* @throws AssertionError if any pending tasks are active, however it will not throw for suspended
* coroutines.
*/
@ExperimentalCoroutinesApi
@Throws(AssertionError::class)
public fun cleanupTestCoroutines()
/**
* Run a block of code in a paused dispatcher.
*
* By pausing the dispatcher any new coroutines will not execute immediately. After block executes, the dispatcher
* will resume auto-advancing.
*
* This is useful when testing functions that start a coroutine. By pausing the dispatcher assertions or
* setup may be done between the time the coroutine is created and started.
*/
@Deprecated(
"Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
public suspend fun pauseDispatcher(block: suspend () -> Unit)
/**
* Pause the dispatcher.
*
* When paused, the dispatcher will not execute any coroutines automatically, and you must call [runCurrent] or
* [advanceTimeBy], or [advanceUntilIdle] to execute coroutines.
*/
@Deprecated(
"Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
public fun pauseDispatcher()
/**
* Resume the dispatcher from a paused state.
*
* Resumed dispatchers will automatically progress through all coroutines scheduled at the current time. To advance
* time and execute coroutines scheduled in the future use, one of [advanceTimeBy],
* or [advanceUntilIdle].
*/
@Deprecated(
"Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.",
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
public fun resumeDispatcher()
}
internal interface SchedulerAsDelayController : DelayController {
val scheduler: TestCoroutineScheduler
/** @suppress */
@Deprecated(
"This property delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
ReplaceWith("this.scheduler.currentTime"),
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
override val currentTime: Long
get() = scheduler.currentTime
/** @suppress */
@Deprecated(
"This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
ReplaceWith("this.scheduler.apply { advanceTimeBy(delayTimeMillis); runCurrent() }"),
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
override fun advanceTimeBy(delayTimeMillis: Long): Long {
val oldTime = scheduler.currentTime
scheduler.advanceTimeBy(delayTimeMillis)
scheduler.runCurrent()
return scheduler.currentTime - oldTime
}
/** @suppress */
@Deprecated(
"This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
ReplaceWith("this.scheduler.advanceUntilIdle()"),
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
override fun advanceUntilIdle(): Long {
val oldTime = scheduler.currentTime
scheduler.advanceUntilIdle()
return scheduler.currentTime - oldTime
}
/** @suppress */
@Deprecated(
"This function delegates to the test scheduler, which may cause confusing behavior unless made explicit.",
ReplaceWith("this.scheduler.runCurrent()"),
level = DeprecationLevel.ERROR
)
// Since 1.6.0, ERROR in 1.7.0 and removed as experimental in 1.8.0
override fun runCurrent(): Unit = scheduler.runCurrent()
/** @suppress */
@ExperimentalCoroutinesApi
override fun cleanupTestCoroutines() {
// process any pending cancellations or completions, but don't advance time
scheduler.runCurrent()
if (!scheduler.isIdle(strict = false)) {
throw UncompletedCoroutinesError(
"Unfinished coroutines during tear-down. Ensure all coroutines are" +
" completed or cancelled by your test."
)
}
}
}