Update kotlinx-coroutines-test (#2973)

This commit introduces the new version of the test module.
Please see README.md and MIGRATION.md for a thorough
discussion of the changes.

Fixes #1203
Fixes #1609
Fixes #2379
Fixes #1749
Fixes #1204
Fixes #1390
Fixes #1222
Fixes #1395
Fixes #1881
Fixes #1910
Fixes #1772
Fixes #1626
Fixes #1742
Fixes #2082
Fixes #2102
Fixes #2405
Fixes #2462

Co-authored-by: Vsevolod Tolstopyatov <[email protected]>
diff --git a/kotlinx-coroutines-test/README.md b/kotlinx-coroutines-test/README.md
index 43ae18f..54450b1 100644
--- a/kotlinx-coroutines-test/README.md
+++ b/kotlinx-coroutines-test/README.md
@@ -2,7 +2,24 @@
 
 Test utilities for `kotlinx.coroutines`.
 
-This package provides testing utilities for effectively testing coroutines.
+## Overview
+
+This package provides utilities for efficiently testing coroutines.
+
+| Name | Description |
+| ---- | ----------- |
+| [runTest] | Runs the test code, automatically skipping delays and handling uncaught exceptions. |
+| [TestCoroutineScheduler] | The shared source of virtual time, used for controlling execution order and skipping delays. |
+| [TestScope] | A [CoroutineScope] that integrates with [runTest], providing access to [TestCoroutineScheduler]. |
+| [TestDispatcher] | A [CoroutineDispatcher] that whose delays are controlled by a [TestCoroutineScheduler]. |
+| [Dispatchers.setMain] | Mocks the main dispatcher using the provided one. If mocked with a [TestDispatcher], its [TestCoroutineScheduler] is used everywhere by default. |
+
+Provided [TestDispatcher] implementations:
+
+| Name | Description |
+| ---- | ----------- |
+| [StandardTestDispatcher] | A simple dispatcher with no special behavior other than being linked to a [TestCoroutineScheduler]. |
+| [UnconfinedTestDispatcher] | A dispatcher that behaves like [Dispatchers.Unconfined]. |
 
 ## Using in your project
 
@@ -13,24 +30,26 @@
 }
 ```
 
-**Do not** depend on this project in your main sources, all utilities are intended and designed to be used only from tests.
+**Do not** depend on this project in your main sources, all utilities here are intended and designed to be used only from tests.
 
 ## Dispatchers.Main Delegation
 
-`Dispatchers.setMain` will override the `Main` dispatcher in test situations. This is helpful when you want to execute a
-test in situations where the platform `Main` dispatcher is not available, or you wish to replace `Dispatchers.Main` with a
-testing dispatcher.
+`Dispatchers.setMain` will override the `Main` dispatcher in test scenarios.
+This is helpful when one wants to execute a test in situations where the platform `Main` dispatcher is not available,
+or to replace `Dispatchers.Main` with a testing dispatcher.
 
-Once you have this dependency in the runtime,
-[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism will overwrite
-[Dispatchers.Main] with a testable implementation.
+On the JVM,
+the [`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) mechanism is responsible
+for overwriting [Dispatchers.Main] with a testable implementation, which by default will delegate its calls to the real
+`Main` dispatcher, if any.
 
-You can override the `Main` implementation using [setMain][setMain] method with any [CoroutineDispatcher] implementation, e.g.:
+The `Main` implementation can be overridden using [Dispatchers.setMain][setMain] method with any [CoroutineDispatcher]
+implementation, e.g.:
 
 ```kotlin
 
 class SomeTest {
-    
+
     private val mainThreadSurrogate = newSingleThreadContext("UI thread")
 
     @Before
@@ -40,10 +59,10 @@
 
     @After
     fun tearDown() {
-        Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
+        Dispatchers.resetMain() // reset the main dispatcher to the original Main dispatcher
         mainThreadSurrogate.close()
     }
-    
+
     @Test
     fun testSomeUI() = runBlocking {
         launch(Dispatchers.Main) {  // Will be launched in the mainThreadSurrogate dispatcher
@@ -52,372 +71,289 @@
     }
 }
 ```
