Skip to content

Commit 46227cf

Browse files
committed
CBOR: Refactor Tests
1 parent 15bbc3f commit 46227cf

File tree

2 files changed

+404
-384
lines changed

2 files changed

+404
-384
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
/*
2+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
@file:OptIn(ExperimentalUnsignedTypes::class)
6+
7+
package kotlinx.serialization.cbor
8+
9+
import kotlinx.serialization.*
10+
import kotlinx.serialization.SimpleSealed.*
11+
import kotlinx.serialization.cbor.internal.*
12+
import kotlin.test.*
13+
14+
class CborDecoderTest {
15+
16+
private val ignoreUnknownKeys = Cbor { ignoreUnknownKeys = true }
17+
18+
@Test
19+
fun testDecodeSimpleObject() {
20+
assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), "bf616163737472ff"))
21+
}
22+
23+
@Test
24+
fun testDecodeComplicatedObject() {
25+
val test = TypesUmbrella(
26+
"Hello, world!",
27+
42,
28+
null,
29+
listOf("a", "b"),
30+
mapOf(1 to true, 2 to false),
31+
Simple("lol"),
32+
listOf(Simple("kek")),
33+
HexConverter.parseHexBinary("cafe"),
34+
HexConverter.parseHexBinary("cafe")
35+
)
36+
// with maps, lists & strings of indefinite length
37+
assertEquals(
38+
test, Cbor.decodeFromHexString(
39+
TypesUmbrella.serializer(),
40+
"bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
41+
)
42+
)
43+
// with maps, lists & strings of definite length
44+
assertEquals(
45+
test, Cbor.decodeFromHexString(
46+
TypesUmbrella.serializer(),
47+
"a9646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c6a62797465537472696e6742cafe6962797465417272617982383521"
48+
)
49+
)
50+
}
51+
52+
@Test
53+
fun testReadByteStringWhenNullable() {
54+
/* A1 # map(1)
55+
* 6A # text(10)
56+
* 62797465537472696E67 # "byteString"
57+
* 44 # bytes(4)
58+
* 01020304 # "\x01\x02\x03\x04"
59+
*/
60+
assertEquals(
61+
expected = NullableByteString(byteArrayOf(1, 2, 3, 4)),
62+
actual = Cbor.decodeFromHexString(
63+
deserializer = NullableByteString.serializer(),
64+
hex = "a16a62797465537472696e674401020304"
65+
)
66+
)
67+
68+
/* A1 # map(1)
69+
* 6A # text(10)
70+
* 62797465537472696E67 # "byteString"
71+
* F6 # primitive(22)
72+
*/
73+
assertEquals(
74+
expected = NullableByteString(byteString = null),
75+
actual = Cbor.decodeFromHexString(
76+
deserializer = NullableByteString.serializer(),
77+
hex = "a16a62797465537472696e67f6"
78+
)
79+
)
80+
}
81+
82+
@Test
83+
fun testNullables() {
84+
Cbor.decodeFromHexString<NullableByteStringDefaultNull>("a0")
85+
}
86+
87+
/**
88+
* CBOR hex data represents serialized versions of [TypesUmbrella] (which does **not** have a root property 'a') so
89+
* decoding to [Simple] (which has the field 'a') is expected to fail.
90+
*/
91+
@Test
92+
fun testIgnoreUnknownKeysFailsWhenCborDataIsMissingKeysThatArePresentInKotlinClass() {
93+
// with maps & lists of indefinite length
94+
assertFailsWithMessage<SerializationException>("Field 'a' is required") {
95+
ignoreUnknownKeys.decodeFromHexString(
96+
Simple.serializer(),
97+
"bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffffff"
98+
)
99+
}
100+
101+
// with maps & lists of definite length
102+
assertFailsWithMessage<SerializationException>("Field 'a' is required") {
103+
ignoreUnknownKeys.decodeFromHexString(
104+
Simple.serializer(),
105+
"a7646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c"
106+
)
107+
}
108+
}
109+
110+
@Test
111+
fun testIgnoreUnknownKeysFailsWhenDecodingIncompleteCbor() {
112+
/* A3 # map(3)
113+
* 63 # text(3)
114+
* 737472 # "str"
115+
* 66 # text(6)
116+
* 737472696E67 # "string"
117+
* 61 # text(1)
118+
* 69 # "i"
119+
* 00 # unsigned(0)
120+
* 66 # text(6)
121+
* 69676E6F7265 # "ignore"
122+
* (missing value associated with "ignore" key)
123+
*/
124+
assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
125+
ignoreUnknownKeys.decodeFromHexString(
126+
TypesUmbrella.serializer(),
127+
"a36373747266737472696e676169006669676e6f7265"
128+
)
129+
}
130+
131+
/* A3 # map(3)
132+
* 63 # text(3)
133+
* 737472 # "str"
134+
* 66 # text(6)
135+
* 737472696E67 # "string"
136+
* 61 # text(1)
137+
* 69 # "i"
138+
* 00 # unsigned(0)
139+
* 66 # text(6)
140+
* 69676E6F7265 # "ignore"
141+
* A2 # map(2)
142+
* (missing map contents associated with "ignore" key)
143+
*/
144+
assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
145+
ignoreUnknownKeys.decodeFromHexString(
146+
TypesUmbrella.serializer(),
147+
"a36373747266737472696e676169006669676e6f7265a2"
148+
)
149+
}
150+
}
151+
152+
@Test
153+
fun testIgnoreUnknownKeysFailsWhenEncounteringPreemptiveBreak() {
154+
/* A3 # map(3)
155+
* 63 # text(3)
156+
* 737472 # "str"
157+
* 66 # text(6)
158+
* 737472696E67 # "string"
159+
* 66 # text(6)
160+
* 69676E6F7265 # "ignore"
161+
* FF # primitive(*)
162+
*/
163+
assertFailsWithMessage<CborDecodingException>("Expected next data item, but found FF") {
164+
ignoreUnknownKeys.decodeFromHexString(
165+
TypesUmbrella.serializer(),
166+
"a36373747266737472696e676669676e6f7265ff"
167+
)
168+
}
169+
}
170+
171+
172+
@Test
173+
fun testDecodeCborWithUnknownField() {
174+
assertEquals(
175+
expected = Simple("123"),
176+
actual = ignoreUnknownKeys.decodeFromHexString(
177+
deserializer = Simple.serializer(),
178+
179+
/* BF # map(*)
180+
* 61 # text(1)
181+
* 61 # "a"
182+
* 63 # text(3)
183+
* 313233 # "123"
184+
* 61 # text(1)
185+
* 62 # "b"
186+
* 63 # text(3)
187+
* 393837 # "987"
188+
* FF # primitive(*)
189+
*/
190+
hex = "bf616163313233616263393837ff"
191+
)
192+
)
193+
}
194+
195+
@Test
196+
fun testDecodeCborWithUnknownNestedIndefiniteFields() {
197+
assertEquals(
198+
expected = Simple("123"),
199+
actual = ignoreUnknownKeys.decodeFromHexString(
200+
deserializer = Simple.serializer(),
201+
202+
/* BF # map(*)
203+
* 61 # text(1)
204+
* 61 # "a"
205+
* 63 # text(3)
206+
* 313233 # "123"
207+
* 61 # text(1)
208+
* 62 # "b"
209+
* BF # map(*)
210+
* 7F # text(*)
211+
* 61 # text(1)
212+
* 78 # "x"
213+
* FF # primitive(*)
214+
* A1 # map(1)
215+
* 61 # text(1)
216+
* 79 # "y"
217+
* 0A # unsigned(10)
218+
* FF # primitive(*)
219+
* 61 # text(1)
220+
* 63 # "c"
221+
* 9F # array(*)
222+
* 01 # unsigned(1)
223+
* 02 # unsigned(2)
224+
* 03 # unsigned(3)
225+
* FF # primitive(*)
226+
* FF # primitive(*)
227+
*/
228+
hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
229+
)
230+
)
231+
}
232+
233+
/**
234+
* The following CBOR diagnostic output demonstrates the additional fields (prefixed with `+` in front of each line)
235+
* present in the encoded CBOR data that does not have associated fields in the Kotlin classes (they will be skipped
236+
* over with `ignoreUnknownKeys` is enabled).
237+
*
238+
* ```diff
239+
* {
240+
* + "extra": [
241+
* + 9,
242+
* + 8,
243+
* + 7
244+
* + ],
245+
* "boxed": [
246+
* [
247+
* "kotlinx.serialization.SimpleSealed.SubSealedA",
248+
* {
249+
* "s": "a",
250+
* + "newA": {
251+
* + "x": 1,
252+
* + "y": 2
253+
* + }
254+
* }
255+
* ],
256+
* [
257+
* "kotlinx.serialization.SimpleSealed.SubSealedB",
258+
* {
259+
* "i": 1
260+
* }
261+
* ]
262+
* ]
263+
* }
264+
* ```
265+
*/
266+
@Test
267+
fun testDecodeCborWithUnknownKeysInSealedClasses() {
268+
/* BF # map(*)
269+
* 65 # text(5)
270+
* 6578747261 # "extra"
271+
* 83 # array(3)
272+
* 09 # unsigned(9)
273+
* 08 # unsigned(8)
274+
* 07 # unsigned(7)
275+
* 65 # text(5)
276+
* 626F786564 # "boxed"
277+
* 9F # array(*)
278+
* 9F # array(*)
279+
* 78 2D # text(45)
280+
* 6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656441 # "kotlinx.serialization.SimpleSealed.SubSealedA"
281+
* BF # map(*)
282+
* 61 # text(1)
283+
* 73 # "s"
284+
* 61 # text(1)
285+
* 61 # "a"
286+
* 64 # text(4)
287+
* 6E657741 # "newA"
288+
* BF # map(*)
289+
* 61 # text(1)
290+
* 78 # "x"
291+
* 01 # unsigned(1)
292+
* 61 # text(1)
293+
* 79 # "y"
294+
* 02 # unsigned(2)
295+
* FF # primitive(*)
296+
* FF # primitive(*)
297+
* FF # primitive(*)
298+
* 9F # array(*)
299+
* 78 2D # text(45)
300+
* 6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656442 # "kotlinx.serialization.SimpleSealed.SubSealedB"
301+
* BF # map(*)
302+
* 61 # text(1)
303+
* 69 # "i"
304+
* 01 # unsigned(1)
305+
* FF # primitive(*)
306+
* FF # primitive(*)
307+
* FF # primitive(*)
308+
* FF # primitive(*)
309+
*/
310+
311+
assertEquals(
312+
expected = SealedBox(
313+
listOf(
314+
SubSealedA("a"),
315+
SubSealedB(1)
316+
)
317+
),
318+
actual = ignoreUnknownKeys.decodeFromHexString(
319+
SealedBox.serializer(),
320+
"bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
321+
)
322+
)
323+
}
324+
325+
@Test
326+
fun testReadCustomByteString() {
327+
assertEquals(
328+
expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
329+
actual = Cbor.decodeFromHexString("bf617843112233ff")
330+
)
331+
}
332+
333+
@Test
334+
fun testReadNullableCustomByteString() {
335+
assertEquals(
336+
expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
337+
actual = Cbor.decodeFromHexString("bf617843112233ff")
338+
)
339+
}
340+
341+
@Test
342+
fun testReadNullCustomByteString() {
343+
assertEquals(
344+
expected = TypeWithNullableCustomByteString(null),
345+
actual = Cbor.decodeFromHexString("bf6178f6ff")
346+
)
347+
}
348+
349+
@Test
350+
fun testReadValueClassWithByteString() {
351+
assertContentEquals(
352+
expected = byteArrayOf(0x11, 0x22, 0x33),
353+
actual = Cbor.decodeFromHexString<ValueClassWithByteString>("43112233").x
354+
)
355+
}
356+
357+
@Test
358+
fun testReadValueClassCustomByteString() {
359+
assertEquals(
360+
expected = ValueClassWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
361+
actual = Cbor.decodeFromHexString("43112233")
362+
)
363+
}
364+
365+
@Test
366+
fun testReadValueClassWithUnlabeledByteString() {
367+
assertContentEquals(
368+
expected = byteArrayOf(
369+
0x11,
370+
0x22,
371+
0x33
372+
),
373+
actual = Cbor.decodeFromHexString<ValueClassWithUnlabeledByteString>("43112233").x.x
374+
)
375+
}
376+
377+
}

0 commit comments

Comments
 (0)