diff options
Diffstat (limited to 'test/init.lua')
-rw-r--r-- | test/init.lua | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/test/init.lua b/test/init.lua new file mode 100644 index 0000000..0a4e05b --- /dev/null +++ b/test/init.lua @@ -0,0 +1,163 @@ +local utils = require 'lem.utils' + +local updatenow = utils.updatenow +local pack = table.pack +local unpack = table.unpack +local format = string.format + +local master + +local function test_assert(v, msg, ...) + if not v then + error(msg or 'assertion failed!', 2) + end + master.assert_cnt = master.assert_cnt + 1 + return v, msg, ... +end + +local function msg_handler(msg) + local trace = debug.traceback(msg, 4) + return trace + --return string.match(trace, '^(.-)\n[^\n]-tester_cut_traceback_here') +end + +local test = {} +test.__index = test + +function test:enter() + self.prev_test = master.activetest + master.activetest = self + self.real_assert = assert + _ENV.assert = test_assert +end + +function test:leave() + master.activetest = self.prev_test + self.prev_test = nil + _ENV.assert = self.real_assert + self.real_assert = nil +end + +function test:depend(t) + if type(t) == 'string' then + t = require('test.'..t) + end + table.insert(self.dependencies, t) +end + +local function tester_cut_traceback_here(self) + local deps = self.dependencies + for i=1,#deps do + local dep = deps[i] + dep:run() + end + if self.test then + self:test() + end +end + +local function tester(...) + -- hack due to the first function called by xpcall + -- not being refered to by name in tracebacks + local ret, msg = tester_cut_traceback_here(...) + return ret, msg +end + +function test:run() + if master == nil then + self:setmaster() + end + if master.have_run[self] then + return self.passed -- TODO return previous result + end + self:enter() + local t1, t2 + t1 = updatenow() + local ret, msg = xpcall(tester, msg_handler, self) + t2 = updatenow() + self.runtime = t2 - t1 + self:leave() + if not ret then + self.error_msg = msg + end + master.have_run[self] = true + self.passed = ret + master.run = master.run + 1 + if ret then + master.passed = master.passed + 1 + else + table.insert(master.failed, self) + end + return ret +end + +function test:setmaster() + assert(master == nil, 'master already set') + master = { + test = self, + assert_cnt = 0, + have_run = {}, + passed = 0, + run = 0, + failed = {}, + } +end + +function test:reset() + if self ~= master.test then + error('you can only run reset on current master task') + end + master = nil +end + +local function new_test(func) + local src = debug.getinfo(func or 2, 'S').short_src + return setmetatable({ + test = func, + dependencies = {}, + source = src, + }, test) +end + +function test:stats() + if self ~= master.test then + error('you can only run stats on current master task') + end + local run, passed, asserts = master.run, master.passed, master.assert_cnt + if run == passed then + print(format('%d/%d All tests passed, %d assertions in %.3fs', + run, passed, asserts, master.test.runtime)) + else + print(format('%d/%d %d failed', run, passed, run-passed)) + end +end + +function test:failed() + if self ~= master.test then + error('you can only run stats on current master task') + end + for i=1,#master.failed do + local t = master.failed[i] + print(t.error_msg) + end +end + +local function assert_fail(cb, ...) + local ret = pack(pcall(cb, ...)) + if ret[1] then assert(false, 'doomed function succeeded') end + return unpack(ret) +end + +local M = {} + +function M.test() + local index = require 'test.all' + index:run() + index:stats() + index:failed() +end + +M.new = new_test +M.fail = assert_fail + +return M |