diff --git a/test/run.lua b/test/run.lua index 3645bb3..05ef85a 100644 --- a/test/run.lua +++ b/test/run.lua @@ -7,6 +7,8 @@ local anselme = require("anselme") local persistent_manager = require("anselme.state.persistent_manager") local ast = require("anselme.ast") +io.stdout:setvbuf("no") + -- simple random to get the same result across lua versions local prev = 0 local function badrandom(a, b) @@ -26,36 +28,50 @@ function math.random(a, b) end end -local function run_loop(run_state, out) +-- handle anselme run loop +local function run_loop(run_state, write_output, interactive) while run_state:active() do local e, data = run_state:step() - table.insert(out, "--- "..e.." ---") + write_output("--- "..e.." ---") if e == "text" then for _, l in ipairs(data) do - table.insert(out, l:format(run_state)) + write_output(l:format(run_state)) end elseif e == "choice" then - local choice = assert(run_state:eval_local("choice"), "no choice selected"):to_lua() - for i, l in ipairs(data) do - if i == choice then - table.insert(out, "=> "..l:format(run_state)) - else - table.insert(out, " > "..l:format(run_state)) + local choice + if interactive then + for i, l in ipairs(data) do + write_output(("%s> %s"):format(i, l:format(run_state))) + end + io.write(("Select choice (1-%s): "):format(#data)) + choice = tonumber(io.read("l")) + else + choice = assert(run_state:eval_local("choice"), "no choice selected"):to_lua() + for i, l in ipairs(data) do + if i == choice then + write_output(("=> %s"):format(l:format(run_state))) + else + write_output((" > %s"):format(l:format(run_state))) + end end end data:choose(choice) elseif e == "return" then - table.insert(out, data:format(run_state)) + write_output(data:format(run_state)) run_state:merge() else - table.insert(out, tostring(data)) + write_output(tostring(data)) end end end -- run a test file and return the result -local function run(path) +local function run(path, interactive) local out = { "--# run #--" } + local write_output + if interactive then write_output = print + else write_output = function(str) table.insert(out, str) end + end math.randomseed() local state = anselme:new() @@ -67,74 +83,76 @@ local function run(path) state:define("wait", "(duration::number)", function(duration) coroutine.yield("wait", duration) end) state:define("run in new branch", "(code)", function(code) local parallel_state = state:branch() - table.insert(out, "--# parallel script #--") + write_output("--# parallel script #--") parallel_state:run(code, "parallel") - run_loop(parallel_state, out) - table.insert(out, "--# main script #--") + run_loop(parallel_state, write_output, interactive) + write_output("--# main script #--") end) local run_state = state:branch() local f = assert(io.open(path, "r")) - local s, block = pcall(anselme.parse, f:read("*a"), path) + local s, block = pcall(anselme.parse, f:read("a"), path) f:close() if not s then - return "--# parse error #--\n"..tostring(block) + write_output("--# parse error #--") + write_output(tostring(block)) + return table.concat(out, "\n") end run_state:run(block) - run_loop(run_state, out) + run_loop(run_state, write_output, interactive) if state:defined("post run check") then local post_run_state = state:branch() post_run_state:run("post run check!") - table.insert(out, "--# post run check #--") - run_loop(post_run_state, out) + write_output("--# post run check #--") + run_loop(post_run_state, write_output, interactive) end - table.insert(out, "--# saved #--") - table.insert(out, persistent_manager:capture(state):format(state)) + write_output("--# saved #--") + write_output(persistent_manager:get_struct(state):format(state)) return table.concat(out, "\n") end --- display an animated loading indicator -io.stdout:setvbuf("no") -local loading = { - loop = { "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" }, - loop_pos = 1, - erase_code = "", - - write = function(self, message) - self:clear() - local str = self.loop[self.loop_pos].." "..message - self.erase_code = ("\b \b"):rep(#str) - io.write(str) - end, - clear = function(self) - io.write(self.erase_code) - end, - update = function(self) - self.loop_pos = self.loop_pos + 1 - if self.loop_pos > #self.loop then self.loop_pos = 1 end - end -} - --- list tests -local tests = {} -for test in lfs.dir("test/tests/") do - if test:match("%.ans$") then - table.insert(tests, "test/tests/"..test) - end -end - -- run! if not arg[1] or arg[1] == "update" then + -- display an animated loading indicator + local loading = { + loop = { "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" }, + loop_pos = 1, + erase_code = "", + + write = function(self, message) + self:clear() + local str = self.loop[self.loop_pos].." "..message + self.erase_code = ("\b \b"):rep(#str) + io.write(str) + end, + clear = function(self) + io.write(self.erase_code) + end, + update = function(self) + self.loop_pos = self.loop_pos + 1 + if self.loop_pos > #self.loop then self.loop_pos = 1 end + end + } + + -- list tests + local tests = {} + for test in lfs.dir("test/tests/") do + if test:match("%.ans$") then + table.insert(tests, "test/tests/"..test) + end + end + local total, failure, errored, notfound = #tests, 0, 0, 0 + -- run tests for i, test in ipairs(tests) do -- status loading:write(("%s/%s tests ran; running %s"):format(i, #tests, test)) @@ -147,7 +165,7 @@ if not arg[1] or arg[1] == "update" then local f = io.open(test:gsub("^test/tests/", "test/results/"), "r") local expected if f then - expected = f:read("*a") + expected = f:read("a") f:close() end @@ -161,7 +179,7 @@ if not arg[1] or arg[1] == "update" then end if arg[1] == "update" then local c = assert(io.open(test, "r")) - local code = c:read("*a") + local code = c:read("a") c:close() print(" Code:") print(" "..code:gsub("\n", "\n ")) @@ -180,7 +198,7 @@ if not arg[1] or arg[1] == "update" then else io.write("Write this result? (y/N/e) ") end - r = io.read("*l"):lower() + r = io.read("l"):lower() until r == "y" or r == "n" or r == "e" or r == "" if r == "y" then local o = assert(io.open(test:gsub("^test/tests/", "test/results/"), "w")) @@ -207,9 +225,12 @@ if not arg[1] or arg[1] == "update" then local successes = total-failure-notfound-errored print(("%s successes, %s failures, %s errors, %s missing result files, out of %s tests"):format(successes, failure, errored, notfound, total)) if successes < total then os.exit(1) end +elseif arg[1] == "interactive" and arg[2] then + run(tostring(arg[2]), true) else print("usage:") print("lua test/run.lua: run all the tests") print("lua test/run.lua update: run all tests, optionally updating incorrect or missing results") + print("lua test/run.lua interactive