Browse Source

Use kotlinx-coroutines-test

Him188 3 years ago
parent
commit
21c4a5ba04

+ 9 - 5
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

+ 1 - 0
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)

+ 1 - 0
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"))

+ 23 - 22
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<MessageChain> {
                 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<MessageChain> {
                 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<KClass<out TemporalAccessor>> = 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<MessageChain> {
                 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<Int> {
                 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) {

+ 6 - 8
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<QQAndroidBot> { 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<QQAndroidBot> { 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"
 

+ 4 - 4
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) {

+ 1 - 0
mirai-core-api/build.gradle.kts

@@ -51,6 +51,7 @@ kotlin {
         commonTest {
             dependencies {
                 runtimeOnly(`log4j-core`)
+                implementation(`kotlinx-coroutines-test`)
             }
         }
 

+ 2 - 2
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())
     }
 }

+ 1 - 0
mirai-core-utils/build.gradle.kts

@@ -43,6 +43,7 @@ kotlin {
         val commonTest by getting {
             dependencies {
                 api(yamlkt)
+                implementation(`kotlinx-coroutines-test`)
             }
         }
 

+ 2 - 1
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<Unit>()
         val override = Symbol("override")
         val initializer = Symbol("initializer")

+ 2 - 1
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
 

+ 1 - 0
mirai-core/build.gradle.kts

@@ -50,6 +50,7 @@ kotlin {
         commonTest {
             dependencies {
                 implementation(kotlin("script-runtime"))
+                implementation(`kotlinx-coroutines-test`)
                 api(yamlkt)
             }
         }

+ 6 - 8
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<Event>.() -> 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()
     }
 

+ 67 - 94
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>()
-            }
-
-            TE(1).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(1), deferred.await())
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            channel.nextEvent<TE>()
         }
+
+        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<TE>()
-            }
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            channel.nextEvent<TE>()
+        }
 
-            TE2(1).broadcast()
-            yield()
-            assertFalse { deferred.isCompleted }
+        TE2(1).broadcast()
+        yield()
+        assertFalse { deferred.isCompleted }
 
-            TE(1).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(1), deferred.await())
-        }
+        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<TE> { it.x == 2 }
-            }
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            channel.nextEvent<TE> { it.x == 2 }
+        }
 
-            TE(1).broadcast()
-            yield()
-            assertFalse { deferred.isCompleted }
+        TE(1).broadcast()
+        yield()
+        assertFalse { deferred.isCompleted }
 
-            TE(2).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(2), deferred.await())
-        }
+        TE(2).broadcast()
+        yield()
+        assertTrue { deferred.isCompleted }
+        assertEquals(TE(2), deferred.await())
     }
 
     @Test
     fun `nextEvent can timeout`() = runBlockingUnit {
         val channel = GlobalEventChannel
 
-        withContext(dispatcher) {
-            assertFailsWith<TimeoutCancellationException> {
-                withTimeout(timeMillis = 1) { channel.nextEvent<TE>(EventPriority.MONITOR) }
-            }
+        assertFailsWith<TimeoutCancellationException> {
+            withTimeout(timeMillis = 1) { channel.nextEvent<TE>(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<TE>(EventPriority.MONITOR) }
-                    assertTrue { result.isFailure }
-                    assertIs<CancellationException>(result.exceptionOrNull())
-                    throw result.exceptionOrNull()!!
-                }
-                assertTrue { job.isActive }
-                job.cancelAndJoin()
-                assertTrue { job.isCancelled }
+        coroutineScope {
+            val job = launch {
+                val result = kotlin.runCatching { channel.nextEvent<TE>(EventPriority.MONITOR) }
+                assertTrue { result.isFailure }
+                assertIs<CancellationException>(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<TE>(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) }
-            }
-
-            TE(1).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(1), deferred.await())
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            withTimeoutOrNull<TE>(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<TE>(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) }
-            }
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            withTimeoutOrNull<TE>(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) }
+        }
 
-            TE2(1).broadcast()
-            yield()
-            assertFalse { deferred.isCompleted }
+        TE2(1).broadcast()
+        yield()
+        assertFalse { deferred.isCompleted }
 
-            TE(1).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(1), deferred.await())
-        }
+        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<TE>(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) { it.x == 2 } }
-            }
+        val deferred = async(start = CoroutineStart.UNDISPATCHED) {
+            withTimeoutOrNull<TE>(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) { it.x == 2 } }
+        }
 
-            TE(1).broadcast()
-            yield()
-            assertFalse { deferred.isCompleted }
+        TE(1).broadcast()
+        yield()
+        assertFalse { deferred.isCompleted }
 
-            TE(2).broadcast()
-            yield()
-            assertTrue { deferred.isCompleted }
-            assertEquals(TE(2), deferred.await())
-        }
+        TE(2).broadcast()
+        yield()
+        assertTrue { deferred.isCompleted }
+        assertEquals(TE(2), deferred.await())
     }
 
     @Test
     fun `nextEventOrNull can timeout`() = runBlockingUnit {
-        withContext(dispatcher) {
-            assertEquals(null,
-                withTimeoutOrNull<TE>(timeMillis = 1) { globalEventChannel().nextEvent(EventPriority.MONITOR) })
-        }
+        assertEquals(null,
+            withTimeoutOrNull<TE>(timeMillis = 1) { globalEventChannel().nextEvent(EventPriority.MONITOR) })
     }
 }

+ 1 - 1
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(

+ 0 - 7
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 {

+ 2 - 1
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

+ 6 - 2
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<CloseableCoroutineDispatcher>()
 
     fun borrowSingleThreadDispatcher(): CoroutineDispatcher {
-        return newSingleThreadContext(this::class.simpleName ?: "CommonAbstractTest")
+        return StandardTestDispatcher()
     }
 
     @AfterTest

+ 10 - 17
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()
         }

+ 7 - 4
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()
     }
 }