From e0275997f925cb4569ce14dcee1fe7f492587b46 Mon Sep 17 00:00:00 2001 From: kalrnlo Date: Sat, 27 Jul 2024 18:59:19 -0600 Subject: [PATCH] Fix tests & CI --- .github/workflows/ci.yml | 2 +- libs/Ratelimit/ratelimit.test.luau | 4 +- libs/Url/url.test.luau | 4 +- libs/isempty/isempty.test.luau | 4 +- libs/race/race.test.luau | 24 ++ libs/testkit.luau | 467 ----------------------------- 6 files changed, 31 insertions(+), 474 deletions(-) create mode 100644 libs/race/race.test.luau delete mode 100644 libs/testkit.luau diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a816ff0..053fb2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,5 +23,5 @@ jobs: run: | find libs -name "*test.luau" | while read -r file; do echo "Running test: $file" - lune "$file" + lune run "$file" done \ No newline at end of file diff --git a/libs/Ratelimit/ratelimit.test.luau b/libs/Ratelimit/ratelimit.test.luau index a463a31..b9e7370 100644 --- a/libs/Ratelimit/ratelimit.test.luau +++ b/libs/Ratelimit/ratelimit.test.luau @@ -1,5 +1,5 @@ local ratelimit = require("ratelimit") -local testkit = require("../testkit") +local testkit = require("../../testkit") local TEST, CASE, CHECK, FINISH = testkit.test() @@ -26,4 +26,4 @@ TEST("ratelimit", function() end end) -if not FINISH() then error(nil, 0) end +return FINISH diff --git a/libs/Url/url.test.luau b/libs/Url/url.test.luau index c4bc32f..9c7f5a9 100644 --- a/libs/Url/url.test.luau +++ b/libs/Url/url.test.luau @@ -1,4 +1,4 @@ -local testkit = require("../testkit") +local testkit = require("../../testkit") local url = require("url") local TEST, CASE, CHECK, FINISH = testkit.test() @@ -37,4 +37,4 @@ TEST("url", function() end end) -if not FINISH() then error(nil, 0) end \ No newline at end of file +return FINISH \ No newline at end of file diff --git a/libs/isempty/isempty.test.luau b/libs/isempty/isempty.test.luau index 3a3ae32..d004ea0 100644 --- a/libs/isempty/isempty.test.luau +++ b/libs/isempty/isempty.test.luau @@ -1,4 +1,4 @@ -local testkit = require("../testkit") +local testkit = require("../../testkit") local isempty = require("isempty") local TEST, CASE, CHECK, FINISH = testkit.test() @@ -13,4 +13,4 @@ TEST("isempty", function() end end) -if not FINISH() then error(nil, 0) end \ No newline at end of file +return FINISH \ No newline at end of file diff --git a/libs/race/race.test.luau b/libs/race/race.test.luau new file mode 100644 index 0000000..491aaeb --- /dev/null +++ b/libs/race/race.test.luau @@ -0,0 +1,24 @@ +local testkit = require("../testkit") +local task = require("@lune/task") +local race = require("race") + +local TEST, CASE, CHECK, FINISH = testkit.test() + +TEST("RACE", function() + CASE("MAIN") + + local result = race({ + function(str: string) + task.wait(5) + return `{str}! i never return ;c` + end, + function(str: string) + task.wait(3) + return `{str}! i return!` + end + }, "yay") + + CHECK(result == "yay! i return!") +end) + +return FINISH \ No newline at end of file diff --git a/libs/testkit.luau b/libs/testkit.luau deleted file mode 100644 index 35ca031..0000000 --- a/libs/testkit.luau +++ /dev/null @@ -1,467 +0,0 @@ --------------------------------------------------------------------------------- --- testkit.luau --- v0.7.3 --------------------------------------------------------------------------------- - -local color = { - white_underline = function(s: string) - return `\27[1;4m{s}\27[0m` - end, - - white = function(s: string) - return `\27[37;1m{s}\27[0m` - end, - - green = function(s: string) - return `\27[32;1m{s}\27[0m` - end, - - red = function(s: string) - return `\27[31;1m{s}\27[0m` - end, - - yellow = function(s: string) - return `\27[33;1m{s}\27[0m` - end, - - red_highlight = function(s: string) - return `\27[41;1;30m{s}\27[0m` - end, - - green_highlight = function(s: string) - return `\27[42;1;30m{s}\27[0m` - end, - - gray = function(s: string) - return `\27[30;1m{s}\27[0m` - end, -} - -local function convert_units(unit: string, value: number): (number, string) - local prefix_colors = { - [4] = color.red, - [3] = color.red, - [2] = color.yellow, - [1] = color.yellow, - [0] = color.green, - [-1] = color.red, - [-2] = color.yellow, - [-3] = color.green, - [-4] = color.red - } - - local prefixes = { - [4] = "T", - [3] ="G", - [2] ="M", - [1] = "k", - [0] = " ", - [-1] = "m", - [-2] = "u", - [-3] = "n", - [-4] = "p" - } - - local order = 0 - - while value >= 1000 do - order += 1 - value /= 1000 - end - - while value ~= 0 and value < 1 do - order -= 1 - value *= 1000 - end - - if value >= 100 then - value = math.floor(value) - elseif value >= 10 then - value = math.floor(value * 1e1) / 1e1 - elseif value >= 1 then - value = math.floor(value * 1e2) / 1e2 - end - - return value, prefix_colors[order](prefixes[order] .. unit) -end - -local WALL = color.gray "│" - --------------------------------------------------------------------------------- --- Testing --------------------------------------------------------------------------------- - -type Test = { - name: string, - case: Case?, - cases: { Case }, - duration: number, - error: { - message: string, - trace: string - }? -} - -type Case = { - name: string, - result: number, - line: number? -} - -local PASS, FAIL, NONE, ERROR = 1, 2, 3, 4 - -local skip: string? -local test: Test? -local tests: { Test } = {} - -local function output_test_result(test: Test) - print(color.white(test.name)) - - for _, case in test.cases do - local status = ({ - [PASS] = color.green "PASS", - [FAIL] = color.red "FAIL", - [NONE] = color.yellow "NONE", - [ERROR] = color.red "FAIL" - })[case.result] - - local line = case.result == FAIL and color.red(`{case.line}:`) or "" - - print(`{status}{WALL} {line}{color.gray(case.name)}`) - end - - if test.error then - print(color.gray "error: " .. color.red(test.error.message)) - print(color.gray "trace: " .. color.red(test.error.trace)) - else - print() - end -end - -local function CASE(name: string) - assert(test, "no active test") - - local case = { - name = name, - result = NONE - } - - test.case = case - table.insert(test.cases, case) -end - -local function CHECK(value: T, stack: number?): T - assert(test, "no active test") - local case = test.case - - if not case then - CASE "" - case = test.case - end - - assert(case, "no active case") - - if case.result ~= FAIL then - case.result = value and PASS or FAIL - case.line = debug.info(stack and stack + 1 or 2, "l") - end - - return value -end - -local function TEST(name: string, fn: () -> ()) - if skip and name ~= skip then return end - - local active = test - assert(not active, "cannot start test while another test is in progress") - - test = { - name = name, - cases = {}, - duration = 0 - }; assert(test) - - table.insert(tests, test) - - local start = os.clock() - local err - local success = xpcall(fn, function(m: string) - err = { message = m, trace = debug.traceback(nil, 2) } - end) - test.duration = os.clock() - start - - if not test.case then CASE "" end - assert(test.case, "no active case") - - if not success then - test.case.result = ERROR - test.error = err - end - - test = nil -end - -local function FINISH(): boolean - local success = true - local total_cases = 0 - local passed_cases = 0 - local duration = 0 - - for _, test in tests do - duration += test.duration - for _, case in test.cases do - total_cases += 1 - if case.result == PASS or case.result == NONE then - passed_cases += 1 - else - success = false - end - end - - output_test_result(test) - end - - print(color.gray(string.format( - `{passed_cases}/{total_cases} test cases passed in %.3f ms.`, - duration*1e3 - ))) - - local fails = total_cases - passed_cases - - print( - ( - fails > 0 - and color.red - or color.green - )(`{fails} {fails == 1 and "fail" or "fails"}`) - ) - - return success, table.clear(tests) -end - -local function SKIP(name: string) - assert(not test, "cannot skip during test") - skip = name -end - --------------------------------------------------------------------------------- --- Benchmarking --------------------------------------------------------------------------------- - -type Bench = { - time_start: number?, - memory_start: number?, - iterations: number? -} - -local bench: Bench? - -function START(iter: number?): number - local n = iter or 1 - assert(n > 0, "iterations must be greater than 0") - assert(bench, "no active benchmark") - assert(not bench.time_start, "clock was already started") - - bench.iterations = n - bench.memory_start = gcinfo() - bench.time_start = os.clock() - return n -end - -local function BENCH(name: string, fn: () -> ()) - local active = bench - assert(not active, "a benchmark is already in progress") - - bench = {}; assert(bench) - - ;(collectgarbage :: any)("collect") - - local mem_start = gcinfo() - local time_start = os.clock() - local err_msg: string? - - local success = xpcall(fn, function(m: string) - err_msg = m .. debug.traceback(nil, 2) - end) - - local time_stop = os.clock() - local mem_stop = gcinfo() - - if not success then - print(`{WALL}{color.red("ERROR")}{WALL} {name}`) - print(color.gray(err_msg :: string)) - else - time_start = bench.time_start or time_start - mem_start = bench.memory_start or mem_start - - local n = bench.iterations or 1 - local d, d_unit = convert_units("s", (time_stop - time_start) / n) - local a, a_unit = convert_units("B", math.round((mem_stop - mem_start) / n * 1e3)) - - local function round(x: number): string - return x > 0 and x < 10 and (x - math.floor(x)) > 0 - and string.format("%2.1f", x) - or string.format("%3.f", x) - end - - print(string.format( - `%s %s %s %s{WALL} %s`, - color.gray(round(d)), - d_unit, - color.gray(round(a)), - a_unit, - color.gray(name) - )) - end - - bench = nil -end - --------------------------------------------------------------------------------- --- Printing --------------------------------------------------------------------------------- - -local function print2(v: unknown) - type Buffer = { n: number, [number]: string } - type Cyclic = { n: number, [{}]: number } - - -- overkill concatenationless string buffer - local function tos(value: any, stack: number, str: Buffer, cyclic: Cyclic) - local TAB = " " - local indent = table.concat(table.create(stack, TAB)) - - if type(value) == "string" then - local n = str.n - str[n + 1] = "\"" - str[n + 2] = value - str[n + 3] = "\"" - str.n = n + 3 - elseif type(value) ~= "table" then - local n = str.n - str[n + 1] = value == nil and "nil" or tostring(value) - str.n = n + 1 - elseif next(value) == nil then - local n = str.n - str[n + 1] = "{}" - str.n = n + 1 - else -- is table - local tabbed_indent = indent .. TAB - - if cyclic[value] then - str.n += 1 - str[str.n] = color.gray(`CYCLIC REF {cyclic[value]}`) - return - else - cyclic.n += 1 - cyclic[value] = cyclic.n - end - - str.n += 3 - str[str.n - 2] = "{ " - str[str.n - 1] = color.gray(tostring(cyclic[value])) - str[str.n - 0] = "\n" - - local i, v = next(value, nil) - while v ~= nil do - local n = str.n - str[n + 1] = tabbed_indent - - if type(i) ~= "string" then - str[n + 2] = "[" - str[n + 3] = tostring(i) - str[n + 4] = "]" - n += 4 - else - str[n + 2] = tostring(i) - n += 2 - end - - str[n + 1] = " = " - str.n = n + 1 - - tos(v, stack + 1, str, cyclic) - - i, v = next(value, i) - - n = str.n - str[n + 1] = v ~= nil and ",\n" or "\n" - str.n = n + 1 - end - - local n = str.n - str[n + 1] = indent - str[n + 2] = "}" - str.n = n + 2 - end - end - - local str = { n = 0 } - local cyclic = { n = 0 } - tos(v, 0, str, cyclic) - print(table.concat(str)) -end - --------------------------------------------------------------------------------- --- Equality --------------------------------------------------------------------------------- - -local function shallow_eq(a: {}, b: {}): boolean - if #a ~= #b then return false end - - for i, v in next, a do - if b[i] ~= v then - return false - end - end - - for i, v in next, b do - if a[i] ~= v then - return false - end - end - - return true -end - -local function deep_eq(a: {}, b: {}): boolean - if #a ~= #b then return false end - - for i, v in next, a do - if type(b[i]) == "table" and type(v) == "table" then - if deep_eq(b[i], v) == false then return false end - elseif b[i] ~= v then - return false - end - end - - for i, v in next, b do - if type(a[i]) == "table" and type(v) == "table" then - if deep_eq(a[i], v) == false then return false end - elseif a[i] ~= v then - return false - end - end - - return true -end - --------------------------------------------------------------------------------- --- Return --------------------------------------------------------------------------------- - -return { - test = function() - return TEST, CASE, CHECK, FINISH, SKIP - end, - - benchmark = function() - return BENCH, START - end, - - print = print2, - - seq = shallow_eq, - deq = deep_eq, - - color = color -} \ No newline at end of file