local createVersion = require("/apis/libversion") 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 ] [--timeout ] [--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("v" .. createVersion().forSelf()) 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