| 1 | const std = @import("std"); |
| 2 | const json = std.json; |
| 3 | const testing = std.testing; |
| 4 | const parseFromSlice = @import("./static.zig").parseFromSlice; |
| 5 | const Scanner = @import("./Scanner.zig"); |
| 6 | const Value = @import("./dynamic.zig").Value; |
| 7 | |
| 8 | // Support for JSONTestSuite.zig |
| 9 | pub fn ok(s: []const u8) !void { |
| 10 | try testLowLevelScanner(s); |
| 11 | try testHighLevelDynamicParser(s); |
| 12 | } |
| 13 | pub fn err(s: []const u8) !void { |
| 14 | try testing.expect(std.meta.isError(testLowLevelScanner(s))); |
| 15 | try testing.expect(std.meta.isError(testHighLevelDynamicParser(s))); |
| 16 | } |
| 17 | pub fn any(s: []const u8) !void { |
| 18 | testLowLevelScanner(s) catch {}; |
| 19 | testHighLevelDynamicParser(s) catch {}; |
| 20 | } |
| 21 | fn testLowLevelScanner(s: []const u8) !void { |
| 22 | var scanner = Scanner.initCompleteInput(testing.allocator, s); |
| 23 | defer scanner.deinit(); |
| 24 | while (true) { |
| 25 | const token = try scanner.next(); |
| 26 | if (token == .end_of_document) break; |
| 27 | } |
| 28 | } |
| 29 | fn testHighLevelDynamicParser(s: []const u8) !void { |
| 30 | var parsed = try parseFromSlice(Value, testing.allocator, s, .{ |
| 31 | .duplicate_field_behavior = .use_first, |
| 32 | }); |
| 33 | defer parsed.deinit(); |
| 34 | } |
| 35 | |
| 36 | // Additional tests not part of test JSONTestSuite. |
| 37 | test "y_trailing_comma_after_empty" { |
| 38 | try roundTrip( |
| 39 | \\{"1":[],"2":{},"3":"4"} |
| 40 | ); |
| 41 | } |
| 42 | test "n_object_closed_missing_value" { |
| 43 | try err( |
| 44 | \\{"a":} |
| 45 | ); |
| 46 | } |
| 47 | |
| 48 | fn roundTrip(s: []const u8) !void { |
| 49 | try testing.expect(try Scanner.validate(testing.allocator, s)); |
| 50 | |
| 51 | var parsed = try parseFromSlice(Value, testing.allocator, s, .{}); |
| 52 | defer parsed.deinit(); |
| 53 | |
| 54 | const rendered = try json.Stringify.valueAlloc(testing.allocator, parsed.value, .{}); |
| 55 | defer testing.allocator.free(rendered); |
| 56 | |
| 57 | try testing.expectEqualStrings(s, rendered); |
| 58 | } |
| 59 | |
| 60 | test "truncated UTF-8 sequence" { |
| 61 | try err("\"\xc2\""); |
| 62 | try err("\"\xdf\""); |
| 63 | try err("\"\xed\xa0\""); |
| 64 | try err("\"\xf0\x80\""); |
| 65 | try err("\"\xf0\x80\x80\""); |
| 66 | } |
| 67 | |
| 68 | test "invalid continuation byte" { |
| 69 | try err("\"\xc2\x00\""); |
| 70 | try err("\"\xc2\x7f\""); |
| 71 | try err("\"\xc2\xc0\""); |
| 72 | try err("\"\xc3\xc1\""); |
| 73 | try err("\"\xc4\xf5\""); |
| 74 | try err("\"\xc5\xff\""); |
| 75 | try err("\"\xe4\x80\x00\""); |
| 76 | try err("\"\xe5\x80\x10\""); |
| 77 | try err("\"\xe6\x80\xc0\""); |
| 78 | try err("\"\xe7\x80\xf5\""); |
| 79 | try err("\"\xe8\x00\x80\""); |
| 80 | try err("\"\xf2\x00\x80\x80\""); |
| 81 | try err("\"\xf0\x80\x00\x80\""); |
| 82 | try err("\"\xf1\x80\xc0\x80\""); |
| 83 | try err("\"\xf2\x80\x80\x00\""); |
| 84 | try err("\"\xf3\x80\x80\xc0\""); |
| 85 | try err("\"\xf4\x80\x80\xf5\""); |
| 86 | } |
| 87 | |
| 88 | test "disallowed overlong form" { |
| 89 | try err("\"\xc0\x80\""); |
| 90 | try err("\"\xc0\x90\""); |
| 91 | try err("\"\xc1\x80\""); |
| 92 | try err("\"\xc1\x90\""); |
| 93 | try err("\"\xe0\x80\x80\""); |
| 94 | try err("\"\xf0\x80\x80\x80\""); |
| 95 | } |
| 96 | |
| 97 | test "out of UTF-16 range" { |
| 98 | try err("\"\xf4\x90\x80\x80\""); |
| 99 | try err("\"\xf5\x80\x80\x80\""); |
| 100 | try err("\"\xf6\x80\x80\x80\""); |
| 101 | try err("\"\xf7\x80\x80\x80\""); |
| 102 | try err("\"\xf8\x80\x80\x80\""); |
| 103 | try err("\"\xf9\x80\x80\x80\""); |
| 104 | try err("\"\xfa\x80\x80\x80\""); |
| 105 | try err("\"\xfb\x80\x80\x80\""); |
| 106 | try err("\"\xfc\x80\x80\x80\""); |
| 107 | try err("\"\xfd\x80\x80\x80\""); |
| 108 | try err("\"\xfe\x80\x80\x80\""); |
| 109 | try err("\"\xff\x80\x80\x80\""); |
| 110 | } |