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

Fix jvmBase bugs caused by migration to common

This commit is contained in:
Him188 2022-05-25 20:01:51 +01:00
parent 72c108bc54
commit c9d343db89
No known key found for this signature in database
GPG Key ID: BA439CDDCF652375
25 changed files with 332 additions and 111 deletions
mirai-core-api/src
commonMain/kotlin/message/data
jvmBaseMain/kotlin/utils
jvmBaseTest/kotlin/message/data
jvmTest/kotlin/message/data
nativeMain/kotlin/utils
mirai-core-utils/src
commonTest/kotlin/net/mamoe/mirai/utils
jvmBaseTest/kotlin
mirai-core/src

View File

@ -19,8 +19,8 @@ import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1
import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2
import net.mamoe.mirai.message.data.visitor.MessageVisitor
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.asImmutable
import net.mamoe.mirai.utils.castOrNull
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.replaceAllKotlin
import kotlin.jvm.JvmField
import kotlin.jvm.JvmMultifileClass
@ -131,6 +131,9 @@ internal annotation class MessageChainConstructor
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(MessageChain.Serializer::class)
internal class LinearMessageChainImpl @MessageChainConstructor private constructor(
/**
* Must be guaranteed to be immutable
*/
@JvmField
internal val delegate: List<SingleMessage>,
override val hasConstrainSingle: Boolean
@ -212,7 +215,7 @@ internal class LinearMessageChainImpl @MessageChainConstructor private construct
return if (delegate.isEmpty()) {
emptyMessageChain()
} else {
LinearMessageChainImpl(delegate, hasConstrainSingle)
LinearMessageChainImpl(delegate.asImmutable(), hasConstrainSingle)
}
}

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.utils
import io.ktor.utils.io.core.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.Json
@ -50,13 +49,12 @@ public actual class DeviceInfo actual constructor(
@MiraiInternalApi
public actual val guid: ByteArray = generateGuid(androidId, macAddress)
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") // serializable
@Serializable
public actual class Version actual constructor(
public actual val incremental: ByteArray = "5891938".toByteArray(),
public actual val release: ByteArray = "10".toByteArray(),
public actual val codename: ByteArray = "REL".toByteArray(),
public actual val sdk: Int = 29
public actual val incremental: ByteArray,
public actual val release: ByteArray,
public actual val codename: ByteArray,
public actual val sdk: Int
) {
/**
* @since 2.9

View File

@ -13,30 +13,30 @@ package net.mamoe.mirai.message.data
import kotlin.test.Test
import kotlin.test.assertFails
import java.util.List as JdkList
internal open class MessageChainImmutableTest {
private fun msg0(): MessageChain = messageChainOf(
AtAll, PlainText("Hello!"), At(114514),
)
fun msgAsJdk(): JdkList<SingleMessage> {
return msg0() as java.util.List<SingleMessage>
@Test
fun `LinearMessageChainImpl is immutable`() {
runCheck(
messageChainOf(
AtAll, PlainText("Hello!"), At(114514),
) as java.util.List<SingleMessage>
)
}
@Test
fun `direct access`() {
val chain = msgAsJdk()
fun `CombinedMessage is immutable`() {
runCheck(
(AtAll + PlainText("Hello!")) as java.util.List<SingleMessage>,
)
}
private fun runCheck(chain: java.util.List<SingleMessage>) {
assertFails { chain.set(0, AtAll) }
assertFails { chain.remove(0) }
assertFails { chain.clear() }
assertFails { chain.add(PlainText("Hey Hey!")) }
}
@Test
fun `iterator access`() {
val chain = msgAsJdk()
assertFails { chain.iterator().remove() }
assertFails { chain.iterator().also { it.next() }.remove() }
assertFails { chain.listIterator().remove() }

View File

@ -14,10 +14,10 @@ package net.mamoe.mirai.message.data
import kotlin.test.Test
import kotlin.test.assertFails
internal class MessageChainImmutableTest_JDK8 : MessageChainImmutableTest() {
internal class MessageChainImmutableTestJdk8 : MessageChainImmutableTest() {
@Test
fun `access with JDK8 lambda`() {
val chain = msgAsJdk()
val chain = messageChainOf(AtAll, PlainText("Hello!"), At(114514)) as java.util.List<SingleMessage>
assertFails { chain.removeIf { true } }
assertFails { chain.replaceAll { AtAll } }
assertFails { chain.sort { o1, o2 -> o1.javaClass.name.compareTo(o2.javaClass.name) } }

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.utils
import io.ktor.utils.io.core.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlin.random.Random
@ -48,13 +47,12 @@ public actual class DeviceInfo actual constructor(
@MiraiInternalApi
public actual val guid: ByteArray = generateGuid(androidId, macAddress)
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") // serializable
@Serializable
public actual class Version actual constructor(
public actual val incremental: ByteArray = "5891938".toByteArray(),
public actual val release: ByteArray = "10".toByteArray(),
public actual val codename: ByteArray = "REL".toByteArray(),
public actual val sdk: Int = 29
public actual val incremental: ByteArray,
public actual val release: ByteArray,
public actual val codename: ByteArray,
public actual val sdk: Int
) {
/**
* @since 2.9

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.utils
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@ -50,38 +49,6 @@ internal class LateinitMutablePropertyTest {
assertEquals(1, counter)
}
@Test
fun initializerCalledOnceConcurrent() = runBlocking {
val value = Symbol("expected")
val counter = atomic(0)
val verySlowInitializer = CompletableDeferred<Unit>()
val prop by lateinitMutableProperty {
counter.incrementAndGet()
runBlocking { yield(); verySlowInitializer.await() }
value
}
val lock = CompletableDeferred<Unit>()
repeat(10) {
launch {
lock.join()
@Suppress("UNUSED_EXPRESSION")
prop
}
}
lock.complete(Unit) // resume callers
verySlowInitializer.complete(Unit)
assertSame(value, prop)
assertEquals(1, counter.value)
}
@Test
fun setValuePrevailsOnCompetitionWithInitializer() = runBlocking {
val verySlowInitializer = CompletableDeferred<Unit>()

View File

@ -0,0 +1,55 @@
/*
* 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.coroutines.CompletableDeferred
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
import org.junit.jupiter.api.Test
import java.util.concurrent.CompletableFuture
import kotlin.concurrent.thread
import kotlin.test.assertEquals
import kotlin.test.assertSame
internal class LateinitMutablePropertyTestJvm {
@Test
fun `initializer called once if requested by multiple threads`() = runBlocking {
val value = Symbol("expected")
var counter = 0
val verySlowInitializer = CompletableDeferred<Unit>()
val prop by lateinitMutableProperty {
counter++
runBlocking { yield(); verySlowInitializer.await() }
value
}
// requested by 10 threads
val lock = CompletableFuture<Unit>()
repeat(10) {
thread {
lock.join()
@Suppress("UNUSED_EXPRESSION")
prop
}
}
lock.complete(Unit) // resume callers
verySlowInitializer.complete(Unit)
assertSame(value, prop)
assertEquals(1, counter)
}
}

View File

@ -145,6 +145,8 @@ internal abstract class CommonNetworkHandler<Conn>(
this.setState { StateConnecting(ExceptionCollector()) }
?.resumeConnection()
?: this@CommonNetworkHandler.resumeConnection() // concurrently closed by other thread.
println("INITIALIZED RETURN")
}
override fun toString(): String = "StateInitialized"
@ -212,6 +214,9 @@ internal abstract class CommonNetworkHandler<Conn>(
connectResult.await() // propagates exceptions
val connection = connection.await()
this.setState { StateLoading(connection) }
.also {
println(" this.setState { StateLoading(connection) }: " + it)
}
?.resumeConnection()
?: this@CommonNetworkHandler.resumeConnection() // concurrently closed by other thread.
}

View File

@ -9,24 +9,21 @@
package net.mamoe.mirai.internal.utils
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import kotlin.jvm.Volatile
internal class SingleEntrantLock {
@Volatile
@PublishedApi
internal var locker: Any? = null
private val lock = SynchronizedObject()
internal class SingleEntrantLock : SynchronizedObject() {
private val locker: AtomicRef<Any?> = atomic(null)
inline fun <R> withLock(locker: Any, crossinline block: () -> R): R? {
return synchronized(lock) {
if (this.locker === locker) return null
this.locker = locker
return synchronized(this) {
if (this.locker.value === locker) return@synchronized null
this.locker.value = locker
block().also {
this.locker = null
this.locker.value = null
}
}
}

View File

@ -78,7 +78,7 @@ internal class ImageReadingTest : AbstractTest() {
ImageType.JPG
)
}
assertFailsWith(IOException::class) {
assertFailsWith(IllegalStateException::class) {
"FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF E1 00 5A".testMatch(
ImageType.JPG
)

View File

@ -41,7 +41,6 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() {
}
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `test whileSelect onStateChanged on demand`() = runBlockingUnit(singleThreadDispatcher + Job()) {
createNetworkHandler().run {
@ -81,7 +80,6 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() {
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `test whileSelect onStateChanged drop if not listening`() = runBlockingUnit(singleThreadDispatcher + Job()) {
createNetworkHandler().run {

View File

@ -11,7 +11,9 @@ package net.mamoe.mirai.internal.network.framework
import kotlinx.coroutines.CompletableDeferred
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.handler.*
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.utils.ExceptionCollector
@ -28,6 +30,23 @@ internal abstract class TestCommonNetworkHandler(
}
override fun PlatformConn.writeAndFlushOrCloseAsync(packet: OutgoingPacket) {
for (packetReplier in packetRepliers) {
packetReplier.run {
object : PacketReplierContext {
override fun reply(incoming: IncomingPacket) {
collectReceived(incoming)
}
override fun reply(incoming: Packet) {
reply(IncomingPacket(packet.commandName, packet.sequenceId, incoming))
}
override fun reply(incoming: Throwable) {
reply(IncomingPacket(packet.commandName, packet.sequenceId, incoming))
}
}.onSend(packet)
}
}
}
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@ -51,6 +70,24 @@ internal abstract class TestCommonNetworkHandler(
return setState { StateLoading(conn) }
}
private val packetRepliers = mutableListOf<PacketReplier>()
fun addPacketReplier(packetReplier: PacketReplier) {
packetRepliers.add(packetReplier)
}
}
/**
* 应答器, 模拟服务器返回.
*/
internal fun interface PacketReplier {
fun PacketReplierContext.onSend(packet: OutgoingPacket)
}
internal interface PacketReplierContext {
fun reply(incoming: IncomingPacket)
fun reply(incoming: Packet)
fun reply(incoming: Throwable)
}
/**

View File

@ -19,7 +19,6 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
import net.mamoe.mirai.internal.network.handler.TestSelector
import net.mamoe.mirai.internal.network.handler.selector.NetworkHandlerSelector
import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler
import net.mamoe.mirai.utils.cast
/**
* When network is closed, it will reconnect, so that you test for real environment,
@ -45,7 +44,6 @@ internal abstract class AbstractCommonNHTestWithSelector :
override val factory: NetworkHandlerFactory<TestSelectorNetworkHandler> =
NetworkHandlerFactory { _, _ -> TestSelectorNetworkHandler(selector, bot) }
override val network: TestSelectorNetworkHandler get() = bot.network.cast()
}
internal class TestSelectorNetworkHandler(

View File

@ -17,10 +17,7 @@ import net.mamoe.mirai.internal.BotAccount
import net.mamoe.mirai.internal.MockBot
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.PacketLoggingStrategy
import net.mamoe.mirai.internal.network.components.PacketLoggingStrategyImpl
import net.mamoe.mirai.internal.network.components.SsoProcessor
import net.mamoe.mirai.internal.network.components.*
import net.mamoe.mirai.internal.network.framework.components.TestImagePatcher
import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor
import net.mamoe.mirai.internal.network.handler.NetworkHandler
@ -69,9 +66,10 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT
)
set(ImagePatcher, TestImagePatcher())
set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot))
set(AccountSecretsManager, MemoryAccountSecretsManager())
}
fun NetworkHandler.assertState(state: NetworkHandler.State) {
assertEquals(state, state)
assertEquals(this.state, state)
}
}

View File

@ -14,6 +14,11 @@ package net.mamoe.mirai.internal.network.framework
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.internal.*
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.KeyWithCreationTime
import net.mamoe.mirai.internal.network.KeyWithExpiry
import net.mamoe.mirai.internal.network.WLoginSigInfo
import net.mamoe.mirai.internal.network.WLoginSimpleInfo
import net.mamoe.mirai.internal.network.component.ComponentKey
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.component.setAll
@ -29,6 +34,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.utils.subLogger
import net.mamoe.mirai.utils.*
import network.framework.components.TestEventDispatcherImpl
import kotlin.random.Random
import kotlin.test.AfterTest
import kotlin.test.assertEquals
@ -39,10 +45,19 @@ import kotlin.test.assertEquals
*/
internal abstract class AbstractRealNetworkHandlerTest<H : NetworkHandler> : AbstractNetworkHandlerTest() {
abstract val factory: NetworkHandlerFactory<H>
abstract val network: H
/**
* This is shared for all [createBot] by default. `network === bot.network`, unless you change it.
*/
open val network: H by lateinitMutableProperty {
factory.create(createContext(), createAddress())
}
private var botInit = false
var bot: QQAndroidBot by lateinitMutableProperty { botInit = true; createBot() }
var bot: QQAndroidBot by lateinitMutableProperty {
botInit = true
createBot()
}
@AfterTest
fun afterEach() {
@ -52,10 +67,16 @@ internal abstract class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abs
protected open fun createBot(account: BotAccount = MockAccount): QQAndroidBot {
return object : QQAndroidBot(account, MockConfiguration.copy()) {
override fun createBotLevelComponents(): ConcurrentComponentStorage =
super.createBotLevelComponents().apply { setAll(overrideComponents) }
super.createBotLevelComponents().apply {
setAll(overrideComponents)
get(AccountSecretsManager).getSecretsOrCreate(
account,
DeviceInfo.random(Random(1))
).wLoginSigInfo = createWLoginSigInfo(uin)
}
override fun createNetworkHandler(): NetworkHandler =
this@AbstractRealNetworkHandlerTest.createHandler()
this@AbstractRealNetworkHandlerTest.network
}
}
@ -149,7 +170,6 @@ internal abstract class AbstractRealNetworkHandlerTest<H : NetworkHandler> : Abs
return instance
}
open fun createHandler(): NetworkHandler = factory.create(createContext(), createAddress())
open fun createContext(): NetworkHandlerContextImpl =
NetworkHandlerContextImpl(bot, networkLogger, bot.createNetworkLevelComponents())
@ -185,3 +205,83 @@ internal fun AbstractRealNetworkHandlerTest<*>.setSsoProcessor(action: suspend S
override suspend fun login(handler: NetworkHandler) = action(handler)
}
}
private fun createWLoginSigInfo(
uin: Long,
creationTime: Long = currentTimeSeconds(),
random: Random = Random(1)
): WLoginSigInfo {
return WLoginSigInfo(
uin = uin,
encryptA1 = null,
noPicSig = null,
simpleInfo = WLoginSimpleInfo(
uin = uin,
imgType = EMPTY_BYTE_ARRAY,
imgFormat = EMPTY_BYTE_ARRAY,
imgUrl = EMPTY_BYTE_ARRAY,
mainDisplayName = EMPTY_BYTE_ARRAY
), // defaults {}, from asyncContext._G
appPri = 4294967295L, // defaults {}, from asyncContext._G
a2ExpiryTime = creationTime + 2160000L, // or from asyncContext._t403.get_body_data()
loginBitmap = 0,
tgt = getRandomByteArray(16, random),
a2CreationTime = creationTime,
tgtKey = getRandomByteArray(16, random), // from asyncContext._login_bitmap
userStSig = KeyWithCreationTime(getRandomByteArray(16, random), creationTime),
userStKey = EMPTY_BYTE_ARRAY,
userStWebSig = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 6000
),
userA5 = KeyWithCreationTime(getRandomByteArray(16, random), creationTime),
userA8 = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 72000L
),
lsKey = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 1641600L
),
sKey = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 86400L
),
userSig64 = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime),
openId = EMPTY_BYTE_ARRAY,
openKey = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime),
vKey = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 1728000L
),
accessToken = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime),
d2 = KeyWithExpiry(
getRandomByteArray(16, random),
creationTime,
creationTime + 1728000L
),
d2Key = getRandomByteArray(16, random),
sid = KeyWithExpiry(
EMPTY_BYTE_ARRAY,
creationTime,
creationTime + 1728000L
),
aqSig = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime),
psKeyMap = mutableMapOf(),
pt4TokenMap = mutableMapOf(),
superKey = EMPTY_BYTE_ARRAY,
payToken = EMPTY_BYTE_ARRAY,
pf = EMPTY_BYTE_ARRAY,
pfKey = EMPTY_BYTE_ARRAY,
da2 = EMPTY_BYTE_ARRAY,
wtSessionTicket = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime),
wtSessionTicketKey = EMPTY_BYTE_ARRAY,
deviceToken = EMPTY_BYTE_ARRAY,
encryptedDownloadSession = null
)
}

