1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 04:50:26 +08:00

Change tests to multiplatform

This commit is contained in:
Him188 2022-05-24 23:46:11 +01:00
parent 20704b89b4
commit 2396851b63
No known key found for this signature in database
GPG Key ID: BA439CDDCF652375
95 changed files with 941 additions and 640 deletions
buildSrc/src/main/kotlin
mirai-core-api/src/commonMain/kotlin
message/data
utils
mirai-core-utils/src
mirai-core/src
commonMain/kotlin
commonTest/kotlin
jvmBaseMain/kotlin/network/impl/netty
jvmBaseTest/kotlin
nativeTest/kotlin

View File

@ -120,7 +120,7 @@ fun Project.configureHMPPJvm() {
androidMain.dependsOn(jvmBaseMain)
jvmTest.dependsOn(jvmBaseTest)
androidTest.dependsOn(commonTest)
androidTest.dependsOn(jvmBaseTest)
nativeMain.dependsOn(commonMain)
nativeTest.dependsOn(commonTest)

View File

@ -9,6 +9,7 @@
@file:JvmMultifileClass
@file:JvmName("MessageUtils")
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.message.data

View File

@ -181,9 +181,7 @@ internal object DeviceInfoCommonImpl {
@Suppress("DuplicatedCode")
fun equalsImpl(deviceInfo: DeviceInfo, other: Any?): Boolean = deviceInfo.run {
if (deviceInfo === other) return true
if (other !is DeviceInfo) return false
other as DeviceInfo
if (!isSameType(this, other)) return false
if (!display.contentEquals(other.display)) return false
if (!product.contentEquals(other.product)) return false

View File

@ -69,5 +69,6 @@ public expect fun <E> ConcurrentSet(): MutableSet<E>
@Deprecated("", ReplaceWith("getOrElse(key) { default }"))
public fun <K, V : R, R> Map<K, V>.getOrDefault(key: K, default: R): R = getOrElse(key) { default }
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // JDK 1.8
@Deprecated("", ReplaceWith("getOrPut(key) { value }"))
public fun <K, V> MutableMap<K, V>.putIfAbsent(key: K, value: V): V = getOrPut(key) { value }

View File

@ -1,52 +0,0 @@
/*
* 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.utils
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlin.reflect.KProperty
public fun <T : Any> computeOnNullMutableProperty(initializer: () -> T): ComputeOnNullMutableProperty<T> =
ComputeOnNullMutablePropertyImpl(initializer)
public interface ComputeOnNullMutableProperty<V : Any> {
public fun get(): V
public fun set(value: V?)
public operator fun getValue(thisRef: Any?, property: KProperty<*>): V = get()
public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V?): Unit = set(value)
}
private class ComputeOnNullMutablePropertyImpl<T : Any>(
private val initializer: () -> T
) : ComputeOnNullMutableProperty<T> {
private val value = atomic<T?>(null)
private val lock = SynchronizedObject()
override tailrec fun get(): T {
return when (val v = this.value.value) {
null -> synchronized(lock) {
if (this.value.value === null) {
val value = this.initializer()
// compiler inserts
this.value.compareAndSet(null, value) // setValue prevails
return get()
} else this.value.value as T
}
else -> v
}
}
override fun set(value: T?) {
this.value.value = value
}
}

View File

@ -9,55 +9,76 @@
package net.mamoe.mirai.utils
public interface File {
import io.ktor.utils.io.core.*
/**
* Multiplatform implementation of file operations.
*/
public expect interface MiraiFile {
public val name: String
public val path: String
public val parent: MiraiFile?
public val absolutePath: String
public val length: Long
public val parent: File?
public val isFile: Boolean
public val isDirectory: Boolean
public fun exists(): Boolean
public fun resolve(path: String): File
public fun resolve(file: File): File
public fun resolve(path: String): MiraiFile
public fun resolve(file: MiraiFile): MiraiFile
public fun createNewFile()
public fun delete()
public fun mkdirs()
public fun createNewFile(): Boolean
public fun delete(): Boolean
public fun readText(): String
public fun readBytes(): ByteArray
public fun mkdir(): Boolean
public fun mkdirs(): Boolean
public fun writeBytes(data: ByteArray)
public fun writeText(text: String)
public fun input(): Input
public fun output(): Output
public companion object {
public fun create(path: String): File {
TODO("")
}
public fun create(absolutePath: String): MiraiFile
}
}
public fun File.createFileIfNotExists() {
public fun MiraiFile.writeBytes(data: ByteArray) {
return output().use { it.writeFully(data) }
}
public fun MiraiFile.writeText(text: String) {
return output().use { it.writeText(text) }
}
public fun MiraiFile.readText(): String {
return input().use { it.readText() }
}
public fun MiraiFile.readBytes(): ByteArray {
return input().use { it.readBytes() }
}
public fun MiraiFile.createFileIfNotExists() {
if (!this.exists()) {
this.parent?.mkdirs()
this.createNewFile()
}
}
public fun File.resolveCreateFile(relative: String): File = this.resolve(relative).apply { createFileIfNotExists() }
public fun File.resolveCreateFile(relative: File): File = this.resolve(relative).apply { createFileIfNotExists() }
public fun MiraiFile.resolveCreateFile(relative: String): MiraiFile =
this.resolve(relative).apply { createFileIfNotExists() }
public fun File.resolveMkdir(relative: String): File = this.resolve(relative).apply { mkdirs() }
public fun File.resolveMkdir(relative: File): File = this.resolve(relative).apply { mkdirs() }
public fun MiraiFile.resolveCreateFile(relative: MiraiFile): MiraiFile =
this.resolve(relative).apply { createFileIfNotExists() }
public fun File.touch(): File = apply {
public fun MiraiFile.resolveMkdir(relative: String): MiraiFile = this.resolve(relative).apply { mkdirs() }
public fun MiraiFile.resolveMkdir(relative: MiraiFile): MiraiFile = this.resolve(relative).apply { mkdirs() }
public fun MiraiFile.touch(): MiraiFile = apply {
parent?.mkdirs()
createNewFile()
}

View File

@ -66,7 +66,7 @@ public inline fun <T, R> KSerializer<T>.mapPrimitive(
}
public fun <T> File.loadNotBlankAs(
public fun <T> MiraiFile.loadNotBlankAs(
serializer: DeserializationStrategy<T>,
stringFormat: StringFormat,
): T? {
@ -76,7 +76,7 @@ public fun <T> File.loadNotBlankAs(
return stringFormat.decodeFromString(serializer, this.readText())
}
public fun <T> File.loadNotBlankAs(
public fun <T> MiraiFile.loadNotBlankAs(
serializer: DeserializationStrategy<T>,
binaryFormat: BinaryFormat,
): T? {

View File

@ -1,51 +0,0 @@
/*
* 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.utils
import kotlinx.atomicfu.atomic
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
internal class ComputeOnNullMutablePropertyTest {
@Test
fun `can initialize`() {
val prop = computeOnNullMutableProperty { "ok" }
assertEquals("ok", prop.get())
}
@Test
fun `can override`() {
val called = atomic(false)
val prop = computeOnNullMutableProperty { "not ok".also { called.value = true } }
prop.set("ok")
assertEquals("ok", prop.get())
assertFalse { called.value }
}
@Test
fun `can reinitialize 1`() {
val called = atomic(false)
val prop = computeOnNullMutableProperty { "ok".also { called.value = true } }
prop.set("not ok 2")
prop.set(null)
assertEquals("ok", prop.get())
assertTrue { called.value }
}
@Test
fun `can reinitialize 2`() {
val prop = computeOnNullMutableProperty { "ok" }
prop.get()
prop.set(null)
assertEquals("ok", prop.get())
}
}

View File

@ -39,15 +39,15 @@ internal class LateinitMutablePropertyTest {
@Test
fun initializerCalledOnce() {
val value = Symbol("expected")
val counter = atomic(0)
var counter = 0
val prop by lateinitMutableProperty {
counter.incrementAndGet()
counter++
value
}
assertSame(value, prop)
assertSame(value, prop)
assertEquals(1, counter.value)
assertEquals(1, counter)
}
@Test

View File

@ -0,0 +1,31 @@
/*
* 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.utils
import java.io.File
public fun File.createFileIfNotExists() {
if (!this.exists()) {
this.parentFile?.mkdirs()
this.createNewFile()
}
}
public fun File.resolveCreateFile(relative: String): File = this.resolve(relative).apply { createFileIfNotExists() }
public fun File.resolveCreateFile(relative: File): File = this.resolve(relative).apply { createFileIfNotExists() }
public fun File.resolveMkdir(relative: String): File = this.resolve(relative).apply { mkdirs() }
public fun File.resolveMkdir(relative: File): File = this.resolve(relative).apply { mkdirs() }
public fun File.touch(): File = apply {
parentFile?.mkdirs()
createNewFile()
}

View File

@ -0,0 +1,77 @@
/*
* 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.utils
import io.ktor.utils.io.core.*
import io.ktor.utils.io.streams.*
import java.io.File
public actual interface MiraiFile {
public actual val name: String
public actual val parent: MiraiFile?
public actual val absolutePath: String
public actual val length: Long
public actual val isFile: Boolean
public actual val isDirectory: Boolean
public actual fun exists(): Boolean
public actual fun resolve(path: String): MiraiFile
public actual fun resolve(file: MiraiFile): MiraiFile
public actual fun createNewFile(): Boolean
public actual fun delete(): Boolean
public actual fun mkdir(): Boolean
public actual fun mkdirs(): Boolean
public actual fun input(): Input
public actual fun output(): Output
public actual companion object {
public actual fun create(absolutePath: String): MiraiFile {
return File(absolutePath).asMiraiFile()
}
}
}
public fun File.asMiraiFile(): MiraiFile {
return JvmFileAsMiraiFile(this)
}
public fun MiraiFile.toJvmFile(): File {
if (this is JvmFileAsMiraiFile) {
return jvmFile
}
return File(absolutePath)
}
internal class JvmFileAsMiraiFile(
internal val jvmFile: File
) : MiraiFile {
override val name: String get() = jvmFile.name
override val parent: MiraiFile? get() = jvmFile.parentFile?.asMiraiFile()
override val absolutePath: String get() = jvmFile.absolutePath
override val length: Long get() = jvmFile.length()
override val isFile: Boolean get() = jvmFile.isFile
override val isDirectory: Boolean get() = jvmFile.isDirectory
override fun exists(): Boolean = jvmFile.exists()
override fun resolve(path: String): MiraiFile = jvmFile.resolve(path).asMiraiFile()
override fun resolve(file: MiraiFile): MiraiFile = jvmFile.resolve(file.absolutePath).asMiraiFile()
override fun createNewFile(): Boolean = jvmFile.createNewFile()
override fun delete(): Boolean = jvmFile.delete()
override fun mkdir(): Boolean = jvmFile.mkdir()
override fun mkdirs(): Boolean = jvmFile.mkdirs()
override fun input(): Input = jvmFile.inputStream().asInput()
override fun output(): Output = jvmFile.outputStream().asOutput()
}

View File

@ -0,0 +1,39 @@
/*
* 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.utils
import io.ktor.utils.io.core.*
/**
* Multiplatform implementation of file operations.
*/
public actual interface MiraiFile {
public actual val name: String
public actual val parent: MiraiFile?
public actual val absolutePath: String
public actual val length: Long
public actual val isFile: Boolean
public actual val isDirectory: Boolean
public actual fun exists(): Boolean
public actual fun resolve(path: String): MiraiFile
public actual fun resolve(file: MiraiFile): MiraiFile
public actual fun createNewFile(): Boolean
public actual fun delete(): Boolean
public actual fun mkdir(): Boolean
public actual fun mkdirs(): Boolean
public actual fun input(): Input
public actual fun output(): Output
public actual companion object {
public actual fun create(absolutePath: String): MiraiFile {
TODO("Not yet implemented")
}
}
}

View File

@ -85,9 +85,9 @@ internal class GroupMemberListCaches(
return ret
}
private val cacheDir: File by lazy { bot.configuration.groupCacheDir() }
private val cacheDir: MiraiFile by lazy { bot.configuration.groupCacheDir() }
private fun resolveCacheFile(groupCode: Long): File {
private fun resolveCacheFile(groupCode: Long): MiraiFile {
cacheDir.mkdirs()
return cacheDir.resolve("$groupCode.json")
}

View File

@ -175,7 +175,7 @@ internal class MemoryAccountSecretsManager : AccountSecretsManager {
internal class FileCacheAccountSecretsManager(
val file: File,
val file: MiraiFile,
val logger: MiraiLogger,
) : AccountSecretsManager {
@Synchronized
@ -214,7 +214,7 @@ internal class FileCacheAccountSecretsManager(
}
companion object {
fun saveSecretsToFile(file: File, account: BotAccount, secrets: AccountSecrets) {
fun saveSecretsToFile(file: MiraiFile, account: BotAccount, secrets: AccountSecrets) {
file.writeBytes(
TEA.encrypt(
AccountSecretsImpl(secrets).toByteArray(AccountSecretsImpl.serializer()),

View File

@ -19,10 +19,7 @@ import net.mamoe.mirai.internal.network.ProtoBufForCache
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ComponentStorage
import net.mamoe.mirai.internal.utils.actualCacheDir
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.ConcurrentSet
import net.mamoe.mirai.utils.File
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.*
import kotlin.jvm.Volatile
internal interface BdhSessionSyncer {
@ -75,9 +72,9 @@ internal class BdhSessionSyncerImpl(
}
}
private val sessionCacheFile: File
private val sessionCacheFile: MiraiFile
get() = configuration.actualCacheDir().resolve("session.bin")
private val serverListCacheFile: File
private val serverListCacheFile: MiraiFile
get() = configuration.actualCacheDir().resolve("servers.json")
override fun loadServerListFromCache() {

View File

@ -148,7 +148,7 @@ internal class ServerListImpl(
*/
@Synchronized
override fun pollCurrent(): ServerAddress? {
return current.firstOrNull()?.also { address ->
return current.removeFirstOrNull()?.also { address ->
lastPolledAddress = address
}
}

View File

@ -83,9 +83,10 @@ internal abstract class CommonNetworkHandler<Conn>(
*/
protected abstract fun Conn.writeAndFlushOrCloseAsync(packet: OutgoingPacket)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
protected abstract fun Conn.close()
protected inner class PacketDecodePipeline(parentContext: CoroutineContext) :
internal inner class PacketDecodePipeline(parentContext: CoroutineContext) :
CoroutineScope by parentContext.childScope() {
private val packetCodec: PacketCodec by lazy { context[PacketCodec] }

View File

@ -0,0 +1,17 @@
/*
* 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.internal.network.handler.selector
internal open class NetworkChannelException : NetworkException {
constructor() : super(true)
constructor(cause: Throwable?) : super(true, cause)
constructor(message: String) : super(message, true)
constructor(message: String, cause: Throwable?) : super(message, cause, true)
}

View File

@ -9,7 +9,7 @@
package net.mamoe.mirai.internal.network.handler.selector
internal abstract class NetworkException : Exception {
internal open class NetworkException : Exception {
/**
* If true, the selector may recover the network handler by some means.
*/

View File

@ -12,7 +12,7 @@
package net.mamoe.mirai.internal.utils
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.File
import net.mamoe.mirai.utils.MiraiFile
import net.mamoe.mirai.utils.resolveCreateFile
import net.mamoe.mirai.utils.resolveMkdir
import kotlin.jvm.JvmName
@ -22,9 +22,11 @@ internal expect val BotConfiguration.workingDirPath: String
internal expect val BotConfiguration.cacheDirPath: String
internal fun BotConfiguration.actualCacheDir(): File = File.create(workingDirPath).resolveMkdir(cacheDirPath)
internal fun BotConfiguration.contactCacheDir(): File = actualCacheDir().resolveMkdir("contacts")
internal fun BotConfiguration.friendCacheFile(): File = contactCacheDir().resolveCreateFile("friends.json")
internal fun BotConfiguration.groupCacheDir(): File = contactCacheDir().resolveMkdir("groups")
internal fun BotConfiguration.groupCacheFile(groupId: Long): File = groupCacheDir().resolveCreateFile("$groupId.json")
internal fun BotConfiguration.accountSecretsFile(): File = actualCacheDir().resolve("account.secrets")
internal fun BotConfiguration.actualCacheDir(): MiraiFile = MiraiFile.create(workingDirPath).resolveMkdir(cacheDirPath)
internal fun BotConfiguration.contactCacheDir(): MiraiFile = actualCacheDir().resolveMkdir("contacts")
internal fun BotConfiguration.friendCacheFile(): MiraiFile = contactCacheDir().resolveCreateFile("friends.json")
internal fun BotConfiguration.groupCacheDir(): MiraiFile = contactCacheDir().resolveMkdir("groups")
internal fun BotConfiguration.groupCacheFile(groupId: Long): MiraiFile =
groupCacheDir().resolveCreateFile("$groupId.json")
internal fun BotConfiguration.accountSecretsFile(): MiraiFile = actualCacheDir().resolve("account.secrets")

View File

@ -73,33 +73,32 @@ private fun <T : JceStruct> ByteArray.doLoadAs(
Tars.UTF_8.load(deserializer, input)
}
} catch (originalException: Exception) {
val log = BytePacketBuilder()
val build by lazy { log.build() }
try {
val value = log.use { stream ->
stream.appendLine("\nData: ")
stream.appendLine(this.toUHexString(offset = offset, length = length))
stream.appendLine("Trace:")
BytePacketBuilder().use { log ->
val build by lazy { log.build() }
try {
log.appendLine("\nData: ")
log.appendLine(this.toUHexString(offset = offset, length = length))
log.appendLine("Trace:")
this.toReadPacket(offset = offset, length = length).use { input ->
Tars.UTF_8.load(deserializer, input, debugLogger = DebugLogger(stream))
val value = this.toReadPacket(offset = offset, length = length).use { input ->
Tars.UTF_8.load(deserializer, input, debugLogger = DebugLogger(log))
}
}
return value.also {
TarsDecoder.logger.warning(
contextualBugReportException(
"解析 " + deserializer.descriptor.serialName,
"启用 debug 模式后解析正常: $value \n\n${build.readText()}",
originalException
return value.also {
TarsDecoder.logger.warning(
contextualBugReportException(
"解析 " + deserializer.descriptor.serialName,
"启用 debug 模式后解析正常: $value \n\n${build.readText()}",
originalException
)
)
}
} catch (secondFailure: Exception) {
throw contextualBugReportException(
"解析 " + deserializer.descriptor.serialName,
build.readText(),
ExceptionCollector.compressExceptions(originalException, secondFailure)
)
}
} catch (secondFailure: Exception) {
throw contextualBugReportException(
"解析 " + deserializer.descriptor.serialName,
build.readText(),
ExceptionCollector.compressExceptions(originalException, secondFailure)
)
}
}
}

View File

@ -190,8 +190,7 @@ internal class EventTests : AbstractEventTest() {
private fun singleThreaded(step: StepUtil, invoke: suspend EventChannel<Event>.() -> Unit) {
// runBlocking 会完全堵死, 没法退出
@OptIn(ExperimentalCoroutinesApi::class)
val scope = CoroutineScope(newSingleThreadContext("EventTests singleThreaded worker"))
val scope = CoroutineScope(borrowSingleThreadDispatcher())
val job = scope.launch {
invoke(scope.globalEventChannel())
}

View File

@ -19,8 +19,7 @@ import kotlin.test.*
@JvmBlockingBridge
internal class NextEventTest : AbstractEventTest() {
@OptIn(ExperimentalCoroutinesApi::class)
private val dispatcher: CoroutineDispatcher = newSingleThreadContext("NextEventTest")
private val dispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher()
@OptIn(ExperimentalCoroutinesApi::class)
@AfterTest

View File

@ -14,8 +14,9 @@ import net.mamoe.mirai.internal.message.image.calculateImageInfo
import net.mamoe.mirai.internal.test.AbstractTest
import net.mamoe.mirai.message.data.ImageType
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import net.mamoe.mirai.utils.File
import net.mamoe.mirai.utils.MiraiFile
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.readBytes
import net.mamoe.mirai.utils.withUse
import kotlin.test.Test
import kotlin.test.assertEquals
@ -66,9 +67,9 @@ internal class ImageReadingTest : AbstractTest() {
"FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF D0 FF D1 FF D2 FF D3 FF D4 FF D5 FF D6 FF D7 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch(
ImageType.JPG
)
println("Current path: " + net.mamoe.mirai.utils.File.create(".").absolutePath)
println("Current path: " + net.mamoe.mirai.utils.MiraiFile.create(".").absolutePath)
//Issue 1610
File.create("./src/commonTest/resources/image/jpeg-header-issue-1610.bin").readBytes().testRead(
MiraiFile.create("./src/commonTest/resources/image/jpeg-header-issue-1610.bin").readBytes().testRead(
ImageType.JPG
)
//Failed to find

View File

@ -19,7 +19,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class TestMiraiCode : AbstractTest() {
internal class TestMiraiCode : AbstractTest() {
@Test
fun testDynamicMiraiCodeParser() {
fun runTest(args: Int, code: String, parse: (args: Array<String>) -> Unit) {

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.internal.network
import io.ktor.utils.io.core.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.selects.select
@ -73,8 +72,7 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() {
}
// single thread so we can use [yield] to transfer dispatch
@OptIn(ExperimentalCoroutinesApi::class)
private val singleThreadDispatcher: CoroutineDispatcher = newSingleThreadContext("AwaitStateTest")
private val singleThreadDispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher()
@OptIn(ExperimentalCoroutinesApi::class)
@AfterTest

View File

@ -1,18 +1,21 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network
import net.mamoe.mirai.internal.network.components.ServerAddress
import net.mamoe.mirai.internal.network.components.ServerList
import net.mamoe.mirai.internal.network.components.ServerListImpl
import net.mamoe.mirai.internal.test.AbstractTest
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.*
internal class ServerListTest : AbstractTest() {
@ -32,7 +35,7 @@ internal class ServerListTest : AbstractTest() {
val instance = ServerListImpl()
val old = instance.getLastPolledIP()
assertNotNull(old)
assert(old.isEmpty())
assertTrue { old.isEmpty() }
assertNotNull(instance.pollCurrent())
val new = instance.getLastPolledIP()
assertNotNull(new)

View File

@ -7,26 +7,29 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.component
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.isActive
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.components.BotInitProcessor
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTestWithSelector
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTestWithSelector
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushForceOffline
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPushForceOffline
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
internal class BotInitProcessorTest {
class WithoutSelector : AbstractNettyNHTest() {
class WithoutSelector : AbstractCommonNHTest() {
@Test
fun `BotInitProcessor halted`() = runBlockingUnit {
val p = setComponent(BotInitProcessor, object : BotInitProcessor {
@ -43,7 +46,7 @@ internal class BotInitProcessorTest {
}
})
assertTrue { network.isActive }
network.setStateLoading(channel)
network.setStateLoading(conn)
assertEquals(1, p.ranTimes)
assertEquals(0, p.haltedTimes)
assertState(NetworkHandler.State.LOADING)
@ -61,7 +64,7 @@ internal class BotInitProcessorTest {
}
}
class WithSelector : AbstractNettyNHTestWithSelector() {
class WithSelector : AbstractCommonNHTestWithSelector() {
@Test
fun `BotInitProcessor halted`() = runBlockingUnit {
bot.configuration.autoReconnectOnForceOffline = true
@ -79,7 +82,7 @@ internal class BotInitProcessorTest {
}
})
assertTrue { network.isActive }
network.setStateLoading(channel)
network.setStateLoading(conn)
assertEquals(1, p.ranTimes)
assertEquals(0, p.haltedTimes)
assertState(NetworkHandler.State.LOADING)

View File

@ -0,0 +1,73 @@
/*
* 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.internal.network.framework
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.handler.*
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.utils.ExceptionCollector
/**
* You may need to override [createConnection]
*/
internal abstract class TestCommonNetworkHandler(
override val bot: QQAndroidBot,
context: NetworkHandlerContext,
address: SocketAddress,
) : CommonNetworkHandler<PlatformConn>(context, address), ITestNetworkHandler<PlatformConn> {
override suspend fun createConnection(): PlatformConn {
return PlatformConn()
}
override fun PlatformConn.writeAndFlushOrCloseAsync(packet: OutgoingPacket) {
}
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
override fun PlatformConn.close() {
}
override fun setStateClosed(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateClosed(exception) }
}
override fun setStateConnecting(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateConnecting(ExceptionCollector(exception)) }
}
override fun setStateOK(conn: PlatformConn, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
exception?.printStackTrace()
return setState { StateOK(conn, CompletableDeferred(Unit)) }
}
override fun setStateLoading(conn: PlatformConn): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateLoading(conn) }
}
}
/**
* Without selector. When network is closed, it will not reconnect, so that you can check for its states.
*
* @see AbstractCommonNHTestWithSelector
*/
internal expect abstract class AbstractCommonNHTest() :
AbstractRealNetworkHandlerTest<TestCommonNetworkHandler> {
val conn: PlatformConn
override val network: TestCommonNetworkHandler
override val factory: NetworkHandlerFactory<TestCommonNetworkHandler>
protected fun removeOutgoingPacketEncoder()
}
internal expect class PlatformConn()

View File

@ -1,17 +1,16 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("MemberVisibilityCanBePrivate")
package net.mamoe.mirai.internal.network.framework
import io.netty.channel.Channel
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitorImpl
@ -26,18 +25,20 @@ import net.mamoe.mirai.utils.cast
* When network is closed, it will reconnect, so that you test for real environment,
* but you cannot check for its states (it will never be CLOSED until some fatal error, just like in real).
*/
internal abstract class AbstractNettyNHTestWithSelector : AbstractRealNetworkHandlerTest<TestSelectorNetworkHandler>() {
internal abstract class AbstractCommonNHTestWithSelector :
AbstractRealNetworkHandlerTest<TestSelectorNetworkHandler>() {
init {
overrideComponents[BotOfflineEventMonitor] = BotOfflineEventMonitorImpl()
}
val channel = AbstractNettyNHTest.NettyNHTestChannel(
logger = lazy { bot.logger },
)
val conn = PlatformConn()
val selector = TestSelector<TestNettyNH> {
object : TestNettyNH(bot, createContext(), createAddress()) {
override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = channel
val selector = TestSelector<TestCommonNetworkHandler> {
object : TestCommonNetworkHandler(bot, createContext(), createAddress()) {
// override suspend fun createConnection(decodePipeline: PacketDecodePipeline): PlatformConn = channel
override suspend fun createConnection(): PlatformConn {
return conn
}
}
}
@ -48,9 +49,9 @@ internal abstract class AbstractNettyNHTestWithSelector : AbstractRealNetworkHan
}
internal class TestSelectorNetworkHandler(
selector: NetworkHandlerSelector<TestNettyNH>, override val bot: QQAndroidBot,
) : ITestNetworkHandler,
SelectorNetworkHandler<TestNettyNH>(selector) {
selector: NetworkHandlerSelector<TestCommonNetworkHandler>, override val bot: QQAndroidBot,
) : ITestNetworkHandler<PlatformConn>,
SelectorNetworkHandler<TestCommonNetworkHandler>(selector) {
fun currentInstance() = selector.getCurrentInstanceOrCreate()
fun currentInstanceOrNull() = selector.getCurrentInstanceOrNull()
@ -63,12 +64,12 @@ internal class TestSelectorNetworkHandler(
return selector.getCurrentInstanceOrCreate().setStateConnecting(exception)
}
override fun setStateOK(channel: Channel, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return selector.getCurrentInstanceOrCreate().setStateOK(channel, exception)
override fun setStateOK(conn: PlatformConn, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return selector.getCurrentInstanceOrCreate().setStateOK(conn, exception)
}
override fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl? {
return selector.getCurrentInstanceOrCreate().setStateLoading(channel)
override fun setStateLoading(conn: PlatformConn): NetworkHandlerSupport.BaseStateImpl? {
return selector.getCurrentInstanceOrCreate().setStateLoading(conn)
}
}

View File

@ -1,171 +0,0 @@
/*
* 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.internal.network.framework
import io.ktor.utils.io.core.*
import io.netty.channel.Channel
import io.netty.channel.embedded.EmbeddedChannel
import io.netty.util.ReferenceCountUtil
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.serializer
import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor
import net.mamoe.mirai.internal.network.components.RawIncomingPacket
import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext
import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory
import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
import net.mamoe.mirai.internal.network.handler.SocketAddress
import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandler
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.utils.io.ProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.ExceptionCollector
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.error
import java.net.SocketAddress
/**
* You may need to override [createConnection]
*/
internal abstract class TestNettyNH(
override val bot: QQAndroidBot,
context: NetworkHandlerContext,
address: SocketAddress,
) : NettyNetworkHandler(context, address), ITestNetworkHandler {
protected abstract suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel
final override suspend fun createConnection(): Channel {
return createConnection(createDummyDecodePipeline())
}
override fun setStateClosed(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateClosed(exception) }
}
override fun setStateConnecting(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateConnecting(ExceptionCollector(exception)) }
}
override fun setStateOK(channel: Channel, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? {
exception?.printStackTrace()
return setState { StateOK(channel, CompletableDeferred(Unit)) }
}
override fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl? {
return setState { StateLoading(channel) }
}
}
/**
* Without selector. When network is closed, it will not reconnect, so that you can check for its states.
*
* @see AbstractNettyNHTestWithSelector
*/
internal abstract class AbstractNettyNHTest : AbstractRealNetworkHandlerTest<TestNettyNH>() {
init {
overrideComponents[BotOfflineEventMonitor] = object : BotOfflineEventMonitor {
override fun attachJob(bot: AbstractBot, scope: CoroutineScope) {
}
}
}
class NettyNHTestChannel(
val logger: Lazy<MiraiLogger>,
var fakeServer: (NettyNHTestChannel.(msg: Any?) -> Unit)? = null,
) : EmbeddedChannel() {
@OptIn(InternalSerializationApi::class)
fun listen(listener: (OutgoingPacket) -> Any?) {
fakeServer = { packet ->
if (packet is OutgoingPacket) {
val rsp0 = when (val rsp = listener(packet)) {
null -> null
is Unit -> null
is ByteArray -> {
RawIncomingPacket(
commandName = packet.commandName,
sequenceId = packet.sequenceId,
body = rsp
)
}
is RawIncomingPacket -> rsp
is ProtoBuf -> {
RawIncomingPacket(
commandName = packet.commandName,
sequenceId = packet.sequenceId,
body = buildPacket {
writeProtoBuf(
rsp::class.serializer().cast<KSerializer<ProtoBuf>>(),
rsp
)
}.readBytes()
)
}
else -> {
logger.value.error { "Failed to respond $rsp" }
null
}
}
if (rsp0 != null) {
pipeline().fireChannelRead(rsp0)
}
}
ReferenceCountUtil.release(packet)
}
}
public /*internal*/ override fun doRegister() {
super.doRegister() // Set channel state to ACTIVE
// Drop old handlers
pipeline().let { p ->
while (p.first() != null) {
p.removeFirst()
}
}
}
override fun handleInboundMessage(msg: Any?) {
ReferenceCountUtil.release(msg) // Not handled, Drop
}
override fun handleOutboundMessage(msg: Any?) {
fakeServer?.invoke(this, msg) ?: ReferenceCountUtil.release(msg)
}
}
val channel = NettyNHTestChannel(
logger = lazy { bot.logger },
)
override val network: TestNettyNH get() = bot.network as TestNettyNH
override val factory: NetworkHandlerFactory<TestNettyNH> =
NetworkHandlerFactory<TestNettyNH> { context, address ->
object : TestNettyNH(bot, context, address) {
override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel =
channel.apply {
doRegister() // restart channel
setupChannelPipeline(pipeline(), decodePipeline)
}
}
}
protected fun removeOutgoingPacketEncoder() {
kotlin.runCatching {
channel.pipeline().remove("outgoing-packet-encoder")
}
}
}

View File

@ -11,11 +11,9 @@
package net.mamoe.mirai.internal.network.framework
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.internal.BotAccount
import net.mamoe.mirai.internal.MockAccount
import net.mamoe.mirai.internal.MockConfiguration
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.*
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.component.setAll
@ -37,9 +35,9 @@ import kotlin.test.assertEquals
/**
* With real factory and components as in [QQAndroidBot.components].
*
* Extend [AbstractNettyNHTestWithSelector] or [AbstractNettyNHTest].
* Extend [AbstractCommonNHTestWithSelector] or [AbstractCommonNHTest].
*/
internal sealed class AbstractRealNetworkHandlerTest<H : NetworkHandler> : AbstractNetworkHandlerTest() {
internal abstract class AbstractRealNetworkHandlerTest<H : NetworkHandler> : AbstractNetworkHandlerTest() {
abstract val factory: NetworkHandlerFactory<H>
abstract val network: H
@ -137,9 +135,15 @@ internal sealed class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abstr
bot.logger.subLogger("TestEventDispatcherImpl")
)
)
set(BotOfflineEventMonitor, object : BotOfflineEventMonitor {
override fun attachJob(bot: AbstractBot, scope: CoroutineScope) {
}
})
// set(StateObserver, bot.run { stateObserverChain() })
}
fun <T : Any> setComponent(key: ComponentKey<in T>, instance: T): T {
overrideComponents[key] = instance
return instance

View File

@ -19,7 +19,7 @@ import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.hexToBytes
import kotlin.test.BeforeTest
internal abstract class AbstractRealTimeActionTestUnit : AbstractNettyNHTest(), GroupExtensions {
internal abstract class AbstractRealTimeActionTestUnit : AbstractCommonNHTest(), GroupExtensions {
@BeforeTest
internal fun prepareEnv() {
bot.client.wLoginSigInfoField = WLoginSigInfo(
@ -67,7 +67,7 @@ internal abstract class AbstractRealTimeActionTestUnit : AbstractNettyNHTest(),
deviceToken = "Winserver datacenter 2077".toByteArray(),
)
bot.client._bot = bot
network.setStateOK(channel)
network.setStateOK(conn)
removeOutgoingPacketEncoder()
}
}

View File

@ -1,29 +1,29 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.framework
import io.netty.channel.Channel
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
internal interface ITestNetworkHandler : NetworkHandler {
internal interface ITestNetworkHandler<Conn> : NetworkHandler {
val bot: QQAndroidBot
fun setStateClosed(exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl?
fun setStateConnecting(exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl?
fun setStateOK(channel: Channel, exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl?
fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl?
fun setStateOK(conn: Conn, exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl?
fun setStateLoading(conn: Conn): NetworkHandlerSupport.BaseStateImpl?
}
internal val ITestNetworkHandler.eventDispatcher get() = bot.components[EventDispatcher]
internal val ITestNetworkHandler.ssoProcessor get() = bot.components[SsoProcessor]
internal val ITestNetworkHandler<*>.eventDispatcher get() = bot.components[EventDispatcher]
internal val ITestNetworkHandler<*>.ssoProcessor get() = bot.components[SsoProcessor]

View File

@ -1,15 +1,15 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.framework
import io.netty.channel.Channel
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@ -19,9 +19,8 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext
import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.utils.ConcurrentLinkedQueue
import net.mamoe.mirai.utils.TestOnly
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicInteger
/**
* States are manually set.
@ -29,13 +28,15 @@ import java.util.concurrent.atomic.AtomicInteger
internal open class TestNetworkHandler(
override val bot: QQAndroidBot,
context: NetworkHandlerContext,
) : NetworkHandlerSupport(context), ITestNetworkHandler {
) : NetworkHandlerSupport(context), ITestNetworkHandler<TestNetworkHandler.Connection> {
class Connection
@Suppress("EXPOSED_SUPER_CLASS")
internal open inner class TestState(
correspondingState: NetworkHandler.State
) : BaseStateImpl(correspondingState) {
val resumeDeferred = CompletableDeferred<Unit>()
val resumeCount = AtomicInteger(0)
val resumeCount = atomic(0)
val onResume get() = resumeDeferred.onJoin
private val mutex = Mutex()
@ -81,12 +82,12 @@ internal open class TestNetworkHandler(
return setState(NetworkHandler.State.CONNECTING)
}
override fun setStateOK(channel: Channel, exception: Throwable?): TestState? {
override fun setStateOK(conn: Connection, exception: Throwable?): TestState? {
exception?.printStackTrace()
return setState(NetworkHandler.State.OK)
}
override fun setStateLoading(channel: Channel): TestState? {
override fun setStateLoading(conn: Connection): TestState? {
return setState(NetworkHandler.State.LOADING)
}
}

View File

@ -18,8 +18,9 @@ import net.mamoe.mirai.internal.network.components.AccountSecretsImpl
import net.mamoe.mirai.internal.network.components.SsoSession
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.File
import net.mamoe.mirai.utils.MiraiFile
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.writeBytes
internal class TestSsoSession(
@ -53,7 +54,7 @@ internal fun QQAndroidClient.dumpSessionSafe(): ByteArray {
return secrets.toByteArray(AccountSecretsImpl.serializer())
}
internal fun QQAndroidBot.scheduleSafeSessionDump(outputFile: File) {
internal fun QQAndroidBot.scheduleSafeSessionDump(outputFile: MiraiFile) {
this.eventChannel.subscribeAlways<BotOnlineEvent> {
outputFile.writeBytes(client.dumpSessionSafe())
bot.logger.debug { "Dumped safe session to " }

View File

@ -13,26 +13,28 @@ package net.mamoe.mirai.internal.network.handler
import net.mamoe.mirai.internal.network.components.FirstLoginResult
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.TestNettyNH
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.PlatformConn
import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler
import net.mamoe.mirai.internal.network.handler.selector.MaxAttemptsReachedException
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.*
internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractNettyNHTest() {
internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractCommonNHTest() {
internal class FakeFailOnCreatingConnection : AbstractNettyNHTest() {
internal class FakeFailOnCreatingConnection : AbstractCommonNHTest() {
private class MyException : Exception()
private lateinit var throwException: () -> Nothing
override val factory: NetworkHandlerFactory<TestNettyNH> =
NetworkHandlerFactory<TestNettyNH> { context, address ->
object : TestNettyNH(bot, context, address) {
override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel =
override val factory: NetworkHandlerFactory<TestCommonNetworkHandler> =
NetworkHandlerFactory<TestCommonNetworkHandler> { context, address ->
object : TestCommonNetworkHandler(bot, context, address) {
override suspend fun createConnection(): PlatformConn {
throwException()
}
}
}

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.handler
import kotlinx.coroutines.CoroutineScope
@ -14,26 +16,26 @@ import kotlinx.coroutines.Job
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.HeartbeatFailureHandler
import net.mamoe.mirai.internal.network.components.HeartbeatScheduler
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTestWithSelector
import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException
import net.mamoe.mirai.internal.network.impl.netty.NettyChannelException
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTestWithSelector
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
import net.mamoe.mirai.internal.test.runBlockingUnit
import org.junit.jupiter.api.AfterEach
import kotlin.test.*
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.Test
import kotlin.test.assertFails
/**
* Test whether the selector can recover the connection after first successful login.
*/
internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() {
@BeforeTest
fun beforeTest(info: TestInfo) {
println("=".repeat(30) + "BEGIN: ${info.displayName}" + "=".repeat(30))
}
@AfterTest
fun afterTest(info: TestInfo) {
println("=".repeat(31) + "END: ${info.displayName}" + "=".repeat(31))
}
internal class SelectorRecoveryTest : AbstractCommonNHTestWithSelector() {
// @BeforeTest
// fun beforeTest(info: TestInfo) {
// println("=".repeat(30) + "BEGIN: ${info.displayName}" + "=".repeat(30))
// }
//
// @AfterTest
// fun afterTest(info: TestInfo) {
// println("=".repeat(31) + "END: ${info.displayName}" + "=".repeat(31))
// }
@Test
fun `stop on manual close`() = runBlockingUnit {
@ -44,12 +46,11 @@ internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() {
/**
* Emulates system hibernation and network failure.
* @see HeartbeatFailedException
*/
@Test
fun `can recover on heartbeat failure with NettyChannelException`() = runBlockingUnit {
// We allow NetworkException to cause a reconnect.
testRecoverWhenHeartbeatFailWith { NettyChannelException("test IO ex") }
testRecoverWhenHeartbeatFailWith { NetworkException("test IO ex", true) }
bot.components[EventDispatcher].joinBroadcast() // Wait our async connector to complete.

View File

@ -7,21 +7,22 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
package net.mamoe.mirai.internal.network.impl.common
import net.mamoe.mirai.internal.network.components.AccountSecretsImpl
import net.mamoe.mirai.internal.network.components.AccountSecretsManager
import net.mamoe.mirai.internal.network.components.FileCacheAccountSecretsManager
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.internal.utils.accountSecretsFile
import net.mamoe.mirai.utils.DeviceInfo
import net.mamoe.mirai.utils.getRandomByteArray
import net.mamoe.mirai.utils.writeBytes
import kotlin.test.Test
import kotlin.test.assertEquals
internal class AccountSecretsTest : AbstractNettyNHTest() {
internal class AccountSecretsTest : AbstractCommonNHTest() {
@Test
fun `can login with no secrets`() = runBlockingUnit {
val file = bot.configuration.accountSecretsFile()

View File

@ -7,7 +7,9 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.impl.common
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
@ -15,16 +17,17 @@ import kotlinx.coroutines.isActive
import net.mamoe.mirai.internal.MockBot
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.*
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.supervisorJob
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.*
internal class NettyBotLifecycleTest : AbstractNettyNHTest() {
internal class BotLifecycleTest : AbstractCommonNHTest() {
// not allowed anymore

View File

@ -7,25 +7,26 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
package net.mamoe.mirai.internal.network.impl.common
import net.mamoe.mirai.internal.network.components.ServerList
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.TestNettyNH
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
internal class NettyAddressChangedTest : AbstractNettyNHTest() {
internal class CommonNHAddressChangedTest : AbstractCommonNHTest() {
@Test
fun `test login ip changes`() = runBlockingUnit {
networkLogger.debug("before login, Assuming both ip is empty")
val lastConnectedIpOld = bot.components[ServerList].lastConnectedIP
val lastDisconnectedIpOld = bot.components[ServerList].lastDisconnectedIP
assert(lastConnectedIpOld.isEmpty()) { "Assuming lastConnectedIp is empty" }
assert(lastDisconnectedIpOld.isEmpty()) { "Assuming lastDisconnectedIp is empty" }
assertTrue(lastConnectedIpOld.isEmpty(), "Assuming lastConnectedIp is empty")
assertTrue(lastDisconnectedIpOld.isEmpty(), "Assuming lastDisconnectedIp is empty")
networkLogger.debug("Do login, Assuming lastConnectedIp is NOT empty")
bot.login()
@ -37,7 +38,7 @@ internal class NettyAddressChangedTest : AbstractNettyNHTest() {
)
networkLogger.debug("Offline the bot, Assuming lastConnectedIp is equals lastDisconnectedIp")
(bot.network as TestNettyNH).setStateClosed()
(bot.network as TestCommonNetworkHandler).setStateClosed()
assertState(NetworkHandler.State.CLOSED)
assertEquals(
bot.components[ServerList].lastConnectedIP,

View File

@ -7,32 +7,32 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.impl.common
import io.ktor.utils.io.errors.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitorImpl
import net.mamoe.mirai.internal.network.components.FirstLoginResult
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.TestNettyNH
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler
import net.mamoe.mirai.internal.network.framework.setSsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.handler.selector.KeepAliveNetworkHandlerSelector
import net.mamoe.mirai.internal.network.handler.selector.NetworkChannelException
import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler
import net.mamoe.mirai.internal.network.handler.selectorLogger
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.network.CustomLoginFailedException
import net.mamoe.mirai.utils.TestOnly
import net.mamoe.mirai.utils.cast
import org.junit.jupiter.api.AfterEach
import java.io.IOException
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.*
internal class NettyBotNormalLoginTest : AbstractNettyNHTest() {
internal class CommonNHBotNormalLoginTest : AbstractCommonNHTest() {
init {
overrideComponents[BotOfflineEventMonitor] = BotOfflineEventMonitorImpl()
}
@ -41,7 +41,7 @@ internal class NettyBotNormalLoginTest : AbstractNettyNHTest() {
super.factory.create(createContext(), createAddress())
}
override val network: TestNettyNH
override val network: TestCommonNetworkHandler
get() = bot.network.cast<SelectorNetworkHandler<*>>().selector.getCurrentInstanceOrCreate().cast()
override fun createHandler(): NetworkHandler {
@ -50,7 +50,7 @@ internal class NettyBotNormalLoginTest : AbstractNettyNHTest() {
class CusLoginException(message: String?) : CustomLoginFailedException(true, message)
@AfterEach
@AfterTest
fun `close bot`() = runBlockingUnit {
bot.logger.info("[TEST UNIT] Releasing bot....")
bot.closeAndJoin()
@ -74,8 +74,8 @@ internal class NettyBotNormalLoginTest : AbstractNettyNHTest() {
// #1963
@Test
fun `test first login failure with internally handled exceptions2`() = runBlockingUnit {
setSsoProcessor { throw NettyChannelException("test Connection reset by peer") }
assertFailsWith<NettyChannelException>("test Connection reset by peer") { bot.login() }
setSsoProcessor { throw NetworkChannelException("test Connection reset by peer") }
assertFailsWith<NetworkChannelException>("test Connection reset by peer") { bot.login() }
assertState(NetworkHandler.State.CLOSED)
}

View File

@ -7,8 +7,11 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.impl.common
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.isActive
import net.mamoe.mirai.event.Event
@ -17,7 +20,7 @@ import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.event.events.BotReloginEvent
import net.mamoe.mirai.internal.network.components.FirstLoginResult
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.framework.eventDispatcher
import net.mamoe.mirai.internal.network.framework.setSsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.*
@ -25,15 +28,13 @@ import net.mamoe.mirai.internal.test.assertEventBroadcasts
import net.mamoe.mirai.internal.test.assertEventNotBroadcast
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.supervisorJob
import java.util.concurrent.atomic.AtomicReference
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.Test
import kotlin.test.TestInstance
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
internal class NettyHandlerEventTest : AbstractNettyNHTest() {
internal class CommonNHEventTest : AbstractCommonNHTest() {
@Test
fun `BotOnlineEvent after successful logon`() = runBlockingUnit {
assertEventBroadcasts<BotOnlineEvent> {
@ -94,24 +95,24 @@ internal class NettyHandlerEventTest : AbstractNettyNHTest() {
@Test
fun `from CONNECTING TO OK the second time`() = runBlockingUnit {
val ok = AtomicReference(CompletableDeferred<Unit>())
val ok = atomic(CompletableDeferred<Unit>())
setSsoProcessor {
ok.get().join()
ok.value.join()
}
assertState(INITIALIZED)
network.setStateConnecting()
ok.get().complete(Unit)
ok.value.complete(Unit)
network.resumeConnection()
assertState(OK)
ok.set(CompletableDeferred())
ok.value = CompletableDeferred()
network.setStateConnecting()
eventDispatcher.joinBroadcast()
println("Starting receiving events")
assertEventBroadcasts<Event>(2) {
ok.get().complete(Unit)
ok.value.complete(Unit)
network.resumeConnection()
eventDispatcher.joinBroadcast()
}.let { event ->

View File

@ -10,13 +10,13 @@
package net.mamoe.mirai.internal.network.impl.netty
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.Test
import kotlin.test.assertFailsWith
internal class NettyResumeConnectionTest : AbstractNettyNHTest() {
internal class ResumeConnectionTest : AbstractCommonNHTest() {
private val packet = OutgoingPacket("", "", 1, ByteReadPacket.Empty)
@ -43,7 +43,7 @@ internal class NettyResumeConnectionTest : AbstractNettyNHTest() {
@Test
fun `resumeConnection switches a state that can send packet on LOADING`() = runBlockingUnit {
network.setStateLoading(channel)
network.setStateLoading(conn)
network.resumeConnection()
network.sendWithoutExpect(packet)
}

View File

@ -7,44 +7,25 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
package net.mamoe.mirai.internal.network.impl.common
import io.ktor.utils.io.core.*
import io.netty.channel.Channel
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.TestNettyNH
import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext
import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.test.runBlockingUnit
import java.net.SocketAddress
import java.util.concurrent.Executors
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
internal class NettySendPacketTest : AbstractNettyNHTest() {
override val factory: NetworkHandlerFactory<TestNettyNH> = object : NetworkHandlerFactory<TestNettyNH> {
override fun create(context: NetworkHandlerContext, address: SocketAddress): TestNettyNH {
return object : TestNettyNH(bot, context, address) {
override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel =
channel.apply {
doRegister() // restart channel
setupChannelPipeline(pipeline(), decodePipeline)
}
}
}
}
internal class SendPacketTest : AbstractCommonNHTest() {
// single thread so we can use [yield] to transfer dispatch
private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val singleThreadDispatcher = borrowSingleThreadDispatcher()
@Test
fun `sendPacketImpl suspends until a valid state`() = runBlockingUnit(singleThreadDispatcher) {
@ -55,7 +36,7 @@ internal class NettySendPacketTest : AbstractNettyNHTest() {
assertNotNull(network.sendAndExpect(OutgoingPacket("name", "cmd", 1, ByteReadPacket.Empty)))
assertTrue { expectStop.value }
}
network.setStateOK(channel) // then we can send packet.
network.setStateOK(conn) // then we can send packet.
yield() // yields the thread to run `sendAndExpect`
// when we got thread here again, `sendAndExpect` is suspending for response [Packet].
@ -66,7 +47,7 @@ internal class NettySendPacketTest : AbstractNettyNHTest() {
@Test
fun `sendPacketImpl does not suspend if state is valid`() = runBlockingUnit(singleThreadDispatcher) {
network.setStateOK(channel) // then we can send packet.
network.setStateOK(conn) // then we can send packet.
val expectStop = atomic(false)
val job = launch(singleThreadDispatcher, start = CoroutineStart.UNDISPATCHED) {

View File

@ -7,20 +7,23 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.network.impl.netty
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.network.impl.common
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.internal.AbstractBot
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.*
import net.mamoe.mirai.internal.test.assertEventBroadcasts
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.utils.TestOnly
import kotlin.test.*
internal class SetStateTest : AbstractNettyNHTest() {
internal class SetStateTest : AbstractCommonNHTest() {
@Test
fun `setState should ignore duplications INITIALIZED to CLOSED to CLOSED`() {
assertState(INITIALIZED)
@ -32,7 +35,7 @@ internal class SetStateTest : AbstractNettyNHTest() {
@Test
fun `setState should ignore duplications OK to CLOSED to CLOSED`() {
assertNotNull(network.setStateOK(channel))
assertNotNull(network.setStateOK(conn))
assertState(OK)
assertNotNull(network.setStateClosed(IllegalStateException("1")))
assertState(CLOSED)
@ -46,7 +49,7 @@ internal class SetStateTest : AbstractNettyNHTest() {
override fun attachJob(bot: AbstractBot, scope: CoroutineScope) {
}
}
assertNotNull(network.setStateOK(channel))
assertNotNull(network.setStateOK(conn))
assertState(OK)
assertEventBroadcasts<Event> {
assertNotNull(network.setStateClosed(IllegalStateException("1")))
@ -62,7 +65,7 @@ internal class SetStateTest : AbstractNettyNHTest() {
@Test
fun `Precondition - setState should ignore duplications 2 OK to CLOSED to CLOSED`() = runBlockingUnit {
assertNotNull(network.setStateOK(channel))
assertNotNull(network.setStateOK(conn))
assertState(OK)
assertEventBroadcasts<Event> {
assertNotNull(network.setStateClosed(IllegalStateException("1")))

View File

@ -27,7 +27,7 @@ import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.components.*
import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.noticeProcessorPipeline
import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest
import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.utils.io.JceStruct
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
@ -38,7 +38,7 @@ import net.mamoe.mirai.utils.*
/**
* To add breakpoint, see [NoticeProcessorPipelineImpl.process]
*/
internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), GroupExtensions {
internal abstract class AbstractNoticeProcessorTest : AbstractCommonNHTest(), GroupExtensions {
init {
setSystemProp("mirai.network.notice.pipeline.log.full", "true")
}

View File

@ -14,6 +14,7 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent
import net.mamoe.mirai.event.events.MemberPermissionChangeEvent
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
@ -22,7 +23,7 @@ import kotlin.test.assertIs
internal class GroupTransferTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `owner transfers group to other member`() {
fun `owner transfers group to other member`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,
@ -63,7 +64,7 @@ internal class GroupTransferTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `owner transfers group to bot`() {
fun `owner transfers group to bot`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,

View File

@ -15,6 +15,8 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent
import net.mamoe.mirai.event.events.MemberPermissionChangeEvent
import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
@ -26,9 +28,9 @@ import kotlin.test.assertIs
internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `bot member to admin`() {
fun `bot member to admin`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,
toUin = 1230003,
msgType = 44,
@ -74,7 +76,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `bot admin to member`() {
fun `bot admin to member`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,
@ -119,7 +121,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member member to admin`() {
fun `member member to admin`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,
@ -168,7 +170,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member admin to member`() {
fun `member admin to member`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,

View File

@ -15,20 +15,22 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.MemberJoinEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.*
internal class MemberJoinTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `member actively request join`() {
fun `member actively request join`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.StructMsg(
Structmsg.StructMsg(
version = 1,
msgType = 2,
msgSeq = 16300,
msgTime = 1630,
reqUin = 1230001,
msg = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsg(
msg = Structmsg.SystemMsg(
subType = 1,
msgTitle = "加群申请",
msgDescribe = "申请加入 %group_name%",
@ -36,26 +38,26 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() {
srcId = 1,
subSrcId = 5,
actions = mutableListOf(
net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
Structmsg.SystemMsgAction(
name = "拒绝",
result = "已拒绝",
actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
actionInfo = Structmsg.SystemMsgActionInfo(
type = 12,
groupCode = 2230203,
),
detailName = "拒绝",
), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
), Structmsg.SystemMsgAction(
name = "同意",
result = "已同意",
actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
actionInfo = Structmsg.SystemMsgActionInfo(
type = 11,
groupCode = 2230203,
),
detailName = "同意",
), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction(
), Structmsg.SystemMsgAction(
name = "忽略",
result = "已忽略",
actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo(
actionInfo = Structmsg.SystemMsgActionInfo(
type = 14,
groupCode = 2230203,
),
@ -64,7 +66,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() {
),
groupCode = 2230203,
groupMsgType = 1,
groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.GroupInfo(
groupInfo = Structmsg.GroupInfo(
appPrivilegeFlag = 67698880,
),
groupFlagext3 = 128,
@ -96,7 +98,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member request accepted by other admin`() {
fun `member request accepted by other admin`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
@ -143,7 +145,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member request accepted by bot as admin`() {
fun `member request accepted by bot as admin`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
@ -191,7 +193,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `member joins directly when group allows anyone`() {
fun `member joins directly when group allows anyone`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(

View File

@ -14,6 +14,8 @@ package net.mamoe.mirai.internal.notice.processors
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.MemberLeaveEvent
import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans
import net.mamoe.mirai.internal.test.runBlockingUnit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
@ -21,9 +23,9 @@ import kotlin.test.assertIs
internal class MemberQuitTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `member active quit`() {
fun `member active quit`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,
toUin = 1230003,
msgType = 34,
@ -33,7 +35,7 @@ internal class MemberQuitTest : AbstractNoticeProcessorTest() {
realMsgTime = 1629,
msgData = "00 22 07 BB 01 00 12 C4 B1 02 00 30 39 41 36 36 41 32 31 32 33 35 37 32 43 39 35 38 42 42 36 38 45 32 36 44 34 34 32 38 45 32 32 37 32 36 44 39 44 45 41 31 34 41 44 37 30 31 46 31".hexToBytes(),
svrIp = 618,
extGroupKeyInfo = net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.ExtGroupKeyInfo(
extGroupKeyInfo = OnlinePushTrans.ExtGroupKeyInfo(
curMaxSeq = 1626,
curTime = 16298,
),
@ -58,7 +60,7 @@ internal class MemberQuitTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member kick`() {
fun `member kick`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo(
fromUin = 2230203,

View File

@ -9,11 +9,16 @@
package net.mamoe.mirai.internal.notice.processors
import io.ktor.utils.io.core.*
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.FriendMessageSyncEvent
import net.mamoe.mirai.event.events.GroupMessageSyncEvent
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.content
import kotlin.test.Test
import kotlin.test.assertEquals
@ -23,18 +28,18 @@ import kotlin.test.assertIs
internal class MessageSyncTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `can receive group sync from macOS client`() {
fun `can receive group sync from macOS client`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush.PbPushMsg(
msg = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
MsgOnlinePush.PbPushMsg(
msg = MsgComm.Msg(
msgHead = MsgComm.MsgHead(
fromUin = 1230002,
toUin = 1230002,
msgType = 82,
msgSeq = 1772,
msgTime = 1640029614,
msgUid = 144115188088832082,
groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.GroupInfo(
groupInfo = MsgComm.GroupInfo(
groupCode = 2230203,
groupType = 1,
groupInfoSeq = 657,
@ -47,9 +52,9 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() {
fromInstid = 537067835,
userActive = 1,
),
msgBody = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MsgBody(
richText = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichText(
attr = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Attr(
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
attr = ImMsgBody.Attr(
codePage = 0,
time = 1640029614,
random = 25984994,
@ -60,23 +65,23 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() {
fontName = "Helvetica",
),
elems = mutableListOf(
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text(
ImMsgBody.Elem(
text = ImMsgBody.Text(
str = "s",
),
),
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
elemFlags2 = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.ElemFlags2(
ImMsgBody.Elem(
elemFlags2 = ImMsgBody.ElemFlags2(
msgRptCnt = 1,
),
),
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags(
ImMsgBody.Elem(
generalFlags = ImMsgBody.GeneralFlags(
pbReserve = "".hexToBytes(),
),
),
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem(
extraInfo = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.ExtraInfo(
ImMsgBody.Elem(
extraInfo = ImMsgBody.ExtraInfo(
nick = "user2",
level = 1,
groupMask = 1,
@ -112,7 +117,7 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `can receive friend sync from macOS client`() {
fun `can receive friend sync from macOS client`() = runBlockingUnit {
suspend fun runTest() = use {
attributes[KEY_FROM_SYNC] = true

View File

@ -11,12 +11,14 @@
package net.mamoe.mirai.internal.notice.processors
import io.ktor.utils.io.core.*
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.events.GroupTempMessageEvent
import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.PlainText
@ -29,7 +31,7 @@ import kotlin.test.assertIs
internal class MessageTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `group message test`() {
fun `group message test`() = runBlockingUnit {
suspend fun runTest() = use {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush.PbPushMsg(
msg = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
@ -132,7 +134,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `friend message test`() {
fun `friend message test`() = runBlockingUnit {
suspend fun runTest() = use(KEY_FROM_SYNC to false) {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
@ -212,7 +214,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `group temp message test`() {
fun `group temp message test`() = runBlockingUnit {
suspend fun runTest() = use(KEY_FROM_SYNC to false) {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(
@ -306,7 +308,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() {
// for #1410
@Test
suspend fun `group temp message test for issue 1410`() {
fun `group temp message test for issue 1410`() = runBlockingUnit {
suspend fun runTest() = use(KEY_FROM_SYNC to false) {
net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg(
msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead(

View File

@ -20,6 +20,7 @@ import net.mamoe.mirai.event.events.MemberUnmuteEvent
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo
import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack
import net.mamoe.mirai.internal.network.protocol.data.jce.ShareData
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.utils.currentTimeSeconds
import kotlin.test.Test
import kotlin.test.assertEquals
@ -28,7 +29,7 @@ import kotlin.test.assertIs
internal class MuteTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `bot mute`() {
fun `bot mute`() = runBlockingUnit {
suspend fun MuteTest.runTest() = use {
OnlinePushPack.SvcReqPushMsg(
uin = 1230001,
@ -79,7 +80,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `bot unmute`() {
fun `bot unmute`() = runBlockingUnit {
suspend fun MuteTest.runTest() = use {
OnlinePushPack.SvcReqPushMsg(
uin = 1230001,
@ -146,7 +147,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `member mute`() {
fun `member mute`() = runBlockingUnit {
suspend fun MuteTest.runTest() = use {
OnlinePushPack.SvcReqPushMsg(
uin = 1230001,
@ -198,7 +199,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() {
@Test
suspend fun `member unmute`() {
fun `member unmute`() = runBlockingUnit {
suspend fun MuteTest.runTest() = use {
OnlinePushPack.SvcReqPushMsg(
uin = 1230001,

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.internal.notice.processors
import io.ktor.utils.io.core.*
import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.MemberPermission
@ -16,10 +17,11 @@ import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.internal.message.source.OnlineMessageSourceFromGroupImpl
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.utils.hexToBytes
import org.junit.jupiter.api.assertFailsWith
import kotlin.test.Test
import kotlin.test.assertFailsWith
internal class RecallTest : AbstractNoticeProcessorTest() {
@ -91,7 +93,7 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
)
@Test
suspend fun `recall member message without permission`() {
fun `recall member message without permission`() = runBlockingUnit {
val bot = setBot(2)
val group = bot.addGroup(5, 3, MemberPermission.MEMBER).apply {
// owner
@ -105,7 +107,7 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `recall member message`() {
fun `recall member message`() = runBlockingUnit {
val bot = setBot(2)
val group = bot.addGroup(5, 3, MemberPermission.ADMINISTRATOR).apply {
// owner
@ -117,7 +119,7 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `recall administrator message`() {
fun `recall administrator message`() = runBlockingUnit {
val bot = setBot(2)
val group = bot.addGroup(5, 3, MemberPermission.ADMINISTRATOR).apply {
// owner
@ -129,9 +131,9 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
Mirai.recallMessage(bot, source(bot, 1, group.id, group.botPermission))
}
}
@Test
suspend fun `recall administrator message as owner`() {
fun `recall administrator message as owner`() = runBlockingUnit {
val bot = setBot(2)
val group = bot.addGroup(5, 2, MemberPermission.OWNER).apply {
// sender
@ -141,7 +143,7 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
}
@Test
suspend fun `recall owner message`() {
fun `recall owner message`() = runBlockingUnit {
val bot = setBot(2)
val group = bot.addGroup(5, 1, MemberPermission.ADMINISTRATOR).apply {
// sender

View File

@ -12,8 +12,8 @@ package net.mamoe.mirai.internal.test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.event.GlobalEventChannel
import net.mamoe.mirai.utils.ConcurrentLinkedQueue
import net.mamoe.mirai.utils.cast
import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.test.assertEquals

View File

@ -9,48 +9,36 @@
package net.mamoe.mirai.internal.test
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger
import net.mamoe.mirai.utils.MiraiLogger
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Timeout
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.*
import kotlin.test.AfterTest
import kotlin.test.Test
internal expect fun initPlatform()
@Suppress("UnnecessaryOptInAnnotation") // on JVM
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
internal abstract class CommonAbstractTest {
private val dispatchers = mutableListOf<CloseableCoroutineDispatcher>()
fun borrowSingleThreadDispatcher(): CoroutineDispatcher {
return newSingleThreadContext(this::class.simpleName ?: "CommonAbstractTest")
}
@AfterTest
fun closeAllDispatchers() {
for (dispatcher in dispatchers) {
dispatcher.close()
}
}
}
/**
* All test classes should inherit from [AbstractTest]
*/
@Timeout(value = 7, unit = TimeUnit.MINUTES)
abstract class AbstractTest {
init {
initPlatform()
internal expect abstract class AbstractTest() : CommonAbstractTest {
restoreLoggerFactory()
System.setProperty("mirai.network.packet.logger", "true")
System.setProperty("mirai.network.state.observer.logging", "true")
System.setProperty("mirai.network.show.all.components", "true")
System.setProperty("mirai.network.show.components.creation.stacktrace", "true")
System.setProperty("mirai.network.handle.selector.logging", "true")
}
@AfterEach
protected fun restoreLoggerFactory() {
@Suppress("DEPRECATION_ERROR")
MiraiLogger.setDefaultLoggerCreator {
SynchronizedStdoutLogger(it)
}
}
companion object {
init {
Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately.
IMirai::class.simpleName // similarly, load classes.
}
}
companion object
}
internal expect class PlatformInitializationTest() : AbstractTest {

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.internal.utils.io.serialization.tars.internal
import io.ktor.utils.io.core.*
import kotlinx.serialization.Serializable
import net.mamoe.mirai.internal.test.AbstractTest
import net.mamoe.mirai.internal.utils.io.JceStruct
@ -18,13 +19,11 @@ import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.utils.toReadPacket
import net.mamoe.mirai.utils.toUHexString
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFails
class DebugLoggerTest : AbstractTest() {
internal class DebugLoggerTest : AbstractTest() {
fun String.uniteLine(): String = replace("\r\n", "\n").replace("\r", "\n")
@ -36,8 +35,8 @@ class DebugLoggerTest : AbstractTest() {
@Test
fun `can log`() {
val out = ByteArrayOutputStream()
val logger = DebugLogger(PrintStream(out))
val out = BytePacketBuilder()
val logger = DebugLogger(out)
val original = Struct("string", 1)
val bytes = original.toByteArray(Struct.serializer())
val value = bytes.toReadPacket().use { Tars.UTF_8.load(Struct.serializer(), it, logger) }
@ -51,7 +50,7 @@ class DebugLoggerTest : AbstractTest() {
name=int
decodeElementIndex: currentHead == null
endStructure: net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct, null, null
""".trimIndent(), out.toByteArray().decodeToString().trim().uniteLine()
""".trimIndent(), out.build().readBytes().decodeToString().trim().uniteLine()
)
}

View File

@ -9,9 +9,9 @@
package net.mamoe.mirai.internal.network.impl.netty
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
import net.mamoe.mirai.internal.network.handler.selector.NetworkChannelException
internal data class NettyChannelException(
override val message: String? = null,
override val cause: Throwable? = null,
) : NetworkException(true)
) : NetworkChannelException()

View File

@ -0,0 +1,60 @@
/*
* 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.internal.network.framework
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory
import kotlin.test.AfterTest
/**
* Without selector. When network is closed, it will not reconnect, so that you can check for its states.
*
* @see AbstractCommonNHTestWithSelector
*/
internal actual abstract class AbstractCommonNHTest actual constructor() :
AbstractRealNetworkHandlerTest<TestCommonNetworkHandler>() {
actual override val network: TestCommonNetworkHandler by lazy {
factory.create(createContext(), createAddress())
}
private val startedDispatchers = mutableListOf<ExecutorCoroutineDispatcher>()
@AfterTest
fun cleanupDispatchers() {
startedDispatchers.forEach { it.close() }
}
actual override val factory: NetworkHandlerFactory<TestCommonNetworkHandler> =
NetworkHandlerFactory<TestCommonNetworkHandler> { context, address ->
object : TestCommonNetworkHandler(bot, context, address) {
override suspend fun createConnection(): PlatformConn {
return conn.apply {
doRegister() // restart channel
// setupChannelPipeline(
// pipeline(), PacketDecodePipeline(
// coroutineContext.plus(
// NioEventLoopGroup().asCoroutineDispatcher().also { startedDispatchers.add(it) })
// )
// )
}
}
}
}
protected actual fun removeOutgoingPacketEncoder() {
kotlin.runCatching {
conn.pipeline().remove("outgoing-packet-encoder")
}
}
actual val conn: PlatformConn = NettyNHTestChannel()
}
internal actual typealias PlatformConn = NettyNHTestChannel

View File

@ -0,0 +1,14 @@
/*
* 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.internal.network.framework
internal abstract class AbstractNettyNHTest : AbstractCommonNHTest() {
}

View File

@ -0,0 +1,94 @@
/*
* 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.internal.network.framework
import io.ktor.utils.io.core.*
import io.netty.channel.embedded.EmbeddedChannel
import io.netty.util.ReferenceCountUtil
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.serializer
import net.mamoe.mirai.internal.network.components.RawIncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.utils.io.ProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.error
internal class NettyNHTestChannel(
var fakeServer: (NettyNHTestChannel.(msg: Any?) -> Unit)?,
) : EmbeddedChannel() {
constructor() : this(null)
@OptIn(InternalSerializationApi::class)
fun listen(listener: (OutgoingPacket) -> Any?) {
fakeServer = { packet ->
if (packet is OutgoingPacket) {
val rsp0 = when (val rsp = listener(packet)) {
null -> null
is Unit -> null
is ByteArray -> {
RawIncomingPacket(
commandName = packet.commandName,
sequenceId = packet.sequenceId,
body = rsp
)
}
is RawIncomingPacket -> rsp
is ProtoBuf -> {
RawIncomingPacket(
commandName = packet.commandName,
sequenceId = packet.sequenceId,
body = buildPacket {
writeProtoBuf(
rsp::class.serializer().cast<KSerializer<ProtoBuf>>(),
rsp
)
}.readBytes()
)
}
else -> {
logger.error { "Failed to respond $rsp" }
null
}
}
if (rsp0 != null) {
pipeline().fireChannelRead(rsp0)
}
}
ReferenceCountUtil.release(packet)
}
}
public /*internal*/ override fun doRegister() {
super.doRegister() // Set channel state to ACTIVE
// Drop old handlers
pipeline().let { p ->
while (p.first() != null) {
p.removeFirst()
}
}
}
override fun handleInboundMessage(msg: Any?) {
ReferenceCountUtil.release(msg) // Not handled, Drop
}
override fun handleOutboundMessage(msg: Any?) {
fakeServer?.invoke(this, msg) ?: ReferenceCountUtil.release(msg)
}
companion object {
private val logger by lazy {
MiraiLogger.Factory.create(NettyNHTestChannel::class)
}
}
}

View File

@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds
/**
* @see awaitKt
*/
internal class NettyUtilsTest : AbstractTest() {
internal class CommonNHUtilsTest : AbstractTest() {
companion object {
private val channel = EmbeddedChannel()

View File

@ -0,0 +1,40 @@
/*
* 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.internal.test
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.setSystemProp
import org.junit.jupiter.api.Timeout
import java.util.concurrent.TimeUnit
@Timeout(value = 7, unit = TimeUnit.MINUTES)
internal actual abstract class AbstractTest actual constructor() : CommonAbstractTest() {
actual companion object {
init {
initPlatform()
@Suppress("DEPRECATION_ERROR")
MiraiLogger.setDefaultLoggerCreator {
SynchronizedStdoutLogger(it)
}
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.handle.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.
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -25,7 +25,11 @@ import net.mamoe.mirai.internal.utils.io.ProtocolStruct
import net.mamoe.mirai.utils.*
import net.mamoe.yamlkt.Yaml
import net.mamoe.yamlkt.YamlBuilder
import java.io.File
import java.net.URL
import kotlin.reflect.KType
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.typeOf
private val logger: MiraiLogger by lazy { MiraiLogger.Factory.create(Desensitizer::class) }
@ -91,7 +95,6 @@ internal class Desensitizer private constructor(
.renderToString()
}
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> ValueDescAnalyzer.generateAndDesensitize(
value: T,
desensitizer: Desensitizer = instance,
@ -116,7 +119,6 @@ internal class Desensitizer private constructor(
}
else -> {
map.getOrPut(value.toByteArray().toUHexString()) { replacement.toByteArray().toUHexString() }
map.getOrDefault()
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -7,13 +7,13 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.internal.notice.test
package net.mamoe.mirai.internal.testFramework.test
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
import net.mamoe.mirai.internal.test.AbstractTest
import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
import net.mamoe.yamlkt.Yaml
import net.mamoe.yamlkt.YamlBuilder

View File

@ -12,9 +12,12 @@ package net.mamoe.mirai.internal.utils
import kotlinx.serialization.Transient
import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer
import net.mamoe.mirai.utils.toUHexString
import java.lang.reflect.Modifier
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.jvm.javaField
internal class StructureToStringTransformerLegacy : StructureToStringTransformer {
override fun transform(any: Any?): String = any._miraiContentToString()

View File

@ -0,0 +1,35 @@
/*
* 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.internal.network.framework
import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory
/**
* Without selector. When network is closed, it will not reconnect, so that you can check for its states.
*
* @see AbstractCommonNHTestWithSelector
*/
internal actual abstract class AbstractCommonNHTest actual constructor() :
AbstractRealNetworkHandlerTest<TestCommonNetworkHandler>() {
actual override val network: TestCommonNetworkHandler
get() = TODO("Not yet implemented")
actual override val factory: NetworkHandlerFactory<TestCommonNetworkHandler>
get() = TODO("Not yet implemented")
protected actual fun removeOutgoingPacketEncoder() {
}
actual val conn: PlatformConn
get() = TODO("Not yet implemented")
}
internal actual class PlatformConn

View File

@ -0,0 +1,10 @@
/*
* 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.internal

View File

@ -0,0 +1,53 @@
/*
* 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.internal.test
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.setSystemProp
import kotlin.test.Test
internal actual fun initPlatform() {
}
internal actual class PlatformInitializationTest actual constructor() : AbstractTest() {
@Test
actual fun test() {
}
}
/**
* All test classes should inherit from [AbstractTest]
*/
internal actual abstract class AbstractTest actual constructor() : CommonAbstractTest() {
actual companion object {
init {
initPlatform()
@Suppress("DEPRECATION_ERROR")
MiraiLogger.setDefaultLoggerCreator {
SynchronizedStdoutLogger(it)
}
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.handle.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.
}
}
}