55package kotlinx.serialization.protobuf
66
77import kotlinx.serialization.*
8+ import kotlinx.serialization.descriptors.*
9+ import kotlinx.serialization.encoding.*
810import kotlin.test.*
911
1012class 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