View File

@ -18,13 +18,22 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.handler.logger
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.DeviceInfo
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.lateinitMutableProperty
import kotlin.random.Random
internal open class TestSsoProcessor(private val bot: QQAndroidBot) : SsoProcessor {
val deviceInfo = bot.configuration.createDeviceInfo(bot)
override var client: QQAndroidClient by lateinitMutableProperty {
QQAndroidClient(bot.account, device = deviceInfo, accountSecrets = AccountSecretsImpl(deviceInfo, bot.account))
QQAndroidClient(
bot.account,
device = deviceInfo,
accountSecrets = bot.components[AccountSecretsManager].getSecretsOrCreate(
bot.account,
DeviceInfo.random(Random(1))
)
)
}
override val ssoSession: SsoSession get() = bot.client
override val firstLoginResult: AtomicRef<FirstLoginResult?> = atomic(null)

View File

@ -45,7 +45,7 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractCommonNHTest()
throw MyException()
}
val selector = TestSelector(3) { createHandler() }
val selector = TestSelector(3) { factory.create(createContext(), createAddress()) }
assertFailsWith<Throwable> { selector.awaitResumeInstance() }
}
@ -63,7 +63,7 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractCommonNHTest()
throw object : NetworkException(true) {}
}
val selector = TestSelector(3) { createHandler() }
val selector = TestSelector(3) { factory.create(createContext(), createAddress()) }
assertFailsWith<MaxAttemptsReachedException> { selector.awaitResumeInstance() }.let {
assertIs<NetworkException>(it.cause)
}
@ -74,7 +74,7 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractCommonNHTest()
throwException = {
throw MyException()
}
val selector = TestSelector(3) { createHandler() }
val selector = TestSelector(3) { factory.create(createContext(), createAddress()) }
assertFailsWith<MaxAttemptsReachedException> { selector.awaitResumeInstance() }.let {
assertIs<MyException>(it.cause)
}

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.network.impl.common
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.isActive
import kotlinx.coroutines.job
import net.mamoe.mirai.internal.MockBot
import net.mamoe.mirai.internal.network.components.EventDispatcher
import net.mamoe.mirai.internal.network.components.SsoProcessor
@ -84,7 +85,7 @@ internal class BotLifecycleTest : AbstractCommonNHTest() {
conf {
parentCoroutineContext = CoroutineName("Overrode")
}
networkHandlerProvider { createHandler() }
networkHandlerProvider { factory.create(createContext(), createAddress()) }
}
assertEquals("Overrode", bot.coroutineContext[CoroutineName]!!.name)
}
@ -96,7 +97,7 @@ internal class BotLifecycleTest : AbstractCommonNHTest() {
conf {
parentCoroutineContext = parentJob
}
networkHandlerProvider { createHandler() }
networkHandlerProvider { factory.create(createContext(), createAddress()) }
}
assertEquals(1, parentJob.children.count())
assertEquals(bot.supervisorJob, parentJob.children.first())
@ -121,8 +122,9 @@ internal class BotLifecycleTest : AbstractCommonNHTest() {
data = StatSvc.SimpleGet.Response.Error(1, "test error"),
)
)
assertFalse { network.isActive }
network.coroutineContext.job.join()
network.assertState(CLOSED) // we do not use selector in this test so it will be CLOSED. It will recover (reconnect) instead in real.
assertFalse { network.isActive }
}

