-- The spec runner: `lua spec/run.lua` from anywhere in the repo. -- -- Expected environment. Plain Lua 5.4 with the repo root on package.path, which -- this file arranges from `arg[0]`, plus the rocks the extension depends on -- (api7-lua-tinyyaml, toml2lua, luv) and dkjson for the specs' JSON decoding. `mise run -- check` puts the ./.rocks tree on LUA_PATH/LUA_CPATH first and is the intended -- entry point; a bare `lua spec/run.lua` also works if those rocks are on the -- default path. `panto lua spec/run.lua` works too — panto's own rocks tree -- already carries luv. -- -- Missing optional rocks do not fail the run. A test that needs one requires it -- lazily and returns `"skip", reason`; the runner prints a SKIP line, counts it, -- and still exits 0. Only a real assertion failure or an unexpected error exits -- 1. `mise run deps` installs everything, so a local run exercises all of it. -- -- Test files. Every spec/test_*.lua returns an ordered array of { name, fn }. -- `fn` asserts and returns nothing to pass, or returns "skip", reason. Ordering -- is the array's, so a file reads top to bottom. local script = (arg and arg[0]) or "spec/run.lua" local spec_dir = script:match("^(.*)/[^/]+$") or "." local root = spec_dir:match("^(.*)/[^/]+$") or "." package.path = root .. "/?.lua;" .. root .. "/?/init.lua;" .. package.path local function test_files() local found = {} local pipe = io.popen("ls '" .. spec_dir .. "'/test_*.lua 2>/dev/null") if not pipe then return found end for line in pipe:lines() do found[#found + 1] = line end pipe:close() table.sort(found) return found end local function traceback(err) return debug.traceback(tostring(err), 2) end local passed, skipped, failed = 0, 0, 0 local function record(label, ok, first, second) if not ok then failed = failed + 1 print("FAIL " .. label) print(first) elseif first == "skip" then skipped = skipped + 1 print("SKIP " .. label .. " — " .. tostring(second)) else passed = passed + 1 print("ok " .. label) end end for _, file in ipairs(test_files()) do local name = file:match("[^/]+$") local chunk, load_err = loadfile(file) if not chunk then record(name, false, "could not load: " .. tostring(load_err)) else local loaded, cases = xpcall(chunk, traceback) if not loaded then record(name, false, cases) elseif type(cases) ~= "table" then record(name, false, "expected an array of { name, fn }, got " .. type(cases)) else for _, case in ipairs(cases) do local ok, first, second = xpcall(case[2], traceback) record(name .. ": " .. tostring(case[1]), ok, first, second) end end end end print(string.format("\n%d passed, %d skipped, %d failed", passed, skipped, failed)) os.exit(failed == 0 and 0 or 1)