mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 08:50:15 +08:00
[CONSOLE] Integration Test (#1741)
* [CONSOLE] Realtime Test Unit * Rename to IntegrationTest; IDEA Debugging support * External Plugins support * Simply redesign
This commit is contained in:
parent
6f55816b08
commit
8d6b4b4970
35
mirai-console/backend/integration-test/README.md
Normal file
35
mirai-console/backend/integration-test/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Console - Integration Test
|
||||
|
||||
Mirai Console 一体化测试单元 (目前仅内部测试)
|
||||
|
||||
---
|
||||
|
||||
## 使用 Integration Test Framework
|
||||
|
||||
TODO
|
||||
|
||||
### 添加一个新测试
|
||||
|
||||
#### 创建 Integration Test 测试点
|
||||
|
||||
创建一个新的子测试单元并继承 `AbstractTestPoint`
|
||||
|
||||
- 在其 `beforeConsoleStartup()` 准备测试环境 (如写入配置文件, etc)
|
||||
- 在其 `onConsoleStartSuccessfully()` 检查插件相关行为是否正确
|
||||
|
||||
然后在 `MiraiConsoleIntegrationTestLauncher.points` 添加新单元的完整类路径
|
||||
|
||||
----
|
||||
|
||||
## Mirai Console Internal Testing
|
||||
|
||||
### 添加一个新测试 (CONSOLE 内部测试)
|
||||
|
||||
在 `test/testpoints` 添加新测试点,
|
||||
然后在 [`MiraiConsoleIntegrationTestBootstrap.kt`](test/MiraiConsoleIntegrationTestBootstrap.kt)
|
||||
添加相关单元
|
||||
|
||||
### 创建配套子插件
|
||||
|
||||
在 `testers` 创建新的文件夹即可创建新的配套插件, 可用于测试插件依赖, etc
|
||||
|
94
mirai-console/backend/integration-test/build.gradle.kts
Normal file
94
mirai-console/backend/integration-test/build.gradle.kts
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("UnusedImport")
|
||||
|
||||
import java.util.Base64
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("java")
|
||||
}
|
||||
|
||||
version = Versions.console
|
||||
description = "Mirai Console Backend Real-Time Testing Unit"
|
||||
|
||||
kotlin {
|
||||
explicitApiWarning()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":mirai-core-api"))
|
||||
api(project(":mirai-core-utils"))
|
||||
api(project(":mirai-console-compiler-annotations"))
|
||||
api(project(":mirai-console"))
|
||||
api(project(":mirai-console-terminal"))
|
||||
|
||||
api(`kotlin-stdlib-jdk8`)
|
||||
api(`kotlinx-atomicfu-jvm`)
|
||||
api(`kotlinx-coroutines-core-jvm`)
|
||||
api(`kotlinx-serialization-core-jvm`)
|
||||
api(`kotlinx-serialization-json-jvm`)
|
||||
api(`kotlin-reflect`)
|
||||
api(`kotlin-test-junit5`)
|
||||
|
||||
|
||||
api(`yamlkt-jvm`)
|
||||
api(`jetbrains-annotations`)
|
||||
api(`caller-finder`)
|
||||
api(`kotlinx-coroutines-jdk8`)
|
||||
|
||||
|
||||
val asmVersion = Versions.asm
|
||||
fun asm(module: String) = "org.ow2.asm:asm-$module:$asmVersion"
|
||||
|
||||
api(asm("tree"))
|
||||
api(asm("util"))
|
||||
api(asm("commons"))
|
||||
|
||||
}
|
||||
|
||||
val subplugins = mutableListOf<TaskProvider<Jar>>()
|
||||
|
||||
val mcit_test = tasks.named<Test>("test")
|
||||
mcit_test.configure {
|
||||
val test0 = this
|
||||
doFirst {
|
||||
// For IDEA Debugging
|
||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
val extArgs = test0.jvmArgs!!.asSequence().map { extArg ->
|
||||
Base64.getEncoder().encodeToString(extArg.toByteArray())
|
||||
}.joinToString(",")
|
||||
test0.jvmArgs = mutableListOf()
|
||||
test0.environment("IT_ARGS", extArgs)
|
||||
|
||||
// For plugins coping
|
||||
val jars = subplugins.asSequence()
|
||||
.map { it.get() }
|
||||
.flatMap { it.outputs.files.files.asSequence() }
|
||||
.toList()
|
||||
|
||||
test0.environment("IT_PLUGINS", jars.size)
|
||||
jars.forEachIndexed { index, jar ->
|
||||
test0.environment("IT_PLUGINS_$index", jar.absolutePath)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
if (project.path.removePrefix(":").startsWith("mirai-console.integration-test.tp.")) {
|
||||
project.afterEvaluate {
|
||||
val tk = tasks.named<Jar>("jar")
|
||||
subplugins.add(tk)
|
||||
mcit_test.configure { dependsOn(tk) }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest
|
||||
|
||||
/**
|
||||
* IntegrationTest 测试单元
|
||||
*
|
||||
* 每个被注册的单元都会在 console 启动的各个阶段调用相关的函数, 可以在相关函数执行测试代码
|
||||
*
|
||||
* ## 注册单元
|
||||
*
|
||||
* 每个单元都需要被注册, 即被添加进 [MiraiConsoleIntegrationTestLauncher.points]
|
||||
*
|
||||
* @see MiraiConsoleIntegrationTestLauncher
|
||||
* @see AbstractTestPointAsPlugin
|
||||
*/
|
||||
public abstract class AbstractTestPoint {
|
||||
/**
|
||||
* 本函数会在 console 启动前调用, 可以在此处进行环境配置
|
||||
*/
|
||||
protected open fun beforeConsoleStartup() {}
|
||||
|
||||
/**
|
||||
* 本函数会在 console 启动成功后立即调用, 可进行环境检查, 命令执行测试, 或更多
|
||||
*/
|
||||
protected open fun onConsoleStartSuccessfully() {}
|
||||
|
||||
// access
|
||||
internal companion object {
|
||||
internal fun AbstractTestPoint.internalOSS() {
|
||||
onConsoleStartSuccessfully()
|
||||
}
|
||||
|
||||
internal fun AbstractTestPoint.internalBCS() {
|
||||
beforeConsoleStartup()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest
|
||||
|
||||
import net.mamoe.mirai.console.extension.PluginComponentStorage
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
|
||||
/**
|
||||
* IntegrationTest 测试单元 (Plugin mode)
|
||||
*
|
||||
* 该单元除了拥有 [AbstractTestPoint] 具有的功能之外, 还可以直接模拟一个插件的行为.
|
||||
*
|
||||
* 在此单元里, 可以像写正常的 console 插件一样在此写测试时插件
|
||||
*/
|
||||
public abstract class AbstractTestPointAsPlugin : AbstractTestPoint() {
|
||||
protected abstract fun newPluginDescription(): JvmPluginDescription
|
||||
|
||||
protected open fun KotlinPlugin.onInit() {}
|
||||
protected open fun KotlinPlugin.onLoad0(storage: PluginComponentStorage) {}
|
||||
protected open fun KotlinPlugin.onEnable0() {}
|
||||
protected open fun KotlinPlugin.onDisable0() {}
|
||||
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
@PublishedApi
|
||||
internal abstract class TestPointPluginImpl(
|
||||
private val impl: AbstractTestPointAsPlugin
|
||||
) : KotlinPlugin(impl.newPluginDescription()) {
|
||||
|
||||
init {
|
||||
impl.apply { onInit() }
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal constructor(
|
||||
impl: Class<out AbstractTestPointAsPlugin>
|
||||
) : this(impl.kotlin.objectInstance ?: impl.newInstance())
|
||||
|
||||
override fun onDisable() {
|
||||
impl.apply { onDisable0() }
|
||||
}
|
||||
|
||||
override fun onEnable() {
|
||||
impl.apply { onEnable0() }
|
||||
}
|
||||
|
||||
override fun PluginComponentStorage.onLoad() {
|
||||
impl.apply { onLoad0(this@onLoad) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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
|
||||
*/
|
||||
|
||||
@file:JvmName("IntegrationTestBootstrap")
|
||||
|
||||
package net.mamoe.console.integrationtest
|
||||
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.mamoe.console.integrationtest.AbstractTestPoint.Companion.internalBCS
|
||||
import net.mamoe.console.integrationtest.AbstractTestPoint.Companion.internalOSS
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.terminal.ConsoleTerminalExperimentalApi
|
||||
import net.mamoe.mirai.console.terminal.ConsoleTerminalSettings
|
||||
import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* 入口点为 /test/MiraiConsoleIntegrationTestBootstrap.kt 并非此函数(文件),
|
||||
* 不要直接执行此函数
|
||||
*/
|
||||
@OptIn(ConsoleTerminalExperimentalApi::class)
|
||||
@PublishedApi
|
||||
internal fun main() {
|
||||
// PRE CHECK
|
||||
kotlin.run {
|
||||
if (!System.getenv("MIRAI_CONSOLE_INTEGRATION_TEST").orEmpty().toBoolean()) {
|
||||
error("Don't launch IntegrationTestBootstrap directly. See /test/MiraiConsoleIntegrationTestBootstrap.kt")
|
||||
}
|
||||
}
|
||||
// @context: env.testunit = true
|
||||
// @context: env.inJUnitProcess = false
|
||||
// @context: env.exitProcessSafety = true
|
||||
// @context: process.type = sandbox
|
||||
// @context: process.cwd = /mirai-console/backend/build/rttu
|
||||
// @context: process.timeout = 5min
|
||||
|
||||
ConsoleTerminalSettings.setupAnsi = false
|
||||
ConsoleTerminalSettings.noConsole = true
|
||||
|
||||
val testUnits: List<AbstractTestPoint> = readStringListFromEnv("IT_POINTS").asSequence()
|
||||
.onEach { println("[MCIT] Loading test point: $it") }
|
||||
.map { Class.forName(it) }
|
||||
.map { it.kotlin.objectInstance ?: it.newInstance() }
|
||||
.map { it.cast<AbstractTestPoint>() }
|
||||
.toList()
|
||||
|
||||
File("plugins").mkdirs()
|
||||
prepareConsole()
|
||||
|
||||
testUnits.forEach { (it as? AbstractTestPointAsPlugin)?.generatePluginJar() }
|
||||
testUnits.forEach { it.internalBCS() }
|
||||
|
||||
MiraiConsoleTerminalLoader.startAsDaemon()
|
||||
|
||||
if (!MiraiConsole.isActive) {
|
||||
error("Failed to start console")
|
||||
}
|
||||
|
||||
// I/main: mirai-console started successfully.
|
||||
|
||||
testUnits.forEach { it.internalOSS() }
|
||||
|
||||
runBlocking {
|
||||
MiraiConsole.job.cancelAndJoin()
|
||||
}
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
private fun File.mkparents(): File = apply { parentFile?.mkdirs() }
|
||||
private fun prepareConsole() {
|
||||
File("config/Console/Logger.yml").mkparents().writeText(
|
||||
"""
|
||||
defaultPriority: ALL
|
||||
loggers:
|
||||
Bot: ALL
|
||||
"""
|
||||
)
|
||||
|
||||
readStringListFromEnv("IT_PLUGINS").forEach { path ->
|
||||
val jarFile = File(path)
|
||||
val target = File("plugins/${jarFile.name}").mkparents()
|
||||
jarFile.copyTo(target, overwrite = true)
|
||||
println("[MCIT] Copied external plugin: $jarFile")
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractTestPointAsPlugin.generatePluginJar() {
|
||||
val simpleName = this.javaClass.simpleName
|
||||
val point = this
|
||||
val jarFile = File("plugins").resolve("$simpleName.jar")
|
||||
// PluginMainPoint: net.mamoe.console.integrationtestAbstractTestPointAsPlugin$TestPointPluginImpl
|
||||
jarFile.mkparents()
|
||||
ZipOutputStream(
|
||||
FileOutputStream(jarFile).buffered()
|
||||
).use { zipOutputStream ->
|
||||
|
||||
// META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
zipOutputStream.putNextEntry(
|
||||
ZipEntry(
|
||||
"META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin"
|
||||
)
|
||||
)
|
||||
val delegateClassName = "net.mamoe.console.integrationtest.tpd.$simpleName"
|
||||
zipOutputStream.write(delegateClassName.toByteArray())
|
||||
|
||||
// MainClass
|
||||
val internalClassName = delegateClassName.replace('.', '/')
|
||||
zipOutputStream.putNextEntry(ZipEntry("$internalClassName.class"))
|
||||
val classWriter = ClassWriter(ClassWriter.COMPUTE_MAXS)
|
||||
val superName = "net/mamoe/console/integrationtest/AbstractTestPointAsPlugin\$TestPointPluginImpl"
|
||||
classWriter.visit(
|
||||
Opcodes.V1_8,
|
||||
Opcodes.ACC_PUBLIC,
|
||||
internalClassName,
|
||||
null,
|
||||
superName,
|
||||
null
|
||||
)
|
||||
classWriter.visitMethod(
|
||||
Opcodes.ACC_PUBLIC,
|
||||
"<init>", "()V", null, null
|
||||
)!!.let { initMethod ->
|
||||
initMethod.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
initMethod.visitLdcInsn(Type.getType(point.javaClass))
|
||||
initMethod.visitMethodInsn(Opcodes.INVOKESPECIAL, superName, "<init>", "(Ljava/lang/Class;)V", false)
|
||||
initMethod.visitInsn(Opcodes.RETURN)
|
||||
initMethod.visitMaxs(0, 0)
|
||||
initMethod.visitEnd()
|
||||
}
|
||||
|
||||
zipOutputStream.write(classWriter.toByteArray())
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest
|
||||
|
||||
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintStream
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
// TODO: 不完整, 还无法完全公开, 目前仅允许 console 内部使用
|
||||
/**
|
||||
* MiraiConsoleIntegrationTest 启动器
|
||||
*/
|
||||
public class MiraiConsoleIntegrationTestLauncher {
|
||||
/** java.exe 路径 */
|
||||
public var javaexec: String by lateinitMutableProperty { findJavaExec() }
|
||||
|
||||
/**
|
||||
* 测试环境运行目录, **每次启动前会直接删除该文件夹的内容**(IMPORTANT)
|
||||
*/
|
||||
public var workingDir: File = File("mirai-console-integration-test")
|
||||
|
||||
/** 额外 JVM 参数 */
|
||||
public var vmoptions: MutableList<String> = mutableListOf()
|
||||
|
||||
/** 额外环境变量 */
|
||||
public var extraEnvironment: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
/** 类路径, 需要包含 MiraiConsoleIntegrationTest Framework */
|
||||
public var classpath: String by lateinitMutableProperty { ManagementFactory.getRuntimeMXBean().classPath }
|
||||
|
||||
/** 标准输出重定向位置 */
|
||||
public var output: OutputStream = System.out
|
||||
/** 标准错误重定向位置 */
|
||||
public var error: OutputStream = System.err
|
||||
/** [MiraiConsoleIntegrationTestLauncher] 启动日志的输出 */
|
||||
public var log: PrintStream = System.out
|
||||
|
||||
/** 测试单元完整类名, 需要可以在 [classpath] 中找到 */
|
||||
public var points: MutableCollection<String> = mutableListOf()
|
||||
/** 测试环境的额外插件, 为文件路径, 相对于 [workingDir] */
|
||||
public var plugins: MutableCollection<String> = mutableListOf()
|
||||
|
||||
public fun launch() {
|
||||
workingDir.deleteRecursively()
|
||||
workingDir.mkdirs()
|
||||
val isDebugging = vmoptions.any { it.startsWith("-agentlib:") }
|
||||
|
||||
val builder = ProcessBuilder(
|
||||
javaexec,
|
||||
*vmoptions.toTypedArray(),
|
||||
"-cp", classpath,
|
||||
"net.mamoe.console.integrationtest.IntegrationTestBootstrap",
|
||||
)
|
||||
.directory(workingDir)
|
||||
// .inheritIO() // No output in idea
|
||||
val env = builder.environment()
|
||||
env.putAll(extraEnvironment)
|
||||
env["MIRAI_CONSOLE_INTEGRATION_TEST"] = "true"
|
||||
saveStringListToEnv("IT_PLUGINS", plugins, env)
|
||||
saveStringListToEnv("IT_POINTS", points, env)
|
||||
|
||||
log.println("[MCIT] Launching IntegrationTest")
|
||||
log.println("[MCIT] `- Arguments: ${builder.command().joinToString(" ")}")
|
||||
log.println("[MCIT] `- Directory: ${builder.directory().absoluteFile}")
|
||||
log.println("[MCIT] `- Debugging: $isDebugging")
|
||||
if (isDebugging) {
|
||||
log.println("[MCIT] Running in debug mode. Watchdog thread will not start")
|
||||
}
|
||||
|
||||
val process = builder.start()
|
||||
|
||||
val timedOut = AtomicBoolean(false)
|
||||
val watchdog = thread {
|
||||
if (isDebugging) return@thread
|
||||
try {
|
||||
Thread.sleep(TimeUnit.MINUTES.toMillis(5))
|
||||
timedOut.set(true)
|
||||
process.destroyForcibly()
|
||||
} catch (ignored: InterruptedException) {
|
||||
}
|
||||
}
|
||||
|
||||
thread { process.inputStream.copyTo(output) }
|
||||
thread { process.errorStream.copyTo(error) }
|
||||
|
||||
val rsp = process.waitFor()
|
||||
if (timedOut.get()) {
|
||||
error("Mirai console daemon timed out")
|
||||
}
|
||||
watchdog.interrupt()
|
||||
if (rsp != 0) error("Rsp $rsp")
|
||||
}
|
||||
}
|
||||
|
||||
private fun findJavaExec(): String {
|
||||
findJavaExec0()?.let { return it.absolutePath }
|
||||
System.err.println("[MCIT] WARNING: Unable to determine the current runtime executable path.")
|
||||
System.err.println("[MCIT] WARNING: Using default executable to launch test unit")
|
||||
return "java"
|
||||
}
|
||||
|
||||
private fun findJavaExec0(): File? {
|
||||
val ext = if ("windows" in System.getProperty("os.name").lowercase()) {
|
||||
".exe"
|
||||
} else ""
|
||||
|
||||
val javaHome = File(System.getProperty("java.home"))
|
||||
javaHome.resolve("bin/java$ext").takeIf { it.exists() }?.let { return it }
|
||||
javaHome.resolve("java$ext").takeIf { it.exists() }?.let { return it }
|
||||
|
||||
|
||||
javaHome.resolve("jre/bin/java$ext").takeIf { it.exists() }?.let { return it }
|
||||
javaHome.resolve("jre/java$ext").takeIf { it.exists() }?.let { return it }
|
||||
|
||||
return null
|
||||
}
|
26
mirai-console/backend/integration-test/src/utils.kt
Normal file
26
mirai-console/backend/integration-test/src/utils.kt
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest
|
||||
|
||||
internal fun readStringListFromEnv(key: String): MutableList<String> {
|
||||
val size = System.getenv(key)!!.toInt()
|
||||
val rsp = mutableListOf<String>()
|
||||
for (i in 0 until size) {
|
||||
rsp.add(System.getenv("${key}_$i")!!)
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
|
||||
internal fun saveStringListToEnv(key: String, value: Collection<String>, env: MutableMap<String, String>) {
|
||||
env[key] = value.size.toString()
|
||||
value.forEachIndexed { index, v ->
|
||||
env["${key}_$index"] = v
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest
|
||||
|
||||
import net.mamoe.console.integrationtest.testpoints.DoNothingPoint
|
||||
import net.mamoe.console.integrationtest.testpoints.MCITBSelfAssertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
class MiraiConsoleIntegrationTestBootstrap {
|
||||
@Test
|
||||
fun bootstrap() {
|
||||
/*
|
||||
implementation note:
|
||||
不使用 @TempDir 是为了保存最后一次失败快照, 便于 debug
|
||||
*/
|
||||
val workingDir = File("build/IntegrationTest") // mirai-console/backend/integration-test/build/IntegrationTest
|
||||
val launcher = MiraiConsoleIntegrationTestLauncher()
|
||||
launcher.workingDir = workingDir
|
||||
launcher.plugins = readStringListFromEnv("IT_PLUGINS")
|
||||
launcher.points = listOf<Any>(
|
||||
DoNothingPoint,
|
||||
MCITBSelfAssertions,
|
||||
).asSequence().map { v ->
|
||||
when (v) {
|
||||
is Class<*> -> v
|
||||
is KClass<*> -> v.java
|
||||
else -> v.javaClass
|
||||
}
|
||||
}.map { it.name }.toMutableList()
|
||||
launcher.vmoptions = mutableListOf(
|
||||
*ManagementFactory.getRuntimeMXBean().inputArguments.filterNot {
|
||||
it.startsWith("-Djava.security.manager=")
|
||||
}.toTypedArray(),
|
||||
*System.getenv("IT_ARGS")!!.splitToSequence(",").map {
|
||||
Base64.getDecoder().decode(it).decodeToString()
|
||||
}.filter { it.isNotEmpty() }.toList().toTypedArray()
|
||||
)
|
||||
launcher.launch()
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest.testpoints
|
||||
|
||||
import net.mamoe.console.integrationtest.AbstractTestPointAsPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
import net.mamoe.mirai.utils.info
|
||||
|
||||
/*
|
||||
DoNothingPoint: Example
|
||||
*/
|
||||
internal object DoNothingPoint : AbstractTestPointAsPlugin() {
|
||||
var enableCalled = false
|
||||
override fun newPluginDescription(): JvmPluginDescription {
|
||||
return JvmPluginDescription(
|
||||
id = "net.mamoe.testpoint.do-nothing",
|
||||
version = "1.1.0",
|
||||
name = "DoNothing",
|
||||
)
|
||||
}
|
||||
|
||||
override fun KotlinPlugin.onEnable0() {
|
||||
logger.info { "DoNothing.onEnable() called" }
|
||||
enableCalled = true
|
||||
}
|
||||
|
||||
override fun KotlinPlugin.onDisable0() {
|
||||
logger.info { "DoNothing.onDisable() called" }
|
||||
}
|
||||
|
||||
override fun onConsoleStartSuccessfully() {
|
||||
assert(enableCalled) {
|
||||
"DoNothing.onEnable() not called."
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest.testpoints
|
||||
|
||||
import net.mamoe.console.integrationtest.AbstractTestPointAsPlugin
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.description
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
import kotlin.test.*
|
||||
|
||||
/*
|
||||
MCITBSelfAssertions: 用于检查 Integration Test 可以正常加载 AbstractTestPointAsPlugin 与 外部测试插件
|
||||
*/
|
||||
internal object MCITBSelfAssertions : AbstractTestPointAsPlugin() {
|
||||
override fun newPluginDescription(): JvmPluginDescription {
|
||||
return JvmPluginDescription(
|
||||
id = "net.mamoe.testpoint.mirai-console-self-assertions",
|
||||
version = "1.0.0",
|
||||
name = "MCITBSelfAssertions",
|
||||
)
|
||||
}
|
||||
|
||||
var called = false
|
||||
|
||||
override fun KotlinPlugin.onEnable0() {
|
||||
called = true
|
||||
assertFails { error("") }
|
||||
assertTrue { true }
|
||||
assertFalse { false }
|
||||
assertFailsWith<InternalError> { throw InternalError("") }
|
||||
assertEquals("", "")
|
||||
assertSame(this, this)
|
||||
}
|
||||
|
||||
override fun onConsoleStartSuccessfully() {
|
||||
assertTrue(called, "Mirai Console IntegrationTestBootstrap Internal Error")
|
||||
|
||||
assertTrue("MCITSelfTestPlugin not found") {
|
||||
PluginManager.plugins.any { it.description.id == "net.mamoe.tester.mirai-console-self-test" }
|
||||
}
|
||||
}
|
||||
}
|
1
mirai-console/backend/integration-test/testers/.gitignore
vendored
Normal file
1
mirai-console/backend/integration-test/testers/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build.gradle.kts
|
@ -0,0 +1 @@
|
||||
net.mamoe.console.integrationtest.ep.mcitselftest.MCITSelfTestPlugin
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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.console.integrationtest.ep.mcitselftest
|
||||
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
import net.mamoe.mirai.utils.info
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/*
|
||||
MCITSelfTestPlugin: 用于测试 Integration-test 可正常加载
|
||||
@see /test/testpoints/MCITBSelfAssertions
|
||||
*/
|
||||
public object MCITSelfTestPlugin : KotlinPlugin(
|
||||
JvmPluginDescription(
|
||||
id = "net.mamoe.tester.mirai-console-self-test",
|
||||
version = "1.0.0",
|
||||
name = "MCITSelfTestPlugin",
|
||||
)
|
||||
) {
|
||||
override fun onEnable() {
|
||||
logger.info { "MCITSelfTestPlugin.onEnable() called" }
|
||||
|
||||
assertTrue { true }
|
||||
}
|
||||
}
|
13
mirai-console/backend/integration-test/testers/README.md
Normal file
13
mirai-console/backend/integration-test/testers/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Integration Test - Sub Testers
|
||||
|
||||
Integration Test 的测试插件, 放置在本文件夹内的全部插件均为 console 内部测试用插件
|
||||
|
||||
如果您不是正在修改 mirai-console, 则不需要阅读此文件及此模块
|
||||
|
||||
---
|
||||
|
||||
创建新测试插件只需要在本文件夹创建新的目录, 然后重载 (Reimport gradle projects)
|
||||
|
||||
如果需要添加新的依赖, 请在 [`IntegrationTest/build.gradle.kts`](../build.gradle.kts) 添加相关依赖 (使用 `testApi`) 并标注哪个测试框架使用此依赖, 为何使用此依赖
|
||||
|
||||
如果需要自定义 `build.gradle.kts`, 请在 IDEA 右键 `build.gradle.kts` 并选择 `Git > Add File`
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019-2021 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/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("UnusedImport")
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("java")
|
||||
}
|
||||
|
||||
version = "0.0.0"
|
||||
|
||||
kotlin {
|
||||
explicitApiWarning()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":mirai-console.integration-test"))
|
||||
}
|
@ -52,6 +52,34 @@ includeConsoleProject(":mirai-console-compiler-annotations", "tools/compiler-ann
|
||||
includeConsoleProject(":mirai-console", "backend/mirai-console")
|
||||
includeConsoleProject(":mirai-console.codegen", "backend/codegen")
|
||||
includeConsoleProject(":mirai-console-terminal", "frontend/mirai-console-terminal")
|
||||
|
||||
// region mirai-console.integration-test
|
||||
includeConsoleProject(":mirai-console.integration-test", "backend/integration-test")
|
||||
|
||||
val consoleIntegrationTestSubPluginBuildGradleKtsTemplate by lazy {
|
||||
rootProject.projectDir
|
||||
.resolve("mirai-console/backend/integration-test/testers")
|
||||
.resolve("tester.template.gradle.kts")
|
||||
.readText()
|
||||
}
|
||||
|
||||
@Suppress("SimpleRedundantLet")
|
||||
fun includeConsoleITPlugin(path: File) {
|
||||
path.resolve("build.gradle.kts").takeIf { !it.isFile }?.let { initScript ->
|
||||
initScript.writeText(consoleIntegrationTestSubPluginBuildGradleKtsTemplate)
|
||||
}
|
||||
|
||||
val projectPath = ":mirai-console.integration-test.tp.${path.name}"
|
||||
include(projectPath)
|
||||
project(projectPath).projectDir = path
|
||||
}
|
||||
rootProject.projectDir
|
||||
.resolve("mirai-console/backend/integration-test/testers")
|
||||
.listFiles()?.asSequence().orEmpty()
|
||||
.filter { it.isDirectory }
|
||||
.forEach { includeConsoleITPlugin(it) }
|
||||
// endregion
|
||||
|
||||
includeConsoleProject(":mirai-console-compiler-common", "tools/compiler-common")
|
||||
includeConsoleProject(":mirai-console-intellij", "tools/intellij-plugin")
|
||||
includeConsoleProject(":mirai-console-gradle", "tools/gradle-plugin")
|
||||
|
Loading…
Reference in New Issue
Block a user