From 53c1c764ba355ec4d838628ae4dd04d134d5ac82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20Reuh=20Fildadut?= Date: Wed, 27 Dec 2023 23:19:19 +0100 Subject: [PATCH] Add testing framework More tests will arrive shortly... --- ast/Struct.lua | 1 + ast/abstract/Node.lua | 3 +- test/results/persist.ans | 13 +++ test/results/resume anchors.ans | 37 +++++++++ test/results/symbol alias.ans | 11 +++ test/run.lua | 143 ++++++++++++++++++++++++++++++++ test/tests/persist.ans | 11 +++ test/tests/resume anchors.ans | 34 ++++++++ test/tests/symbol alias.ans | 11 +++ 9 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 test/results/persist.ans create mode 100644 test/results/resume anchors.ans create mode 100644 test/results/symbol alias.ans create mode 100644 test/run.lua create mode 100644 test/tests/persist.ans create mode 100644 test/tests/resume anchors.ans create mode 100644 test/tests/symbol alias.ans diff --git a/ast/Struct.lua b/ast/Struct.lua index 8e8bce7..b9bf1bb 100644 --- a/ast/Struct.lua +++ b/ast/Struct.lua @@ -71,6 +71,7 @@ Struct = ast.abstract.Runtime { -- _:_ has higher priority than _,_ table.insert(l, e[1]:format(state, operator_priority["_:_"], ...)..":"..e[2]:format_right(state, operator_priority["_:_"], ...)) end + table.sort(l) return ("{%s}"):format(table.concat(l, ", ")) end, diff --git a/ast/abstract/Node.lua b/ast/abstract/Node.lua index e7920da..ff65e0a 100644 --- a/ast/abstract/Node.lua +++ b/ast/abstract/Node.lua @@ -207,7 +207,8 @@ Node = class { end, -- return a pretty string representation of the node. - -- for non-runtime nodes (what was generated by a parse without any evaluation), this should return valid Anselme code that is functionnally equivalent to the parsed code. note that it currently does not preserve comment. the returned code should additionally always be the same given the same non-runtime input. + -- for non-runtime nodes (what was generated by a parse without any evaluation), this should return valid Anselme code that is functionnally equivalent to the parsed code. note that it currently does not preserve comment. + -- assuming nothing was mutated in the node, the returned string should remain the same - so if make sure the function is deterministic, e.g. sort if you use pairs() -- redefine _format, not this - note that _format is a mandary method for all nodes. -- state is optional and should only be relevant for runtime nodes; if specified, only show what is relevant for the current branch. -- indentation_level and parent_priority are optional value that respectively keep track in nester :format calls of the indentation level (number) and parent operator priority (number); if the node has a strictly lower priority than the parent node, parentheses will be added diff --git a/test/results/persist.ans b/test/results/persist.ans new file mode 100644 index 0000000..8b8b478 --- /dev/null +++ b/test/results/persist.ans @@ -0,0 +1,13 @@ +--# run #-- +--- text --- +| {}"pouet=" {}"7" {}" (7)"| +--- text --- +| {}"d=" {}"7" {}" (7)"| +--- text --- +| {}"d=" {}"9" {}" (9)"| +--- text --- +| {}"pouet=" {}"9" {}" (9)"| +--- return --- +() +--# saved #-- +{"pouet":9} \ No newline at end of file diff --git a/test/results/resume anchors.ans b/test/results/resume anchors.ans new file mode 100644 index 0000000..0f13d5f --- /dev/null +++ b/test/results/resume anchors.ans @@ -0,0 +1,37 @@ +--# run #-- +--- text --- +| {}"<<>>"| +| {}"a"| +--- choice --- + > | {}"A "| +=> | {}"B "| +--- text --- +| {}"boum"| +| {}"h " {}"8" {}""| +--- text --- +| {}"<<>>"| +| {}"a"| +--- choice --- +=> | {}"A "| + > | {}"B "| +--- text --- +| {}"b"| +| {}"c"| +| {}"d"| +| {}"e"| +| {}"f"| +| {}"g"| +| {}"h " {}"8" {}""| +--- text --- +| {}"<<>>"| +| {}"c"| +| {}"d"| +| {}"e"| +| {}"f"| +| {}"g"| +--- text --- +| {}"h " {}"8" {}""| +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/results/symbol alias.ans b/test/results/symbol alias.ans new file mode 100644 index 0000000..fa61347 --- /dev/null +++ b/test/results/symbol alias.ans @@ -0,0 +1,11 @@ +--# run #-- +--- text --- +| {}"c=" {}"2" {}" (2)"| +| {}"l=" {}"*[1, 2, 3]" {}" (*[1,2,3])"| +--- text --- +| {}"c=" {}"5" {}" (5)"| +| {}"l=" {}"*[1, 5, 3]" {}" (*[1,5,3])"| +--- return --- +() +--# saved #-- +{} \ No newline at end of file diff --git a/test/run.lua b/test/run.lua new file mode 100644 index 0000000..976d21c --- /dev/null +++ b/test/run.lua @@ -0,0 +1,143 @@ +local lfs = require("lfs") + +local anselme = require("anselme") +local persistent_manager = require("state.persistent_manager") + +local function run(path) + local state = anselme:new() + state:load_stdlib() + + local run_state = state:branch() + + local f = assert(io.open(path, "r")) + local block = anselme.parse(f:read("*a"), path) + f:close() + + run_state:run(block) + + local out = { "--# run #--" } + + while run_state:active() do + local e, data = run_state:step() + table.insert(out, "--- "..e.." ---") + if e == "text" then + for _, l in ipairs(data) do + table.insert(out, 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)) + end + end + data:choose(choice) + elseif e == "return" then + table.insert(out, data:format(run_state)) + run_state:merge() + else + table.insert(out, tostring(data)) + end + end + + table.insert(out, "--# saved #--") + table.insert(out, persistent_manager:capture(run_state):format(run_state)) + + return table.concat(out, "\n") +end + +local tests = {} +for test in lfs.dir("test/tests/") do + if test:match("%.ans$") then + table.insert(tests, "test/tests/"..test) + end +end + +if arg[1] == "help" then + print("usage:") + print("lua test/run.lua: run all the tests") + print("lua test/run.lua write: write missing result files") + print("lua test/run.lua help: display this message") +elseif arg[1] == "write" then + for _, test in ipairs(tests) do + local f = io.open(test:gsub("^test/tests/", "test/results/"), "r") + if f then + f:close() + else + repeat + local rerun = false + print("* "..test) + local c = assert(io.open(test, "r")) + local code = c:read("*a") + c:close() + print(" Code:") + print(" "..code:gsub("\n", "\n ")) + local s, result = pcall(run, test) + if not s then + print(" Unexpected error: "..tostring(result)) + local r + repeat + io.write("Edit this file? (y/N) ") + r = io.read("*l"):lower() + until r == "y" or r == "n" or r == "" + if r == "y" then + os.execute(("micro %q"):format(test)) -- hardcoded but oh well + rerun = true + end + else + print(" Result:") + print(" "..result:gsub("\n", "\n ")) + local r + repeat + io.write("Write this result? (y/N/e) ") + 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")) + o:write(result) + o:close() + elseif r == "e" then + os.execute(("micro %q"):format(test)) -- hardcoded but oh well + rerun = true + end + end + until not rerun + end + end +elseif not arg[1] then + local total, failure, errored, notfound = #tests, 0, 0, 0 + + for _, test in ipairs(tests) do + local f = io.open(test:gsub("^test/tests/", "test/results/"), "r") + if f then + local s, result = pcall(run, test) + if not s then + errored = errored + 1 + print("* "..test) + print(" Unexpected error: "..tostring(result)) + else + local expected = f:read("*a") + if result ~= expected then + failure = failure + 1 + print("* "..test) + print(" Expected: \n "..expected:gsub("\n", "\n ")) + print(" But received: \n "..result:gsub("\n", "\n ")) + print("") + end + end + f:close() + else + notfound = notfound + 1 + print("* "..test) + print(" Result file not found.") + print("") + end + end + + print("#### Results ####") + print(("%s successes, %s failures, %s errors, %s missing result files, out of %s tests"):format(total-failure-notfound-errored, failure, errored, notfound, total)) +else + print("unknown command, run `lua test/run.lua help` for usage") +end diff --git a/test/tests/persist.ans b/test/tests/persist.ans new file mode 100644 index 0000000..41ce8c5 --- /dev/null +++ b/test/tests/persist.ans @@ -0,0 +1,11 @@ +| pouet={persist("pouet", 7)} (7) + +:&d = persist("pouet", 12) + +| d={d} (7) + +d = 9 + +| d={d} (9) + +| pouet={persist("pouet", 21)} (9) diff --git a/test/tests/resume anchors.ans b/test/tests/resume anchors.ans new file mode 100644 index 0000000..2a8c81b --- /dev/null +++ b/test/tests/resume anchors.ans @@ -0,0 +1,34 @@ +:c = () +:choice = 2 + +:g = $ + |c + #pouet + |d + +:f = $(anchor=()) + :x = 8 + anchor ~> + |a + | A |> + | b + c = #pouet + g! + | e + _ + | f + | g + | B |> + | boum + + |h {x} + +| <<>> +f(c) + +| <<>> +choice = 1 +f(c) + +| <<>> +f(c) diff --git a/test/tests/symbol alias.ans b/test/tests/symbol alias.ans new file mode 100644 index 0000000..e714de5 --- /dev/null +++ b/test/tests/symbol alias.ans @@ -0,0 +1,11 @@ +:l = *[1,2,3] + +:&c = l(2) + +| c={c} (2) +| l={l} (*[1,2,3]) + +c = 5 + +| c={c} (5) +| l={l} (*[1,5,3])