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
|
-- See Copyright Notice in the file LICENSE
-- arrays: deep comparison
local function eq (t1, t2, lut)
if t1 == t2 then return true end
if type(t1) ~= "table" or type(t2) ~= "table" or #t1 ~= #t2 then
return false
end
lut = lut or {} -- look-up table: are these 2 arrays already compared?
lut[t1] = lut[t1] or {}
if lut[t1][t2] then return true end
lut[t2] = lut[t2] or {}
lut[t1][t2], lut[t2][t1] = true, true
for k,v in ipairs (t1) do
if not eq (t2[k], v, lut) then return false end -- recursion
end
return true
end
-- a "nil GUID", to be used instead of nils in datasets
local NT = "b5f74fe5-46f4-483a-8321-e58ba2fa0e17"
-- pack vararg in table, replacing nils with "NT" items
local function packNT (...)
local t = {}
for i=1, select ("#", ...) do
local v = select (i, ...)
t[i] = (v == nil) and NT or v
end
return t
end
-- unpack table into vararg, replacing "NT" items with nils
local function unpackNT (t)
local len = #t
local function unpack_from (i)
local v = t[i]
if v == NT then v = nil end
if i == len then return v end
return v, unpack_from (i+1)
end
if len > 0 then return unpack_from (1) end
end
-- print results (deep into arrays)
local function print_results (val, indent, lut)
indent = indent or ""
lut = lut or {} -- look-up table
local str = tostring (val)
if type (val) == "table" then
if lut[val] then
io.write (indent, str, "\n")
else
lut[val] = true
io.write (indent, str, "\n")
for i,v in ipairs (val) do
print_results (v, " " .. indent, lut) -- recursion
end
end
else
io.write (indent, val == NT and "nil" or str, "\n")
end
end
-- returns:
-- 1) true, if success; false, if failure
-- 2) test results table or error_message
local function test_function (test, func)
local res
local t = packNT (pcall (func, unpackNT (test[1])))
if t[1] then
table.remove (t, 1)
res = t
else
res = t[2] --> error_message
end
local how = (type (res) == type (test[2])) and
(type (res) == "string" or eq (res, test[2])) -- allow error messages to differ
return how, res
end
-- returns:
-- 1) true, if success; false, if failure
-- 2) test results table or error_message
-- 3) test results table or error_message
local function test_method (test, constructor, name)
local res1, res2
local ok, r = pcall (constructor, unpackNT (test[1]))
if ok then
local t = packNT (pcall (r[name], r, unpackNT (test[2])))
if t[1] then
table.remove (t, 1)
res1, res2 = t
else
res1, res2 = 2, t[2] --> 2, error_message
end
else
res1, res2 = 1, r --> 1, error_message
end
return eq (res1, test[3]), res1, res2
end
-- returns: a list of failed tests
local function test_set (set, lib)
local list = {}
if type (set.Func) == "function" then
local func = set.Func
for i,test in ipairs (set) do
local ok, res = test_function (test, func)
if not ok then
table.insert (list, {i=i, res})
end
end
elseif type (set.Method) == "string" then
for i,test in ipairs (set) do
local ok, res1, res2 = test_method (test, lib.new, set.Method)
if not ok then
table.insert (list, {i=i, res1, res2})
end
end
else
error ("neither set.Func nor set.Method is valid")
end
return list
end
return {
eq = eq,
NT = NT,
print_results = print_results,
test_function = test_function,
test_method = test_method,
test_set = test_set,
}
|