diff --git a/buildSrc/src/main/kotlin/ProjectConfigure.kt b/buildSrc/src/main/kotlin/ProjectConfigure.kt index ed75b8a77..2049318dc 100644 --- a/buildSrc/src/main/kotlin/ProjectConfigure.kt +++ b/buildSrc/src/main/kotlin/ProjectConfigure.kt @@ -16,7 +16,10 @@ import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.* -import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinTarget @@ -61,11 +64,12 @@ fun Project.preConfigureJvmTarget() { fun Project.configureJvmTarget() { val defaultVer = jvmVersion() - tasks.withType(KotlinCompile::class) - .filter { it.name.contains("test", ignoreCase = true) } - .forEach { task -> - task.kotlinOptions.freeCompilerArgs += "-opt-in=net.mamoe.mirai.utils.TestOnly" + configure(kotlinSourceSets.orEmpty()) { + languageSettings { + optIn("net.mamoe.mirai.utils.TestOnly") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") } + } extensions.findByType(JavaPluginExtension::class.java)?.run { sourceCompatibility = defaultVer diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 633049065..874455250 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -73,6 +73,7 @@ fun kotlinx(id: String, version: String) = "org.jetbrains.kotlinx:kotlinx-$id:$v fun ktor(id: String, version: String = Versions.ktor) = "io.ktor:ktor-$id:$version" val `kotlinx-coroutines-core` = kotlinx("coroutines-core", Versions.coroutines) +val `kotlinx-coroutines-test` = kotlinx("coroutines-test", Versions.coroutines) val `kotlinx-coroutines-jdk8` = kotlinx("coroutines-jdk8", Versions.coroutines) val `kotlinx-coroutines-swing` = kotlinx("coroutines-swing", Versions.coroutines) val `kotlinx-coroutines-debug` = kotlinx("coroutines-debug", Versions.coroutines) diff --git a/mirai-console/backend/mirai-console/build.gradle.kts b/mirai-console/backend/mirai-console/build.gradle.kts index d135b25e0..6bf4ed0e1 100644 --- a/mirai-console/backend/mirai-console/build.gradle.kts +++ b/mirai-console/backend/mirai-console/build.gradle.kts @@ -65,6 +65,7 @@ dependencies { testApi(project(":mirai-core")) testApi(`kotlin-stdlib-jdk8`) + testApi(`kotlinx-coroutines-test`) "consoleRuntimeClasspath"(project) "consoleRuntimeClasspath"(project(":mirai-core-utils")) diff --git a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt index a6eca0a09..b065f13f7 100644 --- a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt +++ b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console.command import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import net.mamoe.mirai.console.MiraiConsoleImplementation import net.mamoe.mirai.console.Testing import net.mamoe.mirai.console.Testing.withTesting @@ -191,7 +192,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleExecute() = runBlocking { + fun testSimpleExecute() = runTest { simpleCommand.withRegistration { assertEquals("test", withTesting { assertSuccess(simpleCommand.execute(sender, "test")) @@ -200,7 +201,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `test raw command`() = runBlocking { + fun `test raw command`() = runTest { rawCommand.withRegistration { val result = withTesting { assertSuccess(rawCommand.execute(sender, PlainText("a1"), PlainText("a2"), PlainText("a3"))) @@ -221,7 +222,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `test enum argument`() = runBlocking { + fun `test enum argument`() = runTest { val enum = TestEnumArgCommand() enum.withRegistration { @@ -291,7 +292,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `test temporal argument`() = runBlocking { + fun `test temporal argument`() = runTest { val command = TestTemporalArgCommand() command.withRegistration { val temporal: List> = listOf( @@ -322,7 +323,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsSplitting() = runBlocking { + fun testSimpleArgsSplitting() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "ttt", "tt").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test ttt tt"))) @@ -331,7 +332,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsEscape() = runBlocking { + fun testSimpleArgsEscape() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test esc\\ ape"))) @@ -340,7 +341,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsQuote() = runBlocking { + fun testSimpleArgsQuote() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \"esc ape\""))) @@ -349,7 +350,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsQuoteReject() = runBlocking { + fun testSimpleArgsQuoteReject() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "es\"c", "ape\"").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test es\"c ape\""))) @@ -358,7 +359,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsQuoteEscape() = runBlocking { + fun testSimpleArgsQuoteEscape() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "\"esc", "ape\"").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \\\"esc ape\""))) @@ -367,7 +368,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsMultipleQuotes() = runBlocking { + fun testSimpleArgsMultipleQuotes() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape", "1 2").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \"esc ape\" \"1 2\""))) @@ -376,7 +377,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsMisplacedQuote() = runBlocking { + fun testSimpleArgsMisplacedQuote() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape", "1\"", "\"2").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \"esc ape\" 1\" \"2 "))) @@ -385,7 +386,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsQuoteSpaceEscape() = runBlocking { + fun testSimpleArgsQuoteSpaceEscape() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test \"esc", "ape\"").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test\\ \"esc ape\""))) @@ -394,7 +395,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParse() = runBlocking { + fun testSimpleArgsStopParse() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape ").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test -- esc ape "))) @@ -403,7 +404,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParse2() = runBlocking { + fun testSimpleArgsStopParse2() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "esc ape test\\12\"\"3").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test -- esc ape test\\12\"\"3"))) @@ -412,7 +413,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParseReject() = runBlocking { + fun testSimpleArgsStopParseReject() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test--", "esc", "ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test-- esc ape "))) @@ -421,7 +422,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParseEscape() = runBlocking { + fun testSimpleArgsStopParseEscape() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "--", "esc", "ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \\-- esc ape"))) @@ -430,7 +431,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParseEscape2() = runBlocking { + fun testSimpleArgsStopParseEscape2() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", " --", "esc", "ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \\ -- esc ape"))) @@ -439,7 +440,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun testSimpleArgsStopParseQuote() = runBlocking { + fun testSimpleArgsStopParseQuote() = runTest { simpleCommand.withRegistration { assertEquals(arrayOf("test", "--", "esc", "ape").joinToString(), withTesting { assertSuccess(simpleCommand.execute(sender, PlainText("test \"--\" esc ape"))) @@ -450,7 +451,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { val image = Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f") @Test - fun `PlainText and Image args splitting`() = runBlocking { + fun `PlainText and Image args splitting`() = runTest { simpleCommand.withRegistration { val result = withTesting { assertSuccess(simpleCommand.execute(sender, buildMessageChain { @@ -472,7 +473,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `executing command by string command`() = runBlocking { + fun `executing command by string command`() = runTest { compositeCommand.withRegistration { val result = withTesting { assertSuccess(sender.executeCommand("/testComposite mute 1")) @@ -490,7 +491,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `composite command executing`() = runBlocking { + fun `composite command executing`() = runTest { compositeCommand.withRegistration { assertEquals(1, withTesting { assertSuccess(compositeCommand.execute(sender, "mute 1")) @@ -499,7 +500,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { } @Test - fun `test first param command sender`() = runBlocking { + fun `test first param command sender`() = runTest { object : CompositeCommand(owner, "cmd") { @SubCommand fun handle(sender: CommandSender, arg: String) { diff --git a/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt b/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt index c1da959fa..f966ff1a5 100644 --- a/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt +++ b/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt @@ -12,8 +12,7 @@ package net.mamoe.mirai.console.command import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.runBlocking -import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge +import kotlinx.coroutines.test.runTest import net.mamoe.mirai.Bot import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors @@ -29,15 +28,14 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals @OptIn(ExperimentalCommandDescriptors::class) -@JvmBlockingBridge internal class LoginCommandTest : AbstractCommandTest() { @Test - fun `login with provided password`() = runBlocking { + fun `login with provided password`() = runTest { val myId = 123L val myPwd = "password001" - val bot = awaitDeferred { cont -> + val bot = awaitDeferred { cont -> val command = object : LoginCommandImpl() { override suspend fun doLogin(bot: Bot) { cont.complete(bot as QQAndroidBot) @@ -53,7 +51,7 @@ internal class LoginCommandTest : AbstractCommandTest() { } @Test - fun `login with saved plain password`() = runBlocking { + fun `login with saved plain password`() = runTest { val myId = 123L val myPwd = "password001" @@ -66,7 +64,7 @@ internal class LoginCommandTest : AbstractCommandTest() { ) }) - val bot = awaitDeferred { cont -> + val bot = awaitDeferred { cont -> val command = object : LoginCommandImpl() { override suspend fun doLogin(bot: Bot) { cont.complete(bot as QQAndroidBot) @@ -82,7 +80,7 @@ internal class LoginCommandTest : AbstractCommandTest() { } @Test - fun `login with saved md5 password`() = runBlocking { + fun `login with saved md5 password`() = runTest { val myId = 123L val myPwd = "password001" diff --git a/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt b/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt index 11c6b4de4..cde2280f4 100644 --- a/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt +++ b/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt @@ -23,7 +23,7 @@ internal class TestCoroutineUtils { // TODO TestCoroutineUtils disabled manually: on CI real time measurement is not precise causing tests to fail. // @Test -// fun `test launchTimedTask 0 time`() = runBlocking { +// fun `test launchTimedTask 0 time`() = runTest { // val scope = CoroutineScope(SupervisorJob()) // // val result = withTimeoutOrNull(6000) { @@ -39,7 +39,7 @@ internal class TestCoroutineUtils { // } // // @Test -// fun `test launchTimedTask finishes 1 time`() = runBlocking { +// fun `test launchTimedTask finishes 1 time`() = runTest { // val scope = CoroutineScope(SupervisorJob()) // // withTimeout(4000) { @@ -55,7 +55,7 @@ internal class TestCoroutineUtils { // } // // @Test -// fun `test launchTimedTask finishes multiple times`() = runBlocking { +// fun `test launchTimedTask finishes multiple times`() = runTest { // val scope = CoroutineScope(SupervisorJob()) // // val resumedTimes = AtomicInteger(0) @@ -77,7 +77,7 @@ internal class TestCoroutineUtils { // } // // @Test -// fun `test launchTimedTask interval less than delay`() = runBlocking { +// fun `test launchTimedTask interval less than delay`() = runTest { // val scope = CoroutineScope(SupervisorJob()) // // withTimeout(5000) { diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index 21792c886..a65eb94a2 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -51,6 +51,7 @@ kotlin { commonTest { dependencies { runtimeOnly(`log4j-core`) + implementation(`kotlinx-coroutines-test`) } } diff --git a/mirai-core-api/src/commonTest/kotlin/message.data/MessageUtilsTest.kt b/mirai-core-api/src/commonTest/kotlin/message.data/MessageUtilsTest.kt index fd0cc7574..228effd43 100644 --- a/mirai-core-api/src/commonTest/kotlin/message.data/MessageUtilsTest.kt +++ b/mirai-core-api/src/commonTest/kotlin/message.data/MessageUtilsTest.kt @@ -9,7 +9,7 @@ package net.mamoe.mirai.message.data import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -26,7 +26,7 @@ internal class MessageUtilsTest { } @Test - fun `flow toMessageChain`(): Unit = runBlocking { + fun `flow toMessageChain`() = runTest { assertEquals(messageChainOf(PlainText("1")), flowOf(PlainText("1")).toMessageChain()) } } \ No newline at end of file diff --git a/mirai-core-utils/build.gradle.kts b/mirai-core-utils/build.gradle.kts index 18ef0921b..ab64bb09a 100644 --- a/mirai-core-utils/build.gradle.kts +++ b/mirai-core-utils/build.gradle.kts @@ -43,6 +43,7 @@ kotlin { val commonTest by getting { dependencies { api(yamlkt) + implementation(`kotlinx-coroutines-test`) } } diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt index fdce2772f..f055bb9ec 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.utils import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield import kotlin.test.Test import kotlin.test.assertEquals @@ -50,7 +51,7 @@ internal class LateinitMutablePropertyTest { } @Test - fun setValuePrevailsOnCompetitionWithInitializer() = runBlocking { + fun setValuePrevailsOnCompetitionWithInitializer() = runTest { val verySlowInitializer = CompletableDeferred() val override = Symbol("override") val initializer = Symbol("initializer") diff --git a/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt b/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt index 45e11bdc2..a755ab3eb 100644 --- a/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt +++ b/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.utils import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield import org.junit.jupiter.api.Test import java.util.concurrent.CompletableFuture @@ -20,7 +21,7 @@ import kotlin.test.assertSame internal class LateinitMutablePropertyTestJvm { @Test - fun `initializer called once if requested by multiple threads`() = runBlocking { + fun `initializer called once if requested by multiple threads`() = runTest { val value = Symbol("expected") var counter = 0 diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index e66be6626..2a9a8e6ed 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -50,6 +50,7 @@ kotlin { commonTest { dependencies { implementation(kotlin("script-runtime")) + implementation(`kotlinx-coroutines-test`) api(yamlkt) } } diff --git a/mirai-core/src/commonTest/kotlin/event/EventTests.kt b/mirai-core/src/commonTest/kotlin/event/EventTests.kt index 3d33eb1b2..20cc30b02 100644 --- a/mirai-core/src/commonTest/kotlin/event/EventTests.kt +++ b/mirai-core/src/commonTest/kotlin/event/EventTests.kt @@ -11,7 +11,9 @@ package net.mamoe.mirai.internal.event import kotlinx.atomicfu.atomic import kotlinx.coroutines.* +import kotlinx.coroutines.test.runTest import net.mamoe.mirai.event.* +import net.mamoe.mirai.utils.childScope import kotlin.coroutines.EmptyCoroutineContext import kotlin.test.AfterTest import kotlin.test.Test @@ -112,7 +114,7 @@ internal class EventTests : AbstractEventTest() { } @Test - fun `test concurrent listening 2`() = runBlocking { + fun `test concurrent listening 2`() = runTest { resetEventListeners() val registered = atomic(0) val called = atomic(0) @@ -190,15 +192,11 @@ internal class EventTests : AbstractEventTest() { open class PriorityTestEvent : AbstractEvent() private fun singleThreaded(step: StepUtil, invoke: suspend EventChannel.() -> Unit) { - // runBlocking 会完全堵死, 没法退出 - val scope = CoroutineScope(borrowSingleThreadDispatcher()) - val job = scope.launch { + runTest(borrowSingleThreadDispatcher()) { + val scope = this.childScope() invoke(scope.globalEventChannel()) + scope.cancel() } - runBlocking { - job.join() - } - scope.cancel() step.throws() } diff --git a/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt b/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt index 083801039..49b99317b 100644 --- a/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt +++ b/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt @@ -19,15 +19,6 @@ import kotlin.test.* @JvmBlockingBridge internal class NextEventTest : AbstractEventTest() { - private val dispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher() - - @OptIn(ExperimentalCoroutinesApi::class) - @AfterTest - fun stopDispatcher() { - (dispatcher as CloseableCoroutineDispatcher).close() - } - - data class TE( val x: Int ) : AbstractEvent() @@ -44,66 +35,58 @@ internal class NextEventTest : AbstractEventTest() { fun `nextEvent can receive`() = runBlockingUnit { val channel = GlobalEventChannel - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - channel.nextEvent() - } - - TE(1).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(1), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + channel.nextEvent() } + + TE(1).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(1), deferred.await()) } @Test fun `nextEvent can filter type`() = runBlockingUnit { val channel = GlobalEventChannel - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - channel.nextEvent() - } - - TE2(1).broadcast() - yield() - assertFalse { deferred.isCompleted } - - TE(1).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(1), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + channel.nextEvent() } + + TE2(1).broadcast() + yield() + assertFalse { deferred.isCompleted } + + TE(1).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(1), deferred.await()) } @Test fun `nextEvent can filter by filter`() = runBlockingUnit { val channel = GlobalEventChannel - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - channel.nextEvent { it.x == 2 } - } - - TE(1).broadcast() - yield() - assertFalse { deferred.isCompleted } - - TE(2).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(2), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + channel.nextEvent { it.x == 2 } } + + TE(1).broadcast() + yield() + assertFalse { deferred.isCompleted } + + TE(2).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(2), deferred.await()) } @Test fun `nextEvent can timeout`() = runBlockingUnit { val channel = GlobalEventChannel - withContext(dispatcher) { - assertFailsWith { - withTimeout(timeMillis = 1) { channel.nextEvent(EventPriority.MONITOR) } - } + assertFailsWith { + withTimeout(timeMillis = 1) { channel.nextEvent(EventPriority.MONITOR) } } } @@ -111,18 +94,16 @@ internal class NextEventTest : AbstractEventTest() { fun `nextEvent can cancel`() = runBlockingUnit { val channel = GlobalEventChannel - withContext(dispatcher) { - coroutineScope { - val job = launch { - val result = kotlin.runCatching { channel.nextEvent(EventPriority.MONITOR) } - assertTrue { result.isFailure } - assertIs(result.exceptionOrNull()) - throw result.exceptionOrNull()!! - } - assertTrue { job.isActive } - job.cancelAndJoin() - assertTrue { job.isCancelled } + coroutineScope { + val job = launch { + val result = kotlin.runCatching { channel.nextEvent(EventPriority.MONITOR) } + assertTrue { result.isFailure } + assertIs(result.exceptionOrNull()) + throw result.exceptionOrNull()!! } + assertTrue { job.isActive } + job.cancelAndJoin() + assertTrue { job.isCancelled } } } @@ -132,59 +113,51 @@ internal class NextEventTest : AbstractEventTest() { @Test fun `nextEventOrNull can receive`() = runBlockingUnit { - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } - } - - TE(1).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(1), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } } + + TE(1).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(1), deferred.await()) } @Test fun `nextEventOrNull can filter type`() = runBlockingUnit { - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } - } - - TE2(1).broadcast() - yield() - assertFalse { deferred.isCompleted } - - TE(1).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(1), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } } + + TE2(1).broadcast() + yield() + assertFalse { deferred.isCompleted } + + TE(1).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(1), deferred.await()) } @Test fun `nextEventOrNull can filter by filter`() = runBlockingUnit { - withContext(dispatcher) { - val deferred = async(start = CoroutineStart.UNDISPATCHED) { - withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) { it.x == 2 } } - } - - TE(1).broadcast() - yield() - assertFalse { deferred.isCompleted } - - TE(2).broadcast() - yield() - assertTrue { deferred.isCompleted } - assertEquals(TE(2), deferred.await()) + val deferred = async(start = CoroutineStart.UNDISPATCHED) { + withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) { it.x == 2 } } } + + TE(1).broadcast() + yield() + assertFalse { deferred.isCompleted } + + TE(2).broadcast() + yield() + assertTrue { deferred.isCompleted } + assertEquals(TE(2), deferred.await()) } @Test fun `nextEventOrNull can timeout`() = runBlockingUnit { - withContext(dispatcher) { - assertEquals(null, - withTimeoutOrNull(timeMillis = 1) { globalEventChannel().nextEvent(EventPriority.MONITOR) }) - } + assertEquals(null, + withTimeoutOrNull(timeMillis = 1) { globalEventChannel().nextEvent(EventPriority.MONITOR) }) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/ForwardMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/ForwardMessageProtocolTest.kt index 729ea7cce..9b5875430 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/ForwardMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/ForwardMessageProtocolTest.kt @@ -98,7 +98,7 @@ internal class ForwardMessageProtocolTest : AbstractMessageProtocolTest() { // // TODO: 2022/5/23 test for download ForwardMessage // @Test // fun `can receive and download ForwardMessage`() { -// val message = runBlocking { +// val message = runTest { // runWithFacade { // net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( // msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( diff --git a/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt b/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt index 4ef5310f3..a88a29973 100644 --- a/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt @@ -73,13 +73,6 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() { // single thread so we can use [yield] to transfer dispatch private val singleThreadDispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher() - @OptIn(ExperimentalCoroutinesApi::class) - @AfterTest - fun after() { - (singleThreadDispatcher as CloseableCoroutineDispatcher).close() - } - - @Test fun `test whileSelect onStateChanged drop if not listening`() = runBlockingUnit(singleThreadDispatcher + Job()) { createNetworkHandler().run { diff --git a/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt b/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt index c653b4f01..d19306079 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.network.component import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.isActive +import kotlinx.coroutines.test.runTest import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.network.components.BotInitProcessor import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest @@ -31,7 +32,7 @@ import kotlin.test.assertTrue internal class BotInitProcessorTest { class WithoutSelector : AbstractCommonNHTest() { @Test - fun `BotInitProcessor halted`() = runBlockingUnit { + fun `BotInitProcessor halted`() = runTest { val p = setComponent(BotInitProcessor, object : BotInitProcessor { var ranTimes = 0 var haltedTimes = 0 diff --git a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt index b44f6bd65..ecfa3682a 100644 --- a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt +++ b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt @@ -9,7 +9,11 @@ package net.mamoe.mirai.internal.test -import kotlinx.coroutines.* +import kotlinx.coroutines.CloseableCoroutineDispatcher +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher import kotlin.test.AfterTest import kotlin.test.Test @@ -22,7 +26,7 @@ internal abstract class CommonAbstractTest { private val dispatchers = mutableListOf() fun borrowSingleThreadDispatcher(): CoroutineDispatcher { - return newSingleThreadContext(this::class.simpleName ?: "CommonAbstractTest") + return StandardTestDispatcher() } @AfterTest diff --git a/mirai-core/src/commonTest/kotlin/test/utils.kt b/mirai-core/src/commonTest/kotlin/test/utils.kt index 1e9f3b662..fecc6107c 100644 --- a/mirai-core/src/commonTest/kotlin/test/utils.kt +++ b/mirai-core/src/commonTest/kotlin/test/utils.kt @@ -1,40 +1,33 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.test -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds fun runBlockingUnit( context: CoroutineContext = EmptyCoroutineContext, - block: suspend CoroutineScope.() -> Unit -) { - return runBlocking(context) { - withTimeout(60.seconds) { // always checks for infinite runs. - block() - } - } -} + block: suspend TestScope.() -> Unit +) = runTest(context) { block() } fun runBlockingUnit( context: CoroutineContext = EmptyCoroutineContext, timeout: Duration, - block: suspend CoroutineScope.() -> Unit + block: suspend TestScope.() -> Unit ) { - runBlockingUnit(context) { + runTest(context, dispatchTimeoutMs = timeout.inWholeMilliseconds) { withTimeout(timeout) { block() } diff --git a/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt index 2c6012121..73c8f9dcb 100644 --- a/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt @@ -10,8 +10,9 @@ package net.mamoe.mirai.internal.event import kotlinx.coroutines.cancel -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import net.mamoe.mirai.event.* +import net.mamoe.mirai.utils.childScope import java.util.concurrent.atomic.AtomicInteger import kotlin.test.Test import kotlin.test.assertEquals @@ -20,12 +21,14 @@ internal class JvmMethodEventsTestJava : AbstractEventTest() { private val called = AtomicInteger(0) @Test - fun test() = runBlocking { + fun test() = runTest { val host = TestHost(called) - globalEventChannel().registerListenerHost(host) - runBlocking { TestEvent().broadcast() } + val scope = this.childScope() + scope.globalEventChannel().registerListenerHost(host) + TestEvent().broadcast() assertEquals(3, called.get(), null) host.cancel() // reset listeners + scope.cancel() } }