Bladeren bron

[core/android] Implement mirai-core tests for Android target with AndroidSDK

Him188 2 jaren geleden
bovenliggende
commit
40d4957837
32 gewijzigde bestanden met toevoegingen van 500 en 133 verwijderingen
  1. 17 0
      buildSrc/src/main/kotlin/Android.kt
  2. 1 11
      mirai-core-api/src/jvmBaseTest/kotlin/utils/JvmDeviceInfoTest.kt
  3. 23 0
      mirai-core-api/src/jvmTest/kotlin/utils/JvmDeviceInfoTestJvm.kt
  4. 32 0
      mirai-core-utils/src/androidInstrumentedTest/kotlin/AndroidUnwrapTest.kt
  5. 2 1
      mirai-core-utils/src/commonMain/kotlin/Services.kt
  6. 17 0
      mirai-core/build.gradle.kts
  7. 0 37
      mirai-core/src/androidInstrumentedTest/kotlin/test/initPlatform.android.kt
  8. 3 13
      mirai-core/src/androidInstrumentedTest/kotlin/test/initializeTestJvm.kt
  9. 12 0
      mirai-core/src/androidInstrumentedTest/kotlin/testFramework/currentPlatform.kt
  10. 11 2
      mirai-core/src/androidMain/kotlin/utils/crypto/EcdhAndroid.kt
  11. 11 0
      mirai-core/src/androidUnitTest/kotlin/package.kt
  12. 23 0
      mirai-core/src/androidUnitTest/kotlin/test/Logger.kt
  13. 49 0
      mirai-core/src/androidUnitTest/kotlin/test/initializeTestJvm.kt
  14. 28 0
      mirai-core/src/androidUnitTest/kotlin/testFramework/currentPlatform.kt
  15. 3 2
      mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt
  16. 3 2
      mirai-core/src/commonTest/kotlin/message/ImageBuilderTest.kt
  17. 15 7
      mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt
  18. 51 0
      mirai-core/src/commonTest/kotlin/testFramework/Platform.kt
  19. 25 0
      mirai-core/src/commonTest/kotlin/testFramework/rules/DisabledOnJvmLikePlatform.kt
  20. 5 28
      mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt
  21. 61 0
      mirai-core/src/jvmBaseTest/kotlin/testFramework/rules/DisabledOnPlatform.kt
  22. 11 5
      mirai-core/src/jvmBaseTest/kotlin/testFramework/test/RecordingNoticeProcessorTest.kt
  23. 14 0
      mirai-core/src/jvmTest/kotlin/test/initializeTestJvm.kt
  24. 12 0
      mirai-core/src/jvmTest/kotlin/testFramework/currentPlatform.kt
  25. 12 0
      mirai-core/src/linuxX64Test/kotlin/testFramework/currentPlatform.kt
  26. 1 1
      mirai-core/src/macosArm64Test/kotlin/package.kt
  27. 12 0
      mirai-core/src/macosArm64Test/kotlin/testFramework/currentPlatform.kt
  28. 10 0
      mirai-core/src/macosX64Test/kotlin/package.kt
  29. 12 0
      mirai-core/src/macosX64Test/kotlin/testFramework/currentPlatform.kt
  30. 10 0
      mirai-core/src/mingwX64Test/kotlin/package.kt
  31. 12 0
      mirai-core/src/mingwX64Test/kotlin/testFramework/currentPlatform.kt
  32. 2 24
      mirai-core/src/nativeTest/kotlin/test/PlatformInitializationTest.kt

+ 17 - 0
buildSrc/src/main/kotlin/Android.kt

@@ -12,6 +12,7 @@
 import com.android.build.api.dsl.LibraryExtension
 import org.gradle.api.JavaVersion
 import org.gradle.api.Project
+import org.gradle.api.tasks.testing.Test
 import org.gradle.kotlin.dsl.*
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -68,9 +69,18 @@ private fun Project.configureAndroidTargetWithJvm() {
                 compileOnly(`android-runtime`)
             }
         }
+
+        tasks.all {
+            if (this.name == "androidTest") {
+                this as Test
+                this.environment(PROP_MIRAI_ANDROID_SDK_KIND, "jdk")
+            }
+        }
     }
 }
 
