blob: 5cf1a1c42eedd80170c9044df7b80c339519cc87 [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
245 void testIntegerArray() {
246 var data = new byte[] {0x04, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00};
247
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100248 var decoder = Decoder.ofArray(Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100249
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100250 assertEquals(List.of(4, 258), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100251 }
252
253 @Test
254 void testDictionaryEntry() {
255 var data =
256 new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};
257
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100258 record TestEntry(String key, int value) {}
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100259
260 var decoder =
261 Decoder.ofStructure(
Matthias Andreas Benkard35f7a202021-12-14 19:29:26 +0100262 TestEntry.class, Decoder.ofString(UTF_8), Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
263 assertEquals(new TestEntry("a key", 514), decoder.decode(ByteBuffer.wrap(data)));
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100264 }
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100265
266 @Test
267 void testPaddedPrimitives() {
268 var data =
269 new byte[] {
270 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271 0x00, 0x40, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
272 };
273
274 record TestRecord(short s, long l, double d) {}
275
276 var decoder =
277 Decoder.ofStructure(
278 TestRecord.class,
279 Decoder.ofShort().withByteOrder(BIG_ENDIAN),
280 Decoder.ofLong().withByteOrder(LITTLE_ENDIAN),
281 Decoder.ofDouble());
282 assertEquals(new TestRecord((short) 1, 2, 3.25), decoder.decode(ByteBuffer.wrap(data)));
283 }
284
285 @Test
286 void testEmbeddedMaybe() {
287 var data = new byte[] {0x01, 0x01};
288
289 record TestRecord(Optional<Byte> set, Optional<Byte> unset) {}
290
291 var decoder =
292 Decoder.ofStructure(
293 TestRecord.class, Decoder.ofMaybe(Decoder.ofByte()), Decoder.ofMaybe(Decoder.ofByte()));
294 assertEquals(
295 new TestRecord(Optional.of((byte) 1), Optional.empty()),
296 decoder.decode(ByteBuffer.wrap(data)));
297 }
298
299 @Test
300 void testRecordComponentMismatch() {
301 record TestRecord(Optional<Byte> set) {}
302
303 var maybeDecoder = Decoder.ofMaybe(Decoder.ofByte());
304 assertThrows(
305 IllegalArgumentException.class,
306 () -> Decoder.ofStructure(TestRecord.class, maybeDecoder, maybeDecoder));
307 }
308
309 @Test
310 void testTrivialRecord() {
311 var data = new byte[] {0x00};
312
313 record TestRecord() {}
314
315 var decoder = Decoder.ofStructure(TestRecord.class);
316 assertEquals(new TestRecord(), decoder.decode(ByteBuffer.wrap(data)));
317 }
318
319 @Test
320 void testTwoElementTrivialRecordArray() {
321 var data = new byte[] {0x00, 0x00};
322
323 record TestRecord() {}
324
325 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
326 assertEquals(
327 List.of(new TestRecord(), new TestRecord()), decoder.decode(ByteBuffer.wrap(data)));
328 }
329
330 @Test
331 void testSingletonTrivialRecordArray() {
332 var data = new byte[] {0x00};
333
334 record TestRecord() {}
335
336 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
337 assertEquals(List.of(new TestRecord()), decoder.decode(ByteBuffer.wrap(data)));
338 }
339
340 @Test
341 void testEmptyTrivialRecordArray() {
342 var data = new byte[] {};
343
344 record TestRecord() {}
345
346 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
347 assertEquals(List.of(), decoder.decode(ByteBuffer.wrap(data)));
348 }
349
350 @Test
351 void testVariantArray() {
352 var data = new byte[] {};
353
354 record TestRecord() {}
355
356 var decoder = Decoder.ofArray(Decoder.ofStructure(TestRecord.class));
357 assertEquals(List.of(), decoder.decode(ByteBuffer.wrap(data)));
358 }
359
360 @Test
361 void testInvalidVariantSignature() {
362 var data = new byte[] {0x00, 0x00, 0x2E};
363
364 var decoder = Decoder.ofVariant();
365 assertThrows(IllegalArgumentException.class, () -> decoder.decode(ByteBuffer.wrap(data)));
366 }
367
368 @Test
369 void testMissingVariantSignature() {
370 var data = new byte[] {0x01};
371
372 var decoder = Decoder.ofVariant();
373 assertThrows(IllegalArgumentException.class, () -> decoder.decode(ByteBuffer.wrap(data)));
374 }
375
376 @Test
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100377 void testSimpleVariantRecord() throws ParseException {
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100378 // signature: "(bynqiuxtdsogvmiai)"
379 var data =
380 new byte[] {
381 0x01, // b
382 0x02, // y
383 0x00, 0x03, // n
384 0x00, 0x04, // q
385 0x00, 0x00, // (padding)
386 0x00, 0x00, 0x00, 0x05, // i
387 0x00, 0x00, 0x00, 0x06, // u
388 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, // x
389 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // t
390 0x40, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // d
391 0x68, 0x69, 0x00, // s
392 0x68, 0x69, 0x00, // o
393 0x68, 0x69, 0x00, // g
394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // (padding)
395 0x00, 0x00, 0x00, 0x09, 0x00, 0x69, // v
396 0x00, 0x00, // (padding)
397 0x00, 0x00, 0x00, 0x0a, // mi
398 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0c, // ai
399 68, 62, 49, 46, 43, // framing offsets
400 0x00, 0x28, 0x62, 0x79, 0x6E, 0x71, 0x69, 0x75, 0x78, 0x74, 0x64, 0x73, 0x6F, 0x67, 0x76,
401 0x6D, 0x69, 0x61, 0x69, 0x29
402 };
403
404 var decoder = Decoder.ofVariant();
405 assertArrayEquals(
406 new Object[] {
407 true,
408 (byte) 2,
409 (short) 3,
410 (short) 4,
411 (int) 5,
412 (int) 6,
413 (long) 7,
414 (long) 8,
415 (double) 3.25,
416 "hi",
417 "hi",
418 "hi",
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100419 new Variant(Signature.parse("i"), 9),
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100420 Optional.of(10),
421 List.of(11, 12)
422 },
Matthias Andreas Benkard31c61e72021-12-16 20:06:39 +0100423 (Object[]) decoder.decode(ByteBuffer.wrap(data)).value());
424 }
425
426 @Test
427 void testSignatureString() throws ParseException {
428 var data =
429 new byte[] {
430 0x28, 0x62, 0x79, 0x6E, 0x71, 0x69, 0x75, 0x78, 0x74, 0x64, 0x73, 0x6F, 0x67, 0x76, 0x6D,
431 0x69, 0x61, 0x69, 0x29
432 };
433
434 var signature = Signature.parse(ByteBuffer.wrap(data));
435 assertEquals("(bynqiuxtdsogvmiai)", signature.toString());
Matthias Andreas Benkarde5a5c752021-12-15 19:53:55 +0100436 }
Matthias Andreas Benkard261532a2021-12-12 20:09:27 +0100437}