-Calling `setMain` or `resetMain` immediately changes the `Main` dispatcher globally. The testable version of 
-`Dispatchers.Main` installed by the `ServiceLoader` will delegate to the dispatcher provided by `setMain`.
 
-## runBlockingTest
+Calling `setMain` or `resetMain` immediately changes the `Main` dispatcher globally.
 
-To test regular suspend functions or coroutines started with `launch` or `async` use the [runBlockingTest] coroutine 
-builder that provides extra test control to coroutines. 
+If `Main` is overridden with a [TestDispatcher], then its [TestCoroutineScheduler] is used when new [TestDispatcher] or
+[TestScope] instances are created without [TestCoroutineScheduler] being passed as an argument.
 
-1. Auto-advancing of time for regular suspend functions
-2. Explicit time control for testing multiple coroutines
-3. Eager execution of `launch` or `async` code blocks
-4. Pause, manually advance, and restart the execution of coroutines in a test
-5. Report uncaught exceptions as test failures
+## runTest
 
-### Testing regular suspend functions
+[runTest] is the way to test code that involves coroutines. `suspend` functions can be called inside it.
 
-To test regular suspend functions, which may have a delay, you can use the [runBlockingTest] builder to start a testing 
-coroutine. Any calls to `delay` will automatically advance virtual time by the amount delayed.
+**IMPORTANT: in order to work with on Kotlin/JS, the result of `runTest` must be immediately `return`-ed from each test.**
+The typical invocation of [runTest] thus looks like this:
 
 ```kotlin
 @Test
-fun testFoo() = runBlockingTest { // a coroutine with an extra test control
-    val actual = foo() 
+fun testFoo() = runTest {
+    // code under test
+}
+```
+
+In more advanced scenarios, it's possible instead to use the following form:
+```kotlin
+@Test
+fun testFoo(): TestResult {
+    // initialize some test state
+    return runTest {
+        // code under test
+    }
+}
+```
+
+[runTest] is similar to running the code with `runBlocking` on Kotlin/JVM and Kotlin/Native, or launching a new promise
+on Kotlin/JS. The main differences are the following:
+
+* **The calls to `delay` are automatically skipped**, preserving the relative execution order of the tasks. This way,
+  it's possible to make tests finish more-or-less immediately.
+* **Controlling the virtual time**: in case just skipping delays is not sufficient, it's possible to more carefully
+  guide the execution, advancing the virtual time by a duration, draining the queue of the awaiting tasks, or running
+  the tasks scheduled at the present moment.
+* **Handling uncaught exceptions** spawned in the child coroutines by throwing them at the end of the test.
+* **Waiting for asynchronous callbacks**.
+  Sometimes, especially when working with third-party code, it's impossible to mock all the dispatchers in use.
+  [runTest] will handle the situations where some code runs in dispatchers not integrated with the test module.
+
+## Delay-skipping
+
+To test regular suspend functions, which may have a delay, just run them inside the [runTest] block.
+
+```kotlin
+@Test
+fun testFoo() = runTest { // a coroutine with an extra test control
+    val actual = foo()
     // ...
 }
 
 suspend fun foo() {
-    delay(1_000) // auto-advances virtual time by 1_000ms due to runBlockingTest
+    delay(1_000) // when run in `runTest`, will finish immediately instead of delaying
     // ...
 }
 ```
 
-`runBlockingTest` returns `Unit` so it may be used in a single expression with common testing libraries.
+## `launch` and `async`
 
-### Testing `launch` or `async`
+The coroutine dispatcher used for tests is single-threaded, meaning that the child coroutines of the [runTest] block
+will run on the thread that started the test, and will never run in parallel.
 
