summaryrefslogtreecommitdiff
path: root/spec/run.lua
blob: 5476e7b28dd443b0717ee6b690ea95790872a6e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
-- 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)