diff options
Diffstat (limited to 'encoding/json/+test')
-rw-r--r-- | encoding/json/+test/lexer.ha | 62 | ||||
-rw-r--r-- | encoding/json/+test/test_load.ha | 164 | ||||
-rw-r--r-- | encoding/json/+test/test_value.ha | 49 |
3 files changed, 275 insertions, 0 deletions
diff --git a/encoding/json/+test/lexer.ha b/encoding/json/+test/lexer.ha new file mode 100644 index 0000000..b4c098e --- /dev/null +++ b/encoding/json/+test/lexer.ha @@ -0,0 +1,62 @@ +use io; +use memio; +use strings; + +@test fn lex() void = { + const cases: [_](str, []token) = [ + ("true", [true]), + ("false", [false]), + ("null", [_null]), + ("1234", [1234.0]), + ("12.34", [12.34]), + ("12.34e5", [12.34e5]), + ("12.34E5", [12.34e5]), + ("12.34e+5", [12.34e5]), + ("12.34e-5", [12.34e-5]), + ("12e5", [12.0e5]), + ("-1234", [-1234.0]), + (`"hello world"`, ["hello world"]), + (`"\"\\\/\b\f\n\r\t\u0020"`, ["\"\\/\b\f\n\r\t\u0020"]), + ("[ null, null ]", [arraystart, _null, comma, _null, arrayend]), + ]; + + for (let i = 0z; i < len(cases); i += 1) { + const src = strings::toutf8(cases[i].0); + const src = memio::fixed(src); + const lexer = newlexer(&src); + defer close(&lexer); + + for (let j = 0z; j < len(cases[i].1); j += 1) { + const want = cases[i].1[j]; + const have = lex(&lexer)! as token; + assert(tokeq(want, have)); + }; + + assert(lex(&lexer) is io::EOF); + }; +}; + +fn tokeq(want: token, have: token) bool = { + match (want) { + case _null => + return have is _null; + case comma => + return have is comma; + case colon => + return have is colon; + case arraystart => + return have is arraystart; + case arrayend => + return have is arrayend; + case objstart => + return have is objstart; + case objend => + return have is objend; + case let b: bool => + return have as bool == b; + case let f: f64 => + return have as f64 == f; + case let s: str => + return have as str == s; + }; +}; diff --git a/encoding/json/+test/test_load.ha b/encoding/json/+test/test_load.ha new file mode 100644 index 0000000..bf53777 --- /dev/null +++ b/encoding/json/+test/test_load.ha @@ -0,0 +1,164 @@ +use fmt; + +fn roundtrip(input: str, expected: value) void = { + const val = loadstr(input)!; + defer finish(val); + assert(equal(val, expected)); + const s = dumpstr(val); + defer free(s); + const val = loadstr(s)!; + defer finish(val); + assert(equal(val, expected)); +}; + +fn errassert(input: str, expected_loc: (uint, uint)) void = { + const loc = loadstr(input) as invalid; + if (loc.0 != expected_loc.0 || loc.1 != expected_loc.1) { + fmt::errorfln("=== JSON:\n{}", input)!; + fmt::errorfln("=== expected error location:\n({}, {})", + expected_loc.0, expected_loc.1)!; + fmt::errorfln("=== actual error location:\n({}, {})", + loc.0, loc.1)!; + abort(); + }; +}; + +@test fn load() void = { + let obj = newobject(); + defer finish(obj); + let obj2 = newobject(); + defer finish(obj2); + + roundtrip(`1234`, 1234.0); + roundtrip(`[]`, []); + roundtrip(`[1, 2, 3, null]`, [1.0, 2.0, 3.0, _null]); + roundtrip(`{}`, obj); + set(&obj, "hello", "world"); + set(&obj, "answer", 42.0); + roundtrip(`{ "hello": "world", "answer": 42 }`, obj); + reset(&obj); + roundtrip(`[[] ]`, [[]]); + roundtrip(`[""]`, [""]); + roundtrip(`["a"]`, ["a"]); + roundtrip(`[false]`, [false]); + roundtrip(`[null, 1, "1", {}]`, [_null, 1.0, "1", obj]); + roundtrip(`[null]`, [_null]); + roundtrip("[1\n]", [1.0]); + roundtrip(`[1,null,null,null,2]`, [1.0, _null, _null, _null, 2.0]); + set(&obj, "", 0.0); + roundtrip(`{"":0}`, obj); + reset(&obj); + set(&obj, "foo\0bar", 42.0); + roundtrip(`{"foo\u0000bar": 42}`, obj); + reset(&obj); + set(&obj, "min", -1.0e+28); + set(&obj, "max", 1.0e+28); + roundtrip(`{"min": -1.0e+28, "max": 1.0e+28}`, obj); + reset(&obj); + set(&obj, "id", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + set(&obj2, "id", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + set(&obj, "x", [obj2]); + roundtrip(`{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}`, obj); + reset(&obj); + reset(&obj2); + set(&obj, "a", []); + roundtrip(`{"a":[]}`, obj); + roundtrip("{\n" `"a": []` "\n}", obj); + reset(&obj); + roundtrip(`"\u0060\u012a\u12AB"`, "\u0060\u012a\u12AB"); + roundtrip(`"\"\\\/\b\f\n\r\t"`, "\"\\/\b\f\n\r\t"); + roundtrip(`"\\u0000"`, `\u0000`); + roundtrip(`"\""`, `"`); + roundtrip(`"a/*b*/c/*d//e"`, "a/*b*/c/*d//e"); + roundtrip(`"\\a"`, `\a`); + roundtrip(`"\\n"`, `\n`); + roundtrip(`"\u0012"`, "\u0012"); + roundtrip(`[ "asd"]`, ["asd"]); + roundtrip(`"new\u000Aline"`, "new\nline"); + roundtrip(`"\u0000"`, "\0"); + roundtrip(`"\u002c"`, "\u002c"); + roundtrip(`"asd "`, "asd "); + roundtrip(`" "`, " "); + roundtrip(`"\u0821"`, "\u0821"); + roundtrip(`"\u0123"`, "\u0123"); + roundtrip(`"\u0061\u30af\u30EA\u30b9"`, "\u0061\u30af\u30EA\u30b9"); + roundtrip(`"\uA66D"`, "\uA66D"); + roundtrip(`"\u005C"`, `\`); + roundtrip(`"\u0022"`, `"`); + roundtrip(`""`, ""); + roundtrip(` [] `, []); + + errassert(`[1,,]`, (1, 4)); + errassert(`[1 true]`, (1, 7)); + errassert(`["": 1]`, (1, 4)); + errassert(`[,1]`, (1, 2)); + errassert(`[1,,2]`, (1, 4)); + errassert(`["",]`, (1, 5)); + errassert(`["x"`, (1, 5)); + errassert(`[x`, (1, 2)); + errassert(`[3[4]]`, (1, 3)); + errassert(`[1:2]`, (1, 3)); + errassert(`[,]`, (1, 2)); + errassert(`[-]`, (1, 3)); + errassert(`[ , ""]`, (1, 5)); + errassert("[\"a\",\n4\n,1,", (3, 4)); + errassert(`[1,]`, (1, 4)); + errassert("[\"\va\"\\f", (1, 3)); + errassert(`[*]`, (1, 2)); + errassert(`[1,`, (1, 4)); + errassert("[1,\n1\n,1", (3, 3)); + errassert(`[{}`, (1, 4)); + errassert(`["x", truth]`, (1, 11)); + errassert(`{[: "x"}`, (1, 2)); + errassert(`{"x", null}`, (1, 5)); + errassert(`{"x"::"b"}`, (1, 6)); + errassert(`{"a":"a" 123}`, (1, 12)); + errassert(`{"a" b}`, (1, 6)); + errassert(`{:"b"}`, (1, 2)); + errassert(`{"a" "b"}`, (1, 8)); + errassert(`{"a":`, (1, 6)); + errassert(`{"a"`, (1, 5)); + errassert(`{1:1}`, (1, 2)); + errassert(`{9999E9999:1}`, (1, 10)); + errassert(`{null:null,null:null}`, (1, 5)); + errassert(`{"id":0,,,,,}`, (1, 9)); + errassert(`{'a':0}`, (1, 2)); + errassert(`{"id":0,}`, (1, 9)); + errassert(`{"a":"b",,"c":"d"}`, (1, 10)); + errassert(`{true: false}`, (1, 5)); + errassert(`{"a":"a`, (1, 8)); + errassert(`{ "foo" : "bar", "a" }`, (1, 22)); + errassert(` `, (1, 2)); + errassert(`<null>`, (1, 1)); + errassert(`["asd]`, (1, 7)); + errassert(`True`, (1, 4)); + errassert(`]`, (1, 1)); + errassert(`}`, (1, 1)); + errassert(`{"x": true,`, (1, 12)); + errassert(`[`, (1, 2)); + errassert(`{`, (1, 2)); + errassert(``, (1, 1)); + errassert("\0", (1, 1)); + errassert(`{"":`, (1, 5)); + errassert(`['`, (1, 2)); + errassert(`["`, (1, 3)); + errassert(`[,`, (1, 2)); + errassert(`[{`, (1, 3)); + errassert(`{[`, (1, 2)); + errassert(`{]`, (1, 2)); + errassert(`[}`, (1, 2)); + errassert(`{'`, (1, 2)); + errassert(`{"`, (1, 3)); + errassert(`{,`, (1, 2)); + errassert(`["\{["\{["\{["\{`, (1, 4)); + errassert(`*`, (1, 1)); + errassert(`\u000A""`, (1, 1)); + errassert("\f", (1, 1)); +}; + +@test fn nestlimit() void = { + const s = `{ "foo": [[[{"bar": ["baz"]}]]] }`; + const val = loadstr(s, 6: nestlimit)!; + finish(val); + assert(loadstr(s, 5: nestlimit) is limitreached); +}; diff --git a/encoding/json/+test/test_value.ha b/encoding/json/+test/test_value.ha new file mode 100644 index 0000000..eca7dcf --- /dev/null +++ b/encoding/json/+test/test_value.ha @@ -0,0 +1,49 @@ +// License: MPL-2.0 +// (c) 2022 Drew DeVault <sir@cmpwn.com> + +@test fn object() void = { + let obj = newobject(); + defer finish(obj); + + set(&obj, "hello", "world"); + set(&obj, "foo", "bar"); + set(&obj, "the answer", 42.0); + + // XXX: Match overhaul? + assert(*(get(&obj, "hello") as *value) as str == "world"); + assert(*(get(&obj, "foo") as *value) as str == "bar"); + assert(*(get(&obj, "the answer") as *value) as f64 == 42.0); + assert(get(&obj, "nonexistent") is void); + + del(&obj, "hello"); + assert(get(&obj, "hello") is void); +}; + +@test fn iterator() void = { + let obj = newobject(); + defer finish(obj); + + set(&obj, "hello", "world"); + set(&obj, "foo", "bar"); + set(&obj, "the answer", 42.0); + + let it = iter(&obj); + assert(next(&it) is (const str, const *value)); + assert(next(&it) is (const str, const *value)); + assert(next(&it) is (const str, const *value)); + assert(next(&it) is void); +}; + +@test fn equal() void = { + let a = newobject(); + defer finish(a); + set(&a, "a", 42.0); + set(&a, "A", "hello"); + + let b = newobject(); + defer finish(b); + set(&b, "A", "hello"); + set(&b, "a", 42.0); + + assert(equal(a, b)); +}; |