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