-Inside of [runBlockingTest], both [launch] and [async] will start a new coroutine that may run concurrently with the 
-test case. 
-
-To make common testing situations easier, by default the body of the coroutine is executed *eagerly* until 
-the first call to [delay] or [yield].
+If several coroutines are waiting to be executed next, the one scheduled after the smallest delay will be chosen.
+The virtual time will automatically advance to the point of its resumption.
 
 ```kotlin
 @Test
-fun testFooWithLaunch() = runBlockingTest {
-    foo()
-    // the coroutine launched by foo() is completed before foo() returns
-    // ...
-}
-
-fun CoroutineScope.foo() {
-     // This coroutines `Job` is not shared with the test code
-     launch {
-         bar()      // executes eagerly when foo() is called due to runBlockingTest
-         println(1) // executes eagerly when foo() is called due to runBlockingTest
-     }
-}
-
-suspend fun bar() {}
-```
-
-`runBlockingTest` will auto-progress virtual time until all coroutines are completed before returning. If any coroutines
-are not able to complete, an [UncompletedCoroutinesError] will be thrown.
-
-*Note:* The default eager behavior of [runBlockingTest] will ignore [CoroutineStart] parameters.
-
-### Testing `launch` or `async` with `delay`
-
-If the coroutine created by `launch` or `async` calls `delay` then the [runBlockingTest] will not auto-progress time 
-right away. This allows tests to observe the interaction of multiple coroutines with different delays.
-
-To control time in the test you can use the [DelayController] interface. The block passed to 
-[runBlockingTest] can call any method on the `DelayController` interface.
-
-```kotlin
-@Test
-fun testFooWithLaunchAndDelay() = runBlockingTest {
-    foo()
-    // the coroutine launched by foo has not completed here, it is suspended waiting for delay(1_000)
-    advanceTimeBy(1_000) // progress time, this will cause the delay to resume
-    // the coroutine launched by foo has completed here
-    // ...
-}
-
-suspend fun CoroutineScope.foo() {
+fun testWithMultipleDelays() = runTest {
     launch {
-        println(1)   // executes eagerly when foo() is called due to runBlockingTest
-        delay(1_000) // suspends until time is advanced by at least 1_000
-        println(2)   // executes after advanceTimeBy(1_000)
+        delay(1_000)
+        println("1. $currentTime") // 1000
+        delay(200)
+        println("2. $currentTime") // 1200
+        delay(2_000)
+        println("4. $currentTime") // 3200
     }
+    val deferred = async {
+        delay(3_000)
+        println("3. $currentTime") // 3000
+        delay(500)
+        println("5. $currentTime") // 3500
+    }
+    deferred.await()
 }
 ```
 
-*Note:* `runBlockingTest` will always attempt to auto-progress time until all coroutines are completed just before 
-exiting. This is a convenience to avoid having to call [advanceUntilIdle][DelayController.advanceUntilIdle] 
-as the last line of many common test cases.
-If any coroutines cannot complete by advancing time, an [UncompletedCoroutinesError] is thrown.
+## Controlling the virtual time
 
