mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 02:20:14 +08:00
cc7f35519e
* Update login protocol Still need testing * Turn off debug option and make t547 null when failed * Fix wrong convert method and improve tips * Remove unused part and improve tips * Fix typo * Inline resultStatus for performance * Rename pow to PoW, the name should be "Proof of Work" * Add shadow and deps-test for kt-bignum * Try to fix deps-test * Fix deps-test again
382 lines
14 KiB
Kotlin
382 lines
14 KiB
Kotlin
/*
|
|
* 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.
|
|
*
|
|
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
|
*/
|
|
|
|
package net.mamoe.mirai.deps.test
|
|
|
|
import org.junit.jupiter.api.Test
|
|
import org.junit.jupiter.api.condition.EnabledIf
|
|
import kotlin.test.assertTrue
|
|
|
|
/**
|
|
* 为每个模块测试 relocated 的依赖是否存在于运行时
|
|
*/
|
|
class CoreShadowRelocationTest : AbstractTest() {
|
|
companion object {
|
|
private const val ByteBufferChannel = "io.ktor.utils.io.ByteBufferChannel"
|
|
private const val HttpClient = "io.ktor.client.HttpClient"
|
|
private const val KtorOkHttp = "io.ktor.client.engine.okhttp.OkHttp"
|
|
private const val OkHttp = "okhttp3.OkHttp"
|
|
private const val OkIO = "okio.ByteString"
|
|
private const val BigInteger = "com.ionspin.kotlin.bignum.integer.BigInteger"
|
|
|
|
fun relocated(string: String): String {
|
|
return "net.mamoe.mirai.internal.deps.$string"
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core-utils`() {
|
|
val fragment = buildTestCases {
|
|
+relocated(`ktor-io`)
|
|
-both(`ktor-client-core`)
|
|
-both(`ktor-client-okhttp`)
|
|
-both(`okhttp3-okhttp`)
|
|
-both(okio)
|
|
-both(`kt-bignum`)
|
|
}
|
|
applyCodeFragment(fragment)
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation("net.mamoe:mirai-core-utils:$miraiLocalVersion")
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core-api with transitive mirai-core-utils`() {
|
|
val fragment = buildTestCases {
|
|
+relocated(`ktor-io`)
|
|
-both(`ktor-client-core`)
|
|
-both(`ktor-client-okhttp`)
|
|
-both(`okhttp3-okhttp`)
|
|
-both(okio)
|
|
-both(`kt-bignum`)
|
|
+relocated(`ExternalResource-input`)
|
|
}
|
|
applyCodeFragment(fragment)
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation("net.mamoe:mirai-core-api:$miraiLocalVersion")
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core with transitive mirai-core-api and mirai-core-utils`() {
|
|
val fragment = buildTestCases {
|
|
+relocated(`ktor-io`)
|
|
+relocated(`ktor-client-core`)
|
|
+relocated(`ktor-client-okhttp`)
|
|
+relocated(`okhttp3-okhttp`)
|
|
+relocated(okio)
|
|
+relocated(`ExternalResource-input`)
|
|
+relocated(`kt-bignum`)
|
|
}
|
|
applyCodeFragment(fragment)
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation("net.mamoe:mirai-core:$miraiLocalVersion")
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
// ktor-io is shadowed into runtime in mirai-core-utils. So without mirai-core-utils,
|
|
// we should expect no relocated ktor-io found, otherwise there will be duplicated classes on Android.
|
|
// https://github.com/mamoe/mirai/issues/2291
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core without transitive mirai-core-api and mirai-core-utils`() {
|
|
val fragment = buildTestCases {
|
|
-both(`ktor-io`)
|
|
+relocated(`ktor-client-core`)
|
|
+relocated(`ktor-client-okhttp`)
|
|
+relocated(`okhttp3-okhttp`)
|
|
+relocated(okio)
|
|
+relocated(`kt-bignum`)
|
|
}
|
|
applyCodeFragment(fragment)
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation("net.mamoe:mirai-core:$miraiLocalVersion") {
|
|
exclude("net.mamoe", "mirai-core-api")
|
|
exclude("net.mamoe", "mirai-core-utils")
|
|
}
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core-api without transitive mirai-core-utils`() {
|
|
val fragment = buildTestCases {
|
|
-both(`ktor-io`)
|
|
-both(`ktor-client-core`)
|
|
-both(`ktor-client-okhttp`)
|
|
-both(`okhttp3-okhttp`)
|
|
-both(okio)
|
|
-both(`kt-bignum`)
|
|
// +relocated(`ExternalResource-input`) // Will fail with no class def found error because there is no runtime ktor-io
|
|
}
|
|
applyCodeFragment(fragment)
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation("net.mamoe:mirai-core-api:$miraiLocalVersion") {
|
|
exclude("net.mamoe", "mirai-core-utils")
|
|
}
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
@Test
|
|
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
|
fun `test mirai-core-all`() {
|
|
val fragment = buildTestCases {
|
|
+relocated(`ktor-io`)
|
|
+relocated(`ktor-client-core`)
|
|
+relocated(`ktor-client-okhttp`)
|
|
+relocated(`okhttp3-okhttp`)
|
|
+relocated(okio)
|
|
+relocated(`ExternalResource-input`)
|
|
+relocated(`kt-bignum`)
|
|
}
|
|
applyCodeFragment(fragment)
|
|
|
|
// mirai-core-all-2.99.0-deps-test-all.jar
|
|
val miraiCoreAllJar =
|
|
mavenLocalDir.resolve("net/mamoe/mirai-core-all/$miraiLocalVersion/mirai-core-all-$miraiLocalVersion-all.jar")
|
|
val path = miraiCoreAllJar.absolutePath.replace("\\", "/") // overcome string escape in source files.
|
|
assertTrue("'$path' does not exist") { miraiCoreAllJar.exists() }
|
|
|
|
buildFile.appendText(
|
|
"""
|
|
dependencies {
|
|
implementation(fileTree("$path"))
|
|
}
|
|
""".trimIndent()
|
|
)
|
|
runGradle("check")
|
|
}
|
|
|
|
|
|
@Suppress("PropertyName")
|
|
private class TestBuilder {
|
|
private val result = StringBuilder(
|
|
"""
|
|
package test
|
|
import org.junit.jupiter.api.*
|
|
import java.lang.reflect.Method
|
|
import kotlin.reflect.jvm.kotlinFunction
|
|
import kotlin.test.assertTrue
|
|
import kotlin.test.assertFalse
|
|
|
|
private val Method.signature: String
|
|
get() = buildString {
|
|
append(kotlinFunction?.toString())
|
|
return@buildString
|
|
append(kotlinFunction?.visibility?.name?.lowercase())
|
|
append(kotlinFunction?.visibility?.name?.lowercase())
|
|
append(' ')
|
|
append(returnType.canonicalName)
|
|
append(' ')
|
|
append(name)
|
|
append('(')
|
|
for (parameter in parameters) {
|
|
append(parameter.type.canonicalName)
|
|
append(' ')
|
|
append(parameter.name)
|
|
append(", ")
|
|
}
|
|
if (parameterCount != 0) {
|
|
deleteAt(lastIndex)
|
|
deleteAt(lastIndex)
|
|
}
|
|
append(')')
|
|
}
|
|
|
|
class MyTest {
|
|
""".trimIndent()
|
|
).append("\n").append("\n")
|
|
|
|
class ClassTestCase(
|
|
val name: String,
|
|
val qualifiedClassName: String,
|
|
)
|
|
|
|
class FunctionTestCase(
|
|
val name: String,
|
|
val qualifiedClassName: String,
|
|
val signature: String,
|
|
val relocated: (FunctionTestCase.() -> FunctionTestCase)? = null,
|
|
)
|
|
|
|
val `ktor-io` = ClassTestCase("ktor-io ByteBufferChannel", ByteBufferChannel)
|
|
val `ktor-client-core` = ClassTestCase("ktor-client-core HttpClient", HttpClient)
|
|
val `ktor-client-okhttp` = ClassTestCase("ktor-client-core OkHttp", KtorOkHttp)
|
|
val `okhttp3-okhttp` = ClassTestCase("okhttp3 OkHttp", OkHttp)
|
|
val okio = ClassTestCase("okio ByteString", OkIO)
|
|
val `kt-bignum` = ClassTestCase("kt-bignum BigInteger", BigInteger)
|
|
val `ExternalResource-input` =
|
|
FunctionTestCase(
|
|
"ExternalResource_input",
|
|
"net.mamoe.mirai.utils.ExternalResource",
|
|
"fun net.mamoe.mirai.utils.ExternalResource.input(): io.ktor.utils.io.core.Input"
|
|
) original@{
|
|
FunctionTestCase(
|
|
"relocated ExternalResource_input",
|
|
"net.mamoe.mirai.utils.ExternalResource",
|
|
"fun net.mamoe.mirai.utils.ExternalResource.input(): ${relocated("io.ktor.utils.io.core.Input")}"
|
|
) {
|
|
this@original
|
|
}
|
|
}
|
|
|
|
class RelocatedClassTestCase(
|
|
val testCase: ClassTestCase
|
|
)
|
|
|
|
class BothClassTestCase(
|
|
val testCase: ClassTestCase
|
|
)
|
|
|
|
|
|
private fun appendHasClass(name: String, qualifiedClassName: String) {
|
|
result.append(
|
|
"""
|
|
@Test
|
|
fun `has ${name}`() {
|
|
Class.forName("$qualifiedClassName")
|
|
}
|
|
""".trimIndent()
|
|
).append("\n")
|
|
}
|
|
|
|
fun appendClassHasMethod(name: String, qualifiedClassName: String, methodSignature: String) {
|
|
result.append(
|
|
"""
|
|
@Test
|
|
fun `has ${name}`() {
|
|
val signatures = Class.forName("$qualifiedClassName").declaredMethods.map { it.signature }
|
|
assertTrue("All signatures: " + signatures.joinToString("\n")) { signatures.any { it == "$methodSignature" } }
|
|
}
|
|
""".trimIndent()
|
|
).append("\n")
|
|
}
|
|
|
|
fun appendClassMethodNotFound(name: String, qualifiedClassName: String, methodSignature: String) {
|
|
result.append(
|
|
"""
|
|
@Test
|
|
fun `has ${name}`() {
|
|
val signatures = Class.forName("$qualifiedClassName").declaredMethods.map { it.signature }
|
|
assertFalse("All signatures: " + signatures.joinToString("\n")) { signatures.any { it == "$methodSignature" } }
|
|
}
|
|
""".trimIndent()
|
|
).append("\n")
|
|
}
|
|
|
|
private fun appendNotFound(name: String, qualifiedClassName: String) {
|
|
result.append(
|
|
"""
|
|
@Test
|
|
fun `no relocated ${name}`() {
|
|
assertThrows<ClassNotFoundException> { Class.forName("$qualifiedClassName") }
|
|
}
|
|
""".trimIndent()
|
|
).append("\n")
|
|
}
|
|
|
|
|
|
/**
|
|
* Asserts a class exists. Also asserts its relocated class does not exist.
|
|
*/
|
|
operator fun FunctionTestCase.unaryPlus() {
|
|
appendClassHasMethod(name, qualifiedClassName, signature)
|
|
relocated?.invoke(this)?.let { inverted ->
|
|
appendClassMethodNotFound(inverted.name, inverted.qualifiedClassName, inverted.signature)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts a class exists. Also asserts its relocated class does not exist.
|
|
*/
|
|
operator fun ClassTestCase.unaryPlus() {
|
|
appendHasClass(name, qualifiedClassName)
|
|
appendNotFound("relocated $name", Companion.relocated(qualifiedClassName))
|
|
}
|
|
|
|
/**
|
|
* Asserts a class does not exist.
|
|
*/
|
|
operator fun ClassTestCase.unaryMinus() {
|
|
appendNotFound(name, qualifiedClassName)
|
|
}
|
|
|
|
|
|
/**
|
|
* Asserts a relocated class exists. Also asserts the original class does not exist.
|
|
*/
|
|
operator fun RelocatedClassTestCase.unaryPlus() {
|
|
this.testCase.run {
|
|
appendHasClass("relocated $name", Companion.relocated(qualifiedClassName))
|
|
appendNotFound(name, qualifiedClassName)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts a relocated class does not exist.
|
|
*/
|
|
operator fun RelocatedClassTestCase.unaryMinus() {
|
|
this.testCase.run {
|
|
appendNotFound("relocated $name", Companion.relocated(qualifiedClassName))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Asserts both the class and its relocated one do not exist.
|
|
*/
|
|
operator fun BothClassTestCase.unaryMinus() {
|
|
-this.testCase
|
|
-relocated(this.testCase)
|
|
}
|
|
|
|
fun relocated(testCase: ClassTestCase): RelocatedClassTestCase = RelocatedClassTestCase(testCase)
|
|
fun relocated(testCase: FunctionTestCase): FunctionTestCase = testCase.relocated!!(testCase)
|
|
fun both(testCase: ClassTestCase) = BothClassTestCase(testCase)
|
|
|
|
fun build(): String = result.append("\n}\n").toString()
|
|
}
|
|
|
|
private inline fun buildTestCases(action: TestBuilder.() -> Unit): String {
|
|
return TestBuilder().apply(action).build()
|
|
}
|
|
|
|
|
|
private fun applyCodeFragment(fragment: String) {
|
|
println("Applying code fragment: \n\n$fragment\n\n\n===========End of Fragment===========")
|
|
testDir.resolve("test.kt").writeText(fragment)
|
|
}
|
|
} |