aboutsummaryrefslogtreecommitdiffstats
path: root/test/init.lua
blob: 97f94ac55964f62f8e1db3842a5c3e1c921ecacb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
--local utils = require 'lem.utils'

--local updatenow = utils.updatenow
local function updatenow() return nil end
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()
	if t1 then
		self.runtime = t2 - t1
	end
	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
		local runtime = master.test.runtime
		local runtimestr = ''
		if runtime then
			runtimestr = format(' in %.3fs', runtime)
		end
		print(format('%d/%d  All tests passed, %d assertions%s',
			run, passed, asserts, runtimestr))
	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