-### Testing `withTimeout` using `runBlockingTest`
-
-Time control can be used to test timeout code. To do so, ensure that the function under test is suspended inside a 
-`withTimeout` block and advance time until the timeout is triggered.
-
-Depending on the code, causing the code to suspend may need to use different mocking or fake techniques. For this 
-example an uncompleted `Deferred<Foo>` is provided to the function under test via parameter injection.
+Inside [runTest], the following operations are supported:
+* `currentTime` gets the current virtual time.
+* `runCurrent()` runs the tasks that are scheduled at this point of virtual time.
+* `advanceUntilIdle()` runs all enqueued tasks until there are no more.
+* `advanceTimeBy(timeDelta)` runs the enqueued tasks until the current virtual time advances by `timeDelta`.
 
 ```kotlin
-@Test(expected = TimeoutCancellationException::class)
-fun testFooWithTimeout() = runBlockingTest {
-    val uncompleted = CompletableDeferred<Foo>() // this Deferred<Foo> will never complete
-    foo(uncompleted)
-    advanceTimeBy(1_000) // advance time, which will cause the timeout to throw an exception
-    // ...
+@Test
+fun testFoo() = runTest {
+    launch {
+        println(1)   // executes during runCurrent()
+        delay(1_000) // suspends until time is advanced by at least 1_000
+        println(2)   // executes during advanceTimeBy(2_000)
+        delay(500)   // suspends until the time is advanced by another 500 ms
+        println(3)   // also executes during advanceTimeBy(2_000)
+        delay(5_000) // will suspend by another 4_500 ms
+        println(4)   // executes during advanceUntilIdle()
+    }
+    // the child coroutine has not run yet
+    runCurrent()
+    // the child coroutine has called println(1), and is suspended on delay(1_000)
+    advanceTimeBy(2_000) // progress time, this will cause two calls to `delay` to resume
+    // the child coroutine has called println(2) and println(3) and suspends for another 4_500 virtual milliseconds
+    advanceUntilIdle() // will run the child coroutine to completion
+    assertEquals(6500, currentTime) // the child coroutine finished at virtual time of 6_500 milliseconds
+}
+```
+
+## Using multiple test dispatchers
+
+The virtual time is controlled by an entity called the [TestCoroutineScheduler], which behaves as the shared source of
+virtual time.
+
+Several dispatchers can be created that use the same [TestCoroutineScheduler], in which case they will share their
+knowledge of the virtual time.
+
+To access the scheduler used for this test, use the [TestScope.testScheduler] property.
+
+```kotlin
+@Test
+fun testWithMultipleDispatchers() = runTest {
+        val scheduler = testScheduler // the scheduler used for this test
+        val dispatcher1 = StandardTestDispatcher(scheduler, name = "IO dispatcher")
+        val dispatcher2 = StandardTestDispatcher(scheduler, name = "Background dispatcher")
+        launch(dispatcher1) {
+            delay(1_000)
+            println("1. $currentTime") // 1000
+            delay(200)
+            println("2. $currentTime") // 1200
+            delay(2_000)
+            println("4. $currentTime") // 3200
+        }
+        val deferred = async(dispatcher2) {
+            delay(3_000)
+            println("3. $currentTime") // 3000
+            delay(500)
+            println("5. $currentTime") // 3500
+        }
+        deferred.await()
+    }
+```
+
+**Note: if [Dispatchers.Main] is replaced by a [TestDispatcher], [runTest] will automatically use its scheduler.
+This is done so that there is no need to go through the ceremony of passing the correct scheduler to [runTest].**
+
+## Accessing the test coroutine scope
+
+Structured concurrency ties coroutines to scopes in which they are launched.
+[TestScope] is a special coroutine scope designed for testing coroutines, and a new one is automatically created
+for [runTest] and used as the receiver for the test body.
+
+However, it can be convenient to access a `CoroutineScope` before the test has started, for example, to perform mocking
+of some
+parts of the system in `@BeforeTest` via dependency injection.
+In these cases, it is possible to manually create [TestScope], the scope for the test coroutines, in advance,
+before the test begins.
+
+[TestScope] on its own does not automatically run the code launched in it.
+In addition, it is stateful in order to keep track of executing coroutines and uncaught exceptions.
+Therefore, it is important to ensure that [TestScope.runTest] is called eventually.
+
+```kotlin
+val scope = TestScope()
+
+@BeforeTest
+fun setUp() {
+    Dispatchers.setMain(StandardTestDispatcher(scope.testScheduler))
+    TestSubject.setScope(scope)
 }
 
-fun CoroutineScope.foo(resultDeferred: Deferred<Foo>) {
+@AfterTest
+fun tearDown() {
+    Dispatchers.resetMain()
+    TestSubject.resetScope()
+}
+
+@Test
+fun testSubject() = scope.runTest {
+    // the receiver here is `testScope`
+}
+```
+
+## Eagerly entering `launch` and `async` blocks
+
+Some tests only test functionality and don't particularly care about the precise order in which coroutines are
+dispatched.
+In these cases, it can be cumbersome to always call [runCurrent] or [yield] to observe the effects of the coroutines
+after they are launched.
+
+If [runTest] executes with an [UnconfinedTestDispatcher], the child coroutines launched at the top level are entered
+*eagerly*, that is, they don't go through a dispatch until the first suspension.
+
+```kotlin
+@Test
+fun testEagerlyEnteringChildCoroutines() = runTest(UnconfinedTestDispatcher()) {
+    var entered = false
+    val deferred = CompletableDeferred<Unit>()
+    var completed = false
     launch {
+        entered = true
+        deferred.await()
+        completed = true
+    }
+    assertTrue(entered) // `entered = true` already executed.
+    assertFalse(completed) // however, the child coroutine then suspended, so it is enqueued.
+    deferred.complete(Unit) // resume the coroutine.
+    assertTrue(completed) // now the child coroutine is immediately completed.
+}
+```
+
+If this behavior is desirable, but some parts of the test still require accurate dispatching, for example, to ensure
+that the code executes on the correct thread, then simply `launch` a new coroutine with the [StandardTestDispatcher].
+
+```kotlin
+@Test
+fun testEagerlyEnteringSomeChildCoroutines() = runTest(UnconfinedTestDispatcher()) {
+    var entered1 = false
+    launch {
+        entered1 = true
+    }
+    assertTrue(entered1) // `entered1 = true` already executed
+
+    var entered2 = false
+    launch(StandardTestDispatcher(testScheduler)) {
+        // this block and every coroutine launched inside it will explicitly go through the needed dispatches
+        entered2 = true
+    }
+    assertFalse(entered2)
+    runCurrent() // need to explicitly run the dispatched continuation
+    assertTrue(entered2)
+}
+```
+
+### Using `withTimeout` inside `runTest`
+
+Timeouts are also susceptible to time control, so the code below will immediately finish.
+
+```kotlin
+@Test
+fun testFooWithTimeout() = runTest {
+    assertFailsWith<TimeoutCancellationException> {
         withTimeout(1_000) {
-            resultDeferred.await() // await() will suspend forever waiting for uncompleted
-            // ...
+            delay(999)
+            delay(2)
+            println("this won't be reached")
         }
     }
 }
 ```
 
