1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Add interactive mode for test runner

This commit is contained in:
Étienne Fildadut 2024-01-03 13:49:51 +01:00
parent 12c32bb8a2
commit 93dadb3b5d

View file

@ -7,6 +7,8 @@ local anselme = require("anselme")
local persistent_manager = require("anselme.state.persistent_manager") local persistent_manager = require("anselme.state.persistent_manager")
local ast = require("anselme.ast") local ast = require("anselme.ast")
io.stdout:setvbuf("no")
-- simple random to get the same result across lua versions -- simple random to get the same result across lua versions
local prev = 0 local prev = 0
local function badrandom(a, b) local function badrandom(a, b)
@ -26,36 +28,50 @@ function math.random(a, b)
end end
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 while run_state:active() do
local e, data = run_state:step() local e, data = run_state:step()
table.insert(out, "--- "..e.." ---") write_output("--- "..e.." ---")
if e == "text" then if e == "text" then
for _, l in ipairs(data) do for _, l in ipairs(data) do
table.insert(out, l:format(run_state)) write_output(l:format(run_state))
end end
elseif e == "choice" then elseif e == "choice" then
local choice = assert(run_state:eval_local("choice"), "no choice selected"):to_lua() local choice
for i, l in ipairs(data) do if interactive then
if i == choice then for i, l in ipairs(data) do
table.insert(out, "=> "..l:format(run_state)) write_output(("%s> %s"):format(i, l:format(run_state)))
else end
table.insert(out, " > "..l:format(run_state)) 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
end end
data:choose(choice) data:choose(choice)
elseif e == "return" then elseif e == "return" then
table.insert(out, data:format(run_state)) write_output(data:format(run_state))
run_state:merge() run_state:merge()
else else
table.insert(out, tostring(data)) write_output(tostring(data))
end end
end end
end end
-- run a test file and return the result -- run a test file and return the result
local function run(path) local function run(path, interactive)
local out = { "--# run #--" } 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() math.randomseed()
local state = anselme:new() 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("wait", "(duration::number)", function(duration) coroutine.yield("wait", duration) end)
state:define("run in new branch", "(code)", function(code) state:define("run in new branch", "(code)", function(code)
local parallel_state = state:branch() local parallel_state = state:branch()
table.insert(out, "--# parallel script #--") write_output("--# parallel script #--")
parallel_state:run(code, "parallel") parallel_state:run(code, "parallel")
run_loop(parallel_state, out) run_loop(parallel_state, write_output, interactive)
table.insert(out, "--# main script #--") write_output("--# main script #--")
end) end)
local run_state = state:branch() local run_state = state:branch()
local f = assert(io.open(path, "r")) 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() f:close()
if not s then if not s then
return "--# parse error #--\n"..tostring(block) write_output("--# parse error #--")
write_output(tostring(block))
return table.concat(out, "\n")
end end
run_state:run(block) run_state:run(block)
run_loop(run_state, out) run_loop(run_state, write_output, interactive)
if state:defined("post run check") then if state:defined("post run check") then
local post_run_state = state:branch() local post_run_state = state:branch()
post_run_state:run("post run check!") post_run_state:run("post run check!")
table.insert(out, "--# post run check #--") write_output("--# post run check #--")
run_loop(post_run_state, out) run_loop(post_run_state, write_output, interactive)
end end
table.insert(out, "--# saved #--") write_output("--# saved #--")
table.insert(out, persistent_manager:capture(state):format(state)) write_output(persistent_manager:get_struct(state):format(state))
return table.concat(out, "\n") return table.concat(out, "\n")
end 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! -- run!
if not arg[1] or arg[1] == "update" then 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 local total, failure, errored, notfound = #tests, 0, 0, 0
-- run tests
for i, test in ipairs(tests) do for i, test in ipairs(tests) do
-- status -- status
loading:write(("%s/%s tests ran; running %s"):format(i, #tests, test)) 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 f = io.open(test:gsub("^test/tests/", "test/results/"), "r")
local expected local expected
if f then if f then
expected = f:read("*a") expected = f:read("a")
f:close() f:close()
end end
@ -161,7 +179,7 @@ if not arg[1] or arg[1] == "update" then
end end
if arg[1] == "update" then if arg[1] == "update" then
local c = assert(io.open(test, "r")) local c = assert(io.open(test, "r"))
local code = c:read("*a") local code = c:read("a")
c:close() c:close()
print(" Code:") print(" Code:")
print(" "..code:gsub("\n", "\n ")) print(" "..code:gsub("\n", "\n "))
@ -180,7 +198,7 @@ if not arg[1] or arg[1] == "update" then
else else
io.write("Write this result? (y/N/e) ") io.write("Write this result? (y/N/e) ")
end end
r = io.read("*l"):lower() r = io.read("l"):lower()
until r == "y" or r == "n" or r == "e" or r == "" until r == "y" or r == "n" or r == "e" or r == ""
if r == "y" then if r == "y" then
local o = assert(io.open(test:gsub("^test/tests/", "test/results/"), "w")) 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 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)) 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 if successes < total then os.exit(1) end
elseif arg[1] == "interactive" and arg[2] then
run(tostring(arg[2]), true)
else else
print("usage:") print("usage:")
print("lua test/run.lua: run all the tests") 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 update: run all tests, optionally updating incorrect or missing results")
print("lua test/run.lua interactive <script>: run the file <script> in interactive mode")
print("lua test/run.lua help: display this message") print("lua test/run.lua help: display this message")
end end