cc-libs/programs/runtest.lua
2026-06-08 05:37:49 +02:00

223 lines
4.7 KiB
Lua

local _VERSION = "1.1.0"
local SUCCESS_MARKER = "__TRAPOS_TEST_OK__"
local DEFAULT_REPORT_PATH = "/trapos-test-report"
local function printUsage()
print("runtest usage:")
print()
print("\t\truntest [--pretty] [--verbose] [--output <path>] [--timeout <seconds>] [--no-timeout] [--shutdown] [test ...]")
print("\t\truntest --version")
print("\t\truntest --help")
end
local function parseArgs(args)
local opts = {
pretty = false,
verbose = false,
shutdown = false,
outputPath = nil,
timeout = nil,
noTimeout = false,
tests = {},
}
local i = 1
while i <= #args do
local arg = args[i]
if arg == "--version" or arg == "-version" then
print("runtest v" .. _VERSION)
return nil
elseif arg == "--help" or arg == "-help" then
printUsage()
return nil
elseif arg == "--pretty" then
opts.pretty = true
elseif arg == "--verbose" or arg == "-v" then
opts.pretty = true
opts.verbose = true
elseif arg == "--output" then
opts.outputPath = args[i + 1]
i = i + 1
elseif arg == "--timeout" then
opts.timeout = args[i + 1]
i = i + 1
elseif arg == "--no-timeout" then
opts.noTimeout = true
elseif arg == "--shutdown" then
opts.shutdown = true
elseif string.sub(arg, 1, 1) == "-" then
print("Unknown option: " .. arg)
printUsage()
return nil
else
opts.tests[#opts.tests + 1] = arg
end
i = i + 1
end
return opts
end
local function normalizeTestPath(path)
if string.sub(path, 1, 1) == "/" then
return path
end
return "/" .. path
end
local function discoverTests()
local tests = {}
if not fs.exists("/tests") then
return tests
end
for _, name in ipairs(fs.list("/tests")) do
local path = "/tests/" .. name
if not fs.isDir(path) and string.sub(name, -4) == ".lua" then
tests[#tests + 1] = path
end
end
table.sort(tests)
return tests
end
local function readLines(path)
local lines = {}
local file = fs.open(path, "r")
if not file then
return lines
end
while true do
local line = file.readLine()
if line == nil then
break
end
lines[#lines + 1] = line
end
file.close()
return lines
end
local function createEmitter(outputPath)
local file = nil
if outputPath then
file = fs.open(outputPath, "w")
end
local function emit(line)
if file then
file.writeLine(line)
else
print(line)
end
end
local function close()
if file then
file.close()
end
end
return emit, close
end
local function renderReport(emit, script, reportLines, ok, verbose, color)
local green = color and string.char(27) .. "[32m" or ""
local red = color and string.char(27) .. "[31m" or ""
local dim = color and string.char(27) .. "[2m" or ""
local reset = color and string.char(27) .. "[0m" or ""
local displayScript = string.sub(script, 1, 1) == "/" and string.sub(script, 2) or script
emit(displayScript)
if verbose then
emit(" " .. dim .. "report: " .. DEFAULT_REPORT_PATH .. reset)
end
if #reportLines == 0 then
if ok then
emit(" " .. green .. "[OK]" .. reset .. " script completed")
else
emit(" " .. red .. "[KO]" .. reset .. " script failed before reporting a case")
end
return
end
for _, line in ipairs(reportLines) do
if string.sub(line, 1, 3) == "OK " then
emit(" " .. green .. "[OK]" .. reset .. " " .. string.sub(line, 4))
elseif string.sub(line, 1, 3) == "KO " then
emit(" " .. red .. "[KO]" .. reset .. " " .. string.sub(line, 4))
elseif verbose and string.sub(line, 1, 4) == "LOG " then
emit(" " .. dim .. "[log] " .. string.sub(line, 5) .. reset)
end
end
end
local opts = parseArgs({ ... })
if not opts then
return
end
local tests = opts.tests
if #tests == 0 then
tests = discoverTests()
else
for i, path in ipairs(tests) do
tests[i] = normalizeTestPath(path)
end
end
if #tests == 0 then
print("FAIL: no tests found")
if opts.shutdown then
os.shutdown()
end
return
end
local emit, closeOutput = createEmitter(opts.outputPath)
local suiteOk = true
for _, script in ipairs(tests) do
fs.delete(DEFAULT_REPORT_PATH)
local scriptArgs = { "--no-marker", "--report", DEFAULT_REPORT_PATH }
if opts.verbose then
scriptArgs[#scriptArgs + 1] = "--verbose"
elseif opts.pretty then
scriptArgs[#scriptArgs + 1] = "--pretty"
end
if opts.noTimeout then
scriptArgs[#scriptArgs + 1] = "--no-timeout"
elseif opts.timeout then
scriptArgs[#scriptArgs + 1] = "--timeout"
scriptArgs[#scriptArgs + 1] = opts.timeout
end
local ok = shell.run(script, table.unpack(scriptArgs))
local reportLines = readLines(DEFAULT_REPORT_PATH)
if opts.pretty then
renderReport(emit, script, reportLines, ok, opts.verbose, opts.outputPath ~= nil)
end
if not ok then
suiteOk = false
break
end
end
closeOutput()
if suiteOk then
print(SUCCESS_MARKER)
else
print("FAIL: CraftOS integration tests failed")
end
if opts.shutdown then
os.shutdown()
end