+private const val PROP_MIRAI_ANDROID_SDK_KIND = "mirai.android.sdk.kind"
+
 @Suppress("UnstableApiUsage")
 private fun Project.configureAndroidTargetWithSdk() {
     extensions.getByType(KotlinMultiplatformExtension::class.java).apply {
@@ -133,6 +143,13 @@ private fun Project.configureAndroidTargetWithSdk() {
         ) {
             dependencies { implementation(kotlin("test-annotations-common"))?.because("configureAndroidTargetWithSdk") }
         }
+
+        tasks.all {
+            if (this.name == "testDebugUnitTest" || this.name == "testReleaseUnitTest") {
+                this as Test
+                this.environment(PROP_MIRAI_ANDROID_SDK_KIND, "adk")
+            }
+        }
     }
 
     // trick for compiler bug

+ 1 - 11
mirai-core-api/src/jvmBaseTest/kotlin/utils/JvmDeviceInfoTest.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -38,14 +38,4 @@ class JvmDeviceInfoTest {
         file.writeText(Json.encodeToString(DeviceInfo.serializer(), device))
         assertEquals(device, file.loadAsDeviceInfo())
     }
-
-
-    // TODO: 2022/10/19 move this to common test when Kotlin supports loading resources in commonMain
-    @Test
-    fun `can deserialize legacy versions before 2_9_0`() {
-        DeviceInfoManager.deserialize(
-            this::class.java.classLoader.getResourceAsStream("device/legacy-device-info-1.json")!!
-                .use { it.readBytes().decodeToString() })
-    }
-
 }

+ 23 - 0
mirai-core-api/src/jvmTest/kotlin/utils/JvmDeviceInfoTestJvm.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils
+
+import kotlin.test.Test
+
+class JvmDeviceInfoTestJvm {
+    @Test
+    fun `can deserialize legacy versions before 2_9_0`() {
+        // resources not available on android
+
+        DeviceInfoManager.deserialize(
+            this::class.java.classLoader.getResourceAsStream("device/legacy-device-info-1.json")!!
+                .use { it.readBytes().decodeToString() })
+    }
+}

+ 32 - 0
mirai-core-utils/src/androidInstrumentedTest/kotlin/AndroidUnwrapTest.kt

@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.utils
+
+import kotlin.test.Test
+import kotlin.test.assertIs
+
+class AndroidUnwrapTest {
+    @Test
+    fun test() {
+        val e =
+            IllegalStateException(
+                OutOfMemoryError().initCause(VerifyError()).apply { addSuppressed(UnsatisfiedLinkError()) }
+            )
+
+        val unwrapped = e.unwrap<IllegalStateException>()
+
+        unwrapped.printStackTrace()
+
+        assertIs<OutOfMemoryError>(unwrapped)
+        assertIs<VerifyError>(unwrapped.cause)
+        assertIs<UnsatisfiedLinkError>(unwrapped.suppressed.first())
+        assertIs<StacktraceException>(unwrapped.suppressed[1])
+    }
+}

+ 2 - 1
mirai-core-utils/src/commonMain/kotlin/Services.kt

@@ -72,7 +72,8 @@ public object Services {
         }
     }
 
