mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-24 14:30:09 +08:00
Rewrite MessageSerializers for new project structure (#2159)
This commit is contained in:
parent
699718f958
commit
e5cad1d0ba
@ -4815,8 +4815,13 @@ public final class net/mamoe/mirai/message/data/MessageSource$Key : net/mamoe/mi
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MessageSource$Serializer : net/mamoe/mirai/internal/message/MessageSourceSerializerImpl {
|
||||
public final class net/mamoe/mirai/message/data/MessageSource$Serializer : kotlinx/serialization/KSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/MessageSource$Serializer;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/MessageSource;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/MessageSource;)V
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MessageSourceAmender : net/mamoe/mirai/message/data/MessageSourceBuilder {
|
||||
|
@ -4815,8 +4815,13 @@ public final class net/mamoe/mirai/message/data/MessageSource$Key : net/mamoe/mi
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MessageSource$Serializer : net/mamoe/mirai/internal/message/MessageSourceSerializerImpl {
|
||||
public final class net/mamoe/mirai/message/data/MessageSource$Serializer : kotlinx/serialization/KSerializer {
|
||||
public static final field INSTANCE Lnet/mamoe/mirai/message/data/MessageSource$Serializer;
|
||||
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
|
||||
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/MessageSource;
|
||||
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
|
||||
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
|
||||
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/MessageSource;)V
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/MessageSourceAmender : net/mamoe/mirai/message/data/MessageSourceBuilder {
|
||||
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.message
|
||||
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
import kotlinx.serialization.encoding.*
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import kotlin.jvm.JvmName
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
internal abstract class AbstractPolymorphicSerializer<T : Any> internal constructor() : KSerializer<T> {
|
||||
|
||||
/**
|
||||
* Base class for all classes that this polymorphic serializer can serialize or deserialize.
|
||||
*/
|
||||
abstract val baseClass: KClass<T>
|
||||
|
||||
final override fun serialize(encoder: Encoder, value: T) {
|
||||
val actualSerializer = findPolymorphicSerializer(encoder, value)
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeStringElement(descriptor, 0, actualSerializer.descriptor.serialName)
|
||||
encodeSerializableElement(descriptor, 1, actualSerializer.cast(), value)
|
||||
}
|
||||
}
|
||||
|
||||
final override fun deserialize(decoder: Decoder): T = decoder.decodeStructure(descriptor) {
|
||||
var klassName: String? = null
|
||||
var value: Any? = null
|
||||
if (decodeSequentially()) {
|
||||
return decodeSequentially(this)
|
||||
}
|
||||
|
||||
mainLoop@ while (true) {
|
||||
when (val index = decodeElementIndex(descriptor)) {
|
||||
CompositeDecoder.DECODE_DONE -> {
|
||||
break@mainLoop
|
||||
}
|
||||
0 -> {
|
||||
klassName = decodeStringElement(descriptor, index)
|
||||
}
|
||||
1 -> {
|
||||
klassName = requireNotNull(klassName) { "Cannot read polymorphic value before its type token" }
|
||||
val serializer = findPolymorphicSerializer(this, klassName)
|
||||
value = decodeSerializableElement(descriptor, index, serializer)
|
||||
}
|
||||
else -> throw SerializationException(
|
||||
"Invalid index in polymorphic deserialization of " +
|
||||
(klassName ?: "unknown class") +
|
||||
"\n Expected 0, 1 or DECODE_DONE(-1), but found $index"
|
||||
)
|
||||
}
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
requireNotNull(value) { "Polymorphic value has not been read for class $klassName" } as T
|
||||
}
|
||||
|
||||
private fun decodeSequentially(compositeDecoder: CompositeDecoder): T {
|
||||
val klassName = compositeDecoder.decodeStringElement(descriptor, 0)
|
||||
val serializer = findPolymorphicSerializer(compositeDecoder, klassName)
|
||||
return compositeDecoder.decodeSerializableElement(descriptor, 1, serializer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookups an actual serializer for given [klassName] withing the current [base class][baseClass].
|
||||
* May use context from the [decoder].
|
||||
*/
|
||||
open fun findPolymorphicSerializerOrNull(
|
||||
decoder: CompositeDecoder,
|
||||
klassName: String?
|
||||
): DeserializationStrategy<out T>? = decoder.serializersModule.getPolymorphic(baseClass, klassName)
|
||||
|
||||
|
||||
/**
|
||||
* Lookups an actual serializer for given [value] within the current [base class][baseClass].
|
||||
* May use context from the [encoder].
|
||||
*/
|
||||
public open fun findPolymorphicSerializerOrNull(
|
||||
encoder: Encoder,
|
||||
value: T
|
||||
): SerializationStrategy<T>? =
|
||||
encoder.serializersModule.getPolymorphic(baseClass, value)
|
||||
}
|
||||
|
||||
|
||||
internal fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
|
||||
decoder: CompositeDecoder,
|
||||
klassName: String?
|
||||
): DeserializationStrategy<out T> =
|
||||
findPolymorphicSerializerOrNull(decoder, klassName) ?: throwSubtypeNotRegistered(klassName, baseClass)
|
||||
|
||||
internal fun <T : Any> AbstractPolymorphicSerializer<T>.findPolymorphicSerializer(
|
||||
encoder: Encoder,
|
||||
value: T
|
||||
): SerializationStrategy<T> =
|
||||
findPolymorphicSerializerOrNull(encoder, value) ?: throwSubtypeNotRegistered(value::class, baseClass)
|
||||
|
||||
@JvmName("throwSubtypeNotRegistered")
|
||||
internal fun throwSubtypeNotRegistered(subClassName: String?, baseClass: KClass<*>): Nothing {
|
||||
val scope = "in the scope of '${baseClass.simpleName}'"
|
||||
throw SerializationException(
|
||||
if (subClassName == null)
|
||||
"Class discriminator was missing and no default polymorphic serializers were registered $scope"
|
||||
else
|
||||
"Class '$subClassName' is not registered for polymorphic serialization $scope.\n" +
|
||||
"Mark the base class as 'sealed' or register the serializer explicitly."
|
||||
)
|
||||
}
|
||||
|
||||
@JvmName("throwSubtypeNotRegistered")
|
||||
internal fun throwSubtypeNotRegistered(subClass: KClass<*>, baseClass: KClass<*>): Nothing =
|
||||
throwSubtypeNotRegistered(subClass.simpleName ?: "$subClass", baseClass)
|
@ -23,7 +23,11 @@ import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.encoding.decodeStructure
|
||||
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.IMirai
|
||||
@ -111,6 +115,7 @@ import kotlin.native.CName
|
||||
* @see FlashImage 闪照
|
||||
* @see Image.flash 转换普通图片为闪照
|
||||
*/
|
||||
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
|
||||
@Serializable(Image.Serializer::class)
|
||||
@NotStableForInheritance
|
||||
public interface Image : Message, MessageContent, CodableMessage {
|
||||
@ -183,17 +188,43 @@ public interface Image : Message, MessageContent, CodableMessage {
|
||||
deserialize = { Image(it) },
|
||||
)
|
||||
|
||||
public object Serializer : KSerializer<Image> by FallbackSerializer("Image")
|
||||
@Deprecated(
|
||||
message = "For internal use only. Deprecated for removal. Please retrieve serializer from MessageSerializers.serializersModule.",
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
@DeprecatedSinceMirai(warningSince = "2.13") // error since 2.15, hidden since 2.16
|
||||
public object Serializer : KSerializer<Image> by FallbackSerializer(SERIAL_NAME)
|
||||
|
||||
// move to mirai-core in 2.16. Delegate Serializer to the implementation from MessageSerializers.
|
||||
@MiraiInternalApi
|
||||
public open class FallbackSerializer(serialName: String) : KSerializer<Image> by Delegate.serializer().map(
|
||||
buildClassSerialDescriptor(serialName) { element("imageId", String.serializer().descriptor) },
|
||||
serialize = { Delegate(imageId) },
|
||||
deserialize = { Image(imageId) },
|
||||
) {
|
||||
public open class FallbackSerializer(serialName: String) : KSerializer<Image> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(serialName) {
|
||||
element("imageId", String.serializer().descriptor)
|
||||
}
|
||||
|
||||
// Note: Manually written to overcome discriminator issues.
|
||||
// Without this implementation you will need `ignoreUnknownKeys` on deserialization.
|
||||
override fun deserialize(decoder: Decoder): Image {
|
||||
decoder.decodeStructure(descriptor) {
|
||||
if (this.decodeSequentially()) {
|
||||
val imageId = this.decodeStringElement(descriptor, 0)
|
||||
return Image(imageId)
|
||||
} else {
|
||||
val index = this.decodeElementIndex(descriptor)
|
||||
check(index == 0)
|
||||
val imageId = this.decodeStringElement(descriptor, index)
|
||||
return Image(imageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Image) {
|
||||
Delegate.serializer().serialize(encoder, Delegate(value.imageId))
|
||||
}
|
||||
|
||||
@SerialName(SERIAL_NAME)
|
||||
@Serializable
|
||||
internal data class Delegate(
|
||||
private data class Delegate(
|
||||
val imageId: String
|
||||
)
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ public interface MarketFace : HummerMessage {
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<HummerMessage, MarketFace>(HummerMessage, { it.safeCast() }) {
|
||||
// Notice that for MarketFaceImpl, its serial name is 'MarketFace';
|
||||
// while for Dice, that is 'Dice' instead of 'MarketFace' again. (Dice extends MarketFace)
|
||||
public const val SERIAL_NAME: String = "MarketFace"
|
||||
}
|
||||
}
|
@ -16,7 +16,9 @@ package net.mamoe.mirai.message.data
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonConfiguration
|
||||
import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.IMirai
|
||||
@ -25,10 +27,12 @@ import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.event.events.MessageEvent
|
||||
import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.action.AsyncRecallResult
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.recall
|
||||
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
@ -111,6 +115,7 @@ import kotlin.jvm.JvmSynthetic
|
||||
*
|
||||
* @see buildMessageSource 构建一个 [OfflineMessageSource]
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
@Serializable(MessageSource.Serializer::class)
|
||||
public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
|
||||
public final override val key: MessageKey<MessageSource>
|
||||
@ -198,9 +203,16 @@ public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
|
||||
return visitor.visitMessageSource(this, data)
|
||||
}
|
||||
|
||||
public object Serializer : MessageSourceSerializerImpl("MessageSource")
|
||||
@Deprecated("Do not use this serializer. Retrieve from `MessageSerializers.serializersModule`.")
|
||||
@DeprecatedSinceMirai(warningSince = "2.13")
|
||||
public object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("MessageSource")
|
||||
|
||||
public companion object Key : AbstractMessageKey<MessageSource>({ it.safeCast() }) {
|
||||
/**
|
||||
* 从 [MessageSerializers] 获取到的对应[序列化器][KSerializer]在参与多态序列化时的[类型标识符][JsonConfiguration.classDiscriminator]的值.
|
||||
*
|
||||
* [OnlineMessageSource] 的部分属性无法通过序列化保存. 所有 [MessageSource] 子类型在序列化时都会序列化为 [OfflineMessageSource]. 反序列化时会得到 [OfflineMessageSource] 而不是原类型.
|
||||
*/
|
||||
public const val SERIAL_NAME: String = "MessageSource"
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import kotlinx.serialization.Polymorphic
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
@ -47,7 +48,7 @@ public data class QuoteReply(
|
||||
/**
|
||||
* 指代被引用的消息. 其中 [MessageSource.originalMessage] 可以控制客户端显示的消息内容.
|
||||
*/
|
||||
public val source: MessageSource
|
||||
public val source: @Polymorphic MessageSource
|
||||
) : Message, MessageMetadata, ConstrainSingle {
|
||||
/**
|
||||
* 从消息链中获取 [MessageSource] 并构造.
|
||||
|
@ -34,4 +34,8 @@ internal data class MarketFaceImpl internal constructor(
|
||||
override fun <D, R> accept(visitor: MessageVisitor<D, R>, data: D): R {
|
||||
return visitor.ex()?.visitMarketFaceImpl(this, data) ?: super.accept(visitor, data)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SERIAL_NAME = MarketFace.SERIAL_NAME
|
||||
}
|
||||
}
|
@ -40,11 +40,11 @@ internal class ImageProtocol : MessageProtocol() {
|
||||
add(ImagePatcherForGroup())
|
||||
|
||||
MessageSerializer.superclassesScope(MessageContent::class, SingleMessage::class) {
|
||||
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
|
||||
add(MessageSerializer(Image::class, Image.Serializer, registerAlsoContextual = true))
|
||||
}
|
||||
|
||||
MessageSerializer.superclassesScope(Image::class, MessageContent::class, SingleMessage::class) {
|
||||
add(MessageSerializer(Image::class, Image.Serializer))
|
||||
add(MessageSerializer(OfflineGroupImage::class, OfflineGroupImage.serializer()))
|
||||
add(MessageSerializer(OfflineFriendImage::class, OfflineFriendImage.serializer()))
|
||||
add(MessageSerializer(OnlineFriendImageImpl::class, OnlineFriendImageImpl.serializer()))
|
||||
|
@ -20,7 +20,9 @@ import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderContext.Co
|
||||
import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.copy
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.map
|
||||
|
||||
|
||||
internal class MarketFaceProtocol : MessageProtocol() {
|
||||
@ -30,16 +32,37 @@ internal class MarketFaceProtocol : MessageProtocol() {
|
||||
|
||||
add(MarketFaceDecoder())
|
||||
|
||||
MessageSerializer.superclassesScope(MarketFace::class, MessageContent::class, SingleMessage::class) {
|
||||
add(
|
||||
MessageSerializer(
|
||||
MarketFaceImpl::class,
|
||||
MarketFaceImpl.serializer()
|
||||
)
|
||||
|
||||
// Serialization overview:
|
||||
// Using MarketFace as serial type:
|
||||
// - convert data to MarketFaceImpl on serialization. Convert them back to subtypes on deserialization.
|
||||
// - serial name is always "MarketFace"
|
||||
// Using subtypes:
|
||||
// - serial name is name of subtype, i.e. "MarketFace" / "Dice".
|
||||
// - Note that we don't use MarketFaceImpl but MarketFace for compatibility concerns.
|
||||
|
||||
add(
|
||||
MessageSerializer(
|
||||
MarketFace::class, MarketFaceImpl.serializer().map(
|
||||
resultantDescriptor = MarketFaceImpl.serializer().descriptor.copy(MarketFace.SERIAL_NAME),
|
||||
deserialize = {
|
||||
it.delegate.toDiceOrNull() ?: it
|
||||
},
|
||||
serialize = {
|
||||
when (it) {
|
||||
is Dice -> MarketFaceImpl(it.toJceStruct())
|
||||
is MarketFaceImpl -> it
|
||||
else -> {
|
||||
error("Unsupported MarketFace type ${it::class.qualifiedName}")
|
||||
}
|
||||
}
|
||||
}
|
||||
), emptyArray()
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
MessageSerializer.superclassesScope(MarketFace::class, MessageContent::class, SingleMessage::class) {
|
||||
add(MessageSerializer(MarketFaceImpl::class, MarketFaceImpl.serializer()))
|
||||
add(MessageSerializer(Dice::class, Dice.serializer()))
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ internal class MusicShareProtocol : MessageProtocol() {
|
||||
add(Sender())
|
||||
|
||||
MessageSerializer.superclassesScope(MessageContent::class, SingleMessage::class) {
|
||||
add(MessageSerializer(MusicShare::class, MusicShare.serializer()))
|
||||
add(MessageSerializer(MusicShare::class, MusicShare.serializer(), registerAlsoContextual = true))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.AnonymousMember
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoder
|
||||
@ -26,6 +27,8 @@ import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer
|
||||
import net.mamoe.mirai.internal.message.source.*
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.copy
|
||||
import net.mamoe.mirai.utils.map
|
||||
|
||||
internal class QuoteReplyProtocol : MessageProtocol(PRIORITY_METADATA) {
|
||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||
@ -36,6 +39,7 @@ internal class QuoteReplyProtocol : MessageProtocol(PRIORITY_METADATA) {
|
||||
currentMessageChain[QuoteReply]?.source?.ensureSequenceIdAvailable()
|
||||
})
|
||||
|
||||
val baseSourceSerializer = MessageSourceSerializerImpl(MessageSource.SERIAL_NAME)
|
||||
MessageSerializer.superclassesScope(MessageSource::class, MessageMetadata::class, SingleMessage::class) {
|
||||
add(
|
||||
MessageSerializer(
|
||||
@ -92,9 +96,37 @@ internal class QuoteReplyProtocol : MessageProtocol(PRIORITY_METADATA) {
|
||||
)
|
||||
)
|
||||
|
||||
add(MessageSerializer(MessageSource::class, MessageSource.serializer()))
|
||||
}
|
||||
|
||||
MessageSerializer.superclassesScope(MessageMetadata::class, SingleMessage::class) {
|
||||
@Suppress("DEPRECATION")
|
||||
add(
|
||||
MessageSerializer(
|
||||
MessageSource::class,
|
||||
OfflineMessageSourceImplData.serializer().map(
|
||||
OfflineMessageSourceImplData.serializer().descriptor.copy(MessageSource.SERIAL_NAME),
|
||||
{ it },
|
||||
{
|
||||
OfflineMessageSourceImplData(
|
||||
kind, ids, botId, time, fromId, targetId,
|
||||
originalMessage, internalIds
|
||||
)
|
||||
}
|
||||
),
|
||||
registerAlsoContextual = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// add(
|
||||
// MessageSerializer(
|
||||
// MessageSource::class,
|
||||
// PolymorphicSerializer(MessageSource::class),
|
||||
// emptyArray(),
|
||||
// registerAlsoContextual = true
|
||||
// )
|
||||
// )
|
||||
|
||||
MessageSerializer.superclassesScope(MessageMetadata::class, SingleMessage::class) {
|
||||
add(MessageSerializer(QuoteReply::class, QuoteReply.serializer()))
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.message.protocol.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
@ -18,7 +19,8 @@ import kotlin.jvm.JvmInline
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Collectd in [MessageProtocol.collectProcessors]
|
||||
* Collectd in [MessageProtocol.collectProcessors].
|
||||
* @see ProcessorCollector.add
|
||||
*/
|
||||
internal class MessageSerializer<T : Any>(
|
||||
/**
|
||||
@ -38,7 +40,7 @@ internal class MessageSerializer<T : Any>(
|
||||
// This can help native targets, which has no reflection support.
|
||||
|
||||
companion object {
|
||||
fun <T : SingleMessage, R> superclassesScope(
|
||||
inline fun <T : SingleMessage, R> superclassesScope(
|
||||
vararg superclasses: KClass<in T>,
|
||||
block: SuperclassesScope<T>.() -> R
|
||||
): R {
|
||||
|
@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.message.source
|
||||
|
||||
import kotlinx.atomicfu.AtomicBoolean
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.Bot
|
||||
@ -32,6 +33,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.internal.utils.structureToString
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
||||
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||
@ -45,7 +47,7 @@ internal class OnlineMessageSourceFromFriendImpl(
|
||||
override val bot: Bot,
|
||||
msg: List<MsgComm.Msg>,
|
||||
) : OnlineMessageSource.Incoming.FromFriend(), IncomingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromFriend")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromFriend")
|
||||
|
||||
override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq }
|
||||
override var isRecalledOrPlanned: AtomicBoolean = atomic(false)
|
||||
@ -78,7 +80,7 @@ internal class OnlineMessageSourceFromStrangerImpl(
|
||||
override val bot: Bot,
|
||||
msg: List<MsgComm.Msg>,
|
||||
) : OnlineMessageSource.Incoming.FromStranger(), IncomingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromStranger")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromStranger")
|
||||
|
||||
override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq }
|
||||
override var isRecalledOrPlanned: AtomicBoolean = atomic(false)
|
||||
@ -150,7 +152,7 @@ internal class OnlineMessageSourceFromTempImpl(
|
||||
override val bot: Bot,
|
||||
msg: List<MsgComm.Msg>,
|
||||
) : OnlineMessageSource.Incoming.FromTemp(), IncomingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromTemp")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromTemp")
|
||||
|
||||
override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq }
|
||||
override val internalIds: IntArray = msg.mapToIntArray { it.msgBody.richText.attr!!.random }
|
||||
@ -187,7 +189,7 @@ internal class OnlineMessageSourceFromGroupImpl(
|
||||
override val bot: Bot,
|
||||
msg: List<MsgComm.Msg>,
|
||||
) : OnlineMessageSource.Incoming.FromGroup(), IncomingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromGroupImpl")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceFromGroupImpl")
|
||||
|
||||
@Transient
|
||||
override var isRecalledOrPlanned: AtomicBoolean = atomic(false)
|
||||
|
@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.message.source
|
||||
|
||||
import kotlinx.atomicfu.AtomicBoolean
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import net.mamoe.mirai.Bot
|
||||
@ -23,6 +24,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.message.data.OfflineMessageSource
|
||||
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||
@ -42,7 +44,7 @@ internal class OfflineMessageSourceImplData(
|
||||
private val originalMessageLazy: Lazy<MessageChain>,
|
||||
override val internalIds: IntArray,
|
||||
) : OfflineMessageSource(), MessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OfflineMessageSource")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OfflineMessageSource")
|
||||
|
||||
override val sequenceIds: IntArray get() = ids
|
||||
override val originalMessage: MessageChain by originalMessageLazy
|
||||
|
@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.message.source
|
||||
import kotlinx.atomicfu.AtomicBoolean
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.*
|
||||
@ -88,7 +89,7 @@ internal class OnlineMessageSourceToFriendImpl(
|
||||
override val sender: Bot,
|
||||
override val target: Friend,
|
||||
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal, OutgoingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
|
||||
|
||||
override val isOriginalMessageInitialized: Boolean
|
||||
get() = true
|
||||
@ -122,7 +123,7 @@ internal class OnlineMessageSourceToStrangerImpl(
|
||||
target: Stranger,
|
||||
) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
|
||||
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
|
||||
|
||||
override val isOriginalMessageInitialized: Boolean
|
||||
get() = true
|
||||
@ -154,7 +155,7 @@ internal class OnlineMessageSourceToTempImpl(
|
||||
target: Member,
|
||||
) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
|
||||
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
|
||||
|
||||
override val isOriginalMessageInitialized: Boolean
|
||||
get() = true
|
||||
@ -183,7 +184,7 @@ internal class OnlineMessageSourceToGroupImpl(
|
||||
override val target: Group,
|
||||
providedSequenceIds: IntArray? = null,
|
||||
) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal, OutgoingMessageSourceInternal {
|
||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
|
||||
object Serializer : KSerializer<MessageSource> by MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
|
||||
|
||||
override val isOriginalMessageInitialized: Boolean
|
||||
get() = true
|
||||
|
@ -195,7 +195,7 @@ internal class MessageSerializationTest : AbstractTest() {
|
||||
serializersModule = module
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
val source = j.decodeFromString(MessageSource.Serializer, a)
|
||||
val source = j.decodeFromString(MessageSerializers.serializersModule.serializer<MessageSource>(), a)
|
||||
println(source.structureToString())
|
||||
assertEquals(
|
||||
expected = Mirai.buildMessageSource(692928873, MessageSourceKind.GROUP) {
|
||||
|
@ -13,6 +13,14 @@ import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.encoding.decodeStructure
|
||||
import kotlinx.serialization.encoding.encodeStructure
|
||||
import kotlinx.serialization.json.*
|
||||
import net.mamoe.mirai.contact.ContactOrBot
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
import net.mamoe.mirai.contact.Group
|
||||
@ -41,10 +49,14 @@ import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||
import net.mamoe.mirai.internal.notice.processors.GroupExtensions
|
||||
import net.mamoe.mirai.internal.test.runBlockingUnit
|
||||
import net.mamoe.mirai.internal.testFramework.dynamicTest
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString
|
||||
import net.mamoe.mirai.utils.*
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.test.*
|
||||
|
||||
@OptIn(TestOnly::class)
|
||||
@ -352,4 +364,294 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
open val format: Json
|
||||
// `serializersModule` is volatile, always return new Json instances.
|
||||
get() = Json {
|
||||
prettyPrint = true
|
||||
serializersModule = MessageSerializers.serializersModule
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization - polymorphism
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
interface PolymorphicWrapper {
|
||||
val message: SingleMessage
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expectedSerialName also known as *poly discriminator*, give `null` to check for no discriminator's presence
|
||||
*/
|
||||
protected open fun <M : SingleMessage, P : PolymorphicWrapper> testPolymorphicIn(
|
||||
polySerializer: KSerializer<P>,
|
||||
polyConstructor: (message: M) -> P,
|
||||
data: M,
|
||||
expectedSerialName: String?,
|
||||
expectedInstance: M = data,
|
||||
) {
|
||||
val string = format.encodeToString(
|
||||
polySerializer,
|
||||
polyConstructor(data)
|
||||
)
|
||||
println(string)
|
||||
var element = format.parseToJsonElement(string)
|
||||
element as JsonObject
|
||||
element = element["message"] as JsonObject
|
||||
if (expectedSerialName != null) {
|
||||
assertEquals(expectedSerialName, element["type"]?.cast<JsonPrimitive>()?.content)
|
||||
} else {
|
||||
assertEquals(null, element["type"])
|
||||
}
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
format.decodeFromString(polySerializer, string).message
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperSingleMessage(
|
||||
override val message: @Polymorphic SingleMessage
|
||||
) : PolymorphicWrapper
|
||||
|
||||
protected open fun <M : SingleMessage> testPolymorphicInSingleMessage(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInSingleMessage") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperSingleMessage.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperSingleMessage,
|
||||
data = data,
|
||||
expectedSerialName = expectedSerialName,
|
||||
expectedInstance = expectedInstance
|
||||
)
|
||||
|
||||
})
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperMessageContent(
|
||||
override val message: @Polymorphic MessageContent
|
||||
) : PolymorphicWrapper
|
||||
|
||||
protected open fun <M : MessageContent> testPolymorphicInMessageContent(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInMessageContent") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperMessageContent.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperMessageContent,
|
||||
data = data,
|
||||
expectedSerialName = expectedSerialName,
|
||||
expectedInstance = expectedInstance
|
||||
)
|
||||
})
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperMessageMetadata(
|
||||
override val message: @Polymorphic MessageMetadata
|
||||
) : PolymorphicWrapper
|
||||
|
||||
protected open fun <M : MessageMetadata> testPolymorphicInMessageMetadata(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInMessageMetadata") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperMessageMetadata.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperMessageMetadata,
|
||||
data = data,
|
||||
expectedSerialName = expectedSerialName,
|
||||
expectedInstance = expectedInstance
|
||||
)
|
||||
})
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization - in MessageChain
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected open fun <M : SingleMessage> testInsideMessageChain(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testInsideMessageChain") {
|
||||
val chain = messageChainOf(data)
|
||||
|
||||
val string = chain.serializeToJsonString(format)
|
||||
println(string)
|
||||
val element = format.parseToJsonElement(string).jsonArray.single().jsonObject
|
||||
assertEquals(expectedSerialName, element["type"]?.cast<JsonPrimitive>()?.content)
|
||||
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
MessageChain.deserializeFromJsonString(string).single()
|
||||
)
|
||||
})
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization - contextual
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Serializable
|
||||
data class ContextualWrapper(
|
||||
val message: @Contextual SingleMessage,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GenericTypedWrapper<T : SingleMessage>(
|
||||
val message: T,
|
||||
)
|
||||
|
||||
protected open fun <M : T, T : SingleMessage> testContextual(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
targetType: KClass<out T> = data::class,
|
||||
) = listOf(
|
||||
testContextualWithWrapper<M, T>(data, expectedSerialName, expectedInstance),
|
||||
testContextualWithStaticType(targetType, data, expectedInstance),
|
||||
testContextualGeneric(targetType, data, expectedInstance),
|
||||
testContextualWithoutWrapper<M, T>(data, expectedInstance)
|
||||
).flatten()
|
||||
|
||||
private fun <M : T, T : SingleMessage> testContextualWithoutWrapper(
|
||||
data: M,
|
||||
expectedInstance: M
|
||||
) = listOf(dynamicTest("testContextualWithoutWrapper") {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val serializer = ContextualSerializer(data::class) as KSerializer<M>
|
||||
val string = format.encodeToString(serializer, data)
|
||||
println(string)
|
||||
val element = format.parseToJsonElement(string).jsonObject
|
||||
assertEquals(null, element["type"])
|
||||
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
format.decodeFromString(serializer, string)
|
||||
)
|
||||
})
|
||||
|
||||
private fun <M : T, T : SingleMessage> testContextualGeneric(
|
||||
targetType: KClass<out T>,
|
||||
data: M,
|
||||
expectedInstance: M
|
||||
) = listOf(dynamicTest("testContextualGeneric") {
|
||||
val messageSerializer: ContextualSerializer<SingleMessage> = ContextualSerializer(targetType).cast()
|
||||
|
||||
/**
|
||||
* ```
|
||||
* data class StaticTypedWrapper(
|
||||
* val message: T
|
||||
* )
|
||||
* ```
|
||||
* without concern of generic types.
|
||||
*/
|
||||
/**
|
||||
* ```
|
||||
* data class StaticTypedWrapper(
|
||||
* val message: T
|
||||
* )
|
||||
* ```
|
||||
* without concern of generic types.
|
||||
*/
|
||||
val serializer = GenericTypedWrapper.serializer(messageSerializer)
|
||||
|
||||
val string = format.encodeToString(serializer, GenericTypedWrapper(data))
|
||||
println(string)
|
||||
val element = format.parseToJsonElement(string).jsonObject["message"]!!.jsonObject
|
||||
|
||||
assertEquals(null, element["type"]?.jsonPrimitive?.content)
|
||||
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
format.decodeFromString(serializer, string).message
|
||||
)
|
||||
})
|
||||
|
||||
private fun <M : T, T : SingleMessage> testContextualWithStaticType(
|
||||
targetType: KClass<out T>,
|
||||
data: M,
|
||||
expectedInstance: M,
|
||||
) = listOf(dynamicTest("testContextualWithStaticType") {
|
||||
val messageSerializer: ContextualSerializer<SingleMessage> = ContextualSerializer(targetType).cast()
|
||||
|
||||
/**
|
||||
* ```
|
||||
* data class StaticTypedWrapper(
|
||||
* val message: T
|
||||
* )
|
||||
* ```
|
||||
* without concern of generic types.
|
||||
*/
|
||||
/**
|
||||
* ```
|
||||
* data class StaticTypedWrapper(
|
||||
* val message: T
|
||||
* )
|
||||
* ```
|
||||
* without concern of generic types.
|
||||
*/
|
||||
val serializer = object : KSerializer<SingleMessage> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("StaticTypedWrapper") {
|
||||
element("message", messageSerializer.descriptor, listOf(Contextual()))
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): SingleMessage {
|
||||
decoder.decodeStructure(descriptor) {
|
||||
if (this.decodeSequentially()) {
|
||||
return this.decodeSerializableElement(
|
||||
descriptor.getElementDescriptor(0),
|
||||
0,
|
||||
messageSerializer
|
||||
)
|
||||
} else {
|
||||
val index = this.decodeElementIndex(descriptor)
|
||||
check(index == 0)
|
||||
return this.decodeSerializableElement(descriptor, index, messageSerializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(encoder: Encoder, value: SingleMessage) {
|
||||
encoder.encodeStructure(descriptor) {
|
||||
encodeSerializableElement(descriptor, 0, messageSerializer, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val string = format.encodeToString(serializer, data)
|
||||
println(string)
|
||||
val element = format.parseToJsonElement(string).jsonObject["message"]!!.jsonObject
|
||||
|
||||
assertEquals(null, element["type"]?.jsonPrimitive?.content)
|
||||
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
format.decodeFromString(serializer, string)
|
||||
)
|
||||
})
|
||||
|
||||
private fun <M : T, T : SingleMessage> testContextualWithWrapper(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M,
|
||||
afterSerialization: (element: JsonObject) -> Unit = {}
|
||||
) = listOf(dynamicTest("testContextualWithWrapper") {
|
||||
val string = format.encodeToString(ContextualWrapper.serializer(), ContextualWrapper(data))
|
||||
println(string)
|
||||
val element = format.parseToJsonElement(string).jsonObject["message"]!!.jsonObject
|
||||
afterSerialization(element)
|
||||
|
||||
assertEquals(expectedSerialName, element["type"]?.jsonPrimitive?.content)
|
||||
|
||||
assertEquals(
|
||||
expectedInstance,
|
||||
format.decodeFromString(ContextualWrapper.serializer(), string).message
|
||||
)
|
||||
})
|
||||
}
|
@ -82,4 +82,17 @@ internal class CustomMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
message(MyCustomMessage(1))
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
// not supported, see https://github.com/mamoe/mirai/issues/2144
|
||||
// @TestFactory
|
||||
// fun `test serialization`(): DynamicTestsResult {
|
||||
// val data = MyCustomMessage(1)
|
||||
// val serialName = "CustomMessage"
|
||||
// return runDynamicTests(
|
||||
// testPolymorphicInMessageMetadata(data, serialName),
|
||||
// testPolymorphicInSingleMessage(data, serialName),
|
||||
// testInsideMessageChain(data, serialName),
|
||||
// testContextual(data),
|
||||
// )
|
||||
// }
|
||||
}
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.decodeAndRefineLight
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.Face
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.message.data.messageChainOf
|
||||
@ -60,4 +63,15 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
||||
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization`(): DynamicTestsResult {
|
||||
val data = Face(1)
|
||||
val serialName = Face.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.FileMessage
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
@ -61,4 +64,16 @@ internal class FileMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
useOrdinaryEquality()
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization`(): DynamicTestsResult {
|
||||
val data = FileMessage("id", 1, "name", 2)
|
||||
val serialName = FileMessage.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
Binary file not shown.
@ -14,6 +14,9 @@ import net.mamoe.mirai.internal.message.LightMessageRefiner.dropMiraiInternalFla
|
||||
import net.mamoe.mirai.internal.message.data.ForwardMessageInternal
|
||||
import net.mamoe.mirai.internal.message.flags.IgnoreLengthCheck
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.castUp
|
||||
@ -143,4 +146,23 @@ internal class ForwardMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization`(): DynamicTestsResult {
|
||||
val data = buildForwardMessage(defaultTarget.castUp()) {
|
||||
add(1, "senderName", time = 123, message = PlainText("simple text"))
|
||||
add(1, "senderName", time = 123) {
|
||||
+PlainText("simple")
|
||||
+Face(1)
|
||||
+Image("{90CCED1C-2D64-313B-5D66-46625CAB31D7}.jpg")
|
||||
}
|
||||
}
|
||||
val serialName = ForwardMessage.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
@ -10,13 +10,22 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.serialization.Polymorphic
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.image.OfflineFriendImage
|
||||
import net.mamoe.mirai.internal.message.image.OfflineGroupImage
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.dynamicTest
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.ImageType
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertIs
|
||||
|
||||
internal class ImageProtocolTest : AbstractMessageProtocolTest() {
|
||||
override val protocols: Array<out MessageProtocol> = arrayOf(ImageProtocol())
|
||||
@ -563,4 +572,67 @@ internal class ImageProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperImage(
|
||||
override val message: @Polymorphic Image
|
||||
) : PolymorphicWrapper
|
||||
|
||||
private fun <M : Image> testPolymorphicInImage(
|
||||
data: M,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInImage") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperImage.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperImage,
|
||||
data = data,
|
||||
expectedSerialName = null,
|
||||
expectedInstance = expectedInstance,
|
||||
)
|
||||
})
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for OfflineGroupImage`(): DynamicTestsResult {
|
||||
val data = Image("{90CCED1C-2D64-313B-5D66-46625CAB31D7}.jpg")
|
||||
assertIs<OfflineGroupImage>(data)
|
||||
val serialName = Image.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInImage(data),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for OfflineFriendImage type 1`(): DynamicTestsResult {
|
||||
val data = Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f") // type 1
|
||||
assertIs<OfflineFriendImage>(data)
|
||||
val serialName = Image.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInImage(data),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for OfflineFriendImage type 2`(): DynamicTestsResult {
|
||||
val data = Image("/000000000-3814297509-BFB7027B9354B8F899A062061D74E206") // type 1
|
||||
assertIs<OfflineFriendImage>(data)
|
||||
val serialName = Image.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInImage(data),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
@ -134,4 +134,6 @@ internal class LongMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// should add tests for refining received LongMessage to normal messages (with a MessageOrigin)
|
||||
}
|
@ -10,10 +10,17 @@
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.serialization.Polymorphic
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.data.MarketFaceImpl
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.dynamicTest
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.Dice
|
||||
import net.mamoe.mirai.message.data.MarketFace
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
@ -194,5 +201,84 @@ internal class MarketFaceProtocolTest : AbstractMessageProtocolTest() {
|
||||
}.doBothChecks()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperMarketFace(
|
||||
override val message: @Polymorphic MarketFace
|
||||
) : PolymorphicWrapper
|
||||
|
||||
@Serializable
|
||||
data class StaticWrapperDice(
|
||||
override val message: Dice
|
||||
) : PolymorphicWrapper
|
||||
|
||||
private fun <M : MarketFace> testPolymorphicInMarketFace(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInMarketFace") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperMarketFace.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperMarketFace,
|
||||
data = data,
|
||||
expectedSerialName = expectedSerialName, // MarketFaceImpl is 'MarketFace', Dice is 'Dice', should include discriminator
|
||||
expectedInstance = expectedInstance,
|
||||
)
|
||||
})
|
||||
|
||||
private fun testStaticDice(
|
||||
data: Dice,
|
||||
expectedInstance: Dice = data,
|
||||
) = listOf(dynamicTest("testStaticDice") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = StaticWrapperDice.serializer(),
|
||||
polyConstructor = ::StaticWrapperDice,
|
||||
data = data,
|
||||
expectedSerialName = null,
|
||||
expectedInstance = expectedInstance,
|
||||
)
|
||||
})
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for MarketFaceImpl`(): DynamicTestsResult {
|
||||
val data = MarketFaceImpl(
|
||||
net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MarketFace(
|
||||
faceName = "5B E5 8F 91 E5 91 86 5D".hexToBytes(),
|
||||
itemType = 6,
|
||||
faceInfo = 1,
|
||||
faceId = "71 26 44 B5 27 94 46 11 99 8A EC 31 86 75 19 D2".hexToBytes(),
|
||||
tabId = 10278,
|
||||
subType = 3,
|
||||
key = "726a53a5372b7289".toByteArray(), /* 37 32 36 61 35 33 61 35 33 37 32 62 37 32 38 39 */
|
||||
imageWidth = 200,
|
||||
imageHeight = 200,
|
||||
pbReserve = "0A 06 08 C8 01 10 C8 01 10 64 1A 0B 51 51 E5 A4 A7 E9 BB 84 E8 84 B8 22 40 68 74 74 70 73 3A 2F 2F 7A 62 2E 76 69 70 2E 71 71 2E 63 6F 6D 2F 69 70 3F 5F 77 76 3D 31 36 37 37 38 32 34 31 26 66 72 6F 6D 3D 61 69 6F 45 6D 6F 6A 69 4E 65 77 26 69 64 3D 31 30 38 39 31 30 2A 06 E6 9D A5 E8 87 AA 30 B5 BB B4 E3 0D 38 B5 BB B4 E3 0D 40 01 50 00".hexToBytes(),
|
||||
)
|
||||
)
|
||||
val serialName = MarketFaceImpl.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMarketFace(data, serialName),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName, targetType = MarketFace::class),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for Dice`(): DynamicTestsResult {
|
||||
val data = Dice(1)
|
||||
val serialName = Dice.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMarketFace(data, serialName),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName, targetType = Dice::class),
|
||||
testStaticDice(data),
|
||||
)
|
||||
}
|
||||
}
|
@ -16,6 +16,9 @@ import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageProcessorAdapter
|
||||
import net.mamoe.mirai.internal.pipeline.replaceProcessor
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.LightApp
|
||||
import net.mamoe.mirai.message.data.MessageOrigin
|
||||
import net.mamoe.mirai.message.data.MessageOriginKind
|
||||
@ -116,4 +119,26 @@ internal class MusicShareProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for MusicShare`(): DynamicTestsResult {
|
||||
val data = MusicShare(
|
||||
kind = NeteaseCloudMusic,
|
||||
title = "ジェリーフィッシュ",
|
||||
summary = "Yunomi/ローラーガール",
|
||||
jumpUrl = "https://y.music.163.com/m/song?id=562591636&uct=QK0IOc%2FSCIO8gBNG%2Bwcbsg%3D%3D&app_version=8.7.46",
|
||||
pictureUrl = "http://p1.music.126.net/KaYSb9oYQzhl2XBeJcj8Rg==/109951165125601702.jpg",
|
||||
musicUrl = "http://music.163.com/song/media/outer/url?id=562591636&userid=324076307&sc=wmv&tn=",
|
||||
brief = "[分享]ジェリーフィッシュ",
|
||||
)
|
||||
|
||||
val serialName = MusicShare.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.PokeMessage
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
@ -82,4 +85,20 @@ internal class PokeMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
message(PokeMessage.ChuoYiChuo)
|
||||
}.doEncoderChecks()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for PokeMessage`(): DynamicTestsResult {
|
||||
val data = PokeMessage.ChuoYiChuo
|
||||
val serialName = PokeMessage.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
@ -9,15 +9,19 @@
|
||||
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.dynamicTest
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.internal.utils.runCoroutineInPlace
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.message.data.MessageSource.Key.quote
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import net.mamoe.mirai.message.data.QuoteReply
|
||||
import net.mamoe.mirai.message.data.messageChainOf
|
||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.Test
|
||||
@ -447,4 +451,97 @@ internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for QuoteReply`(): DynamicTestsResult {
|
||||
val source = MessageSourceBuilder()
|
||||
.sender(123)
|
||||
.target(123)
|
||||
.messages {
|
||||
append("test")
|
||||
}
|
||||
.build(123, MessageSourceKind.FRIEND)
|
||||
|
||||
val data = QuoteReply(source)
|
||||
|
||||
val serialName = QuoteReply.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageMetadata(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MessageSource serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// TODO: 2022/7/20 MessageSource 在 MessageMetadata 的 scope 多态序列化后会输出 'type' = 'MessageSource', 这是期望的行为.
|
||||
// 但是在反序列化时会错误 unknown field 'type'
|
||||
override val format: Json
|
||||
get() = Json {
|
||||
prettyPrint = true
|
||||
serializersModule = MessageSerializers.serializersModule
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperMessageSource(
|
||||
override val message: MessageSource
|
||||
) : PolymorphicWrapper
|
||||
|
||||
private fun <M : MessageSource> testPolymorphicInMessageSource(
|
||||
data: M,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInMessageSource") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperMessageSource.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperMessageSource,
|
||||
data = data,
|
||||
expectedInstance = expectedInstance,
|
||||
expectedSerialName = null,
|
||||
)
|
||||
})
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for OfflineMessageSource`(): DynamicTestsResult {
|
||||
val data = MessageSourceBuilder()
|
||||
.sender(123)
|
||||
.target(123)
|
||||
.messages {
|
||||
append("test")
|
||||
}
|
||||
.build(123, MessageSourceKind.FRIEND)
|
||||
|
||||
val serialName = MessageSource.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageSource(data),
|
||||
testPolymorphicInMessageMetadata(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for OnlineMessageSource`(): DynamicTestsResult {
|
||||
val data = onlineIncomingGroupMessage[MessageSource]!!
|
||||
val expected = (data as OnlineMessageSource).toOffline()
|
||||
|
||||
val serialName = MessageSource.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageSource(data, expectedInstance = expected),
|
||||
testPolymorphicInMessageMetadata(data, serialName, expectedInstance = expected),
|
||||
testPolymorphicInSingleMessage(data, serialName, expectedInstance = expected),
|
||||
testInsideMessageChain(data, serialName, expectedInstance = expected),
|
||||
testContextual(data, serialName, expectedInstance = expected),
|
||||
)
|
||||
}
|
||||
}
|
@ -9,8 +9,15 @@
|
||||
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import kotlinx.serialization.Polymorphic
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.dynamicTest
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.RichMessage
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.Test
|
||||
@ -80,4 +87,66 @@ internal class RichMessageProtocolTest : AbstractMessageProtocolTest() {
|
||||
}
|
||||
|
||||
// no encoder. specially handled, no test for now.
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@Serializable
|
||||
data class PolymorphicWrapperRichMessage(
|
||||
override val message: @Polymorphic RichMessage
|
||||
) : PolymorphicWrapper
|
||||
|
||||
private fun <M : RichMessage> testPolymorphicInRichMessage(
|
||||
data: M,
|
||||
expectedSerialName: String,
|
||||
expectedInstance: M = data,
|
||||
) = listOf(dynamicTest("testPolymorphicInRichMessage") {
|
||||
testPolymorphicIn(
|
||||
polySerializer = PolymorphicWrapperRichMessage.serializer(),
|
||||
polyConstructor = ::PolymorphicWrapperRichMessage,
|
||||
data = data,
|
||||
expectedSerialName = expectedSerialName,
|
||||
expectedInstance = expectedInstance
|
||||
)
|
||||
})
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for RichMessage`(): DynamicTestsResult {
|
||||
val data = net.mamoe.mirai.message.data.SimpleServiceMessage(
|
||||
serviceId = 1,
|
||||
content = """
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="1" templateID="123" action="" brief="[分享]ジェリーフィッシュ" sourceMsgId="0" url="https://y.music.163.com/m/song?id=562591636&uct=QK0IOc%2FSCIO8gBNG%2Bwcbsg%3D%3D&app_version=8.7.46" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2" advertiser_id="0" aid="0"><picture cover="http://p1.music.126.net/KaYSb9oYQzhl2XBeJcj8Rg==/109951165125601702.jpg" w="0" h="0" /><title>ジェリーフィッシュ</title><summary>Yunomi/ローラーガール</summary></item><source name="网易云音乐" icon="https://i.gtimg.cn/open/app_icon/00/49/50/85/100495085_100_m.png" action="" a_actionData="tencent100495085://" appid="100495085" /></msg>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val serialName = net.mamoe.mirai.message.data.SimpleServiceMessage.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInRichMessage(data, serialName),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for LightApp`(): DynamicTestsResult {
|
||||
val data = net.mamoe.mirai.message.data.LightApp(
|
||||
content = """
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="1" templateID="123" action="" brief="[分享]ジェリーフィッシュ" sourceMsgId="0" url="https://y.music.163.com/m/song?id=562591636&uct=QK0IOc%2FSCIO8gBNG%2Bwcbsg%3D%3D&app_version=8.7.46" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2" advertiser_id="0" aid="0"><picture cover="http://p1.music.126.net/KaYSb9oYQzhl2XBeJcj8Rg==/109951165125601702.jpg" w="0" h="0" /><title>ジェリーフィッシュ</title><summary>Yunomi/ローラーガール</summary></item><source name="网易云音乐" icon="https://i.gtimg.cn/open/app_icon/00/49/50/85/100495085_100_m.png" action="" a_actionData="tencent100495085://" appid="100495085" /></msg>
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val serialName = net.mamoe.mirai.message.data.LightApp.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInRichMessage(data, serialName),
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.At
|
||||
import net.mamoe.mirai.message.data.AtAll
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
@ -115,4 +118,51 @@ internal class TextProtocolTest : AbstractMessageProtocolTest() {
|
||||
})
|
||||
}.doBothChecks()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for PlainText`(): DynamicTestsResult {
|
||||
val data = PlainText(
|
||||
content = """foo""",
|
||||
)
|
||||
|
||||
val serialName = PlainText.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for At`(): DynamicTestsResult {
|
||||
val data = At(
|
||||
100
|
||||
)
|
||||
|
||||
val serialName = At.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for AtAll`(): DynamicTestsResult {
|
||||
val data = AtAll
|
||||
val serialName = AtAll.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,9 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.testFramework.DynamicTestsResult
|
||||
import net.mamoe.mirai.internal.testFramework.TestFactory
|
||||
import net.mamoe.mirai.internal.testFramework.runDynamicTests
|
||||
import net.mamoe.mirai.message.data.VipFace
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import kotlin.test.BeforeTest
|
||||
@ -48,4 +51,24 @@ internal class VipFaceProtocolTest : AbstractMessageProtocolTest() {
|
||||
useOrdinaryEquality()
|
||||
}.doDecoderChecks()
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// serialization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@TestFactory
|
||||
fun `test serialization for VipFace`(): DynamicTestsResult {
|
||||
val data = VipFace(
|
||||
VipFace.LiuLian, 1
|
||||
)
|
||||
|
||||
val serialName = VipFace.SERIAL_NAME
|
||||
return runDynamicTests(
|
||||
testPolymorphicInMessageContent(data, serialName),
|
||||
testPolymorphicInSingleMessage(data, serialName),
|
||||
testInsideMessageChain(data, serialName),
|
||||
testContextual(data, serialName),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -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.message.serialization
|
||||
|
||||
import net.mamoe.mirai.internal.test.AbstractTest
|
||||
|
||||
internal abstract class AbstractMessageSerializationTest : AbstractTest()
|
Loading…
Reference in New Issue
Block a user