diff --git a/.lune/tests/channel.luau b/.lune/tests/channel.luau new file mode 100644 index 0000000..b34543d --- /dev/null +++ b/.lune/tests/channel.luau @@ -0,0 +1,48 @@ +--- An MPSC synchronization primitive powered by Lua upvalues which retains only +--- one value at a time. + +--- ## Usage +--- ```luau +--- local send, recv = watch((nil :: any) :: string) +--- task.delay(5, send, "hello, world!") +--- task.spawn(function() +--- local value = recv() +--- print("recevied value:", value) +--- end) +--- ``` +type Watch = { + value: T?, + receivers: { thread }, +} + +--- Crates a new `Watch` channel, returning its send and receive handles. +local function chan(_phantom: T): ((T) -> (), () -> T?) + local watch: Watch = { + value = nil, + receivers = {}, + } + + local function send(value: T) + watch.value = value + + for _, receiver in watch.receivers do + coroutine.resume(receiver, value) + end + end + + local function recv(): T + local value = watch.value + watch.value = nil + + if value == nil then + table.insert(watch.receivers, coroutine.running()) + return coroutine.yield() + end + + return value :: T + end + + return send, recv +end + +return chan \ No newline at end of file diff --git a/.lune/tests/reporter.luau b/.lune/tests/reporter.luau index 425d9c9..fb81dfa 100644 --- a/.lune/tests/reporter.luau +++ b/.lune/tests/reporter.luau @@ -3,43 +3,60 @@ local stdio = require("@lune/stdio") local frktest = require("../../lune_packages/frktest") local Reporter = frktest._reporters.lune_console_reporter -local STYLE = table.freeze({ - suite = function(name: string) - return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` - end, +local watch = require("./channel") - report = function(name: string, state: "run" | "success" | "error" | "skip") - local state_color: stdio.Color = if state == "run" - then "white" - elseif state == "success" then "green" - elseif state == "error" then "red" - elseif state == "skip" then "yellow" - else error("Invalid test state") - return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( - "reset" - )} {name}` - end, +local STYLE = table.freeze({ + suite = function(name: string) + return `{stdio.style("bold")}{stdio.color("purple")}SUITE{stdio.style("reset")} {name}` + end, + + report = function(name: string, state: "run" | "success" | "error" | "skip", elapsed: number) + local state_color: stdio.Color = if state == "run" + then "white" + elseif state == "success" then "green" + elseif state == "error" then "red" + elseif state == "skip" then "yellow" + else error("Invalid test state") + return ` {stdio.style("bold")}{stdio.color(state_color)}{if state == "skip" then "SKIP" else "TEST"}{stdio.style( + "reset" + )} {name} [{stdio.style("dim")}{string.format("%.2fms", elapsed)}{stdio.style("reset")}]` + end, }) local ReporterExt = {} function ReporterExt.init() - frktest.test.on_suite_enter(function(suite) - print(STYLE.suite(suite.name)) - end) + frktest.test.on_suite_enter(function(suite) + print(STYLE.suite(suite.name)) + end) - frktest.test.on_suite_leave(function() - stdio.write("\n") - end) + frktest.test.on_suite_leave(function() + stdio.write("\n") + end) - frktest.test.on_test_leave(function(test) - print(STYLE.report(test.name, if test.failed then "error" else "success")) - end) + local send_ts, recv_ts = watch((nil :: any) :: number) + + frktest.test.on_test_enter(function() + -- Send over some high precision timestamp when the test starts + return send_ts(os.clock()) + end) - frktest.test.on_test_skipped(function(test) - print(STYLE.report(test.name, "skip")) - end) + frktest.test.on_test_leave(function(test) + print( + STYLE.report( + test.name, + if test.failed then "error" else "success", - Reporter.init() + -- Await receival of the timestamp and convert the difference to ms + (os.clock() - recv_ts()) * 1000 + ) + ) + end) + + frktest.test.on_test_skipped(function(test) + print(STYLE.report(test.name, "skip")) + end) + + Reporter.init() end -return setmetatable(ReporterExt, { __index = Reporter }) \ No newline at end of file +return setmetatable(ReporterExt, { __index = Reporter })