[mock] Fix serialization

This commit is contained in:
Karlatemp 2022-09-18 17:55:28 +08:00
parent a8e90c6e89
commit 1ea68d82a7
No known key found for this signature in database
GPG Key ID: BA173CA2B9956C59
4 changed files with 256 additions and 13 deletions

View File

@ -31,11 +31,18 @@ import net.mamoe.mirai.mock.internal.contact.AQQ_RECALL_FAILED_MESSAGE
import net.mamoe.mirai.mock.internal.contact.MockFriendImpl import net.mamoe.mirai.mock.internal.contact.MockFriendImpl
import net.mamoe.mirai.mock.internal.contact.MockImage import net.mamoe.mirai.mock.internal.contact.MockImage
import net.mamoe.mirai.mock.internal.contact.MockStrangerImpl import net.mamoe.mirai.mock.internal.contact.MockStrangerImpl
import net.mamoe.mirai.mock.internal.msgsrc.registerMockMsgSerializers
import net.mamoe.mirai.mock.utils.mock import net.mamoe.mirai.mock.utils.mock
import net.mamoe.mirai.mock.utils.simpleMemberInfo import net.mamoe.mirai.mock.utils.simpleMemberInfo
import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.currentTimeSeconds
internal class MockMiraiImpl : MiraiImpl() { internal class MockMiraiImpl : MiraiImpl() {
companion object {
init {
registerMockMsgSerializers()
}
}
override suspend fun solveBotInvitedJoinGroupRequestEvent( override suspend fun solveBotInvitedJoinGroupRequestEvent(
bot: Bot, bot: Bot,
eventId: Long, eventId: Long,

View File

@ -11,6 +11,7 @@
package net.mamoe.mirai.mock.internal.contact package net.mamoe.mirai.mock.internal.contact
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.Member
@ -95,6 +96,8 @@ internal suspend fun ExternalResource.mockImplUploadAudioAsOnline(bot: MockBot):
) )
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(MockImage.Serializer::class)
internal class MockImage( internal class MockImage(
override val imageId: String, override val imageId: String,
private val urlPath: String, private val urlPath: String,
@ -112,6 +115,8 @@ internal class MockImage(
} }
} }
object Serializer : Image.FallbackSerializer("MockImage")
private val _stringValue: String? by lazy(LazyThreadSafetyMode.NONE) { "[mirai:image:$imageId]" } private val _stringValue: String? by lazy(LazyThreadSafetyMode.NONE) { "[mirai:image:$imageId]" }
override fun getUrl(bot: Bot): String { override fun getUrl(bot: Bot): String {

View File

@ -7,16 +7,98 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE * https://github.com/mamoe/mirai/blob/dev/LICENSE
*/ */
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.mock.internal.msgsrc package net.mamoe.mirai.mock.internal.msgsrc
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl
import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacadeImpl
import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer
import net.mamoe.mirai.message.MessageSerializers
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.mock.internal.contact.AbstractMockContact import net.mamoe.mirai.mock.internal.contact.AbstractMockContact
import net.mamoe.mirai.mock.internal.contact.MockImage
import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.currentTimeSeconds
internal fun registerMockMsgSerializers() {
val serializers = mutableListOf<MessageSerializer<*>>()
MessageSerializer.superclassesScope(Image::class, MessageContent::class, SingleMessage::class) {
serializers.add(
MessageSerializer(
MockImage::class,
MockImage.serializer()
)
)
}
MessageSerializer.superclassesScope(MessageSource::class, MessageMetadata::class, SingleMessage::class) {
serializers.add(
MessageSerializer(
OnlineMsgSrcToGroup::class,
OnlineMsgSrcToGroup.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcToFriend::class,
OnlineMsgSrcToFriend.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcToStranger::class,
OnlineMsgSrcToStranger.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcToTemp::class,
OnlineMsgSrcToTemp.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcFromGroup::class,
OnlineMsgSrcFromGroup.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcFromFriend::class,
OnlineMsgSrcFromFriend.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcFromStranger::class,
OnlineMsgSrcFromStranger.serializer()
)
)
serializers.add(
MessageSerializer(
OnlineMsgSrcFromTemp::class,
OnlineMsgSrcFromTemp.serializer()
)
)
}
val module = MessageProtocolFacadeImpl(listOf(), "").also {
it.serializers.addAll(serializers)
}.createSerializersModule()
MessageSerializers.registerSerializers(module)
}
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcToGroup.Serializer::class)
internal class OnlineMsgSrcToGroup( internal class OnlineMsgSrcToGroup(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -27,8 +109,13 @@ internal class OnlineMsgSrcToGroup(
override val target: Group override val target: Group
) : OnlineMessageSource.Outgoing.ToGroup() { ) : OnlineMessageSource.Outgoing.ToGroup() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcToFriend.Serializer::class)
internal class OnlineMsgSrcToFriend( internal class OnlineMsgSrcToFriend(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -39,8 +126,14 @@ internal class OnlineMsgSrcToFriend(
override val target: Friend override val target: Friend
) : OnlineMessageSource.Outgoing.ToFriend() { ) : OnlineMessageSource.Outgoing.ToFriend() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcToStranger.Serializer::class)
internal class OnlineMsgSrcToStranger( internal class OnlineMsgSrcToStranger(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -51,8 +144,12 @@ internal class OnlineMsgSrcToStranger(
override val target: Stranger override val target: Stranger
) : OnlineMessageSource.Outgoing.ToStranger() { ) : OnlineMessageSource.Outgoing.ToStranger() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcToTemp.Serializer::class)
internal class OnlineMsgSrcToTemp( internal class OnlineMsgSrcToTemp(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -63,19 +160,13 @@ internal class OnlineMsgSrcToTemp(
override val target: Member override val target: Member
) : OnlineMessageSource.Outgoing.ToTemp() { ) : OnlineMessageSource.Outgoing.ToTemp() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
} }
internal class OnlineMsgFromGroup(
override val ids: IntArray,
override val internalIds: IntArray,
override val time: Int,
override val originalMessage: MessageChain,
override val bot: Bot,
override val sender: Member
) : OnlineMessageSource.Incoming.FromGroup() {
override val isOriginalMessageInitialized: Boolean get() = true
}
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcFromFriend.Serializer::class)
internal class OnlineMsgSrcFromFriend( internal class OnlineMsgSrcFromFriend(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -85,8 +176,12 @@ internal class OnlineMsgSrcFromFriend(
override val sender: Friend override val sender: Friend
) : OnlineMessageSource.Incoming.FromFriend() { ) : OnlineMessageSource.Incoming.FromFriend() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromFriend")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcFromStranger.Serializer::class)
internal class OnlineMsgSrcFromStranger( internal class OnlineMsgSrcFromStranger(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -96,8 +191,12 @@ internal class OnlineMsgSrcFromStranger(
override val sender: Stranger override val sender: Stranger
) : OnlineMessageSource.Incoming.FromStranger() { ) : OnlineMessageSource.Incoming.FromStranger() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromStranger")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcFromTemp.Serializer::class)
internal class OnlineMsgSrcFromTemp( internal class OnlineMsgSrcFromTemp(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -107,8 +206,13 @@ internal class OnlineMsgSrcFromTemp(
override val sender: Member override val sender: Member
) : OnlineMessageSource.Incoming.FromTemp() { ) : OnlineMessageSource.Incoming.FromTemp() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromTemp")
} }
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
@Serializable(OnlineMsgSrcFromGroup.Serializer::class)
internal class OnlineMsgSrcFromGroup( internal class OnlineMsgSrcFromGroup(
override val ids: IntArray, override val ids: IntArray,
override val internalIds: IntArray, override val internalIds: IntArray,
@ -118,6 +222,9 @@ internal class OnlineMsgSrcFromGroup(
override val sender: Member override val sender: Member
) : OnlineMessageSource.Incoming.FromGroup() { ) : OnlineMessageSource.Incoming.FromGroup() {
override val isOriginalMessageInitialized: Boolean get() = true override val isOriginalMessageInitialized: Boolean get() = true
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromGroup")
} }
internal typealias MsgSrcConstructor<R> = ( internal typealias MsgSrcConstructor<R> = (
@ -141,6 +248,7 @@ internal inline fun <R> AbstractMockContact.newMsgSrc(
is Stranger, is Stranger,
is Friend, is Friend,
-> this.id -> this.id
else -> error("Invalid contact: $this") else -> error("Invalid contact: $this")
}, },
kind = when (this) { kind = when (this) {

View File

@ -0,0 +1,123 @@
/*
* 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.mock.test.mock
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import net.mamoe.mirai.message.MessageSerializers
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.mock.test.MockBotTestBase
import net.mamoe.mirai.mock.utils.randomImageContent
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
internal class MessageSerializationTest : MockBotTestBase() {
@Suppress("DEPRECATION_ERROR")
private val module
get() = MessageSerializers.serializersModule
private val format
get() = Json {
serializersModule = module
useArrayPolymorphism = false
ignoreUnknownKeys = true
}
private inline fun <reified T : Any> T.serialize(serializer: KSerializer<T> = module.serializer()): String {
return format.encodeToString(serializer, this)
}
private inline fun <reified T : Any> String.deserialize(serializer: KSerializer<T> = module.serializer()): T {
return format.decodeFromString(serializer, this)
}
private inline fun <reified T : Any> testSerialization(t: T, serializer: KSerializer<T> = module.serializer()) {
val deserialized = kotlin.runCatching {
println("Testing ${t::class.simpleName} with serializer $serializer")
val serialized = t.serialize(serializer)
println("Result: ${serializer.descriptor.serialName} $serialized")
serialized.deserialize(serializer)
}.getOrElse {
throw IllegalStateException("Failed to serialize $t", it)
}
val msg = "serialized string: ${t.serialize(serializer)}\ndeserialized string: ${
deserialized.serialize(
serializer
)
}\n"
assert1(
t,
deserialized,
msg
)
}
private fun assert1(t: Any, deserialized: Any, msg: String) {
if (deserialized is MessageSource && t is MessageSource) {
assertSource(t, deserialized, msg)
return
}
if (t is MessageChain && deserialized is MessageChain) {
assertEquals(t.size, deserialized.size)
val iter1 = t.iterator()
val iter2 = deserialized.iterator()
repeat(t.size) {
assert1(iter1.next(), iter2.next(), msg)
}
assertFalse(iter1.hasNext(), msg)
assertFalse(iter2.hasNext(), msg)
return
}
assertEquals(t, deserialized, msg)
}
private fun assertSource(t: MessageSource, deserialized: MessageSource, msg: String) {
assertEquals(t.kind, deserialized.kind, msg)
assertEquals(t.botId, deserialized.botId, msg)
assertEquals(t.fromId, deserialized.fromId, msg)
assertEquals(t.targetId, deserialized.targetId, msg)
assertEquals(t.time, deserialized.time, msg)
Assertions.assertArrayEquals(t.ids, deserialized.ids, msg)
Assertions.assertArrayEquals(t.internalIds, deserialized.internalIds, msg)
assertEquals(t.originalMessage, deserialized.originalMessage, msg)
}
@Test
fun testMockMessageSources() = runTest {
testSerialization(bot.addFriend(1, "").says(""))
testSerialization(bot.addStranger(2, "").says(""))
bot.addGroup(3, "").let { group ->
group.sendMessage("AWA").source.let { testSerialization(messageChainOf(it)) }
group.addMember(6, "").says("A").let { testSerialization(it) }
}
}
@Test
fun testMockResources() = runTest {
testSerialization(bot.uploadMockImage(Image.randomImageContent().toExternalResource().toAutoCloseable()))
"1".toByteArray().toExternalResource().use { data0 ->
testSerialization(bot.uploadOnlineAudio(data0))
testSerialization(bot.asFriend.uploadAudio(data0))
}
}
}