Repository: Kotlin/kotlinx.coroutines
File: kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt (Kotlin)

Code before:
 * In order to support delay skipping, this dispatcher is linked to a [TestCoroutineScheduler], which is used to control
 * the virtual time and can be shared among many test dispatchers.
 * If no [scheduler] is passed as an argument, [Dispatchers.Main] is checked, and if it was mocked with a
 * [TestDispatcher] via [Dispatchers.setMain], the [TestDispatcher.scheduler] of the mock dispatcher is used; if
 * [Dispatchers.Main] is not mocked with a [TestDispatcher], a new [TestCoroutineScheduler] is created.
 *
 * Additionally, [name] can be set to distinguish each dispatcher instance when debugging.
 *
 * @see StandardTestDispatcher for a more predictable [TestDispatcher].
 */
@ExperimentalCoroutinesApi
@Suppress("FunctionName")
public fun UnconfinedTestDispatcher(
    scheduler: TestCoroutineScheduler? = null,
    name: String? = null
): TestDispatcher = UnconfinedTestDispatcherImpl(
    scheduler ?: TestMainDispatcher.currentTestScheduler ?: TestCoroutineScheduler(), name)

private class UnconfinedTestDispatcherImpl(
    override val scheduler: TestCoroutineScheduler,
    private val name: String? = null
) : TestDispatcher() {

    override fun isDispatchNeeded(context: CoroutineContext): Boolean = false

    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        checkSchedulerInContext(scheduler, context)
        scheduler.sendDispatchEvent(context)

        /** copy-pasted from [kotlinx.coroutines.Unconfined.dispatch] */
        /** It can only be called by the [yield] function. See also code of [yield] function. */
        val yieldContext = context[YieldContext]
        if (yieldContext !== null) {
            // report to "yield" that it is an unconfined dispatcher and don't call "block.run()"
            yieldContext.dispatcherWasUnconfined = true
            return
        }
        throw UnsupportedOperationException(
            "Function UnconfinedTestCoroutineDispatcher.dispatch can only be used by " +
                "the yield function. If you wrap Unconfined dispatcher in your code, make sure you properly delegate " +
                "isDispatchNeeded and dispatch calls."
        )
    }

    override fun toString(): String = "${name ?: "UnconfinedTestDispatcher"}[scheduler=$scheduler]"
}

/**
 * Creates an instance of a [TestDispatcher] whose tasks are run inside calls to the [scheduler].
 *

Review comment (suggestion):
Here's a thought: won't the IDE complain that the suppression is redundant? Let's guard against accidentally removing it. Maybe something like this: https://github.com/Kotlin/kotlinx.coroutines/compare/develop...kotlin-community/k2/dev3#diff-ee02d7e5615b49f17adcbbb0cf3359a30222e91ff6a916c2cbc13043d0d79c59R84

If you agree, let's replace the commit that introduces this with the amended one: https://github.com/Kotlin/kotlinx.coroutines/commit/1030449bd84d2b5c9848f6664d85a62bbe95da0d

Code after:
 * In order to support delay skipping, this dispatcher is linked to a [TestCoroutineScheduler], which is used to control
 * the virtual time and can be shared among many test dispatchers.
 * If no [scheduler] is passed as an argument, [Dispatchers.Main] is checked, and if it was mocked with a
 * [TestDispatcher] via [Dispatchers.setMain], the [TestDispatcher.scheduler] of the mock dispatcher is used; if
 * [Dispatchers.Main] is not mocked with a [TestDispatcher], a new [TestCoroutineScheduler] is created.
 *
 * Additionally, [name] can be set to distinguish each dispatcher instance when debugging.
 *
 * @see StandardTestDispatcher for a more predictable [TestDispatcher].
 */
@ExperimentalCoroutinesApi
@Suppress("FunctionName")
public fun UnconfinedTestDispatcher(
    scheduler: TestCoroutineScheduler? = null,
    name: String? = null
): TestDispatcher = UnconfinedTestDispatcherImpl(
    scheduler ?: TestMainDispatcher.currentTestScheduler ?: TestCoroutineScheduler(), name)

private class UnconfinedTestDispatcherImpl(
    override val scheduler: TestCoroutineScheduler,
    private val name: String? = null
) : TestDispatcher() {

    override fun isDispatchNeeded(context: CoroutineContext): Boolean = false

    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") // do not remove the INVISIBLE_REFERENCE suppression: required in K2
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        checkSchedulerInContext(scheduler, context)
        scheduler.sendDispatchEvent(context)

        /** copy-pasted from [kotlinx.coroutines.Unconfined.dispatch] */
        /** It can only be called by the [yield] function. See also code of [yield] function. */
        val yieldContext = context[YieldContext]
        if (yieldContext !== null) {
            // report to "yield" that it is an unconfined dispatcher and don't call "block.run()"
            yieldContext.dispatcherWasUnconfined = true
            return
        }
        throw UnsupportedOperationException(
            "Function UnconfinedTestCoroutineDispatcher.dispatch can only be used by " +
                "the yield function. If you wrap Unconfined dispatcher in your code, make sure you properly delegate " +
                "isDispatchNeeded and dispatch calls."
        )
    }

    override fun toString(): String = "${name ?: "UnconfinedTestDispatcher"}[scheduler=$scheduler]"
}

/**
 * Creates an instance of a [TestDispatcher] whose tasks are run inside calls to the [scheduler].
 *