View File

@ -14,6 +14,11 @@ 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.BotAccount
import net.mamoe.mirai.internal.MockConfiguration
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
import net.mamoe.mirai.internal.network.component.setAll
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor
import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitorImpl
import net.mamoe.mirai.internal.network.components.FirstLoginResult
@ -38,16 +43,23 @@ internal class CommonNHBotNormalLoginTest : AbstractCommonNHTest() {
}
val selector = KeepAliveNetworkHandlerSelector(selectorLogger) {
super.factory.create(createContext(), createAddress())
factory.create(createContext(), createAddress())
}
override val network: TestCommonNetworkHandler
get() = bot.network.cast<SelectorNetworkHandler<*>>().selector.getCurrentInstanceOrCreate().cast()
get() = selector.getCurrentInstanceOrCreate().cast()
override fun createHandler(): NetworkHandler {
return SelectorNetworkHandler(selector)
override fun createBot(account: BotAccount): QQAndroidBot {
return object : QQAndroidBot(account, MockConfiguration.copy()) {
override fun createBotLevelComponents(): ConcurrentComponentStorage =
super.createBotLevelComponents().apply { setAll(overrideComponents) }
override fun createNetworkHandler(): NetworkHandler =
SelectorNetworkHandler(selector)
}
}
class CusLoginException(message: String?) : CustomLoginFailedException(true, message)
@AfterTest

View File

@ -7,6 +7,8 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:OptIn(TestOnly::class)
package net.mamoe.mirai.internal.notice.processors
import kotlinx.serialization.SerializationStrategy
@ -84,7 +86,7 @@ internal abstract class AbstractNoticeProcessorTest : AbstractCommonNHTest(), Gr
createContext(this, attributes)
}, block)
fun setBot(id: Long): QQAndroidBot {
open fun setBot(id: Long): QQAndroidBot {
bot = createBot(BotAccount(id, "a"))
return bot
}

View File

@ -14,13 +14,16 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.internal.QQAndroidBot
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.network.protocol.packet.chat.PbMessageSvc
import net.mamoe.mirai.internal.test.runBlockingUnit
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.utils.hexToBytes
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
internal class RecallTest : AbstractNoticeProcessorTest() {
@ -92,6 +95,16 @@ internal class RecallTest : AbstractNoticeProcessorTest() {
)
)
override fun setBot(id: Long): QQAndroidBot {
return super.setBot(id).also { bot ->
runBlockingUnit { bot.login() }
network.addPacketReplier {
assertEquals("PbMessageSvc.PbMsgWithDraw", it.commandName)
reply(PbMessageSvc.PbMsgWithDraw.Response.Success)
}
}
}
@Test
fun `recall member message without permission`() = runBlockingUnit {
val bot = setBot(2)

View File

@ -9,6 +9,8 @@
package net.mamoe.mirai.internal.test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.debug.DebugProbes
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger
import net.mamoe.mirai.utils.MiraiLogger
@ -18,10 +20,13 @@ import java.util.concurrent.TimeUnit
@Timeout(value = 7, unit = TimeUnit.MINUTES)
internal actual abstract class AbstractTest actual constructor() : CommonAbstractTest() {
@OptIn(ExperimentalCoroutinesApi::class)
actual companion object {
init {
initPlatform()
DebugProbes.install()
@Suppress("DEPRECATION_ERROR")
MiraiLogger.setDefaultLoggerCreator {
SynchronizedStdoutLogger(it)

View File

@ -83,7 +83,6 @@ object ValueDescAnalyzer {
}
}
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> ValueDescAnalyzer.analyze(value: T): ValueDesc {
return analyze(value, typeOf<T>())
}

View File

@ -43,7 +43,7 @@ internal class OptimizeByteArrayAsHexStringTransformerTest : AbstractTest() {
fun `can optimize as hex`() {
assertEquals(
"""
"O".toByteArray() /* 4F 02 */
"4F 02".hexToBytes()
""".trimIndent(), analyzeTransformAndRender(byteArrayOf(0x4f, 0x02))
)
}

View File

@ -126,14 +126,15 @@ internal class ValueDescToStringRendererTest {
val self: MyClass?,
)
@Suppress("ClassName")
data class `MyClass$3`(
val str: String,
val int: Int,
val self: `MyClass$3`?,
)
@Test
fun `class value`() {
data class MyClass2(
val str: String,
val int: Int,
val self: MyClass2?,
)
assertEquals(
"""
${MyClass::class.qualifiedName}(
@ -144,10 +145,19 @@ internal class ValueDescToStringRendererTest {
""".trimIndent(),
ValueDescAnalyzer.analyze(MyClass("str", 1, null)).renderToString(renderer)
)
}
@Test
fun `local class`() {
data class MyClass2(
val str: String,
val int: Int,
val self: MyClass2?,
)
assertEquals(
"""
`${MyClass2::class.java.name}`(
${MyClass2::class.simpleName}(
str = "str",
int = 1,
self = null,
@ -157,6 +167,21 @@ internal class ValueDescToStringRendererTest {
)
}
@Test
fun `class with special name`() {
assertEquals(
"""
`${`MyClass$3`::class.qualifiedName}`(
str = "str",
int = 1,
self = null,
)
""".trimIndent(),
ValueDescAnalyzer.analyze(`MyClass$3`("str", 1, null)).renderToString(renderer)
)
}
@Test
fun `class value nested`() {
data class MyClass2(
@ -182,10 +207,10 @@ internal class ValueDescToStringRendererTest {
assertEquals(
"""
`${MyClass2::class.java.name}`(
${MyClass2::class.simpleName}(
str = "str",
int = 1,
self = `${MyClass2::class.java.name}`(
self = ${MyClass2::class.simpleName}(
str = "str",
int = 1,
self = null,