-    internal fun implementationsDirectly(baseClass: String) = lock.withLock { registered[baseClass]?.toList().orEmpty() }
+    internal fun implementationsDirectly(baseClass: String) =
+        lock.withLock { registered[baseClass]?.toList().orEmpty() }
 
     public fun print(): String {
         lock.withLock {

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

@@ -77,11 +77,28 @@ kotlin {
             }
         }
 
+        findByName("androidMain")?.apply {
+            dependencies {
+                if (rootProject.property("mirai.android.target.api.level")!!.toString().toInt() < 23) {
+                    // Ship with BC if we are targeting 23 or lower where AndroidKeyStore is not stable enough.
+                    // For more info, read `net.mamoe.mirai.internal.utils.crypto.EcdhAndroidKt.create` in `androidMain`.
+                    implementation(bouncycastle)
+                }
+            }
+        }
+
+        // For Android with JDK
         findByName("androidTest")?.apply {
             dependencies {
                 implementation(bouncycastle)
             }
         }
+        // For Android with SDK
+        findByName("androidUnitTest")?.apply {
+            dependencies {
+                implementation(bouncycastle)
+            }
+        }
 
         findByName("jvmMain")?.apply {
             dependencies {

+ 0 - 37
mirai-core/src/androidInstrumentedTest/kotlin/test/initPlatform.android.kt

@@ -1,37 +0,0 @@
-/*
- * Copyright 2019-2023 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.
- *
- * https://github.com/mamoe/mirai/blob/dev/LICENSE
- */
-
-package net.mamoe.mirai.internal.test
-
-//import org.bouncycastle.jce.provider.BouncyCastleProvider
-import net.mamoe.mirai.utils.MiraiLogger
-import org.junit.Test
-import kotlin.test.assertIs
-
-internal actual fun initPlatform() {
-    init
-}
-
-private val init: Unit by lazy {
-//    if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) != null) {
-//        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME)
-//    }
-//    Security.addProvider(BouncyCastleProvider())
-
-    Unit
-}
-
-internal actual class PlatformInitializationTest : AbstractTest() {
-
-    @org.junit.jupiter.api.Test
-    actual fun test() {
-        @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
-        assertIs<net.mamoe.mirai.internal.utils.StdoutLogger>(MiraiLogger.Factory.create(this::class, "1"))
-    }
-}

+ 3 - 13
mirai-core/src/jvmTest/kotlin/test/initPlatform.kt → mirai-core/src/androidInstrumentedTest/kotlin/test/initializeTestJvm.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -9,16 +9,6 @@
 
 package net.mamoe.mirai.internal.test
 
-import kotlin.test.Test
-
-internal actual fun initPlatform() {
-    // nothing to do
-}
-
-
-internal actual class PlatformInitializationTest : AbstractTest() {
-    @Test
-    actual fun test() {
-        // nop
-    }
+internal actual fun initializeTestPlatformBeforeCommon() {
+    // nop
 }

+ 12 - 0
mirai-core/src/androidInstrumentedTest/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.AndroidInstrumentedTest

+ 11 - 2
mirai-core/src/androidMain/kotlin/utils/crypto/EcdhAndroid.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -9,9 +9,14 @@
 
 package net.mamoe.mirai.internal.utils.crypto
 
+import java.security.Provider
 import java.security.Security
 
 internal actual fun Ecdh.Companion.create(): Ecdh<*, *> =
+// WARNING: If you change the SDK version checks here, 
+//          search for usages of `mirai.android.target.api.level` and see if you need to change elsewhere! 
+    //          Especially in mirai-core/build.gradle.kts (configuring bouncy-castle dependency)
+
     if (kotlin.runCatching {
             // When running tests on JVM desktop, `ClassNotFoundException` will be got
             android.os.Build.VERSION.SDK_INT >= 23
@@ -24,5 +29,9 @@ internal actual fun Ecdh.Companion.create(): Ecdh<*, *> =
         // See https://developer.android.com/training/articles/keystore#SupportedKeyPairGenerators for details
 
         // Let's use BC instead, BC is bundled into older Android
-        JceEcdhWithProvider(Security.getProvider("BC"))
+        JceEcdhWithProvider(
+            Security.getProvider("BC")
+                ?: Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider")
+                    .getConstructor().newInstance() as Provider // in tests
+        )
     }

+ 11 - 0
mirai-core/src/androidUnitTest/kotlin/package.kt

@@ -0,0 +1,11 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+
+package net.mamoe.mirai.internal

+ 23 - 0
mirai-core/src/androidUnitTest/kotlin/test/Logger.kt

@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.test
+
+import android.util.Log
+import net.mamoe.mirai.utils.MiraiLogger
+
+/**
+ * Delegate logs to stdout, since android [Log] is not mocked.
+ */
+class JvmLoggerFactory : MiraiLogger.Factory {
+    override fun create(requester: Class<*>, identity: String?): MiraiLogger {
+        @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+        return net.mamoe.mirai.internal.utils.StdoutLogger(identity ?: requester.simpleName)
+    }
+}

+ 49 - 0
mirai-core/src/androidUnitTest/kotlin/test/initializeTestJvm.kt

@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.test
+
+import net.mamoe.mirai.internal.utils.StructureToStringTransformerNew
+import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.PlatformLogger
+import net.mamoe.mirai.utils.Services
+import org.junit.jupiter.api.Test
+import kotlin.test.assertIsNot
+
+internal actual fun initializeTestPlatformBeforeCommon() {
+    Services.register(
+        net.mamoe.mirai.utils.StructureToStringTransformer::class.qualifiedName!!,
+        StructureToStringTransformerNew::class.qualifiedName!!,
+        ::StructureToStringTransformerNew
+    )
+    Services.registerAsOverride(
+        MiraiLogger.Factory::class.qualifiedName!!,
+        "net.mamoe.mirai.utils.MiraiLogger.Factory"
+    ) {
+        JvmLoggerFactory()
+    }
+
+    // force override
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+    net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.setInstance(JvmLoggerFactory())
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+    net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.freeze()
+
+    println("[testFramework] Initialized loggers using JvmLoggerFactory")
+
+
+}
+
+internal class AndroidUnitTestPlatformTest : AbstractTest() {
+    @Test
+    fun usesStdoutLogger() {
+        // PlatformLogger uses android.util.Log and will fail
+        assertIsNot<PlatformLogger>(MiraiLogger.Factory.create(this::class))
+    }
+}

+ 28 - 0
mirai-core/src/androidUnitTest/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+import net.mamoe.mirai.internal.test.AbstractTest
+import org.junit.jupiter.api.Test
+import kotlin.test.assertIs
+
+actual fun currentPlatform(): Platform = when (System.getenv("mirai.android.sdk.kind")) {
+    "jdk" -> Platform.AndroidUnitTestWithJdk
+    "adk" -> Platform.AndroidUnitTestWithAdk
+    else -> throw IllegalStateException("`mirai.android.sdk.kind` must be `jdk` or `adk`. Ensure you are running tests using Gradle test tasks.")
+}
+
+internal class AndroidUnitTestPlatformTest : AbstractTest() {
+
+    @Test
+    fun currentPlatformIsAvailable() {
+        assertIs<Platform.AndroidUnitTest>(currentPlatform())
+    }
+}

+ 3 - 2
mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -15,11 +15,12 @@ import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
 import net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol.Companion.UNSUPPORTED_POKE_MESSAGE_PLAIN
 import net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol.Companion.UNSUPPORTED_MERGED_MESSAGE_PLAIN
 import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
+import net.mamoe.mirai.internal.test.AbstractTest
 import net.mamoe.mirai.message.data.*
 import kotlin.test.Test
 import kotlin.test.assertEquals
 
-internal class CleanupRubbishMessageElementsTest {
+internal class CleanupRubbishMessageElementsTest : AbstractTest() {
     //region
     private val replySource = OfflineMessageSourceImplData(
         kind = MessageSourceKind.GROUP,

+ 3 - 2
mirai-core/src/commonTest/kotlin/message/ImageBuilderTest.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2021 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -10,12 +10,13 @@
 package message
 
 import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.internal.test.AbstractTest
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.ImageType
 import kotlin.test.Test
 import kotlin.test.assertEquals
 
-internal class ImageBuilderTest {
+internal class ImageBuilderTest : AbstractTest() {
     companion object {
         private const val IMAGE_ID = "{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.jpg"
     }

+ 15 - 7
mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt

@@ -10,9 +10,9 @@
 package net.mamoe.mirai.internal.test
 
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlin.test.Test
-
-internal expect fun initPlatform()
+import net.mamoe.mirai.IMirai
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.utils.setSystemProp
 
 /**
  * All test classes should inherit from [AbstractTest]
@@ -23,7 +23,15 @@ expect abstract class AbstractTest() { // public, can be used in other modules
     companion object
 }
 
-internal expect class PlatformInitializationTest() : AbstractTest {
-    @Test
-    fun test()
-}
+internal fun initializeTestCommon() {
+    setSystemProp("mirai.network.packet.logger", "true")
+    setSystemProp("mirai.network.state.observer.logging", "true")
+    setSystemProp("mirai.network.show.all.components", "true")
+    setSystemProp("mirai.network.show.components.creation.stacktrace", "true")
+    setSystemProp("mirai.network.handler.selector.logging", "true")
+
+    Exception() // creates an exception to load relevant classes to estimate invocation time of test cases more accurately.
+    IMirai::class.simpleName // similarly, load classes.
+
+    Mirai // load services
+}

+ 51 - 0
mirai-core/src/commonTest/kotlin/testFramework/Platform.kt

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+
+expect fun currentPlatform(): Platform
+
+enum class PlatformRuntime {
+    JVM,
+    DALVIK,
+    NATIVE,
+}
+
+// see `@DisabledOnPlatform`
+sealed class Platform(
+    val runtime: PlatformRuntime,
+) {
+    sealed class JvmLike(runtime: PlatformRuntime) : Platform(runtime)
+
+    sealed class Android(runtime: PlatformRuntime) : JvmLike(runtime)
+
+    sealed class AndroidUnitTest : Android(PlatformRuntime.JVM)
+    object AndroidUnitTestWithJdk : AndroidUnitTest()
+    object AndroidUnitTestWithAdk : AndroidUnitTest()
+
+    object AndroidInstrumentedTest : Android(PlatformRuntime.DALVIK)
+
+    object Jvm : JvmLike(PlatformRuntime.JVM)
+
+    sealed class Native : Platform(PlatformRuntime.NATIVE)
+
+    sealed class Windows : Native()
+    object MingwX64 : Windows()
+
+    sealed class UnixLike : Native()
+
+    sealed class Linux : UnixLike()
+    object LinuxX64 : Linux()
+
+    sealed class Macos : UnixLike()
+    object MacosX64 : Macos()
+    object MacosArm64 : Macos()
+}
+

+ 25 - 0
mirai-core/src/commonTest/kotlin/testFramework/rules/DisabledOnJvmLikePlatform.kt

@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework.rules
+
+import net.mamoe.mirai.internal.testFramework.Platform
+import kotlin.reflect.KClass
+
+/**
+ * 在 commonTest 使用, 以在目标平台忽略这个 test.
+ *
+ * 此注解只对 JVM 和 Android 平台生效.
+ * 要在 native 平台忽略 test, 请手动写 `if (currentPlatform() is Native) return`.
+ */
+@OptIn(ExperimentalMultiplatform::class)
+@OptionalExpectation
+expect annotation class DisabledOnJvmLikePlatform(
+    vararg val values: KClass<out Platform.JvmLike> // don't read this property
+)

+ 5 - 28
mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt

@@ -13,11 +13,7 @@ import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.debug.DebugProbes
 import kotlinx.coroutines.test.StandardTestDispatcher
-import net.mamoe.mirai.IMirai
-import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger
 import net.mamoe.mirai.internal.testFramework.TestFactory
-import net.mamoe.mirai.utils.MiraiLogger
-import net.mamoe.mirai.utils.setSystemProp
 import org.junit.jupiter.api.BeforeAll
 import org.junit.jupiter.api.TestInfo
 import org.junit.jupiter.api.Timeout
@@ -52,31 +48,12 @@ actual abstract class AbstractTest actual constructor() {
         }
 
         init {
-            initPlatform()
+            initializeTestPlatformBeforeCommon()
+            initializeTestCommon()
 
-            kotlin.runCatching { DebugProbes.install() }
-
-            @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
-            net.mamoe.mirai.utils.MiraiLoggerFactoryImplementationBridge.wrapCurrent {
-                object : MiraiLogger.Factory {
-                    override fun create(requester: Class<*>, identity: String?): MiraiLogger {
-                        return SynchronizedStdoutLogger(identity ?: requester.simpleName)
-                    }
-
-                    override fun create(requester: KClass<*>, identity: String?): MiraiLogger {
-                        return SynchronizedStdoutLogger(identity ?: requester.simpleName)
-                    }
-                }
-            }
-
-            setSystemProp("mirai.network.packet.logger", "true")
-            setSystemProp("mirai.network.state.observer.logging", "true")
-            setSystemProp("mirai.network.show.all.components", "true")
-            setSystemProp("mirai.network.show.components.creation.stacktrace", "true")
-            setSystemProp("mirai.network.handler.selector.logging", "true")
-
-            Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately.
-            IMirai::class.simpleName // similarly, load classes.
+            runCatching { DebugProbes.install() }
         }
     }
 }
+
+internal expect fun initializeTestPlatformBeforeCommon() /* override MiraiLogger.Factory implementation on AndroidUnitTest */

+ 61 - 0
mirai-core/src/jvmBaseTest/kotlin/testFramework/rules/DisabledOnPlatform.kt

@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework.rules
+
+import net.mamoe.mirai.internal.testFramework.Platform
+import net.mamoe.mirai.internal.testFramework.currentPlatform
+import org.junit.jupiter.api.extension.ConditionEvaluationResult
+import org.junit.jupiter.api.extension.ExecutionCondition
+import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.api.extension.ExtensionContext
+import org.junit.platform.commons.util.AnnotationUtils
+import kotlin.jvm.optionals.getOrNull
+import kotlin.reflect.KClass
+import kotlin.reflect.full.isSuperclassOf
+
+
+@Suppress("NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS") // magic
+actual typealias DisabledOnJvmLikePlatform = DisabledOnPlatform
+
+@Target(
+    AnnotationTarget.CLASS,
+    AnnotationTarget.FUNCTION,
+    AnnotationTarget.PROPERTY_GETTER,
+    AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(AnnotationRetention.RUNTIME)
+@MustBeDocumented
+@ExtendWith(DisabledOnPlatformCondition::class)
+annotation class DisabledOnPlatform(
+    vararg val values: KClass<out Platform>
+)
+
+internal object DisabledOnPlatformCondition : ExecutionCondition {
+    private val ENABLED_BY_DEFAULT = ConditionEvaluationResult.enabled("@DisabledOnPlatform is not present")
+
+    override fun evaluateExecutionCondition(context: ExtensionContext?): ConditionEvaluationResult {
+        val annotation = AnnotationUtils.findAnnotation(
+            context!!.element,
+            DisabledOnPlatform::class.java
+        ).getOrNull() ?: return ENABLED_BY_DEFAULT
+
+        for (value in annotation.values) {
+            check(value != Platform::class) { "@DisabledOnPlatform(Platform::class) is invalid. Use `@Disabled` to disable on all platforms" }
+        }
+
+        val currentPlatform = currentPlatform()
+        val currentPlatformClass = currentPlatform::class
+        annotation.values.find { it.isSuperclassOf(currentPlatformClass) }
+            ?.let {
+                return ConditionEvaluationResult.disabled("@DisabledOnPlatform(${it.simpleName}), current = $currentPlatform")
+            }
+        return ENABLED_BY_DEFAULT
+    }
+}

+ 11 - 5
mirai-core/src/jvmBaseTest/kotlin/testFramework/test/RecordingNoticeProcessorTest.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2019-2022 Mamoe Technologies and contributors.
+ * Copyright 2019-2023 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.
@@ -13,7 +13,9 @@ import kotlinx.serialization.Serializable
 import kotlinx.serialization.decodeFromString
 import kotlinx.serialization.protobuf.ProtoNumber
 import net.mamoe.mirai.internal.test.AbstractTest
+import net.mamoe.mirai.internal.testFramework.Platform
 import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
+import net.mamoe.mirai.internal.testFramework.rules.DisabledOnPlatform
 import net.mamoe.mirai.internal.utils.io.ProtocolStruct
 import net.mamoe.yamlkt.Yaml
 import net.mamoe.yamlkt.YamlBuilder
@@ -28,9 +30,9 @@ internal class RecordingNoticeProcessorTest : AbstractTest() {
     ) : ProtocolStruct
 
     @Test
+    @DisabledOnPlatform(Platform.Android::class) // resources not available
     fun `test plain desensitization`() {
-        val text = Thread.currentThread().contextClassLoader.getResource("recording/configs/test.desensitization.yml")!!
-            .readText()
+        val text = loadDesensitizationRules()
         val desensitizer = Desensitizer.create(Yaml.decodeFromString(text))
 
 
@@ -75,9 +77,9 @@ internal class RecordingNoticeProcessorTest : AbstractTest() {
 
 
     @Test
+    @DisabledOnPlatform(Platform.Android::class)
     fun `test long as byte array desensitization`() {
-        val text = Thread.currentThread().contextClassLoader.getResource("recording/configs/test.desensitization.yml")!!
-            .readText()
+        val text = loadDesensitizationRules()
         val desensitizer = Desensitizer.create(Yaml.decodeFromString(text))
 
         val proto = TestProto(TestProto.Proto(123456789))
@@ -90,4 +92,8 @@ internal class RecordingNoticeProcessorTest : AbstractTest() {
             )
         )
     }
+
+    private fun loadDesensitizationRules() =
+        ((Thread.currentThread().contextClassLoader ?: this::class.java.classLoader)
+            .getResource("recording/configs/test.desensitization.yml")!!).readText()
 }

+ 14 - 0
mirai-core/src/jvmTest/kotlin/test/initializeTestJvm.kt

@@ -0,0 +1,14 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.test
+
+internal actual fun initializeTestPlatformBeforeCommon() {
+    // nop
+}

+ 12 - 0
mirai-core/src/jvmTest/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.Jvm

+ 12 - 0
mirai-core/src/linuxX64Test/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.LinuxX64

+ 1 - 1
mirai-core-api/src/androidInstrumentedTest/kotlin/package.kt → mirai-core/src/macosArm64Test/kotlin/package.kt

@@ -7,4 +7,4 @@
  * https://github.com/mamoe/mirai/blob/dev/LICENSE
  */
 
-package net.mameo.mirai
+package net.mamoe.mirai.internal

+ 12 - 0
mirai-core/src/macosArm64Test/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.MacosArm64

+ 10 - 0
mirai-core/src/macosX64Test/kotlin/package.kt

@@ -0,0 +1,10 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal

+ 12 - 0
mirai-core/src/macosX64Test/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.MacosX64

+ 10 - 0
mirai-core/src/mingwX64Test/kotlin/package.kt

@@ -0,0 +1,10 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal

+ 12 - 0
mirai-core/src/mingwX64Test/kotlin/testFramework/currentPlatform.kt

@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019-2023 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.
+ *
+ * https://github.com/mamoe/mirai/blob/dev/LICENSE
+ */
+
+package net.mamoe.mirai.internal.testFramework
+
+actual fun currentPlatform(): Platform = Platform.MingwX64

+ 2 - 24
mirai-core/src/nativeTest/kotlin/test/PlatformInitializationTest.kt

@@ -11,21 +11,7 @@ package net.mamoe.mirai.internal.test
 
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.test.StandardTestDispatcher
-import net.mamoe.mirai.IMirai
 import net.mamoe.mirai.internal.initMirai
-import net.mamoe.mirai.utils.setSystemProp
-import kotlin.test.Test
-
-
-internal actual fun initPlatform() {
-    initMirai()
-}
-
-internal actual class PlatformInitializationTest actual constructor() : AbstractTest() {
-    @Test
-    actual fun test() {
-    }
-}
 
 /**
  * All test classes should inherit from [AbstractTest]
@@ -42,16 +28,8 @@ actual abstract class AbstractTest actual constructor() {
 
     actual companion object {
         init {
-            initPlatform()
-
-            setSystemProp("mirai.network.packet.logger", "true")
-            setSystemProp("mirai.network.state.observer.logging", "true")
-            setSystemProp("mirai.network.show.all.components", "true")
-            setSystemProp("mirai.network.show.components.creation.stacktrace", "true")
-            setSystemProp("mirai.network.handler.selector.logging", "true")
-
-            Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately.
-            IMirai::class.simpleName // similarly, load classes.
+            initMirai()
+            initializeTestCommon()
         }
     }