blob: d72a1b69ac26517df2f5dc8b33f228c0a3578ebe [file] [log] [blame]
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01001package eu.mulk.jgvariant.core;
2
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +01003import static java.nio.ByteOrder.BIG_ENDIAN;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01004import static java.nio.ByteOrder.LITTLE_ENDIAN;
5import static java.nio.charset.StandardCharsets.UTF_8;
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +01006import static org.junit.jupiter.api.Assertions.assertAll;
7import static org.junit.jupiter.api.Assertions.assertArrayEquals;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +01008import static org.junit.jupiter.api.Assertions.assertEquals;
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +01009import static org.junit.jupiter.api.Assertions.assertThrows;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010010
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010011import java.nio.ByteBuffer;
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +010012import java.text.ParseException;
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010013import java.util.List;
14import java.util.Optional;
15import org.junit.jupiter.api.Test;
16
17/**
18 * Tests based on the examples given in <a
19 * href="https://people.gnome.org/~desrt/gvariant-serialisation.pdf">~desrt/gvariant-serialisation.pdf</a>.
20 */
21class DecoderTest {
22
23 @Test
24 void testString() {
25 var data = new byte[] {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00};
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010026 var decoder = Decoder.ofString(UTF_8);
27 assertEquals("hello world", decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010028 }
29
30 @Test
31 void testMaybe() {
32 var data =
33 new byte[] {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0x00};
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010034 var decoder = Decoder.ofMaybe(Decoder.ofString(UTF_8));
35 assertEquals(Optional.of("hello world"), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010036 }
37
38 @Test
39 void testBooleanArray() {
40 var data = new byte[] {0x01, 0x00, 0x00, 0x01, 0x01};
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010041 var decoder = Decoder.ofArray(Decoder.ofBoolean());
42 assertEquals(List.of(true, false, false, true, true), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010043 }
44
45 @Test
46 void testStructure() {
47 var data =
48 new byte[] {
49 0x66, 0x6F, 0x6F, 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x04
50 };
51
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010052 record TestRecord(String s, int i) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010053
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010054 var decoder = Decoder.ofStructure(TestRecord.class, Decoder.ofString(UTF_8), Decoder.ofInt());
55 assertEquals(new TestRecord("foo", -1), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010056 }
57
58 @Test
59 void testComplexStructureArray() {
60 var data =
61 new byte[] {
62 0x68,
63 0x69,
64 0x00,
65 0x00,
66 (byte) 0xfe,
67 (byte) 0xff,
68 (byte) 0xff,
69 (byte) 0xff,
70 0x03,
71 0x00,
72 0x00,
73 0x00,
74 0x62,
75 0x79,
76 0x65,
77 0x00,
78 (byte) 0xff,
79 (byte) 0xff,
80 (byte) 0xff,
81 (byte) 0xff,
82 0x04,
83 0x09,
84 0x15
85 };
86
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010087 record TestRecord(String s, int i) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010088
89 var decoder =
90 Decoder.ofArray(
91 Decoder.ofStructure(
92 TestRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010093 Decoder.ofString(UTF_8),
94 Decoder.ofInt().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010095 assertEquals(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +010096 List.of(new TestRecord("hi", -2), new TestRecord("bye", -1)),
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +010097 decoder.decode(ByteBuffer.wrap(data)));
98 }
99
100 @Test
101 void testStringArray() {
102 var data =
103 new byte[] {
104 0x69, 0x00, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E,
105 0x67, 0x73, 0x3F, 0x00, 0x02, 0x06, 0x0a, 0x13
106 };
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100107 var decoder = Decoder.ofArray(Decoder.ofString(UTF_8));
108 assertEquals(List.of("i", "can", "has", "strings?"), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100109 }
110
111 @Test
112 void testNestedStructure() {
113 var data =
114 new byte[] {
115 0x69, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
116 0x73, 0x3F, 0x00, 0x04, 0x0d, 0x05
117 };
118
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100119 record TestChild(byte b, String s) {}
120 record TestParent(TestChild tc, List<String> as) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100121
122 var decoder =
123 Decoder.ofStructure(
124 TestParent.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100125 Decoder.ofStructure(TestChild.class, Decoder.ofByte(), Decoder.ofString(UTF_8)),
126 Decoder.ofArray(Decoder.ofString(UTF_8)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100127
128 assertEquals(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100129 new TestParent(new TestChild((byte) 0x69, "can"), List.of("has", "strings?")),
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100130 decoder.decode(ByteBuffer.wrap(data)));
131 }
132
133 @Test
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100134 void testNestedStructureVariant() {
135 var data =
136 new byte[] {
137 0x69, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
138 0x73, 0x3F, 0x00, 0x04, 0x0d, 0x05, 0x00, 0x28, 0x28, 0x79, 0x73, 0x29, 0x61, 0x73, 0x29
139 };
140
141 var decoder = Decoder.ofVariant();
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100142 var variant = decoder.decode(ByteBuffer.wrap(data));
143 var result = (Object[]) variant.value();
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100144
145 assertAll(
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100146 () -> assertEquals(Signature.parse("((ys)as)"), variant.signature()),
Matthias Andreas Benkard55c34812021-12-14 21:51:10 +0100147 () -> assertEquals(2, result.length),
148 () -> assertArrayEquals(new Object[] {(byte) 0x69, "can"}, (Object[]) result[0]),
149 () -> assertEquals(List.of("has", "strings?"), result[1]));
150 }
151
152 @Test
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100153 void testSimpleStructure() {
154 var data = new byte[] {0x60, 0x70};
155
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100156 record TestRecord(byte b1, byte b2) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100157
158 var decoder =
159 Decoder.ofStructure(
160 TestRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100161 Decoder.ofByte().withByteOrder(LITTLE_ENDIAN),
162 Decoder.ofByte().withByteOrder(LITTLE_ENDIAN));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100163
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100164 assertEquals(new TestRecord((byte) 0x60, (byte) 0x70), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100165 }
166
167 @Test
168 void testPaddedStructureRight() {
169 var data = new byte[] {0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00};
170
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100171 record TestRecord(int b1, byte b2) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100172
173 var decoder =
174 Decoder.ofStructure(
175 TestRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100176 Decoder.ofInt().withByteOrder(LITTLE_ENDIAN),
177 Decoder.ofByte().withByteOrder(LITTLE_ENDIAN));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100178
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100179 assertEquals(new TestRecord(0x60, (byte) 0x70), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100180 }
181
182 @Test
183 void testPaddedStructureLeft() {
184 var data = new byte[] {0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00};
185
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100186 record TestRecord(byte b1, int b2) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100187
188 var decoder =
189 Decoder.ofStructure(
190 TestRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100191 Decoder.ofByte().withByteOrder(LITTLE_ENDIAN),
192 Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100193
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100194 assertEquals(new TestRecord((byte) 0x60, 0x70), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100195 }
196
197 @Test
198 void testSimpleStructureArray() {
199 var data =
200 new byte[] {
201 0x60,
202 0x00,
203 0x00,
204 0x00,
205 0x70,
206 0x00,
207 0x00,
208 0x00,
209 (byte) 0x88,
210 0x02,
211 0x00,
212 0x00,
213 (byte) 0xF7,
214 0x00,
215 0x00,
216 0x00
217 };
218
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100219 record TestRecord(int b1, byte b2) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100220
221 var decoder =
222 Decoder.ofArray(
223 Decoder.ofStructure(
224 TestRecord.class,
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100225 Decoder.ofInt().withByteOrder(LITTLE_ENDIAN),
226 Decoder.ofByte().withByteOrder(LITTLE_ENDIAN)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100227
228 assertEquals(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100229 List.of(new TestRecord(96, (byte) 0x70), new TestRecord(648, (byte) 0xf7)),
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100230 decoder.decode(ByteBuffer.wrap(data)));
231 }
232
233 @Test
234 void testByteArray() {
235 var data = new byte[] {0x04, 0x05, 0x06, 0x07};
236
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100237 var decoder = Decoder.ofArray(Decoder.ofByte());
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100238
239 assertEquals(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100240 List.of((byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07),
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100241 decoder.decode(ByteBuffer.wrap(data)));
242 }
243
244 @Test
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100245 void testPrimitiveByteArray() {
246 var data = new byte[] {0x04, 0x05, 0x06, 0x07};
247
248 var decoder = Decoder.ofByteArray();
249
250 assertArrayEquals(data, decoder.decode(ByteBuffer.wrap(data)));
251 }
252
253 @Test
254 void testPrimitiveByteArrayRecord() {
255 var data = new byte[] {0x04, 0x05, 0x06, 0x07};
256
257 record TestRecord(byte[] bytes) {}
258
259 var decoder = Decoder.ofStructure(TestRecord.class, Decoder.ofByteArray());
260
261 assertArrayEquals(data, decoder.decode(ByteBuffer.wrap(data)).bytes());
262 }
263
264 @Test
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100265 void testIntegerArray() {
266 var data = new byte[] {0x04, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00};
267
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100268 var decoder = Decoder.ofArray(Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100269
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100270 assertEquals(List.of(4, 258), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100271 }
272
273 @Test
274 void testDictionaryEntry() {
275 var data =
276 new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};
277
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100278 record TestEntry(String key, int value) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100279
280 var decoder =
281 Decoder.ofStructure(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100282 TestEntry.class, Decoder.ofString(UTF_8), Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
283 assertEquals(new TestEntry("a key", 514), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100284 }
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100285
286 @Test
287 void testPaddedPrimitives() {
288 var data =
289 new byte[] {
290 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291 0x00, 0x40, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
292 };
293
294 record TestRecord(short s, long l, double d) {}
295
296 var decoder =
297 Decoder.ofStructure(
298 TestRecord.class,
299 Decoder.ofShort().withByteOrder(BIG_ENDIAN),
300 Decoder.ofLong().withByteOrder(LITTLE_ENDIAN),
301 Decoder.ofDouble());
302 assertEquals(new TestRecord((short) 1, 2, 3.25), decoder.decode(ByteBuffer.wrap(data)));
303 }
304
305 @Test
306 void testEmbeddedMaybe() {
307 var data = new byte[] {0x01, 0x01};
308
309 record TestRecord(Optional<Byte> set, Optional<Byte> unset) {}
310
311 var decoder =
312 Decoder.ofStructure(
313 TestRecord.class, Decoder.ofMaybe(Decoder.ofByte()), Decoder.ofMaybe(Decoder.ofByte()));
314 assertEquals(
315 new TestRecord(Optional.of((byte) 1), Optional.empty()),
316 decoder.decode(ByteBuffer.wrap(data)));
317 }
318
319 @Test
320 void testRecordComponentMismatch() {
321 record TestRecord(Optional<Byte> set) {}
322
323 var maybeDecoder = Decoder.ofMaybe(Decoder.ofByte());
324 assertThrows(
325 IllegalArgumentException.class,
326 () -> Decoder.ofStructure(TestRecord.class, maybeDecoder, maybeDecoder));
327 }
328
329 @Test
330 void testTrivialRecord() {
331 var data = new byte[] {0x00};
332
333 record TestRecord() {}
334
335 var decoder = Decoder.ofStructure(TestRecord.class);
336 assertEquals(new TestRecord(), decoder.decode(ByteBuffer.wrap(data)));
337 }
338
339 @Test
340 void testTwoElementTrivialRecordArray() {
341 var data = new byte[] {0x00, 0x00};
342
343 record TestRecord() {}
344
345 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
346 assertEquals(
347 List.of(new TestRecord(), new TestRecord()), decoder.decode(ByteBuffer.wrap(data)));
348 }
349
350 @Test
351 void testSingletonTrivialRecordArray() {
352 var data = new byte[] {0x00};
353
354 record TestRecord() {}
355
356 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
357 assertEquals(List.of(new TestRecord()), decoder.decode(ByteBuffer.wrap(data)));
358 }
359
360 @Test
361 void testEmptyTrivialRecordArray() {
362 var data = new byte[] {};
363
364 record TestRecord() {}
365
366 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
367 assertEquals(List.of(), decoder.decode(ByteBuffer.wrap(data)));
368 }
369
370 @Test
371 void testVariantArray() {
372 var data = new byte[] {};
373
374 record TestRecord() {}
375
376 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
377 assertEquals(List.of(), decoder.decode(ByteBuffer.wrap(data)));
378 }
379
380 @Test
381 void testInvalidVariantSignature() {
382 var data = new byte[] {0x00, 0x00, 0x2E};
383
384 var decoder = Decoder.ofVariant();
385 assertThrows(IllegalArgumentException.class, () -> decoder.decode(ByteBuffer.wrap(data)));
386 }
387
388 @Test
389 void testMissingVariantSignature() {
390 var data = new byte[] {0x01};
391
392 var decoder = Decoder.ofVariant();
393 assertThrows(IllegalArgumentException.class, () -> decoder.decode(ByteBuffer.wrap(data)));
394 }
395
396 @Test
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100397 void testSimpleVariantRecord() throws ParseException {
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100398 // signature: "(bynqiuxtdsogvmiai)"
399 var data =
400 new byte[] {
401 0x01, // b
402 0x02, // y
403 0x00, 0x03, // n
404 0x00, 0x04, // q
405 0x00, 0x00, // (padding)
406 0x00, 0x00, 0x00, 0x05, // i
407 0x00, 0x00, 0x00, 0x06, // u
408 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, // x
409 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // t
410 0x40, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // d
411 0x68, 0x69, 0x00, // s
412 0x68, 0x69, 0x00, // o
413 0x68, 0x69, 0x00, // g
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (padding)
415 0x00, 0x00, 0x00, 0x09, 0x00, 0x69, // v
416 0x00, 0x00, // (padding)
417 0x00, 0x00, 0x00, 0x0a, // mi
418 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, // ai
419 68, 62, 49, 46, 43, // framing offsets
420 0x00, 0x28, 0x62, 0x79, 0x6E, 0x71, 0x69, 0x75, 0x78, 0x74, 0x64, 0x73, 0x6F, 0x67, 0x76,
421 0x6D, 0x69, 0x61, 0x69, 0x29
422 };
423
424 var decoder = Decoder.ofVariant();
425 assertArrayEquals(
426 new Object[] {
427 true,
428 (byte) 2,
429 (short) 3,
430 (short) 4,
431 (int) 5,
432 (int) 6,
433 (long) 7,
434 (long) 8,
435 (double) 3.25,
436 "hi",
437 "hi",
438 "hi",
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100439 new Variant(Signature.parse("i"), 9),
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100440 Optional.of(10),
441 List.of(11, 12)
442 },
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100443 (Object[]) decoder.decode(ByteBuffer.wrap(data)).value());
444 }
445
446 @Test
447 void testSignatureString() throws ParseException {
448 var data =
449 new byte[] {
450 0x28, 0x62, 0x79, 0x6E, 0x71, 0x69, 0x75, 0x78, 0x74, 0x64, 0x73, 0x6F, 0x67, 0x76, 0x6D,
451 0x69, 0x61, 0x69, 0x29
452 };
453
454 var signature = Signature.parse(ByteBuffer.wrap(data));
455 assertEquals("(bynqiuxtdsogvmiai)", signature.toString());
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100456 }
Matthias Andreas Benkard4e8423d2021-12-19 22:56:09 +0100457
458 @Test
459 void testMap() {
460 var data = new byte[] {0x0A, 0x0B, 0x0C};
461 var decoder = Decoder.ofByteArray().map(bytes -> bytes.length);
462 assertEquals(3, decoder.decode(ByteBuffer.wrap(data)));
463 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100464}