-*Note:* Testing timeouts is simpler with a second coroutine that can be suspended (as in this example). If the 
-call to `withTimeout` is in a regular suspend function, consider calling `launch` or `async` inside your test body to 
-create a second coroutine.
+## Virtual time support with other dispatchers
 
-### Using `pauseDispatcher` for explicit execution of `runBlockingTest`
-
-The eager execution of `launch` and `async` bodies makes many tests easier, but some tests need more fine grained 
-control of coroutine execution.
-
-To disable eager execution, you can call [pauseDispatcher][DelayController.pauseDispatcher] 
-to pause the [TestCoroutineDispatcher] that [runBlockingTest] uses.
-
-When the dispatcher is paused, all coroutines will be added to a queue instead running. In addition, time will never 
-auto-progress due to `delay` on a paused dispatcher.
+Calls to `withContext(Dispatchers.IO)`, `withContext(Dispatchers.Default)` ,and `withContext(Dispatchers.Main)` are
+common in coroutines-based code bases. Unfortunately, just executing code in a test will not lead to these dispatchers
+using the virtual time source, so delays will not be skipped in them.
 
 ```kotlin
-@Test
-fun testFooWithPauseDispatcher() = runBlockingTest {
-    pauseDispatcher {
-        foo()
-        // the coroutine started by foo has not run yet
-        runCurrent() // the coroutine started by foo advances to delay(1_000)
-        // the coroutine started by foo has called println(1), and is suspended on delay(1_000)
-        advanceTimeBy(1_000) // progress time, this will cause the delay to resume
-        // the coroutine started by foo has called println(2) and has completed here
-    }
-    // ...
-}
-
-fun CoroutineScope.foo() {
-    launch {
-        println(1)   // executes after runCurrent() is called
-        delay(1_000) // suspends until time is advanced by at least 1_000
-        println(2)   // executes after advanceTimeBy(1_000)
-    }
-}
-```
-
-Using `pauseDispatcher` gives tests explicit control over the progress of time as well as the ability to enqueue all 
-coroutines. As a best practice consider adding two tests, one paused and one eager, to test coroutines that have 
-non-trivial external dependencies and side effects in their launch body.
-
-*Important:* When passed a lambda block, `pauseDispatcher` will resume eager execution immediately after the block. 
-This will cause time to auto-progress if there are any outstanding `delay` calls that were not resolved before the
-`pauseDispatcher` block returned. In advanced situations tests can call [pauseDispatcher][DelayController.pauseDispatcher] 
-without a lambda block and then explicitly resume the dispatcher with [resumeDispatcher][DelayController.resumeDispatcher].
-
-## Integrating tests with structured concurrency
-
-Code that uses structured concurrency needs a [CoroutineScope] in order to launch a coroutine. In order to integrate 
-[runBlockingTest] with code that uses common structured concurrency patterns tests can provide one (or both) of these
-classes to application code.  
-
- | Name | Description | 
- | ---- | ----------- | 
- | [TestCoroutineScope] | A [CoroutineScope] which provides detailed control over the execution of coroutines for tests and integrates with [runBlockingTest]. |
- | [TestCoroutineDispatcher] | A [CoroutineDispatcher] which can be used for tests and integrates with [runBlockingTest]. |
- 
- Both classes are provided to allow for various testing needs. Depending on the code that's being 
- tested, it may be easier to provide a [TestCoroutineDispatcher]. For example [Dispatchers.setMain][setMain] will accept
- a [TestCoroutineDispatcher] but not a [TestCoroutineScope].
- 
- [TestCoroutineScope] will always use a [TestCoroutineDispatcher] to execute coroutines. It 
- also uses [TestCoroutineExceptionHandler] to convert uncaught exceptions into test failures.
-
-By providing [TestCoroutineScope] a test case is able to control execution of coroutines, as well as ensure that 
-uncaught exceptions thrown by coroutines are converted into test failures.
-
-### Providing `TestCoroutineScope` from `runBlockingTest`
-
-In simple cases, tests can use the [TestCoroutineScope] created by [runBlockingTest] directly.
-
-```kotlin
-@Test
-fun testFoo() = runBlockingTest {        
-    foo() // runBlockingTest passed in a TestCoroutineScope as this
-}
-
-fun CoroutineScope.foo() {
-    launch {  // CoroutineScope for launch is the TestCoroutineScope provided by runBlockingTest
-        // ...
-    }
-}
-```
-
-This style is preferred when the `CoroutineScope` is passed through an extension function style.
-
-### Providing an explicit `TestCoroutineScope`
-
-In many cases, the direct style is not preferred because [CoroutineScope] may need to be provided through another means 
-such as dependency injection or service locators.
-
-Tests can declare a [TestCoroutineScope] explicitly in the class to support these use cases.
-
-Since [TestCoroutineScope] is stateful in order to keep track of executing coroutines and uncaught exceptions, it is 
-important to ensure that [cleanupTestCoroutines][TestCoroutineScope.cleanupTestCoroutines] is called after every test case. 
-
-```kotlin
-class TestClass {
-    private val testScope = TestCoroutineScope()
-    private lateinit var subject: Subject
-    
-    @Before
-    fun setup() {
-        // provide the scope explicitly, in this example using a constructor parameter
-        subject = Subject(testScope)
-    }
-    
-    @After
-    fun cleanUp() {
-        testScope.cleanupTestCoroutines()
-    }
-    
-    @Test
-    fun testFoo() = testScope.runBlockingTest {
-        // TestCoroutineScope.runBlockingTest uses the Dispatcher and exception handler provided by `testScope`
-        subject.foo()
-    }
-}
-
-class Subject(val scope: CoroutineScope) {
-    fun foo() {
-        scope.launch {
-            // launch uses the testScope injected in setup
-        }
-    }
-}
-```
-
-*Note:* [TestCoroutineScope], [TestCoroutineDispatcher], and [TestCoroutineExceptionHandler] are interfaces to enable 
-test libraries to provide library specific integrations. For example, a JUnit4 `@Rule` may call 
-[Dispatchers.setMain][setMain] then expose [TestCoroutineScope] for use in tests.
-
-### Providing an explicit `TestCoroutineDispatcher`
-
-While providing a [TestCoroutineScope] is slightly preferred due to the improved uncaught exception handling, there are 
-many situations where it is easier to provide a [TestCoroutineDispatcher]. For example [Dispatchers.setMain][setMain] 
-does not accept a [TestCoroutineScope] and requires a [TestCoroutineDispatcher] to control coroutine execution in 
-tests.
-
-The main difference between `TestCoroutineScope` and `TestCoroutineDispatcher` is how uncaught exceptions are handled. 
-When using `TestCoroutineDispatcher` uncaught exceptions thrown in coroutines will use regular 
-[coroutine exception handling](https://kotlinlang.org/docs/reference/coroutines/exception-handling.html). 
-`TestCoroutineScope` will always use `TestCoroutineDispatcher` as it's dispatcher.
-
-A test can use a `TestCoroutineDispatcher` without declaring an explicit `TestCoroutineScope`. This is preferred 
-when the class under test allows a test to provide a [CoroutineDispatcher] but does not allow the test to provide a 
-[CoroutineScope].
-
-Since [TestCoroutineDispatcher] is stateful in order to keep track of executing coroutines, it is 
-important to ensure that [cleanupTestCoroutines][DelayController.cleanupTestCoroutines] is called after every test case. 
-
-```kotlin
-class TestClass {
-    private val testDispatcher = TestCoroutineDispatcher()
-        
-    @Before
-    fun setup() {
-        // provide the scope explicitly, in this example using a constructor parameter
-        Dispatchers.setMain(testDispatcher)
-    }
-    
-    @After
-    fun cleanUp() {
-        Dispatchers.resetMain()
-        testDispatcher.cleanupTestCoroutines()
-    }
-    
-    @Test
-    fun testFoo() = testDispatcher.runBlockingTest {
-        // TestCoroutineDispatcher.runBlockingTest uses `testDispatcher` to run coroutines 
-        foo()
-    }
-}
-
-fun foo() {
-    MainScope().launch { 
-        // launch will use the testDispatcher provided by setMain
-    }
-}
-```
-
-*Note:* Prefer to provide `TestCoroutineScope` when it does not complicate code since it will also elevate exceptions 
-to test failures. However, exposing a `CoroutineScope` to callers of a function may lead to complicated code, in which 
-case this is the preferred pattern.
-
-### Using `TestCoroutineScope` and `TestCoroutineDispatcher` without `runBlockingTest`
-
-It is supported to use both [TestCoroutineScope] and [TestCoroutineDispatcher] without using the [runBlockingTest] 
-builder. Tests may need to do this in situations such as introducing multiple dispatchers and library writers may do 
-this to provide alternatives to `runBlockingTest`.
-
-```kotlin
-@Test
-fun testFooWithAutoProgress() {
-    val scope = TestCoroutineScope()
-    scope.foo()
-    // foo is suspended waiting for time to progress
-    scope.advanceUntilIdle()
-    // foo's coroutine will be completed before here
-}
-
-fun CoroutineScope.foo() {
-    launch {
-        println(1)            // executes eagerly when foo() is called due to TestCoroutineScope
-        delay(1_000)          // suspends until time is advanced by at least 1_000
-        println(2)            // executes after advanceTimeUntilIdle
-    }
-} 
-```
-
-## Using time control with `withContext`
-
-Calls to `withContext(Dispatchers.IO)` or `withContext(Dispatchers.Default)` are common in coroutines based codebases. 
-Both dispatchers are not designed to interact with `TestCoroutineDispatcher`.
-
-Tests should provide a `TestCoroutineDispatcher` to replace these dispatchers if the `withContext` calls `delay` in the
-function under test. For example, a test that calls `veryExpensiveOne` should provide a `TestCoroutineDispatcher` using
-either dependency injection, a service locator, or a default parameter. 
-
-```kotlin
-suspend fun veryExpensiveOne() = withContext(Dispatchers.Default) {
+suspend fun veryExpensiveFunction() = withContext(Dispatchers.Default) {
     delay(1_000)
-    1 // for very expensive values of 1
+    1
+}
+
+fun testExpensiveFunction() = runTest {
+    val result = veryExpensiveFunction() // will take a whole real-time second to execute
+    // the virtual time at this point is still 0
 }
 ```
 
-In situations where the code inside the `withContext` is very simple, it is not as important to provide a test 
-dispatcher. The function `veryExpensiveTwo` will behave identically in a `TestCoroutineDispatcher` and 
-`Dispatchers.Default` after the thread switch for `Dispatchers.Default`. Because `withContext` always returns a value by
-directly, there is no need to inject a `TestCoroutineDispatcher` into this function.
-
-```kotlin
-suspend fun veryExpensiveTwo() = withContext(Dispatchers.Default) {
-    2 // for very expensive values of 2
-}
-```
-
-Tests should provide a `TestCoroutineDispatcher` to code that calls `withContext` to provide time control for 
-delays, or when execution control is needed to test complex logic.
-
+Tests should, when possible, replace these dispatchers with a [TestDispatcher] if the `withContext` calls `delay` in the
+function under test. For example, `veryExpensiveFunction` above should allow mocking with a [TestDispatcher] using
+either dependency injection, a service locator, or a default parameter, if it is to be used with virtual time.
 
 ### Status of the API
 
@@ -426,36 +362,32 @@
 Changes during experimental may have deprecation applied when possible, but it is not
 advised to use the API in stable code before it leaves experimental due to possible breaking changes.
 
-If you have any suggestions for improvements to this experimental API please share them them on the 
+If you have any suggestions for improvements to this experimental API please share them them on the
 [issue tracker](https://github.com/Kotlin/kotlinx.coroutines/issues).
 
 <!--- MODULE kotlinx-coroutines-core -->
 <!--- INDEX kotlinx.coroutines -->
 
-[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
-[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html
-[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html
-[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html
-[delay]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html
-[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html
-[CoroutineStart]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/index.html
 [CoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html
+[CoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/index.html
+[Dispatchers.Unconfined]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-unconfined.html
+[Dispatchers.Main]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-main.html
+[yield]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html
 [ExperimentalCoroutinesApi]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-experimental-coroutines-api/index.html
 
 <!--- MODULE kotlinx-coroutines-test -->
 <!--- INDEX kotlinx.coroutines.test -->
 
+[runTest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html
+[TestCoroutineScheduler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/index.html
+[TestScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/index.html
+[TestDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/index.html
+[Dispatchers.setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html
+[StandardTestDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-standard-test-dispatcher.html
+[UnconfinedTestDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html
 [setMain]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/set-main.html
-[runBlockingTest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html
-[UncompletedCoroutinesError]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-uncompleted-coroutines-error/index.html
-[DelayController]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/index.html
-[DelayController.advanceUntilIdle]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/advance-until-idle.html
-[DelayController.pauseDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/pause-dispatcher.html
-[TestCoroutineDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-dispatcher/index.html
-[DelayController.resumeDispatcher]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/resume-dispatcher.html
-[TestCoroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/index.html
-[TestCoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-exception-handler/index.html
-[TestCoroutineScope.cleanupTestCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scope/cleanup-test-coroutines.html
-[DelayController.cleanupTestCoroutines]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-delay-controller/cleanup-test-coroutines.html
+[TestScope.testScheduler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-scope/test-scheduler.html
+[TestScope.runTest]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html
+[runCurrent]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-current.html
 
 <!--- END -->