From 6f3d4f58815af3600e73bd41618377c144bbbe2e Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 27 Sep 2022 18:20:30 +0200 Subject: [PATCH 1/4] Adjust Gradle configuration to gradually introduce warnings as errors in a safe manner * Also, add tests that verify our disabled assertions * Fix nullability in AgentPremain that used to work by accident (because we disabled those assertions) --- build.gradle | 13 +-------- ...nfigure-compilation-conventions.gradle.kts | 20 +++++++++++++ .../jvm/src/debug/AgentPremain.kt | 4 +-- .../jvm/test/NoParamAssertionsTest.kt | 29 +++++++++++++++++++ 4 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts create mode 100644 kotlinx-coroutines-core/jvm/test/NoParamAssertionsTest.kt diff --git a/build.gradle b/build.gradle index 59c28893ab..d5b231d969 100644 --- a/build.gradle +++ b/build.gradle @@ -168,18 +168,7 @@ configure(subprojects.findAll { !sourceless.contains(it.name) && it.name != core apply plugin: "bom-conventions" // Configure subprojects with Kotlin sources -configure(subprojects.findAll { !sourceless.contains(it.name) }) { - // Use atomicfu plugin, it also adds all the necessary dependencies - apply plugin: 'kotlinx-atomicfu' - - // Configure options for all Kotlin compilation tasks - tasks.withType(AbstractKotlinCompile).all { - kotlinOptions.freeCompilerArgs += OptInPreset.optInAnnotations.collect { "-Xopt-in=" + it } - kotlinOptions.freeCompilerArgs += "-progressive" - // Remove null assertions to get smaller bytecode on Android - kotlinOptions.freeCompilerArgs += ["-Xno-param-assertions", "-Xno-receiver-assertions", "-Xno-call-assertions"] - } -} +apply plugin: "configure-compilation-conventions" if (build_snapshot_train) { println "Hacking test tasks, removing stress and flaky tests" diff --git a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts new file mode 100644 index 0000000000..d7f2b01e43 --- /dev/null +++ b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +import org.jetbrains.kotlin.gradle.tasks.* + +configure(subprojects) { + if (name in sourceless) return@configure + apply(plugin = "kotlinx-atomicfu") + tasks.withType(KotlinCompile::class).all { + kotlinOptions { + val newOptions = + listOf( + "-progressive", "-Xno-param-assertions", "-Xno-receiver-assertions", + "-Xno-call-assertions" + ) + optInAnnotations.map { "-Xopt-in=$it" } + freeCompilerArgs = freeCompilerArgs + newOptions + } + } +} diff --git a/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt b/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt index 4b0ce3f31e..2372249b5c 100644 --- a/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt +++ b/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt @@ -36,13 +36,13 @@ internal object AgentPremain { internal object DebugProbesTransformer : ClassFileTransformer { override fun transform( - loader: ClassLoader, + loader: ClassLoader?, className: String, classBeingRedefined: Class<*>?, protectionDomain: ProtectionDomain, classfileBuffer: ByteArray? ): ByteArray? { - if (className != "kotlin/coroutines/jvm/internal/DebugProbesKt") { + if (loader == null || className != "kotlin/coroutines/jvm/internal/DebugProbesKt") { return null } /* diff --git a/kotlinx-coroutines-core/jvm/test/NoParamAssertionsTest.kt b/kotlinx-coroutines-core/jvm/test/NoParamAssertionsTest.kt new file mode 100644 index 0000000000..5e1c4625e7 --- /dev/null +++ b/kotlinx-coroutines-core/jvm/test/NoParamAssertionsTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.coroutines + +import kotlinx.coroutines.* +import org.junit.Test +import kotlin.test.* + + +class NoParamAssertionsTest : TestBase() { + // These tests verify that we haven't omitted "-Xno-param-assertions" and "-Xno-receiver-assertions" + + @Test + fun testNoReceiverAssertion() { + val function: (ThreadLocal, Int) -> ThreadContextElement = ThreadLocal::asContextElement + @Suppress("UNCHECKED_CAST") + val unsafeCasted = function as ((ThreadLocal?, Int) -> ThreadContextElement) + unsafeCasted(null, 42) + } + + @Test + fun testNoParamAssertion() { + val function: (ThreadLocal, Any) -> ThreadContextElement = ThreadLocal::asContextElement + @Suppress("UNCHECKED_CAST") + val unsafeCasted = function as ((ThreadLocal?, Any?) -> ThreadContextElement) + unsafeCasted(ThreadLocal.withInitial { Any() }, null) + } +} From b61fe8d02de96da02da4566ba360d32ea241fc73 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 27 Sep 2022 19:03:08 +0200 Subject: [PATCH 2/4] Enable warnings as errors where appropriate --- ...nfigure-compilation-conventions.gradle.kts | 31 ++++++++++++++++++- .../src/ReactorFlow.kt | 3 +- .../kotlinx-coroutines-rx3/src/RxMaybe.kt | 1 + 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts index d7f2b01e43..0f92c60dcd 100644 --- a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts @@ -4,16 +4,45 @@ import org.jetbrains.kotlin.gradle.tasks.* +val graduallyIntroducedWarningAsErrorProjects = setOf( + // UI + "kotlinx-coroutines-android", + "kotlinx-coroutines-javafx", + "kotlinx-coroutines-swing", + + // Reactive + "kotlinx-coroutines-jdk9", + "kotlinx-coroutines-reactive", + "kotlinx-coroutines-reactor", + "kotlinx-coroutines-rx2", + "kotlinx-coroutines-rx3", + + // Integration + "kotlinx-coroutines-guava", + "kotlinx-coroutines-jdk8", + "kotlinx-coroutines-play-services", + "kotlinx-coroutines-slf4j", + + // Top-level + "kotlinx-coroutines-debug", + + ) + configure(subprojects) { if (name in sourceless) return@configure apply(plugin = "kotlinx-atomicfu") + val projectName = name tasks.withType(KotlinCompile::class).all { + val isMainTaskName = name == "compileKotlin" || name == "compileKotlinJvm" kotlinOptions { + if (projectName in graduallyIntroducedWarningAsErrorProjects && isMainTaskName) { + allWarningsAsErrors = true + } val newOptions = listOf( "-progressive", "-Xno-param-assertions", "-Xno-receiver-assertions", "-Xno-call-assertions" - ) + optInAnnotations.map { "-Xopt-in=$it" } + ) + optInAnnotations.map { "-opt-in=$it" } freeCompilerArgs = freeCompilerArgs + newOptions } } diff --git a/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt b/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt index 0fc743f937..6a77bbf3a6 100644 --- a/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt +++ b/reactive/kotlinx-coroutines-reactor/src/ReactorFlow.kt @@ -32,8 +32,7 @@ private class FlowAsFlux( private val flow: Flow, private val context: CoroutineContext ) : Flux() { - override fun subscribe(subscriber: CoreSubscriber?) { - if (subscriber == null) throw NullPointerException() + override fun subscribe(subscriber: CoreSubscriber) { val hasContext = !subscriber.currentContext().isEmpty val source = if (hasContext) flow.flowOn(subscriber.currentContext().asCoroutineContext()) else flow subscriber.onSubscribe(FlowSubscription(source, subscriber, context)) diff --git a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt index 2e50481732..39306e29ce 100644 --- a/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt +++ b/reactive/kotlinx-coroutines-rx3/src/RxMaybe.kt @@ -43,6 +43,7 @@ private class RxMaybeCoroutine( ) : AbstractCoroutine(parentContext, false, true) { override fun onCompleted(value: T) { try { + @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") // KT-54201 if (value == null) subscriber.onComplete() else subscriber.onSuccess(value) } catch (e: Throwable) { handleUndeliverableException(e, context) From e264843f2c8c2536290eafba2f0b3260be48e81f Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Tue, 27 Sep 2022 19:13:59 +0200 Subject: [PATCH 3/4] Enable allWarningsAsErrors in core module as well --- ...nfigure-compilation-conventions.gradle.kts | 1 + .../common/src/channels/AbstractChannel.kt | 21 +++++++++++++++---- .../common/src/channels/ArrayChannel.kt | 1 + .../common/src/channels/Broadcast.kt | 3 ++- .../common/src/channels/Channel.kt | 1 + .../src/internal/DispatchedContinuation.kt | 4 +++- .../common/src/internal/DispatchedTask.kt | 1 - .../jvm/src/channels/Actor.kt | 8 ++++++- .../jvm/src/debug/AgentPremain.kt | 1 + .../jvm/src/internal/MainDispatchers.kt | 3 --- 10 files changed, 33 insertions(+), 11 deletions(-) diff --git a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts index 0f92c60dcd..a83208131e 100644 --- a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts @@ -24,6 +24,7 @@ val graduallyIntroducedWarningAsErrorProjects = setOf( "kotlinx-coroutines-slf4j", // Top-level + "kotlinx-coroutines-core", "kotlinx-coroutines-debug", ) diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt index 0fe07488ed..2f2fe506e0 100644 --- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt @@ -2,6 +2,8 @@ * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("UNCHECKED_CAST", "UNUSED_PARAMETER") + package kotlinx.coroutines.channels import kotlinx.atomicfu.* @@ -16,6 +18,7 @@ import kotlin.jvm.* /** * Abstract send channel. It is a base class for all send channel implementations. */ +@Suppress("UNCHECKED_CAST", "UNUSED_PARAMETER") internal abstract class AbstractSendChannel( @JvmField protected val onUndeliveredElement: OnUndeliveredElement? ) : SendChannel { @@ -122,7 +125,12 @@ internal abstract class AbstractSendChannel( return sendSuspend(element) } - @Suppress("DEPRECATION", "DEPRECATION_ERROR") + @Suppress("DEPRECATION_ERROR") + @Deprecated( + level = DeprecationLevel.ERROR, + message = "Deprecated in the favour of 'trySend' method", + replaceWith = ReplaceWith("trySend(element).isSuccess") + ) // see super() override fun offer(element: E): Boolean { // Temporary migration for offer users who rely on onUndeliveredElement try { @@ -705,6 +713,11 @@ internal abstract class AbstractChannel( onCancellationConstructor = onUndeliveredElementReceiveCancellationConstructor ) + @Deprecated( + message = "Deprecated in favor of onReceiveCatching extension", + level = DeprecationLevel.ERROR, + replaceWith = ReplaceWith("onReceiveCatching") + ) // See super() override val onReceiveOrNull: SelectClause1 get() = SelectClause1Impl( clauseObject = this, @@ -726,7 +739,7 @@ internal abstract class AbstractChannel( if (selectResult is Closed<*>) throw selectResult.receiveException else selectResult as E - private fun processResultSelectReceiveCatching(ignoredParam: Any?, selectResult: Any?): Any? = + private fun processResultSelectReceiveCatching(ignoredParam: Any?, selectResult: Any?): Any = if (selectResult is Closed<*>) ChannelResult.closed(selectResult.closeCause) else ChannelResult.success(selectResult as E) @@ -735,8 +748,8 @@ internal abstract class AbstractChannel( else selectResult as E private val onUndeliveredElementReceiveCancellationConstructor: OnCancellationConstructor? = onUndeliveredElement?.let { - { select: SelectInstance<*>, ignoredParam: Any?, element: Any? -> - { cause: Throwable -> if (element !is Closed<*>) onUndeliveredElement.callUndeliveredElement(element as E, select.context) } + { select: SelectInstance<*>, _: Any?, element: Any? -> + { _: Throwable -> if (element !is Closed<*>) onUndeliveredElement.callUndeliveredElement(element as E, select.context) } } } diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt index 93f99cf1f6..47a1a72fae 100644 --- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt @@ -158,6 +158,7 @@ internal open class ArrayChannel( // Too late, already cancelled, but we removed it from the queue and need to notify on undelivered element. // The only exception is when this "send" operation is an `onSend` clause that has to be re-registered // in the corresponding `select` invocation. + @Suppress("NAME_SHADOWING") val send = send!! if (!(send is SendElementSelectWithUndeliveredHandler<*> && send.trySelectResult == REREGISTER)) send.undeliveredElement() diff --git a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt index b1c24b456d..ba39d4ff7e 100644 --- a/kotlinx-coroutines-core/common/src/channels/Broadcast.kt +++ b/kotlinx-coroutines-core/common/src/channels/Broadcast.kt @@ -47,10 +47,11 @@ public fun ReceiveChannel.broadcast( start: CoroutineStart = CoroutineStart.LAZY ): BroadcastChannel { val scope = GlobalScope + Dispatchers.Unconfined + CoroutineExceptionHandler { _, _ -> } + val channel = this // We can run this coroutine in the context that ignores all exceptions, because of `onCompletion = consume()` // which passes all exceptions upstream to the source ReceiveChannel return scope.broadcast(capacity = capacity, start = start, onCompletion = { cancelConsumed(it) }) { - for (e in this@broadcast) { + for (e in channel) { send(e) } } diff --git a/kotlinx-coroutines-core/common/src/channels/Channel.kt b/kotlinx-coroutines-core/common/src/channels/Channel.kt index 5ceb515f95..51c0214fe2 100644 --- a/kotlinx-coroutines-core/common/src/channels/Channel.kt +++ b/kotlinx-coroutines-core/common/src/channels/Channel.kt @@ -360,6 +360,7 @@ public interface ReceiveChannel { * * @suppress **Deprecated**: in favor of onReceiveCatching extension. */ + @Suppress("DEPRECATION_ERROR") @Deprecated( message = "Deprecated in favor of onReceiveCatching extension", level = DeprecationLevel.ERROR, diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt index bb2bbf6d92..c196147333 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedContinuation.kt @@ -207,6 +207,7 @@ internal class DispatchedContinuation( // We inline it to save an entry on the stack in cases where it shows (unconfined dispatcher) // It is used only in Continuation.resumeCancellableWith + @Suppress("NOTHING_TO_INLINE") inline fun resumeCancellableWith( result: Result, noinline onCancellation: ((cause: Throwable) -> Unit)? @@ -235,7 +236,7 @@ internal class DispatchedContinuation( } // inline here is to save us an entry on the stack for the sake of better stacktraces - + @Suppress("NOTHING_TO_INLINE") inline fun resumeCancelled(state: Any?): Boolean { val job = context[Job] if (job != null && !job.isActive) { @@ -247,6 +248,7 @@ internal class DispatchedContinuation( return false } + @Suppress("NOTHING_TO_INLINE") inline fun resumeUndispatchedWith(result: Result) { withContinuationContext(continuation, countOrElement) { continuation.resumeWith(result) diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt index d982f95bdf..1de1bff479 100644 --- a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt +++ b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt @@ -167,7 +167,6 @@ internal fun DispatchedTask.dispatch(mode: Int) { } } -@Suppress("UNCHECKED_CAST") internal fun DispatchedTask.resume(delegate: Continuation, undispatched: Boolean) { // This resume is never cancellable. The result is always delivered to delegate continuation. val state = takeState() diff --git a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt index 76d8aae3eb..e8a9152e09 100644 --- a/kotlinx-coroutines-core/jvm/src/channels/Actor.kt +++ b/kotlinx-coroutines-core/jvm/src/channels/Actor.kt @@ -162,7 +162,12 @@ private class LazyActorCoroutine( return super.send(element) } - @Suppress("DEPRECATION", "DEPRECATION_ERROR") + @Suppress("DEPRECATION_ERROR") + @Deprecated( + level = DeprecationLevel.ERROR, + message = "Deprecated in the favour of 'trySend' method", + replaceWith = ReplaceWith("trySend(element).isSuccess") + ) // See super() override fun offer(element: E): Boolean { start() return super.offer(element) @@ -181,6 +186,7 @@ private class LazyActorCoroutine( return closed } + @Suppress("UNCHECKED_CAST") override val onSend: SelectClause2> get() = SelectClause2Impl( clauseObject = this, regFunc = LazyActorCoroutine<*>::onSendRegFunction as RegistrationFunction, diff --git a/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt b/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt index 2372249b5c..fb5c5b1b06 100644 --- a/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt +++ b/kotlinx-coroutines-core/jvm/src/debug/AgentPremain.kt @@ -26,6 +26,7 @@ internal object AgentPremain { }.getOrNull() ?: DebugProbesImpl.enableCreationStackTraces @JvmStatic + @Suppress("UNUSED_PARAMETER") fun premain(args: String?, instrumentation: Instrumentation) { AgentInstallationType.isInstalledStatically = true instrumentation.addTransformer(DebugProbesTransformer) diff --git a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt index e87952b419..0c55d92eb8 100644 --- a/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt +++ b/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt @@ -98,9 +98,6 @@ private class MissingMainCoroutineDispatcher( override fun limitedParallelism(parallelism: Int): CoroutineDispatcher = missing() - override suspend fun delay(time: Long) = - missing() - override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle = missing() From b3ccd325d488c333d94c81a495c1c40b99c37c60 Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Wed, 28 Sep 2022 15:45:50 +0200 Subject: [PATCH 4/4] Fix or suppress all warnings in the test module --- ...nfigure-compilation-conventions.gradle.kts | 27 +------------------ .../common/src/channels/ArrayChannel.kt | 8 +++--- .../common/src/TestCoroutineScheduler.kt | 6 ++--- .../common/src/TestDispatcher.kt | 2 +- .../src/migration/TestBuildersDeprecated.kt | 2 +- .../src/migration/TestCoroutineDispatcher.kt | 12 +++++++++ .../TestCoroutineExceptionHandler.kt | 1 + .../jvm/src/migration/TestCoroutineScope.kt | 1 + 8 files changed, 24 insertions(+), 35 deletions(-) diff --git a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts index a83208131e..1c3f486a36 100644 --- a/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/configure-compilation-conventions.gradle.kts @@ -4,31 +4,6 @@ import org.jetbrains.kotlin.gradle.tasks.* -val graduallyIntroducedWarningAsErrorProjects = setOf( - // UI - "kotlinx-coroutines-android", - "kotlinx-coroutines-javafx", - "kotlinx-coroutines-swing", - - // Reactive - "kotlinx-coroutines-jdk9", - "kotlinx-coroutines-reactive", - "kotlinx-coroutines-reactor", - "kotlinx-coroutines-rx2", - "kotlinx-coroutines-rx3", - - // Integration - "kotlinx-coroutines-guava", - "kotlinx-coroutines-jdk8", - "kotlinx-coroutines-play-services", - "kotlinx-coroutines-slf4j", - - // Top-level - "kotlinx-coroutines-core", - "kotlinx-coroutines-debug", - - ) - configure(subprojects) { if (name in sourceless) return@configure apply(plugin = "kotlinx-atomicfu") @@ -36,7 +11,7 @@ configure(subprojects) { tasks.withType(KotlinCompile::class).all { val isMainTaskName = name == "compileKotlin" || name == "compileKotlinJvm" kotlinOptions { - if (projectName in graduallyIntroducedWarningAsErrorProjects && isMainTaskName) { + if (isMainTaskName) { allWarningsAsErrors = true } val newOptions = diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt index 47a1a72fae..abf18ac39a 100644 --- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt +++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt @@ -158,10 +158,10 @@ internal open class ArrayChannel( // Too late, already cancelled, but we removed it from the queue and need to notify on undelivered element. // The only exception is when this "send" operation is an `onSend` clause that has to be re-registered // in the corresponding `select` invocation. - @Suppress("NAME_SHADOWING") - val send = send!! - if (!(send is SendElementSelectWithUndeliveredHandler<*> && send.trySelectResult == REREGISTER)) - send.undeliveredElement() + send!!.apply { + if (!(this is SendElementSelectWithUndeliveredHandler<*> && trySelectResult == REREGISTER)) + undeliveredElement() + } } } if (replacement !== POLL_FAILED && replacement !is Closed<*>) { diff --git a/kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt b/kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt index e735c6d4de..5f7198cfff 100644 --- a/kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt +++ b/kotlinx-coroutines-test/common/src/TestCoroutineScheduler.kt @@ -97,7 +97,7 @@ public class TestCoroutineScheduler : AbstractCoroutineContextElement(TestCorout currentTime = event.time event } - event.dispatcher.processEvent(event.time, event.marker) + event.dispatcher.processEvent(event.marker) return true } @@ -132,7 +132,7 @@ public class TestCoroutineScheduler : AbstractCoroutineContextElement(TestCorout val event = synchronized(lock) { events.removeFirstIf { it.time <= timeMark } ?: return } - event.dispatcher.processEvent(event.time, event.marker) + event.dispatcher.processEvent(event.marker) } } @@ -173,7 +173,7 @@ public class TestCoroutineScheduler : AbstractCoroutineContextElement(TestCorout } } } - event.dispatcher.processEvent(event.time, event.marker) + event.dispatcher.processEvent(event.marker) } } diff --git a/kotlinx-coroutines-test/common/src/TestDispatcher.kt b/kotlinx-coroutines-test/common/src/TestDispatcher.kt index 9616bb0b23..8ed8192b9e 100644 --- a/kotlinx-coroutines-test/common/src/TestDispatcher.kt +++ b/kotlinx-coroutines-test/common/src/TestDispatcher.kt @@ -23,7 +23,7 @@ public abstract class TestDispatcher internal constructor() : CoroutineDispatche public abstract val scheduler: TestCoroutineScheduler /** Notifies the dispatcher that it should process a single event marked with [marker] happening at time [time]. */ - internal fun processEvent(time: Long, marker: Any) { + internal fun processEvent(marker: Any) { check(marker is Runnable) marker.run() } diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt b/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt index eabdffb2c8..35d237a7f5 100644 --- a/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt +++ b/kotlinx-coroutines-test/jvm/src/migration/TestBuildersDeprecated.kt @@ -159,7 +159,7 @@ public fun runTestWithLegacyScope( context: CoroutineContext = EmptyCoroutineContext, dispatchTimeoutMs: Long = DEFAULT_DISPATCH_TIMEOUT_MS, testBody: suspend TestCoroutineScope.() -> Unit -): TestResult { +) { if (context[RunningInRunTest] != null) throw IllegalStateException("Calls to `runTest` can't be nested. Please read the docs on `TestResult` for details.") val testScope = TestBodyCoroutine(createTestCoroutineScope(context + RunningInRunTest)) diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt index 08f428f249..40a0f5dfab 100644 --- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt +++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineDispatcher.kt @@ -61,6 +61,10 @@ public class TestCoroutineDispatcher(public override val scheduler: TestCoroutin scheduler.registerEvent(this, 0, block, context) { false } /** @suppress */ + @Deprecated( + "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.", + level = DeprecationLevel.WARNING + ) override suspend fun pauseDispatcher(block: suspend () -> Unit) { val previous = dispatchImmediately dispatchImmediately = false @@ -72,11 +76,19 @@ public class TestCoroutineDispatcher(public override val scheduler: TestCoroutin } /** @suppress */ + @Deprecated( + "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.", + level = DeprecationLevel.WARNING + ) override fun pauseDispatcher() { dispatchImmediately = false } /** @suppress */ + @Deprecated( + "Please use a dispatcher that is paused by default, like `StandardTestDispatcher`.", + level = DeprecationLevel.WARNING + ) override fun resumeDispatcher() { dispatchImmediately = true } diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt index 9da521f05c..aeb0f35882 100644 --- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt +++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineExceptionHandler.kt @@ -42,6 +42,7 @@ public interface UncaughtExceptionCaptor { /** * An exception handler that captures uncaught exceptions in tests. */ +@Suppress("DEPRECATION") @Deprecated( "Deprecated for removal without a replacement. " + "It may be to define one's own `CoroutineExceptionHandler` if you just need to handle '" + diff --git a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt index 4a2cbc5c2c..5af83f5197 100644 --- a/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt +++ b/kotlinx-coroutines-test/jvm/src/migration/TestCoroutineScope.kt @@ -86,6 +86,7 @@ private class TestCoroutineScopeImpl( /** These jobs existed before the coroutine scope was used, so it's alright if they don't get cancelled. */ private val initialJobs = coroutineContext.activeJobs() + @Deprecated("Please call `runTest`, which automatically performs the cleanup, instead of using this function.") override fun cleanupTestCoroutines() { val delayController = coroutineContext.delayController val hasUnfinishedJobs = if (delayController != null) {