lib/preserves/src/validation/test.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 const smoke = struct {
  4     const preserves = @import("preserves");
  5 
  6     test "smoke: every atom and compound kind round-trips" {
  7         const allocator = std.testing.allocator;
  8         var arena = std.heap.ArenaAllocator.init(allocator);
  9         defer arena.deinit();
 10         const a = arena.allocator();
 11 
 12         const V = preserves.Value(preserves.NoEmbedded);
 13 
 14         const atoms = [_]V{
 15             V.initBoolean(true),
 16             V.initDouble(3.14),
 17             V.initI128(-42),
 18             V.initU128(@as(u128, @intCast(std.math.maxInt(i128))) + 7),
 19             try V.initString(a, "hello"),
 20             try V.initByteString(a, &[_]u8{ 0xde, 0xad, 0xbe, 0xef }),
 21             try V.initSymbol(a, "greet"),
 22         };
 23         const expected_atom_classes = [_]preserves.AtomClass{
 24             .boolean,
 25             .double,
 26             .signed_integer,
 27             .signed_integer,
 28             .string,
 29             .byte_string,
 30             .symbol,
 31         };
 32         inline for (expected_atom_classes, 0..) |want, i| {
 33             try std.testing.expectEqual(want, atoms[i].atomClass().?);
 34         }
 35 
 36         const fields = try a.alloc(V, 2);
 37         fields[0] = V.initI128(1);
 38         fields[1] = V.initI128(2);
 39         const label = try V.initSymbol(a, "pair");
 40         const rec = try V.initRecord(a, label, fields);
 41         try std.testing.expectEqual(preserves.CompoundClass.record, rec.compoundClass().?);
 42 
 43         const seq_items = try a.alloc(V, 3);
 44         seq_items[0] = V.initBoolean(false);
 45         seq_items[1] = V.initI128(10);
 46         seq_items[2] = try V.initString(a, "x");
 47         const seq = V.initSequence(seq_items);
 48         try std.testing.expectEqual(preserves.CompoundClass.sequence, seq.compoundClass().?);
 49 
 50         const set_items = try a.alloc(V, 2);
 51         set_items[0] = V.initI128(100);
 52         set_items[1] = V.initI128(200);
 53         const set_val = V.initSet(set_items);
 54         try std.testing.expectEqual(preserves.CompoundClass.set, set_val.compoundClass().?);
 55 
 56         const dict_entries = try a.alloc(V.DictionaryEntry, 1);
 57         dict_entries[0] = .{
 58             .key = try V.initSymbol(a, "k"),
 59             .value = V.initI128(99),
 60         };
 61         const dict = V.initDictionary(dict_entries);
 62         try std.testing.expectEqual(preserves.CompoundClass.dictionary, dict.compoundClass().?);
 63 
 64         try std.testing.expect(rec.eql(rec));
 65         try std.testing.expect(seq.eql(seq));
 66         try std.testing.expect(set_val.eql(set_val));
 67         try std.testing.expect(dict.eql(dict));
 68         try std.testing.expect(!rec.eql(seq));
 69     }
 70 };
 71 
 72 const roundtrip = struct {
 73     const preserves = @import("preserves");
 74 
 75     const Value = preserves.Value;
 76     const NoEmbedded = preserves.NoEmbedded;
 77     const SignedInteger = preserves.SignedInteger;
 78     const Tier = preserves.integer_mod.Tier;
 79 
 80     const V = Value(NoEmbedded);
 81     const packed_limits: preserves.packed_reader.Limits = .{
 82         .max_depth = 64,
 83         .max_nodes = 65_536,
 84         .max_collection_items = 4096,
 85         .max_retained_bytes = 16 * 1024 * 1024,
 86     };
 87 
 88     fn parseHexBytes(allocator: std.mem.Allocator, text: []const u8) ![]u8 {
 89         var bytes: std.ArrayListUnmanaged(u8) = .empty;
 90         defer bytes.deinit(allocator);
 91 
 92         var tokens = std.mem.tokenizeScalar(u8, text, ' ');
 93         while (tokens.next()) |token| {
 94             try bytes.append(allocator, try std.fmt.parseInt(u8, token, 16));
 95         }
 96 
 97         return bytes.toOwnedSlice(allocator);
 98     }
 99 
100     fn packedRoundtrip(allocator: std.mem.Allocator, v: V) !V {
101         const bytes = try preserves.encodePacked(NoEmbedded, allocator, v);
102         defer allocator.free(bytes);
103         return try preserves.decodePacked(NoEmbedded, allocator, bytes, packed_limits);
104     }
105 
106     fn textRoundtrip(allocator: std.mem.Allocator, v: V) !V {
107         const text = try preserves.encodeText(NoEmbedded, allocator, v);
108         defer allocator.free(text);
109         return try preserves.decodeText(NoEmbedded, allocator, text);
110     }
111 
112     fn expectPackedRoundtrip(allocator: std.mem.Allocator, v: V) !void {
113         var rt = try packedRoundtrip(allocator, v);
114         defer rt.deinit(allocator);
115         try std.testing.expect(v.eql(rt));
116     }
117 
118     fn expectTextRoundtrip(allocator: std.mem.Allocator, v: V) !void {
119         var rt = try textRoundtrip(allocator, v);
120         defer rt.deinit(allocator);
121         try std.testing.expect(v.eql(rt));
122     }
123 
124     test "packed round-trip: every atom kind" {
125         const allocator = std.testing.allocator;
126         var arena = std.heap.ArenaAllocator.init(allocator);
127         defer arena.deinit();
128         const a = arena.allocator();
129 
130         const atoms = [_]V{
131             V.initBoolean(true),
132             V.initBoolean(false),
133             V.initDouble(3.14),
134             V.initDouble(-0.0),
135             V.initI128(0),
136             V.initI128(1),
137             V.initI128(-1),
138             V.initI128(std.math.maxInt(i128)),
139             V.initI128(std.math.minInt(i128)),
140             try V.initString(a, ""),
141             try V.initString(a, "hello world"),
142             try V.initByteString(a, &[_]u8{}),
143             try V.initByteString(a, &[_]u8{ 0xde, 0xad, 0xbe, 0xef }),
144             try V.initSymbol(a, "foo"),
145             try V.initSymbol(a, "name-with.dots"),
146         };
147 
148         for (atoms) |at| try expectPackedRoundtrip(allocator, at);
149     }
150 
151     test "packed round-trip: every compound kind" {
152         const allocator = std.testing.allocator;
153         var arena = std.heap.ArenaAllocator.init(allocator);
154         defer arena.deinit();
155         const a = arena.allocator();
156 
157         const label = try V.initSymbol(a, "pair");
158         const fields = try a.alloc(V, 2);
159         fields[0] = V.initI128(1);
160         fields[1] = try V.initString(a, "two");
161         const record = try V.initRecord(a, label, fields);
162         try expectPackedRoundtrip(allocator, record);
163 
164         const seq_items = try a.alloc(V, 3);
165         seq_items[0] = V.initBoolean(true);
166         seq_items[1] = V.initI128(2);
167         seq_items[2] = try V.initSymbol(a, "three");
168         try expectPackedRoundtrip(allocator, V.initSequence(seq_items));
169 
170         const set_items = try a.alloc(V, 3);
171         set_items[0] = V.initI128(10);
172         set_items[1] = V.initI128(20);
173         set_items[2] = V.initI128(30);
174         try expectPackedRoundtrip(allocator, V.initSet(set_items));
175 
176         const dict_entries = try a.alloc(V.DictionaryEntry, 2);
177         dict_entries[0] = .{ .key = try V.initSymbol(a, "k1"), .value = V.initI128(1) };
178         dict_entries[1] = .{ .key = try V.initSymbol(a, "k2"), .value = V.initI128(2) };
179         try expectPackedRoundtrip(allocator, V.initDictionary(dict_entries));
180     }
181 
182     test "packed round-trip: nested composite" {
183         const allocator = std.testing.allocator;
184         var arena = std.heap.ArenaAllocator.init(allocator);
185         defer arena.deinit();
186         const a = arena.allocator();
187 
188         const inner_seq_items = try a.alloc(V, 2);
189         inner_seq_items[0] = V.initBoolean(true);
190         inner_seq_items[1] = V.initI128(-7);
191         const inner_seq = V.initSequence(inner_seq_items);
192 
193         const inner_set_items = try a.alloc(V, 2);
194         inner_set_items[0] = V.initI128(100);
195         inner_set_items[1] = V.initI128(-5);
196         const inner_set = V.initSet(inner_set_items);
197 
198         const outer_dict_entries = try a.alloc(V.DictionaryEntry, 2);
199         outer_dict_entries[0] = .{
200             .key = try V.initSymbol(a, "seq"),
201             .value = inner_seq,
202         };
203         outer_dict_entries[1] = .{
204             .key = try V.initSymbol(a, "set"),
205             .value = inner_set,
206         };
207         const dict = V.initDictionary(outer_dict_entries);
208 
209         const rec_fields = try a.alloc(V, 1);
210         rec_fields[0] = dict;
211         const rec_label = try V.initSymbol(a, "wrap");
212         const root = try V.initRecord(a, rec_label, rec_fields);
213 
214         try expectPackedRoundtrip(allocator, root);
215     }
216 
217     test "packed round-trip: integer tier preservation (i128)" {
218         const allocator = std.testing.allocator;
219         const v = V.initI128(std.math.maxInt(i128));
220         try std.testing.expectEqual(Tier.i128, @as(Tier, v.signed_integer.repr));
221         var rt = try packedRoundtrip(allocator, v);
222         defer rt.deinit(allocator);
223         try std.testing.expectEqual(Tier.i128, @as(Tier, rt.signed_integer.repr));
224         try std.testing.expect(v.eql(rt));
225     }
226 
227     test "packed round-trip: integer tier preservation (u128)" {
228         const allocator = std.testing.allocator;
229         const v = V.initU128(std.math.maxInt(u128));
230         try std.testing.expectEqual(Tier.u128, @as(Tier, v.signed_integer.repr));
231         var rt = try packedRoundtrip(allocator, v);
232         defer rt.deinit(allocator);
233         try std.testing.expectEqual(Tier.u128, @as(Tier, rt.signed_integer.repr));
234         try std.testing.expect(v.eql(rt));
235     }
236 
237     test "packed round-trip: integer tier preservation (300-bit big)" {
238         const allocator = std.testing.allocator;
239         var raw: [38]u8 = undefined;
240         raw[0] = 0x01;
241         @memset(raw[1..], 0x00);
242         var si = try SignedInteger.fromCanonicalBytes(allocator, &raw);
243         defer si.deinit(allocator);
244         try std.testing.expectEqual(Tier.big, @as(Tier, si.repr));
245 
246         const v: V = .{ .signed_integer = try si.clone(allocator) };
247         var owned = v;
248         defer owned.deinit(allocator);
249 
250         var rt = try packedRoundtrip(allocator, owned);
251         defer rt.deinit(allocator);
252         try std.testing.expectEqual(Tier.big, @as(Tier, rt.signed_integer.repr));
253         try std.testing.expect(owned.eql(rt));
254     }
255 
256     test "packed encoding canonicalizes set element order" {
257         const allocator = std.testing.allocator;
258         var arena = std.heap.ArenaAllocator.init(allocator);
259         defer arena.deinit();
260         const a = arena.allocator();
261 
262         const unsorted = try a.alloc(V, 3);
263         unsorted[0] = V.initI128(30);
264         unsorted[1] = V.initI128(10);
265         unsorted[2] = V.initI128(20);
266         const unsorted_set = V.initSet(unsorted);
267 
268         const sorted = try a.alloc(V, 3);
269         sorted[0] = V.initI128(10);
270         sorted[1] = V.initI128(20);
271         sorted[2] = V.initI128(30);
272         const sorted_set = V.initSet(sorted);
273 
274         const a_bytes = try preserves.encodePacked(NoEmbedded, allocator, unsorted_set);
275         defer allocator.free(a_bytes);
276         const b_bytes = try preserves.encodePacked(NoEmbedded, allocator, sorted_set);
277         defer allocator.free(b_bytes);
278         try std.testing.expectEqualSlices(u8, a_bytes, b_bytes);
279     }
280 
281     test "packed set order follows encoded representation rather than semantic order" {
282         const allocator = std.testing.allocator;
283         var items = [_]V{ V.initI128(-1), V.initI128(0) };
284         try std.testing.expectEqual(std.math.Order.lt, items[0].compare(items[1]));
285 
286         const encoded = try preserves.encodePacked(
287             NoEmbedded,
288             allocator,
289             V.initSet(&items),
290         );
291         defer allocator.free(encoded);
292         try std.testing.expectEqualSlices(
293             u8,
294             &.{ 0xb6, 0xb0, 0x00, 0xb0, 0x01, 0xff, 0x84 },
295             encoded,
296         );
297 
298         var decoded = try preserves.decodePacked(NoEmbedded, allocator, encoded, packed_limits);
299         defer decoded.deinit(allocator);
300         try std.testing.expectEqual(@as(i128, 0), try decoded.set[0].signed_integer.toI128());
301         try std.testing.expectEqual(@as(i128, -1), try decoded.set[1].signed_integer.toI128());
302     }
303 
304     test "packed encoding canonicalizes dictionary key order" {
305         const allocator = std.testing.allocator;
306         var arena = std.heap.ArenaAllocator.init(allocator);
307         defer arena.deinit();
308         const a = arena.allocator();
309 
310         const unsorted = try a.alloc(V.DictionaryEntry, 2);
311         unsorted[0] = .{ .key = try V.initSymbol(a, "z"), .value = V.initI128(1) };
312         unsorted[1] = .{ .key = try V.initSymbol(a, "a"), .value = V.initI128(2) };
313 
314         const sorted = try a.alloc(V.DictionaryEntry, 2);
315         sorted[0] = .{ .key = try V.initSymbol(a, "a"), .value = V.initI128(2) };
316         sorted[1] = .{ .key = try V.initSymbol(a, "z"), .value = V.initI128(1) };
317 
318         const a_bytes = try preserves.encodePacked(NoEmbedded, allocator, V.initDictionary(unsorted));
319         defer allocator.free(a_bytes);
320         const b_bytes = try preserves.encodePacked(NoEmbedded, allocator, V.initDictionary(sorted));
321         defer allocator.free(b_bytes);
322         try std.testing.expectEqualSlices(u8, a_bytes, b_bytes);
323     }
324 
325     test "packed encoding rejects duplicate set members" {
326         const allocator = std.testing.allocator;
327         var arena = std.heap.ArenaAllocator.init(allocator);
328         defer arena.deinit();
329         const a = arena.allocator();
330 
331         const duplicate_items = try a.alloc(V, 2);
332         duplicate_items[0] = V.initI128(1);
333         duplicate_items[1] = V.initI128(1);
334 
335         try std.testing.expectError(
336             error.DuplicateSetElement,
337             preserves.encodePacked(NoEmbedded, allocator, V.initSet(duplicate_items)),
338         );
339     }
340 
341     test "packed decoding rejects duplicate set members" {
342         const allocator = std.testing.allocator;
343         const duplicate = try parseHexBytes(allocator, "B6 B0 01 01 B0 01 01 84");
344         defer allocator.free(duplicate);
345 
346         try std.testing.expectError(
347             error.DuplicateSetElement,
348             preserves.decodePacked(NoEmbedded, allocator, duplicate, packed_limits),
349         );
350     }
351 
352     test "packed encoding rejects duplicate dictionary keys" {
353         const allocator = std.testing.allocator;
354         var arena = std.heap.ArenaAllocator.init(allocator);
355         defer arena.deinit();
356         const a = arena.allocator();
357 
358         const duplicate_entries = try a.alloc(V.DictionaryEntry, 2);
359         duplicate_entries[0] = .{
360             .key = try V.initSymbol(a, "dup"),
361             .value = V.initI128(1),
362         };
363         duplicate_entries[1] = .{
364             .key = try V.initSymbol(a, "dup"),
365             .value = V.initI128(2),
366         };
367 
368         try std.testing.expectError(
369             error.DuplicateDictionaryKey,
370             preserves.encodePacked(NoEmbedded, allocator, V.initDictionary(duplicate_entries)),
371         );
372     }
373 
374     test "packed decoding rejects duplicate dictionary keys" {
375         const allocator = std.testing.allocator;
376         const duplicate = try parseHexBytes(allocator, "B7 B1 01 61 81 B1 01 61 80 84");
377         defer allocator.free(duplicate);
378 
379         try std.testing.expectError(
380             error.DuplicateDictionaryKey,
381             preserves.decodePacked(NoEmbedded, allocator, duplicate, packed_limits),
382         );
383     }
384 
385     test "text round-trip: atoms and compounds" {
386         const allocator = std.testing.allocator;
387         var arena = std.heap.ArenaAllocator.init(allocator);
388         defer arena.deinit();
389         const a = arena.allocator();
390 
391         try expectTextRoundtrip(allocator, V.initBoolean(true));
392         try expectTextRoundtrip(allocator, V.initBoolean(false));
393         try expectTextRoundtrip(allocator, V.initI128(0));
394         try expectTextRoundtrip(allocator, V.initI128(-12345));
395         try expectTextRoundtrip(allocator, V.initI128(std.math.maxInt(i128)));
396         try expectTextRoundtrip(allocator, V.initU128(std.math.maxInt(u128)));
397         try expectTextRoundtrip(allocator, try V.initString(a, "hello, world"));
398         try expectTextRoundtrip(allocator, try V.initByteString(a, &[_]u8{ 0x00, 0x01, 0xff }));
399         try expectTextRoundtrip(allocator, try V.initSymbol(a, "plain"));
400         try expectTextRoundtrip(allocator, try V.initSymbol(a, "has space"));
401 
402         const rec_fields = try a.alloc(V, 2);
403         rec_fields[0] = V.initI128(1);
404         rec_fields[1] = try V.initString(a, "two");
405         const rec = try V.initRecord(a, try V.initSymbol(a, "pair"), rec_fields);
406         try expectTextRoundtrip(allocator, rec);
407 
408         const seq_items = try a.alloc(V, 3);
409         seq_items[0] = V.initBoolean(true);
410         seq_items[1] = V.initI128(2);
411         seq_items[2] = try V.initString(a, "x");
412         try expectTextRoundtrip(allocator, V.initSequence(seq_items));
413 
414         const dict_entries = try a.alloc(V.DictionaryEntry, 2);
415         dict_entries[0] = .{ .key = try V.initSymbol(a, "name"), .value = try V.initString(a, "Alice") };
416         dict_entries[1] = .{ .key = try V.initSymbol(a, "age"), .value = V.initI128(30) };
417         try expectTextRoundtrip(allocator, V.initDictionary(dict_entries));
418     }
419 
420     test "text round-trip: double uses decimal form" {
421         const allocator = std.testing.allocator;
422         try expectTextRoundtrip(allocator, V.initDouble(3.14));
423         try expectTextRoundtrip(allocator, V.initDouble(-0.5));
424         try expectTextRoundtrip(allocator, V.initDouble(1.0));
425         try expectTextRoundtrip(allocator, V.initDouble(1e10));
426     }
427 };
428 
429 const @"packed" = @import("packed/root.zig");
430 
431 const edges = struct {
432     const preserves = @import("preserves");
433 
434     const NoEmbedded = preserves.NoEmbedded;
435     const V = preserves.Value(NoEmbedded);
436     const packed_limits: preserves.packed_reader.Limits = .{
437         .max_depth = 64,
438         .max_nodes = 65_536,
439         .max_collection_items = 4096,
440         .max_retained_bytes = 16 * 1024 * 1024,
441     };
442 
443     fn parseHexBytes(allocator: std.mem.Allocator, text: []const u8) ![]u8 {
444         var bytes: std.ArrayListUnmanaged(u8) = .empty;
445         defer bytes.deinit(allocator);
446 
447         var tokens = std.mem.tokenizeScalar(u8, text, ' ');
448         while (tokens.next()) |token| {
449             try bytes.append(allocator, try std.fmt.parseInt(u8, token, 16));
450         }
451 
452         return bytes.toOwnedSlice(allocator);
453     }
454 
455     fn encodeAlloc(allocator: std.mem.Allocator, item: V) ![]u8 {
456         return preserves.encodePacked(NoEmbedded, allocator, item);
457     }
458 
459     fn decodeAlloc(allocator: std.mem.Allocator, bytes: []const u8) !V {
460         return preserves.decodePacked(NoEmbedded, allocator, bytes, packed_limits);
461     }
462 
463     fn expectPackedHex(
464         allocator: std.mem.Allocator,
465         item: V,
466         hex: []const u8,
467     ) !void {
468         const expected = try parseHexBytes(allocator, hex);
469         defer allocator.free(expected);
470 
471         const encoded = try encodeAlloc(allocator, item);
472         defer allocator.free(encoded);
473         try std.testing.expectEqualSlices(u8, expected, encoded);
474 
475         var decoded = try decodeAlloc(allocator, encoded);
476         defer decoded.deinit(allocator);
477         try std.testing.expect(item.eql(decoded));
478     }
479 
480     test "packed direct vectors replay byte strings symbols doubles records and sets" {
481         const allocator = std.testing.allocator;
482 
483         var byte_value = try V.initByteString(allocator, &.{ 0x00, 0xff, 0x41 });
484         defer byte_value.deinit(allocator);
485         try expectPackedHex(allocator, byte_value, "B2 03 00 FF 41");
486 
487         var symbol_value = try V.initSymbol(allocator, "demo");
488         defer symbol_value.deinit(allocator);
489         try expectPackedHex(allocator, symbol_value, "B3 04 64 65 6D 6F");
490 
491         const double = V.initDouble(1.0);
492         try expectPackedHex(allocator, double, "87 08 3F F0 00 00 00 00 00 00");
493 
494         const record_fields = try allocator.alloc(V, 2);
495         record_fields[0] = V.initI128(101);
496         record_fields[1] = try V.initString(allocator, "Blackwell");
497         var record_value = try V.initRecord(
498             allocator,
499             try V.initSymbol(allocator, "person"),
500             record_fields,
501         );
502         defer record_value.deinit(allocator);
503         try expectPackedHex(
504             allocator,
505             record_value,
506             "B4 B3 06 70 65 72 73 6F 6E B0 01 65 B1 09 42 6C 61 63 6B 77 65 6C 6C 84",
507         );
508 
509         const set_items = try allocator.alloc(V, 3);
510         set_items[0] = V.initI128(3);
511         set_items[1] = V.initBoolean(false);
512         set_items[2] = try V.initString(allocator, "x");
513         var set_value = V.initSet(set_items);
514         defer set_value.deinit(allocator);
515         try expectPackedHex(allocator, set_value, "B6 80 B0 01 03 B1 01 78 84");
516     }
517 
518     test "packed scanner rejects annotation tag explicitly" {
519         const annotation = [_]u8{ 0x85, 0xB1, 0x01, 'a', 0x81 };
520         try std.testing.expectError(
521             error.AnnotationNotSupported,
522             decodeAlloc(std.testing.allocator, &annotation),
523         );
524     }
525 
526     test "packed decoder reports typed malformed byte stream errors" {
527         const allocator = std.testing.allocator;
528 
529         const cases = [_]struct {
530             hex: []const u8,
531             err: anyerror,
532         }{
533             .{ .hex = "85 B1 01 61 81", .err = error.AnnotationNotSupported },
534             .{ .hex = "B0 01 00", .err = error.NonCanonicalInteger },
535             .{ .hex = "B5 B1 01 61", .err = error.UnexpectedEof },
536             .{ .hex = "81 80", .err = error.UnexpectedTrailingBytes },
537         };
538 
539         for (cases) |case| {
540             const bytes = try parseHexBytes(allocator, case.hex);
541             defer allocator.free(bytes);
542             try std.testing.expectError(case.err, decodeAlloc(allocator, bytes));
543         }
544     }
545 
546     fn isTypedDecodeError(err: anyerror) bool {
547         return switch (err) {
548             error.OutOfMemory,
549             error.UnexpectedEof,
550             error.UnexpectedTrailingBytes,
551             error.UnexpectedEndMarker,
552             error.UnknownTag,
553             error.OverlongVarint,
554             error.VarintTooWide,
555             error.NonCanonicalInteger,
556             error.InvalidDoubleLength,
557             error.DuplicateSetElement,
558             error.DuplicateDictionaryKey,
559             error.InvalidUtf8,
560             error.EmbeddedNotSupported,
561             => true,
562             else => false,
563         };
564     }
565 
566     fn runArbitraryBytesCase(allocator: std.mem.Allocator, bytes: []const u8) !void {
567         var decoded = decodeAlloc(allocator, bytes) catch |err| {
568             try std.testing.expect(isTypedDecodeError(err));
569             return;
570         };
571         defer decoded.deinit(allocator);
572 
573         const reencoded = try encodeAlloc(allocator, decoded);
574         defer allocator.free(reencoded);
575 
576         var reparsed = try decodeAlloc(allocator, reencoded);
577         defer reparsed.deinit(allocator);
578         try std.testing.expect(decoded.eql(reparsed));
579     }
580 };
581 
582 test {
583     _ = @import("packed/test.zig");
584 }
585 
586 test "preserves validation package namespace" {
587     std.testing.refAllDecls(smoke);
588     std.testing.refAllDecls(roundtrip);
589     std.testing.refAllDecls(@"packed");
590     std.testing.refAllDecls(edges);
591 }