Skip to content

Commit 4150215

Browse files
add some test
1 parent 18768b7 commit 4150215

File tree

3 files changed

+162
-3
lines changed

3 files changed

+162
-3
lines changed

formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoUnknownFields.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public class ProtoMessage internal constructor(
2828
public fun asByteArray(): ByteArray = fields.fold(ByteArray(0)) { acc, protoField -> acc + protoField.asWireContent() }
2929

3030
public constructor(vararg fields: ProtoField) : this(fields.toList())
31+
32+
public operator fun plus(other: ProtoMessage): ProtoMessage = merge(other)
33+
3134
public fun merge(other: ProtoMessage): ProtoMessage {
3235
return ProtoMessage(fields + other.fields)
3336
}
@@ -50,6 +53,21 @@ public class ProtoMessage internal constructor(
5053
}
5154
}
5255

56+
public fun ProtoMessage?.merge(other: ProtoMessage?): ProtoMessage {
57+
return when {
58+
this == null -> other ?: ProtoMessage.Empty
59+
other == null -> this
60+
else -> this + other
61+
}
62+
}
63+
64+
public fun ProtoMessage?.merge(vararg fields: ProtoField): ProtoMessage {
65+
return when {
66+
this == null -> ProtoMessage(fields.toList())
67+
else -> this.merge(ProtoMessage(fields.toList()))
68+
}
69+
}
70+
5371
@OptIn(ExperimentalSerializationApi::class)
5472
@Serializable(with = ProtoFieldSerializer::class)
5573
@KeepGeneratedSerializer

formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,7 @@ internal open class ProtobufDecoder(
381381
}
382382
val serializer = ProtoFieldSerializer
383383
val restoredTag = index2IdMap?.get(unknownHolderIndex)?.let { currentTag.overrideId(it) } ?: currentTag
384-
return serializer.deserialize(this, restoredTag).let {
385-
previous?.merge(it) ?: ProtoMessage(it)
386-
}
384+
return previous.merge(serializer.deserialize(this, restoredTag))
387385
}
388386

