mirror of
https://github.com/mamoe/mirai.git
synced 2026-08-22 14:13:32 +08:00
[build] Rewrite shadow relocation
This commit is contained in:
@@ -95,6 +95,7 @@ tasks.register("publishMiraiLocalArtifacts", Exec::class) {
|
||||
"./gradlew",
|
||||
publishMiraiArtifactsToMavenLocal.name,
|
||||
"--no-daemon",
|
||||
"--stacktrace",
|
||||
"-Pkotlin.compiler.execution.strategy=in-process"
|
||||
)
|
||||
standardOutput = System.out
|
||||
|
||||
@@ -23,7 +23,7 @@ abstract class AbstractTest {
|
||||
const val miraiLocalVersion = "2.99.0-deps-test" // do Search Everywhere before changing this
|
||||
const val REASON_LOCAL_ARTIFACT_NOT_AVAILABLE = "local artifacts not available"
|
||||
|
||||
private val mavenLocalDir: File by lazy {
|
||||
val mavenLocalDir: File by lazy {
|
||||
org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator(
|
||||
org.gradle.api.internal.artifacts.mvnsettings.DefaultMavenSettingsProvider(DefaultMavenFileLocations())
|
||||
).localMavenRepository
|
||||
@@ -165,9 +165,20 @@ abstract class AbstractTest {
|
||||
if (context.executionException.isPresent) {
|
||||
val inst = context.requiredTestInstance as AbstractTest
|
||||
println("====================== build.gradle ===========================")
|
||||
println(inst.tempDir.resolve("build.gradle").readText())
|
||||
println(inst.tempDir.resolveFirstExisting("build.gradle", "build.gradle.kts").readTextIfFound())
|
||||
println("==================== settings.gradle ==========================")
|
||||
println(inst.tempDir.resolve("settings.gradle").readText())
|
||||
println(inst.tempDir.resolveFirstExisting("settings.gradle", "settings.gradle.kts").readTextIfFound())
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.resolveFirstExisting(vararg files: String): File? {
|
||||
return files.asSequence().map { resolve(it) }.firstOrNull { it.exists() }
|
||||
}
|
||||
|
||||
private fun File?.readTextIfFound(): String =
|
||||
when {
|
||||
this == null -> "(not found)"
|
||||
exists() -> readText()
|
||||
else -> "($name not found)"
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,20 @@ import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.condition.EnabledIf
|
||||
|
||||
class CoreDependencyResolutionTest : AbstractTest() {
|
||||
private val testCode = """
|
||||
package test
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPERIMENTAL_API_USAGE")
|
||||
fun main () {
|
||||
println(net.mamoe.mirai.BotFactory)
|
||||
println(net.mamoe.mirai.Mirai)
|
||||
println(net.mamoe.mirai.internal.testHttpClient())
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test resolve JVM root from Kotlin JVM`() {
|
||||
mainSrcDir.resolve("main.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
fun main () {
|
||||
println(net.mamoe.mirai.BotFactory)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
mainSrcDir.resolve("main.kt").writeText(testCode)
|
||||
buildFile.writeText(
|
||||
"""
|
||||
plugins {
|
||||
@@ -36,6 +39,9 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core:$miraiLocalVersion")
|
||||
}
|
||||
kotlin.sourceSets.all {
|
||||
languageSettings.optIn("net.mamoe.mirai.utils.TestOnly")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
runGradle("build")
|
||||
@@ -44,14 +50,7 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test resolve JVM from Kotlin JVM`() {
|
||||
mainSrcDir.resolve("main.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
fun main () {
|
||||
println(net.mamoe.mirai.BotFactory)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
mainSrcDir.resolve("main.kt").writeText(testCode)
|
||||
buildFile.writeText(
|
||||
"""
|
||||
plugins {
|
||||
@@ -64,6 +63,9 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core-jvm:$miraiLocalVersion")
|
||||
}
|
||||
kotlin.sourceSets.all {
|
||||
languageSettings.optIn("net.mamoe.mirai.utils.TestOnly")
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
runGradle("build")
|
||||
@@ -72,14 +74,7 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test resolve JVM and Native from common`() {
|
||||
commonMainSrcDir.resolve("main.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
fun main () {
|
||||
println(net.mamoe.mirai.BotFactory)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
commonMainSrcDir.resolve("main.kt").writeText(testCode)
|
||||
buildFile.writeText(
|
||||
"""
|
||||
|import org.apache.tools.ant.taskdefs.condition.Os
|
||||
@@ -111,6 +106,9 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|kotlin.sourceSets.all {
|
||||
| languageSettings.optIn("net.mamoe.mirai.utils.TestOnly")
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
@@ -120,14 +118,7 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test resolve Native from common`() {
|
||||
nativeMainSrcDir.resolve("main.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
fun main () {
|
||||
println(net.mamoe.mirai.BotFactory)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
nativeMainSrcDir.resolve("main.kt").writeText(testCode)
|
||||
buildFile.writeText(
|
||||
"""
|
||||
|import org.apache.tools.ant.taskdefs.condition.Os
|
||||
@@ -159,6 +150,9 @@ class CoreDependencyResolutionTest : AbstractTest() {
|
||||
| }
|
||||
| }
|
||||
|}
|
||||
|kotlin.sourceSets.all {
|
||||
| languageSettings.optIn("net.mamoe.mirai.utils.TestOnly")
|
||||
|}
|
||||
""".trimMargin()
|
||||
)
|
||||
|
||||
|
||||
@@ -11,31 +11,77 @@ 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 fun relocated(string: String): String {
|
||||
return "net.mamoe.mirai.internal.deps.$string"
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `test OkHttp filtered out`() {
|
||||
testDir.resolve("test.kt").writeText(
|
||||
fun `test mirai-core-utils`() {
|
||||
val fragment = buildTestCases {
|
||||
+relocated(`ktor-io`)
|
||||
-both(`ktor-client-core`)
|
||||
-both(`ktor-client-okhttp`)
|
||||
-both(`okhttp3-okhttp`)
|
||||
-both(okio)
|
||||
}
|
||||
applyCodeFragment(fragment)
|
||||
buildFile.appendText(
|
||||
"""
|
||||
package test
|
||||
import org.junit.jupiter.api.*
|
||||
class MyTest {
|
||||
@Test
|
||||
fun `test base dependency`() {
|
||||
assertThrows<ClassNotFoundException> {
|
||||
Class.forName("io.ktor.client.engine.okhttp.OkHttp")
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun `test transitive dependency`() {
|
||||
assertThrows<ClassNotFoundException> {
|
||||
Class.forName("okhttp3.OkHttpClient")
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
applyCodeFragment(fragment)
|
||||
buildFile.appendText(
|
||||
"""
|
||||
dependencies {
|
||||
@@ -46,25 +92,20 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
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 `no duplicated class when dependency shared across modules`() {
|
||||
testDir.resolve("test.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
import org.junit.jupiter.api.*
|
||||
class MyTest {
|
||||
@Test
|
||||
fun `test base dependency`() {
|
||||
assertThrows<ClassNotFoundException> {
|
||||
Class.forName("net.mamoe.mirai.internal.deps.io.ktor.utils.io.ByteBufferChannel") // should only present in mirai-core-utils
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
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)
|
||||
}
|
||||
applyCodeFragment(fragment)
|
||||
buildFile.appendText(
|
||||
"""
|
||||
dependencies {
|
||||
@@ -80,23 +121,21 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `relocated ktor presents in mirai-core-utils`() {
|
||||
testDir.resolve("test.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
import org.junit.jupiter.api.*
|
||||
class MyTest {
|
||||
@Test
|
||||
fun `test base dependency`() {
|
||||
Class.forName("net.mamoe.mirai.internal.deps.io.ktor.utils.io.ByteBufferChannel")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
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)
|
||||
}
|
||||
applyCodeFragment(fragment)
|
||||
buildFile.appendText(
|
||||
"""
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core-utils:$miraiLocalVersion")
|
||||
implementation("net.mamoe:mirai-core-api:$miraiLocalVersion") {
|
||||
exclude("net.mamoe", "mirai-core-utils")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
@@ -105,26 +144,141 @@ class CoreShadowRelocationTest : AbstractTest() {
|
||||
|
||||
@Test
|
||||
@EnabledIf("isMiraiLocalAvailable", disabledReason = REASON_LOCAL_ARTIFACT_NOT_AVAILABLE)
|
||||
fun `relocated ktor presents transitively in mirai-core`() {
|
||||
testDir.resolve("test.kt").writeText(
|
||||
"""
|
||||
package test
|
||||
import org.junit.jupiter.api.*
|
||||
class MyTest {
|
||||
@Test
|
||||
fun `test base dependency`() {
|
||||
Class.forName("net.mamoe.mirai.internal.deps.io.ktor.utils.io.ByteBufferChannel")
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
fun `test mirai-core-all`() {
|
||||
val fragment = buildTestCases {
|
||||
+relocated(`ktor-io`)
|
||||
+relocated(`ktor-client-core`)
|
||||
+relocated(`ktor-client-okhttp`)
|
||||
+relocated(`okhttp3-okhttp`)
|
||||
+relocated(okio)
|
||||
}
|
||||
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")
|
||||
assertTrue("'${miraiCoreAllJar.absolutePath}' does not exist") { miraiCoreAllJar.exists() }
|
||||
|
||||
buildFile.appendText(
|
||||
"""
|
||||
dependencies {
|
||||
implementation("net.mamoe:mirai-core:$miraiLocalVersion")
|
||||
implementation(fileTree("${miraiCoreAllJar.absolutePath}"))
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
runGradle("check")
|
||||
}
|
||||
|
||||
|
||||
@Suppress("PropertyName")
|
||||
private class TestBuilder {
|
||||
private val result = StringBuilder(
|
||||
"""
|
||||
package test
|
||||
import org.junit.jupiter.api.*
|
||||
class MyTest {
|
||||
""".trimIndent()
|
||||
).append("\n").append("\n")
|
||||
|
||||
class TestCase(
|
||||
val name: String,
|
||||
val qualifiedClassName: String,
|
||||
)
|
||||
|
||||
val `ktor-io` = TestCase("ktor-io ByteBufferChannel", ByteBufferChannel)
|
||||
val `ktor-client-core` = TestCase("ktor-client-core HttpClient", HttpClient)
|
||||
val `ktor-client-okhttp` = TestCase("ktor-client-core OkHttp", KtorOkHttp)
|
||||
val `okhttp3-okhttp` = TestCase("okhttp3 OkHttp", OkHttp)
|
||||
val okio = TestCase("okio ByteString", OkIO)
|
||||
|
||||
class Relocated(
|
||||
val testCase: TestCase
|
||||
)
|
||||
|
||||
class Both(
|
||||
val testCase: TestCase
|
||||
)
|
||||
|
||||
|
||||
private fun appendHas(name: String, qualifiedClassName: String) {
|
||||
result.append(
|
||||
"""
|
||||
@Test
|
||||
fun `has ${name}`() {
|
||||
Class.forName("$qualifiedClassName")
|
||||
}
|
||||
""".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 TestCase.unaryPlus() {
|
||||
appendHas(name, qualifiedClassName)
|
||||
appendNotFound("relocated $name", Companion.relocated(qualifiedClassName))
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a class does not exist.
|
||||
*/
|
||||
operator fun TestCase.unaryMinus() {
|
||||
appendNotFound(name, qualifiedClassName)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts a relocated class exists. Also asserts the original class does not exist.
|
||||
*/
|
||||
operator fun Relocated.unaryPlus() {
|
||||
this.testCase.run {
|
||||
appendHas("relocated $name", Companion.relocated(qualifiedClassName))
|
||||
appendNotFound(name, qualifiedClassName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts a relocated class does not exist.
|
||||
*/
|
||||
operator fun Relocated.unaryMinus() {
|
||||
this.testCase.run {
|
||||
appendNotFound("relocated $name", Companion.relocated(qualifiedClassName))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts both the class and its relocated one do not exist.
|
||||
*/
|
||||
operator fun Both.unaryMinus() {
|
||||
-this.testCase
|
||||
-relocated(this.testCase)
|
||||
}
|
||||
|
||||
fun relocated(testCase: TestCase): Relocated = Relocated(testCase)
|
||||
fun both(testCase: TestCase) = Both(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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user