summaryrefslogtreecommitdiff
path: root/encoding/json/+test
diff options
context:
space:
mode:
authorLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:11:21 +0200
committerLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:51:35 +0200
commitae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch)
tree5f462459ae4b47d22114eed717d1382d08cf4dfe /encoding/json/+test
Initial commit (import old repo)HEADmain
Diffstat (limited to 'encoding/json/+test')
-rw-r--r--encoding/json/+test/lexer.ha62
-rw-r--r--encoding/json/+test/test_load.ha164
-rw-r--r--encoding/json/+test/test_value.ha49
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));
+};