mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
285 lines
9.6 KiB
Zig
285 lines
9.6 KiB
Zig
const std = @import("std");
|
|
const mem = std.mem;
|
|
const testing = std.testing;
|
|
|
|
const ObjectMap = @import("dynamic.zig").ObjectMap;
|
|
const Array = @import("dynamic.zig").Array;
|
|
const Value = @import("dynamic.zig").Value;
|
|
const Parser = @import("dynamic.zig").Parser;
|
|
|
|
test "json.parser.dynamic" {
|
|
var p = Parser.init(testing.allocator, .alloc_if_needed);
|
|
defer p.deinit();
|
|
|
|
const s =
|
|
\\{
|
|
\\ "Image": {
|
|
\\ "Width": 800,
|
|
\\ "Height": 600,
|
|
\\ "Title": "View from 15th Floor",
|
|
\\ "Thumbnail": {
|
|
\\ "Url": "http://www.example.com/image/481989943",
|
|
\\ "Height": 125,
|
|
\\ "Width": 100
|
|
\\ },
|
|
\\ "Animated" : false,
|
|
\\ "IDs": [116, 943, 234, 38793],
|
|
\\ "ArrayOfObject": [{"n": "m"}],
|
|
\\ "double": 1.3412,
|
|
\\ "LargeInt": 18446744073709551615
|
|
\\ }
|
|
\\}
|
|
;
|
|
|
|
var tree = try p.parse(s);
|
|
defer tree.deinit();
|
|
|
|
var root = tree.root;
|
|
|
|
var image = root.object.get("Image").?;
|
|
|
|
const width = image.object.get("Width").?;
|
|
try testing.expect(width.integer == 800);
|
|
|
|
const height = image.object.get("Height").?;
|
|
try testing.expect(height.integer == 600);
|
|
|
|
const title = image.object.get("Title").?;
|
|
try testing.expect(mem.eql(u8, title.string, "View from 15th Floor"));
|
|
|
|
const animated = image.object.get("Animated").?;
|
|
try testing.expect(animated.bool == false);
|
|
|
|
const array_of_object = image.object.get("ArrayOfObject").?;
|
|
try testing.expect(array_of_object.array.items.len == 1);
|
|
|
|
const obj0 = array_of_object.array.items[0].object.get("n").?;
|
|
try testing.expect(mem.eql(u8, obj0.string, "m"));
|
|
|
|
const double = image.object.get("double").?;
|
|
try testing.expect(double.float == 1.3412);
|
|
|
|
const large_int = image.object.get("LargeInt").?;
|
|
try testing.expect(mem.eql(u8, large_int.number_string, "18446744073709551615"));
|
|
}
|
|
|
|
const writeStream = @import("./write_stream.zig").writeStream;
|
|
test "write json then parse it" {
|
|
var out_buffer: [1000]u8 = undefined;
|
|
|
|
var fixed_buffer_stream = std.io.fixedBufferStream(&out_buffer);
|
|
const out_stream = fixed_buffer_stream.writer();
|
|
var jw = writeStream(out_stream, 4);
|
|
|
|
try jw.beginObject();
|
|
|
|
try jw.objectField("f");
|
|
try jw.emitBool(false);
|
|
|
|
try jw.objectField("t");
|
|
try jw.emitBool(true);
|
|
|
|
try jw.objectField("int");
|
|
try jw.emitNumber(1234);
|
|
|
|
try jw.objectField("array");
|
|
try jw.beginArray();
|
|
|
|
try jw.arrayElem();
|
|
try jw.emitNull();
|
|
|
|
try jw.arrayElem();
|
|
try jw.emitNumber(12.34);
|
|
|
|
try jw.endArray();
|
|
|
|
try jw.objectField("str");
|
|
try jw.emitString("hello");
|
|
|
|
try jw.endObject();
|
|
|
|
var parser = Parser.init(testing.allocator, .alloc_if_needed);
|
|
defer parser.deinit();
|
|
var tree = try parser.parse(fixed_buffer_stream.getWritten());
|
|
defer tree.deinit();
|
|
|
|
try testing.expect(tree.root.object.get("f").?.bool == false);
|
|
try testing.expect(tree.root.object.get("t").?.bool == true);
|
|
try testing.expect(tree.root.object.get("int").?.integer == 1234);
|
|
try testing.expect(tree.root.object.get("array").?.array.items[0].null == {});
|
|
try testing.expect(tree.root.object.get("array").?.array.items[1].float == 12.34);
|
|
try testing.expect(mem.eql(u8, tree.root.object.get("str").?.string, "hello"));
|
|
}
|
|
|
|
fn testParse(arena_allocator: std.mem.Allocator, json_str: []const u8) !Value {
|
|
var p = Parser.init(arena_allocator, .alloc_if_needed);
|
|
return (try p.parse(json_str)).root;
|
|
}
|
|
|
|
test "parsing empty string gives appropriate error" {
|
|
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_allocator.deinit();
|
|
try testing.expectError(error.UnexpectedEndOfInput, testParse(arena_allocator.allocator(), ""));
|
|
}
|
|
|
|
test "parse tree should not contain dangling pointers" {
|
|
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_allocator.deinit();
|
|
|
|
var p = Parser.init(arena_allocator.allocator(), .alloc_if_needed);
|
|
defer p.deinit();
|
|
|
|
var tree = try p.parse("[]");
|
|
defer tree.deinit();
|
|
|
|
// Allocation should succeed
|
|
var i: usize = 0;
|
|
while (i < 100) : (i += 1) {
|
|
try tree.root.array.append(Value{ .integer = 100 });
|
|
}
|
|
try testing.expectEqual(tree.root.array.items.len, 100);
|
|
}
|
|
|
|
test "integer after float has proper type" {
|
|
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_allocator.deinit();
|
|
const parsed = try testParse(arena_allocator.allocator(),
|
|
\\{
|
|
\\ "float": 3.14,
|
|
\\ "ints": [1, 2, 3]
|
|
\\}
|
|
);
|
|
try std.testing.expect(parsed.object.get("ints").?.array.items[0] == .integer);
|
|
}
|
|
|
|
test "escaped characters" {
|
|
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_allocator.deinit();
|
|
const input =
|
|
\\{
|
|
\\ "backslash": "\\",
|
|
\\ "forwardslash": "\/",
|
|
\\ "newline": "\n",
|
|
\\ "carriagereturn": "\r",
|
|
\\ "tab": "\t",
|
|
\\ "formfeed": "\f",
|
|
\\ "backspace": "\b",
|
|
\\ "doublequote": "\"",
|
|
\\ "unicode": "\u0105",
|
|
\\ "surrogatepair": "\ud83d\ude02"
|
|
\\}
|
|
;
|
|
|
|
const obj = (try testParse(arena_allocator.allocator(), input)).object;
|
|
|
|
try testing.expectEqualSlices(u8, obj.get("backslash").?.string, "\\");
|
|
try testing.expectEqualSlices(u8, obj.get("forwardslash").?.string, "/");
|
|
try testing.expectEqualSlices(u8, obj.get("newline").?.string, "\n");
|
|
try testing.expectEqualSlices(u8, obj.get("carriagereturn").?.string, "\r");
|
|
try testing.expectEqualSlices(u8, obj.get("tab").?.string, "\t");
|
|
try testing.expectEqualSlices(u8, obj.get("formfeed").?.string, "\x0C");
|
|
try testing.expectEqualSlices(u8, obj.get("backspace").?.string, "\x08");
|
|
try testing.expectEqualSlices(u8, obj.get("doublequote").?.string, "\"");
|
|
try testing.expectEqualSlices(u8, obj.get("unicode").?.string, "ą");
|
|
try testing.expectEqualSlices(u8, obj.get("surrogatepair").?.string, "😂");
|
|
}
|
|
|
|
test "string copy option" {
|
|
const input =
|
|
\\{
|
|
\\ "noescape": "aą😂",
|
|
\\ "simple": "\\\/\n\r\t\f\b\"",
|
|
\\ "unicode": "\u0105",
|
|
\\ "surrogatepair": "\ud83d\ude02"
|
|
\\}
|
|
;
|
|
|
|
var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_allocator.deinit();
|
|
const allocator = arena_allocator.allocator();
|
|
|
|
var parser = Parser.init(allocator, .alloc_if_needed);
|
|
const tree_nocopy = try parser.parse(input);
|
|
const obj_nocopy = tree_nocopy.root.object;
|
|
|
|
parser = Parser.init(allocator, .alloc_always);
|
|
const tree_copy = try parser.parse(input);
|
|
const obj_copy = tree_copy.root.object;
|
|
|
|
for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| {
|
|
try testing.expectEqualSlices(u8, obj_nocopy.get(field_name).?.string, obj_copy.get(field_name).?.string);
|
|
}
|
|
|
|
const nocopy_addr = &obj_nocopy.get("noescape").?.string[0];
|
|
const copy_addr = &obj_copy.get("noescape").?.string[0];
|
|
|
|
var found_nocopy = false;
|
|
for (input, 0..) |_, index| {
|
|
try testing.expect(copy_addr != &input[index]);
|
|
if (nocopy_addr == &input[index]) {
|
|
found_nocopy = true;
|
|
}
|
|
}
|
|
try testing.expect(found_nocopy);
|
|
}
|
|
|
|
test "Value.jsonStringify" {
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try @as(Value, .null).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "null");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try (Value{ .bool = true }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "true");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try (Value{ .integer = 42 }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "42");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try (Value{ .number_string = "43" }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "43");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try (Value{ .float = 42 }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "4.2e+01");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
try (Value{ .string = "weeee" }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "\"weeee\"");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
var vals = [_]Value{
|
|
.{ .integer = 1 },
|
|
.{ .integer = 2 },
|
|
.{ .number_string = "3" },
|
|
};
|
|
try (Value{
|
|
.array = Array.fromOwnedSlice(undefined, &vals),
|
|
}).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "[1,2,3]");
|
|
}
|
|
{
|
|
var buffer: [10]u8 = undefined;
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
var obj = ObjectMap.init(testing.allocator);
|
|
defer obj.deinit();
|
|
try obj.putNoClobber("a", .{ .string = "b" });
|
|
try (Value{ .object = obj }).jsonStringify(.{}, fbs.writer());
|
|
try testing.expectEqualSlices(u8, fbs.getWritten(), "{\"a\":\"b\"}");
|
|
}
|
|
}
|