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

Code before:
                    "Both a TestCoroutineScheduler $ctxScheduler and TestDispatcher $dispatcher linked to " +
                        "another scheduler were passed."
                }
            }
            dispatcher
        }
        null -> {
            scheduler = context[TestCoroutineScheduler] ?: TestCoroutineScheduler()
            StandardTestDispatcher(scheduler)
        }
        else -> throw IllegalArgumentException("Dispatcher must implement TestDispatcher: $dispatcher")
    }
    var scope: TestCoroutineScopeImpl? = null
    val exceptionHandler = when (val exceptionHandler = context[CoroutineExceptionHandler]) {
        is UncaughtExceptionCaptor -> exceptionHandler
        null -> {
            val lock = SynchronizedObject()
            CoroutineExceptionHandler { context, throwable ->
                val reported = synchronized(lock) {
                    scope!!.reportException(throwable)
                }
                if (!reported)
                    throw throwable // let this exception crash everything
            }
        }
        else -> throw IllegalArgumentException("A CoroutineExceptionHandler was passed to TestCoroutineScope")
    }
    val job: Job = context[Job] ?: Job()
    return TestCoroutineScopeImpl(context + scheduler + dispatcher + exceptionHandler + job).also {
        scope = it
    }
}


private inline val CoroutineContext.delayController: DelayController?
    get() {
        val handler = this[ContinuationInterceptor]
        return handler as? DelayController
    }


/**
 * The current virtual time on [testScheduler][TestCoroutineScope.testScheduler].
 * @see TestCoroutineScheduler.currentTime
 */
@ExperimentalCoroutinesApi
public val TestCoroutineScope.currentTime: Long
    get() = coroutineContext.delayController?.currentTime ?: testScheduler.currentTime

/**
 * Advances the [testScheduler][TestCoroutineScope.testScheduler] by [delayTimeMillis] and runs the tasks up to that

Review comment (suggestion):
Please consider making the exception message a bit more detailed and/or actionable

Code after:
        }
        null -> {
            scheduler = context[TestCoroutineScheduler] ?: TestCoroutineScheduler()
            StandardTestDispatcher(scheduler)
        }
        else -> throw IllegalArgumentException("Dispatcher must implement TestDispatcher: $dispatcher")
    }
    var scope: TestCoroutineScopeImpl? = null
    val ownExceptionHandler = run {
        val lock = SynchronizedObject()
        object : AbstractCoroutineContextElement(CoroutineExceptionHandler), TestCoroutineScopeExceptionHandler {
            override fun handleException(context: CoroutineContext, exception: Throwable) {
                val reported = synchronized(lock) {
                    scope!!.reportException(exception)
                }
                if (!reported)
                    throw exception // let this exception crash everything
            }
        }
    }
    val exceptionHandler = when (val exceptionHandler = context[CoroutineExceptionHandler]) {
        is UncaughtExceptionCaptor -> exceptionHandler
        null -> ownExceptionHandler
        is TestCoroutineScopeExceptionHandler -> ownExceptionHandler
        else -> throw IllegalArgumentException(
            "A CoroutineExceptionHandler was passed to TestCoroutineScope. " +
                "Please pass it as an argument to a `launch` or `async` block on an already-created scope " +
                "if uncaught exceptions require special treatment."
        )
    }
    val job: Job = context[Job] ?: Job()
    return TestCoroutineScopeImpl(context + scheduler + dispatcher + exceptionHandler + job).also {
        scope = it
    }
}

/** A marker that shows that this [CoroutineExceptionHandler] was created for [TestCoroutineScope]. With this,
 * constructing a new [TestCoroutineScope] with the [CoroutineScope.coroutineContext] of an existing one will override
 * the exception handler, instead of failing. */
private interface TestCoroutineScopeExceptionHandler: CoroutineExceptionHandler

private inline val CoroutineContext.delayController: DelayController?
    get() {
        val handler = this[ContinuationInterceptor]
        return handler as? DelayController
    }


/**
 * The current virtual time on [testScheduler][TestCoroutineScope.testScheduler].
 * @see TestCoroutineScheduler.currentTime