Skip to content

Commit b526d58

Browse files
authored
test: add cjson and simdjson fixture coverage (#43)
1 parent 11a644a commit b526d58

7 files changed

Lines changed: 703 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ jobs:
5454
needs: rust
5555
steps:
5656
- uses: actions/checkout@v4
57+
with:
58+
submodules: recursive
5759

5860
- name: Install Rust (stable)
5961
run: |

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@
44
[submodule "tests/vendor/JSONTestSuite"]
55
path = tests/vendor/JSONTestSuite
66
url = https://github.com/nst/JSONTestSuite
7+
[submodule "tests/vendor/cJSON"]
8+
path = tests/vendor/cJSON
9+
url = https://github.com/DaveGamble/cJSON.git
10+
[submodule "tests/vendor/simdjson"]
11+
path = tests/vendor/simdjson
12+
url = https://github.com/simdjson/simdjson.git
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Third-party JSON fixture sources
2+
3+
qjson reuses mature upstream JSON test data through git submodules instead of
4+
copying large C/C++ test harnesses into this repository.
5+
6+
- `tests/vendor/cJSON`: `DaveGamble/cJSON`, MIT licensed. Rust and Lua tests
7+
consume every `tests/inputs/test*` JSON fixture with matching `.expected`
8+
files, the JSON files from `tests/json-patch-tests/`, and Rust ports parser
9+
literals from cJSON number/string/array tests. `tests/inputs/test6` is an
10+
upstream HTML error page, so qjson keeps it as a negative parse case.
11+
- `tests/vendor/simdjson`: `simdjson/simdjson`, dual Apache-2.0/MIT licensed.
12+
qjson uses the MIT option and consumes every single-document `.json` file in
13+
`jsonexamples/`; the `.ndjson` streaming example is split by line so every
14+
record is parsed as an individual JSON document.
15+
16+
The upstream submodules carry their own license files:
17+
18+
- `tests/vendor/cJSON/LICENSE`
19+
- `tests/vendor/simdjson/LICENSE-MIT`
20+
21+
The local Rust and Lua harnesses are qjson tests; the upstream C/C++ harnesses
22+
are left in the submodules as source material rather than compiled here.

tests/lua/cjson_compat_spec.lua

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,65 @@
11
local qjson = require("qjson")
22
local cjson = require("cjson")
33

4-
describe("qjson vs lua-cjson", function()
4+
local function read_file(path)
5+
local f = assert(io.open(path, "rb"))
6+
local s = f:read("*a")
7+
f:close()
8+
return s
9+
end
10+
11+
local function deep_equal(a, b)
12+
if a == b then
13+
return true
14+
end
15+
if type(a) ~= type(b) then
16+
return false
17+
end
18+
if type(a) ~= "table" then
19+
return false
20+
end
21+
for k, v in pairs(a) do
22+
if not deep_equal(v, b[k]) then
23+
return false
24+
end
25+
end
26+
for k in pairs(b) do
27+
if a[k] == nil then
28+
return false
29+
end
30+
end
31+
return true
32+
end
33+
34+
local function assert_materializes_like_lua_cjson(src)
35+
assert.is_true(deep_equal(qjson.materialize(qjson.decode(src)), cjson.decode(src)))
36+
end
37+
38+
local function assert_encodes_like_lua_cjson(src)
39+
local out = qjson.encode(qjson.decode(src))
40+
assert.is_true(deep_equal(cjson.decode(out), cjson.decode(src)))
41+
end
42+
43+
local function assert_equivalent_json(src)
44+
assert_materializes_like_lua_cjson(src)
45+
assert_encodes_like_lua_cjson(src)
46+
end
47+
48+
local function assert_fixture_paths(paths)
49+
for _, path in ipairs(paths) do
50+
local p = path
51+
52+
it("materializes like lua-cjson for fixture " .. p, function()
53+
assert_materializes_like_lua_cjson(read_file(p))
54+
end)
55+
56+
it("encodes a lua-cjson-equivalent value for fixture " .. p, function()
57+
assert_encodes_like_lua_cjson(read_file(p))
58+
end)
59+
end
60+
end
61+
62+
describe("qjson lua-cjson compatibility smoke", function()
563
it("agrees on simple string field", function()
664
local s = '{"a":"x"}'
765
assert.are.equal(cjson.decode(s).a, qjson.parse(s):get_str("a"))
@@ -27,3 +85,40 @@ describe("qjson vs lua-cjson", function()
2785
assert.are.equal(cjson.decode(s).body.model, qjson.parse(s):get_str("body.model"))
2886
end)
2987
end)
88+
89+
describe("qjson cJSON upstream fixtures", function()
90+
assert_fixture_paths({
91+
"tests/vendor/cJSON/tests/inputs/test1",
92+
"tests/vendor/cJSON/tests/inputs/test2",
93+
"tests/vendor/cJSON/tests/inputs/test3",
94+
"tests/vendor/cJSON/tests/inputs/test4",
95+
"tests/vendor/cJSON/tests/inputs/test5",
96+
"tests/vendor/cJSON/tests/inputs/test7",
97+
"tests/vendor/cJSON/tests/inputs/test8",
98+
"tests/vendor/cJSON/tests/inputs/test9",
99+
"tests/vendor/cJSON/tests/inputs/test10",
100+
"tests/vendor/cJSON/tests/inputs/test11",
101+
"tests/vendor/cJSON/tests/json-patch-tests/cjson-utils-tests.json",
102+
"tests/vendor/cJSON/tests/json-patch-tests/package.json",
103+
"tests/vendor/cJSON/tests/json-patch-tests/spec_tests.json",
104+
"tests/vendor/cJSON/tests/json-patch-tests/tests.json",
105+
})
106+
end)
107+
108+
describe("qjson simdjson upstream fixtures", function()
109+
assert_fixture_paths({
110+
"tests/vendor/simdjson/jsonexamples/citm_catalog.json",
111+
"tests/vendor/simdjson/jsonexamples/example_config.json",
112+
"tests/vendor/simdjson/jsonexamples/twitter.json",
113+
})
114+
115+
it("materializes and encodes each simdjson NDJSON record like lua-cjson", function()
116+
local src = read_file("tests/vendor/simdjson/jsonexamples/amazon_cellphones.ndjson")
117+
local records = 0
118+
for line in src:gmatch("([^\r\n]+)") do
119+
records = records + 1
120+
assert_equivalent_json(line)
121+
end
122+
assert.is_true(records >= 793)
123+
end)
124+
end)

0 commit comments

Comments
 (0)