389387
internal fun decodeRawElement(): ByteArray {

formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufUnknownFieldsTest.kt

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package kotlinx.serialization.protobuf
66

77
import kotlinx.serialization.*
8+
import kotlinx.serialization.descriptors.*
9+
import kotlinx.serialization.encoding.*
810
import kotlin.test.*
911

1012
class ProtobufUnknownFieldsTest {
@@ -47,6 +49,22 @@ class ProtobufUnknownFieldsTest {
4749
@Test
4850
fun testDecodeWithUnknownField() {
4951
val data = BuildData(42, "42", byteArrayOf(42, 42, 42), listOf(42, 42, 42), InnerData("42", 42, listOf("42", "42", "42")))
52+
53+
/**
54+
* 1: 42
55+
* 2: {"42"}
56+
* 3: {"***"}
57+
* 4: 42
58+
* 4: 42
59+
* 4: 42
60+
* 5: {
61+
* 1: {"42"}
62+
* 2: 42
63+
* 3: {"42"}
64+
* 3: {"42"}
65+
* 3: {"42"}
66+
* }
67+
*/
5068
val encoded = "082a120234321a032a2a2a202a202a202a2a120a023432102a1a0234321a0234321a023432"
5169
val decoded = ProtoBuf.decodeFromHexString(DataWithUnknownFields.serializer(), encoded)
5270
assertEquals(data.a, decoded.a)
@@ -99,14 +117,139 @@ class ProtobufUnknownFieldsTest {
99117
@Test
100118
fun testUnknownFieldBeforeKnownField() {
101119
val data = BuildData(42, "42", byteArrayOf(42, 42, 42), listOf(42, 42, 42), InnerData("42", 42, listOf("42", "42", "42")))
120+
121+
/**
122+
* 1: 42
123+
* 2: {"42"}
124+
* 3: {"***"}
125+
* 4: 42
126+
* 4: 42
127+
* 4: 42
128+
* 5: {
129+
* 1: {"42"}
130+
* 2: 42
131+
* 3: {"42"}
132+
* 3: {"42"}
133+
* 3: {"42"}
134+
* }
135+
* }
136+
*/
102137
val hex = "082a120234321a032a2a2a202a202a202a2a120a023432102a1a0234321a0234321a023432"
103138
val decoded = ProtoBuf.decodeFromHexString(DataWithStaggeredFields.serializer(), hex)
104139
assertEquals(3, decoded.unknownFields.size)
105140
assertEquals("42", decoded.b)
106141
assertEquals(listOf(42, 42, 42), decoded.d)
107142

108143
val encoded = ProtoBuf.encodeToHexString(DataWithStaggeredFields.serializer(), decoded)
144+
/**
145+
* fields are re-ordered but acceptable in protobuf wire data
146+
*
147+
* 2: {"42"}
148+
* 1: 42
149+
* 3: {"***"}
150+
* 5: {
151+
* 1: {"42"}
152+
* 2: 42
153+
* 3: {"42"}
154+
* 3: {"42"}
155+
* 3: {"42"}
156+
* }
157+
* 4: 42
158+
* 4: 42
159+
* 4: 42
160+
*/
161+
assertEquals("12023432082a1a032a2a2a2a120a023432102a1a0234321a0234321a023432202a202a202a", encoded)
109162
val decodeOrigin = ProtoBuf.decodeFromHexString(BuildData.serializer(), encoded)
110163
assertEquals(data, decodeOrigin)
111164
}
165+
166+
@Serializable
167+
data class TotalKnownData(@ProtoUnknownFields val fields: ProtoMessage = ProtoMessage.Empty)
168+
169+
@Serializable
170+
data class NestedUnknownData(val a: Int, @ProtoNumber(5) val inner: TotalKnownData, @ProtoUnknownFields val unknown: ProtoMessage)
171+
172+
@Test
173+
fun testDecodeNestedUnknownData() {
174+
/**
175+
* 1: 42
176+
* 2: {"42"}
177+
* 3: {"***"}
178+
* 4: 42
179+
* 4: 42
180+
* 4: 42
181+
* 5: {
182+
* 1: {"42"}
183+
* 2: 42
184+
* 3: {"42"}
185+
* 3: {"42"}
186+
* 3: {"42"}
187+
* }
188+
*/
189+
val hex = "082a120234321a032a2a2a202a202a202a2a120a023432102a1a0234321a0234321a023432"
190+
val decoded = ProtoBuf.decodeFromHexString(NestedUnknownData.serializer(), hex)
191+
assertEquals(5, decoded.unknown.size)
192+
}
193+
194+
object CustomSerializer: KSerializer<DataWithUnknownFields> {
195+
override val descriptor: SerialDescriptor
196+
get() = buildClassSerialDescriptor("CustomData") {
197+
element<Int>("a", annotations = listOf(ProtoNumber(1)))
198+
element<ProtoMessage>("unknownFields", annotations = listOf(ProtoUnknownFields()))
199+
}
200+
201+
override fun deserialize(decoder: Decoder): DataWithUnknownFields {
202+
var a = 0
203+
var unknownFields = ProtoMessage.Empty
204+
decoder.decodeStructure(descriptor) {
205+
loop@ while (true) {
206+
when (val index = decodeElementIndex(descriptor)) {
207+
CompositeDecoder.DECODE_DONE -> break@loop
208+
0 -> a = decodeIntElement(descriptor, index)
209+
1 -> unknownFields += decodeSerializableElement(descriptor, index, ProtoMessage.serializer())
210+
else -> error("Unexpected index: $index")
211+
}
212+
}
213+
}
214+
return DataWithUnknownFields(a, unknownFields)
215+
}
216+
217+
override fun serialize(encoder: Encoder, value: DataWithUnknownFields) {
218+
encoder.encodeStructure(descriptor) {
219+
encodeIntElement(descriptor, 0, value.a)
220+
encodeSerializableElement(descriptor, 1, ProtoMessage.serializer(), value.unknownFields)
221+
}
222+
}
223+
}
224+
225+
@Test
226+
fun testCustomSerializer() {
227+
val data = BuildData(42, "42", byteArrayOf(42, 42, 42), listOf(42, 42, 42), InnerData("42", 42, listOf("42", "42", "42")))
228+
229+
/**
230+
* 1: 42
231+
* 2: {"42"}
232+
* 3: {"***"}
233+
* 4: 42
234+
* 4: 42
235+
* 4: 42
236+
* 5: {
237+
* 1: {"42"}
238+
* 2: 42
239+
* 3: {"42"}
240+
* 3: {"42"}
241+
* 3: {"42"}
242+
* }
243+
*/
244+
val encoded = "082a120234321a032a2a2a202a202a202a2a120a023432102a1a0234321a0234321a023432"
245+
val decoded = ProtoBuf.decodeFromHexString(CustomSerializer, encoded)
246+
247+
assertEquals(data.a, decoded.a)
248+
assertEquals(6, decoded.unknownFields.size)
249+
250+
val encoded2 = ProtoBuf.encodeToHexString(CustomSerializer, decoded)
251+
assertEquals(encoded, encoded2)
252+
val data2 = ProtoBuf.decodeFromHexString(BuildData.serializer(), encoded2)
253+
assertEquals(data, data2)
254+
}
112255
}

0 commit comments

Comments
 (0)