xref: /aosp_15_r20/external/bcc/tests/lua/luaunit.lua (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1*387f9dfdSAndroid Build Coastguard Worker--[[
2*387f9dfdSAndroid Build Coastguard Worker        luaunit.lua
3*387f9dfdSAndroid Build Coastguard Worker
4*387f9dfdSAndroid Build Coastguard WorkerDescription: A unit testing framework
5*387f9dfdSAndroid Build Coastguard WorkerHomepage: https://github.com/bluebird75/luaunit
6*387f9dfdSAndroid Build Coastguard WorkerDevelopment by Philippe Fremy <[email protected]>
7*387f9dfdSAndroid Build Coastguard WorkerBased on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit)
8*387f9dfdSAndroid Build Coastguard WorkerLicense: BSD License, see LICENSE.txt
9*387f9dfdSAndroid Build Coastguard WorkerVersion: 3.2
10*387f9dfdSAndroid Build Coastguard Worker]]--
11*387f9dfdSAndroid Build Coastguard Worker
12*387f9dfdSAndroid Build Coastguard Workerrequire("math")
13*387f9dfdSAndroid Build Coastguard Workerlocal M={}
14*387f9dfdSAndroid Build Coastguard Worker
15*387f9dfdSAndroid Build Coastguard Worker-- private exported functions (for testing)
16*387f9dfdSAndroid Build Coastguard WorkerM.private = {}
17*387f9dfdSAndroid Build Coastguard Worker
18*387f9dfdSAndroid Build Coastguard WorkerM.VERSION='3.2'
19*387f9dfdSAndroid Build Coastguard Worker
20*387f9dfdSAndroid Build Coastguard Worker--[[ Some people like assertEquals( actual, expected ) and some people prefer
21*387f9dfdSAndroid Build Coastguard WorkerassertEquals( expected, actual ).
22*387f9dfdSAndroid Build Coastguard Worker]]--
23*387f9dfdSAndroid Build Coastguard WorkerM.ORDER_ACTUAL_EXPECTED = true
24*387f9dfdSAndroid Build Coastguard WorkerM.PRINT_TABLE_REF_IN_ERROR_MSG = false
25*387f9dfdSAndroid Build Coastguard WorkerM.TABLE_EQUALS_KEYBYCONTENT = true
26*387f9dfdSAndroid Build Coastguard WorkerM.LINE_LENGTH=80
27*387f9dfdSAndroid Build Coastguard Worker
28*387f9dfdSAndroid Build Coastguard Worker-- set this to false to debug luaunit
29*387f9dfdSAndroid Build Coastguard Workerlocal STRIP_LUAUNIT_FROM_STACKTRACE=true
30*387f9dfdSAndroid Build Coastguard Worker
31*387f9dfdSAndroid Build Coastguard WorkerM.VERBOSITY_DEFAULT = 10
32*387f9dfdSAndroid Build Coastguard WorkerM.VERBOSITY_LOW     = 1
33*387f9dfdSAndroid Build Coastguard WorkerM.VERBOSITY_QUIET   = 0
34*387f9dfdSAndroid Build Coastguard WorkerM.VERBOSITY_VERBOSE = 20
35*387f9dfdSAndroid Build Coastguard Worker
36*387f9dfdSAndroid Build Coastguard Worker-- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values
37*387f9dfdSAndroid Build Coastguard Worker-- EXPORT_ASSERT_TO_GLOBALS = true
38*387f9dfdSAndroid Build Coastguard Worker
39*387f9dfdSAndroid Build Coastguard Worker-- we need to keep a copy of the script args before it is overridden
40*387f9dfdSAndroid Build Coastguard Workerlocal cmdline_argv = rawget(_G, "arg")
41*387f9dfdSAndroid Build Coastguard Worker
42*387f9dfdSAndroid Build Coastguard WorkerM.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests
43*387f9dfdSAndroid Build Coastguard Worker
44*387f9dfdSAndroid Build Coastguard WorkerM.USAGE=[[Usage: lua <your_test_suite.lua> [options] [testname1 [testname2] ... ]
45*387f9dfdSAndroid Build Coastguard WorkerOptions:
46*387f9dfdSAndroid Build Coastguard Worker  -h, --help:             Print this help
47*387f9dfdSAndroid Build Coastguard Worker  --version:              Print version information
48*387f9dfdSAndroid Build Coastguard Worker  -v, --verbose:          Increase verbosity
49*387f9dfdSAndroid Build Coastguard Worker  -q, --quiet:            Set verbosity to minimum
50*387f9dfdSAndroid Build Coastguard Worker  -e, --error:            Stop on first error
51*387f9dfdSAndroid Build Coastguard Worker  -f, --failure:          Stop on first failure or error
52*387f9dfdSAndroid Build Coastguard Worker  -o, --output OUTPUT:    Set output type to OUTPUT
53*387f9dfdSAndroid Build Coastguard Worker                          Possible values: text, tap, junit, nil
54*387f9dfdSAndroid Build Coastguard Worker  -n, --name NAME:        For junit only, mandatory name of xml file
55*387f9dfdSAndroid Build Coastguard Worker  -p, --pattern PATTERN:  Execute all test names matching the Lua PATTERN
56*387f9dfdSAndroid Build Coastguard Worker                          May be repeated to include severals patterns
57*387f9dfdSAndroid Build Coastguard Worker                          Make sure you escape magic chars like +? with %
58*387f9dfdSAndroid Build Coastguard Worker  testname1, testname2, ... : tests to run in the form of testFunction,
59*387f9dfdSAndroid Build Coastguard Worker                              TestClass or TestClass.testMethod
60*387f9dfdSAndroid Build Coastguard Worker]]
61*387f9dfdSAndroid Build Coastguard Worker
62*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
63*387f9dfdSAndroid Build Coastguard Worker--
64*387f9dfdSAndroid Build Coastguard Worker--                 general utility functions
65*387f9dfdSAndroid Build Coastguard Worker--
66*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
67*387f9dfdSAndroid Build Coastguard Worker
68*387f9dfdSAndroid Build Coastguard Workerlocal crossTypeOrdering = {
69*387f9dfdSAndroid Build Coastguard Worker    number = 1,
70*387f9dfdSAndroid Build Coastguard Worker    boolean = 2,
71*387f9dfdSAndroid Build Coastguard Worker    string = 3,
72*387f9dfdSAndroid Build Coastguard Worker    table = 4,
73*387f9dfdSAndroid Build Coastguard Worker    other = 5
74*387f9dfdSAndroid Build Coastguard Worker}
75*387f9dfdSAndroid Build Coastguard Workerlocal crossTypeComparison = {
76*387f9dfdSAndroid Build Coastguard Worker    number = function(a, b) return a < b end,
77*387f9dfdSAndroid Build Coastguard Worker    string = function(a, b) return a < b end,
78*387f9dfdSAndroid Build Coastguard Worker    other = function(a, b) return tostring(a) < tostring(b) end,
79*387f9dfdSAndroid Build Coastguard Worker}
80*387f9dfdSAndroid Build Coastguard Worker
81*387f9dfdSAndroid Build Coastguard Workerlocal function crossTypeSort(a, b)
82*387f9dfdSAndroid Build Coastguard Worker    local type_a, type_b = type(a), type(b)
83*387f9dfdSAndroid Build Coastguard Worker    if type_a == type_b then
84*387f9dfdSAndroid Build Coastguard Worker        local func = crossTypeComparison[type_a] or crossTypeComparison.other
85*387f9dfdSAndroid Build Coastguard Worker        return func(a, b)
86*387f9dfdSAndroid Build Coastguard Worker    end
87*387f9dfdSAndroid Build Coastguard Worker    type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other
88*387f9dfdSAndroid Build Coastguard Worker    type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other
89*387f9dfdSAndroid Build Coastguard Worker    return type_a < type_b
90*387f9dfdSAndroid Build Coastguard Workerend
91*387f9dfdSAndroid Build Coastguard Worker
92*387f9dfdSAndroid Build Coastguard Workerlocal function __genSortedIndex( t )
93*387f9dfdSAndroid Build Coastguard Worker    -- Returns a sequence consisting of t's keys, sorted.
94*387f9dfdSAndroid Build Coastguard Worker    local sortedIndex = {}
95*387f9dfdSAndroid Build Coastguard Worker
96*387f9dfdSAndroid Build Coastguard Worker    for key,_ in pairs(t) do
97*387f9dfdSAndroid Build Coastguard Worker        table.insert(sortedIndex, key)
98*387f9dfdSAndroid Build Coastguard Worker    end
99*387f9dfdSAndroid Build Coastguard Worker
100*387f9dfdSAndroid Build Coastguard Worker    table.sort(sortedIndex, crossTypeSort)
101*387f9dfdSAndroid Build Coastguard Worker    return sortedIndex
102*387f9dfdSAndroid Build Coastguard Workerend
103*387f9dfdSAndroid Build Coastguard WorkerM.private.__genSortedIndex = __genSortedIndex
104*387f9dfdSAndroid Build Coastguard Worker
105*387f9dfdSAndroid Build Coastguard Workerlocal function sortedNext(state, control)
106*387f9dfdSAndroid Build Coastguard Worker    -- Equivalent of the next() function of table iteration, but returns the
107*387f9dfdSAndroid Build Coastguard Worker    -- keys in sorted order (see __genSortedIndex and crossTypeSort).
108*387f9dfdSAndroid Build Coastguard Worker    -- The state is a temporary variable during iteration and contains the
109*387f9dfdSAndroid Build Coastguard Worker    -- sorted key table (state.sortedIdx). It also stores the last index (into
110*387f9dfdSAndroid Build Coastguard Worker    -- the keys) used by the iteration, to find the next one quickly.
111*387f9dfdSAndroid Build Coastguard Worker    local key
112*387f9dfdSAndroid Build Coastguard Worker
113*387f9dfdSAndroid Build Coastguard Worker    --print("sortedNext: control = "..tostring(control) )
114*387f9dfdSAndroid Build Coastguard Worker    if control == nil then
115*387f9dfdSAndroid Build Coastguard Worker        -- start of iteration
116*387f9dfdSAndroid Build Coastguard Worker        state.lastIdx = 1
117*387f9dfdSAndroid Build Coastguard Worker        key = state.sortedIdx[1]
118*387f9dfdSAndroid Build Coastguard Worker        return key, state.t[key]
119*387f9dfdSAndroid Build Coastguard Worker    end
120*387f9dfdSAndroid Build Coastguard Worker
121*387f9dfdSAndroid Build Coastguard Worker    -- normally, we expect the control variable to match the last key used
122*387f9dfdSAndroid Build Coastguard Worker    if control ~= state.sortedIdx[state.lastIdx] then
123*387f9dfdSAndroid Build Coastguard Worker        -- strange, we have to find the next value by ourselves
124*387f9dfdSAndroid Build Coastguard Worker        -- the key table is sorted in crossTypeSort() order! -> use bisection
125*387f9dfdSAndroid Build Coastguard Worker        local count = #state.sortedIdx
126*387f9dfdSAndroid Build Coastguard Worker        local lower, upper = 1, count
127*387f9dfdSAndroid Build Coastguard Worker        repeat
128*387f9dfdSAndroid Build Coastguard Worker            state.lastIdx = math.modf((lower + upper) / 2)
129*387f9dfdSAndroid Build Coastguard Worker            key = state.sortedIdx[state.lastIdx]
130*387f9dfdSAndroid Build Coastguard Worker            if key == control then break; end -- key found (and thus prev index)
131*387f9dfdSAndroid Build Coastguard Worker            if crossTypeSort(key, control) then
132*387f9dfdSAndroid Build Coastguard Worker                -- key < control, continue search "right" (towards upper bound)
133*387f9dfdSAndroid Build Coastguard Worker                lower = state.lastIdx + 1
134*387f9dfdSAndroid Build Coastguard Worker            else
135*387f9dfdSAndroid Build Coastguard Worker                -- key > control, continue search "left" (towards lower bound)
136*387f9dfdSAndroid Build Coastguard Worker                upper = state.lastIdx - 1
137*387f9dfdSAndroid Build Coastguard Worker            end
138*387f9dfdSAndroid Build Coastguard Worker        until lower > upper
139*387f9dfdSAndroid Build Coastguard Worker        if lower > upper then -- only true if the key wasn't found, ...
140*387f9dfdSAndroid Build Coastguard Worker            state.lastIdx = count -- ... so ensure no match for the code below
141*387f9dfdSAndroid Build Coastguard Worker        end
142*387f9dfdSAndroid Build Coastguard Worker    end
143*387f9dfdSAndroid Build Coastguard Worker
144*387f9dfdSAndroid Build Coastguard Worker    -- proceed by retrieving the next value (or nil) from the sorted keys
145*387f9dfdSAndroid Build Coastguard Worker    state.lastIdx = state.lastIdx + 1
146*387f9dfdSAndroid Build Coastguard Worker    key = state.sortedIdx[state.lastIdx]
147*387f9dfdSAndroid Build Coastguard Worker    if key then
148*387f9dfdSAndroid Build Coastguard Worker        return key, state.t[key]
149*387f9dfdSAndroid Build Coastguard Worker    end
150*387f9dfdSAndroid Build Coastguard Worker
151*387f9dfdSAndroid Build Coastguard Worker    -- getting here means returning `nil`, which will end the iteration
152*387f9dfdSAndroid Build Coastguard Workerend
153*387f9dfdSAndroid Build Coastguard Worker
154*387f9dfdSAndroid Build Coastguard Workerlocal function sortedPairs(tbl)
155*387f9dfdSAndroid Build Coastguard Worker    -- Equivalent of the pairs() function on tables. Allows to iterate in
156*387f9dfdSAndroid Build Coastguard Worker    -- sorted order. As required by "generic for" loops, this will return the
157*387f9dfdSAndroid Build Coastguard Worker    -- iterator (function), an "invariant state", and the initial control value.
158*387f9dfdSAndroid Build Coastguard Worker    -- (see http://www.lua.org/pil/7.2.html)
159*387f9dfdSAndroid Build Coastguard Worker    return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil
160*387f9dfdSAndroid Build Coastguard Workerend
161*387f9dfdSAndroid Build Coastguard WorkerM.private.sortedPairs = sortedPairs
162*387f9dfdSAndroid Build Coastguard Worker
163*387f9dfdSAndroid Build Coastguard Workerlocal function strsplit(delimiter, text)
164*387f9dfdSAndroid Build Coastguard Worker-- Split text into a list consisting of the strings in text,
165*387f9dfdSAndroid Build Coastguard Worker-- separated by strings matching delimiter (which may be a pattern).
166*387f9dfdSAndroid Build Coastguard Worker-- example: strsplit(",%s*", "Anna, Bob, Charlie,Dolores")
167*387f9dfdSAndroid Build Coastguard Worker    if string.find("", delimiter, 1, true) then -- this would result in endless loops
168*387f9dfdSAndroid Build Coastguard Worker        error("delimiter matches empty string!")
169*387f9dfdSAndroid Build Coastguard Worker    end
170*387f9dfdSAndroid Build Coastguard Worker    local list, pos, first, last = {}, 1
171*387f9dfdSAndroid Build Coastguard Worker    while true do
172*387f9dfdSAndroid Build Coastguard Worker        first, last = text:find(delimiter, pos, true)
173*387f9dfdSAndroid Build Coastguard Worker        if first then -- found?
174*387f9dfdSAndroid Build Coastguard Worker            table.insert(list, text:sub(pos, first - 1))
175*387f9dfdSAndroid Build Coastguard Worker            pos = last + 1
176*387f9dfdSAndroid Build Coastguard Worker        else
177*387f9dfdSAndroid Build Coastguard Worker            table.insert(list, text:sub(pos))
178*387f9dfdSAndroid Build Coastguard Worker            break
179*387f9dfdSAndroid Build Coastguard Worker        end
180*387f9dfdSAndroid Build Coastguard Worker    end
181*387f9dfdSAndroid Build Coastguard Worker    return list
182*387f9dfdSAndroid Build Coastguard Workerend
183*387f9dfdSAndroid Build Coastguard WorkerM.private.strsplit = strsplit
184*387f9dfdSAndroid Build Coastguard Worker
185*387f9dfdSAndroid Build Coastguard Workerlocal function hasNewLine( s )
186*387f9dfdSAndroid Build Coastguard Worker    -- return true if s has a newline
187*387f9dfdSAndroid Build Coastguard Worker    return (string.find(s, '\n', 1, true) ~= nil)
188*387f9dfdSAndroid Build Coastguard Workerend
189*387f9dfdSAndroid Build Coastguard WorkerM.private.hasNewLine = hasNewLine
190*387f9dfdSAndroid Build Coastguard Worker
191*387f9dfdSAndroid Build Coastguard Workerlocal function prefixString( prefix, s )
192*387f9dfdSAndroid Build Coastguard Worker    -- Prefix all the lines of s with prefix
193*387f9dfdSAndroid Build Coastguard Worker    return prefix .. table.concat(strsplit('\n', s), '\n' .. prefix)
194*387f9dfdSAndroid Build Coastguard Workerend
195*387f9dfdSAndroid Build Coastguard WorkerM.private.prefixString = prefixString
196*387f9dfdSAndroid Build Coastguard Worker
197*387f9dfdSAndroid Build Coastguard Workerlocal function strMatch(s, pattern, start, final )
198*387f9dfdSAndroid Build Coastguard Worker    -- return true if s matches completely the pattern from index start to index end
199*387f9dfdSAndroid Build Coastguard Worker    -- return false in every other cases
200*387f9dfdSAndroid Build Coastguard Worker    -- if start is nil, matches from the beginning of the string
201*387f9dfdSAndroid Build Coastguard Worker    -- if final is nil, matches to the end of the string
202*387f9dfdSAndroid Build Coastguard Worker    start = start or 1
203*387f9dfdSAndroid Build Coastguard Worker    final = final or string.len(s)
204*387f9dfdSAndroid Build Coastguard Worker
205*387f9dfdSAndroid Build Coastguard Worker    local foundStart, foundEnd = string.find(s, pattern, start, false)
206*387f9dfdSAndroid Build Coastguard Worker    return foundStart == start and foundEnd == final
207*387f9dfdSAndroid Build Coastguard Workerend
208*387f9dfdSAndroid Build Coastguard WorkerM.private.strMatch = strMatch
209*387f9dfdSAndroid Build Coastguard Worker
210*387f9dfdSAndroid Build Coastguard Workerlocal function xmlEscape( s )
211*387f9dfdSAndroid Build Coastguard Worker    -- Return s escaped for XML attributes
212*387f9dfdSAndroid Build Coastguard Worker    -- escapes table:
213*387f9dfdSAndroid Build Coastguard Worker    -- "   &quot;
214*387f9dfdSAndroid Build Coastguard Worker    -- '   &apos;
215*387f9dfdSAndroid Build Coastguard Worker    -- <   &lt;
216*387f9dfdSAndroid Build Coastguard Worker    -- >   &gt;
217*387f9dfdSAndroid Build Coastguard Worker    -- &   &amp;
218*387f9dfdSAndroid Build Coastguard Worker
219*387f9dfdSAndroid Build Coastguard Worker    return string.gsub( s, '.', {
220*387f9dfdSAndroid Build Coastguard Worker        ['&'] = "&amp;",
221*387f9dfdSAndroid Build Coastguard Worker        ['"'] = "&quot;",
222*387f9dfdSAndroid Build Coastguard Worker        ["'"] = "&apos;",
223*387f9dfdSAndroid Build Coastguard Worker        ['<'] = "&lt;",
224*387f9dfdSAndroid Build Coastguard Worker        ['>'] = "&gt;",
225*387f9dfdSAndroid Build Coastguard Worker    } )
226*387f9dfdSAndroid Build Coastguard Workerend
227*387f9dfdSAndroid Build Coastguard WorkerM.private.xmlEscape = xmlEscape
228*387f9dfdSAndroid Build Coastguard Worker
229*387f9dfdSAndroid Build Coastguard Workerlocal function xmlCDataEscape( s )
230*387f9dfdSAndroid Build Coastguard Worker    -- Return s escaped for CData section, escapes: "]]>"
231*387f9dfdSAndroid Build Coastguard Worker    return string.gsub( s, ']]>', ']]&gt;' )
232*387f9dfdSAndroid Build Coastguard Workerend
233*387f9dfdSAndroid Build Coastguard WorkerM.private.xmlCDataEscape = xmlCDataEscape
234*387f9dfdSAndroid Build Coastguard Worker
235*387f9dfdSAndroid Build Coastguard Workerlocal function stripLuaunitTrace( stackTrace )
236*387f9dfdSAndroid Build Coastguard Worker    --[[
237*387f9dfdSAndroid Build Coastguard Worker    -- Example of  a traceback:
238*387f9dfdSAndroid Build Coastguard Worker    <<stack traceback:
239*387f9dfdSAndroid Build Coastguard Worker        example_with_luaunit.lua:130: in function 'test2_withFailure'
240*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1449: in function <./luaunit.lua:1449>
241*387f9dfdSAndroid Build Coastguard Worker        [C]: in function 'xpcall'
242*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1449: in function 'protectedCall'
243*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1508: in function 'execOneFunction'
244*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1596: in function 'runSuiteByInstances'
245*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1660: in function 'runSuiteByNames'
246*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1736: in function 'runSuite'
247*387f9dfdSAndroid Build Coastguard Worker        example_with_luaunit.lua:140: in main chunk
248*387f9dfdSAndroid Build Coastguard Worker        [C]: in ?>>
249*387f9dfdSAndroid Build Coastguard Worker
250*387f9dfdSAndroid Build Coastguard Worker        Other example:
251*387f9dfdSAndroid Build Coastguard Worker    <<stack traceback:
252*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:545: in function 'assertEquals'
253*387f9dfdSAndroid Build Coastguard Worker        example_with_luaunit.lua:58: in function 'TestToto.test7'
254*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1517: in function <./luaunit.lua:1517>
255*387f9dfdSAndroid Build Coastguard Worker        [C]: in function 'xpcall'
256*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1517: in function 'protectedCall'
257*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1578: in function 'execOneFunction'
258*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1677: in function 'runSuiteByInstances'
259*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1730: in function 'runSuiteByNames'
260*387f9dfdSAndroid Build Coastguard Worker        ./luaunit.lua:1806: in function 'runSuite'
261*387f9dfdSAndroid Build Coastguard Worker        example_with_luaunit.lua:140: in main chunk
262*387f9dfdSAndroid Build Coastguard Worker        [C]: in ?>>
263*387f9dfdSAndroid Build Coastguard Worker
264*387f9dfdSAndroid Build Coastguard Worker    <<stack traceback:
265*387f9dfdSAndroid Build Coastguard Worker        luaunit2/example_with_luaunit.lua:124: in function 'test1_withFailure'
266*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1532: in function <luaunit2/luaunit.lua:1532>
267*387f9dfdSAndroid Build Coastguard Worker        [C]: in function 'xpcall'
268*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1532: in function 'protectedCall'
269*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1591: in function 'execOneFunction'
270*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances'
271*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1743: in function 'runSuiteByNames'
272*387f9dfdSAndroid Build Coastguard Worker        luaunit2/luaunit.lua:1819: in function 'runSuite'
273*387f9dfdSAndroid Build Coastguard Worker        luaunit2/example_with_luaunit.lua:140: in main chunk
274*387f9dfdSAndroid Build Coastguard Worker        [C]: in ?>>
275*387f9dfdSAndroid Build Coastguard Worker
276*387f9dfdSAndroid Build Coastguard Worker
277*387f9dfdSAndroid Build Coastguard Worker    -- first line is "stack traceback": KEEP
278*387f9dfdSAndroid Build Coastguard Worker    -- next line may be luaunit line: REMOVE
279*387f9dfdSAndroid Build Coastguard Worker    -- next lines are call in the program under testOk: REMOVE
280*387f9dfdSAndroid Build Coastguard Worker    -- next lines are calls from luaunit to call the program under test: KEEP
281*387f9dfdSAndroid Build Coastguard Worker
282*387f9dfdSAndroid Build Coastguard Worker    -- Strategy:
283*387f9dfdSAndroid Build Coastguard Worker    -- keep first line
284*387f9dfdSAndroid Build Coastguard Worker    -- remove lines that are part of luaunit
285*387f9dfdSAndroid Build Coastguard Worker    -- kepp lines until we hit a luaunit line
286*387f9dfdSAndroid Build Coastguard Worker    ]]
287*387f9dfdSAndroid Build Coastguard Worker
288*387f9dfdSAndroid Build Coastguard Worker    local function isLuaunitInternalLine( s )
289*387f9dfdSAndroid Build Coastguard Worker        -- return true if line of stack trace comes from inside luaunit
290*387f9dfdSAndroid Build Coastguard Worker        return s:find('[/\\]luaunit%.lua:%d+: ') ~= nil
291*387f9dfdSAndroid Build Coastguard Worker    end
292*387f9dfdSAndroid Build Coastguard Worker
293*387f9dfdSAndroid Build Coastguard Worker    -- print( '<<'..stackTrace..'>>' )
294*387f9dfdSAndroid Build Coastguard Worker
295*387f9dfdSAndroid Build Coastguard Worker    local t = strsplit( '\n', stackTrace )
296*387f9dfdSAndroid Build Coastguard Worker    -- print( prettystr(t) )
297*387f9dfdSAndroid Build Coastguard Worker
298*387f9dfdSAndroid Build Coastguard Worker    local idx = 2
299*387f9dfdSAndroid Build Coastguard Worker
300*387f9dfdSAndroid Build Coastguard Worker    -- remove lines that are still part of luaunit
301*387f9dfdSAndroid Build Coastguard Worker    while t[idx] and isLuaunitInternalLine( t[idx] ) do
302*387f9dfdSAndroid Build Coastguard Worker        -- print('Removing : '..t[idx] )
303*387f9dfdSAndroid Build Coastguard Worker        table.remove(t, idx)
304*387f9dfdSAndroid Build Coastguard Worker    end
305*387f9dfdSAndroid Build Coastguard Worker
306*387f9dfdSAndroid Build Coastguard Worker    -- keep lines until we hit luaunit again
307*387f9dfdSAndroid Build Coastguard Worker    while t[idx] and (not isLuaunitInternalLine(t[idx])) do
308*387f9dfdSAndroid Build Coastguard Worker        -- print('Keeping : '..t[idx] )
309*387f9dfdSAndroid Build Coastguard Worker        idx = idx + 1
310*387f9dfdSAndroid Build Coastguard Worker    end
311*387f9dfdSAndroid Build Coastguard Worker
312*387f9dfdSAndroid Build Coastguard Worker    -- remove remaining luaunit lines
313*387f9dfdSAndroid Build Coastguard Worker    while t[idx] do
314*387f9dfdSAndroid Build Coastguard Worker        -- print('Removing : '..t[idx] )
315*387f9dfdSAndroid Build Coastguard Worker        table.remove(t, idx)
316*387f9dfdSAndroid Build Coastguard Worker    end
317*387f9dfdSAndroid Build Coastguard Worker
318*387f9dfdSAndroid Build Coastguard Worker    -- print( prettystr(t) )
319*387f9dfdSAndroid Build Coastguard Worker    return table.concat( t, '\n')
320*387f9dfdSAndroid Build Coastguard Worker
321*387f9dfdSAndroid Build Coastguard Workerend
322*387f9dfdSAndroid Build Coastguard WorkerM.private.stripLuaunitTrace = stripLuaunitTrace
323*387f9dfdSAndroid Build Coastguard Worker
324*387f9dfdSAndroid Build Coastguard Worker
325*387f9dfdSAndroid Build Coastguard Workerlocal function prettystr_sub(v, indentLevel, keeponeline, printTableRefs, recursionTable )
326*387f9dfdSAndroid Build Coastguard Worker    local type_v = type(v)
327*387f9dfdSAndroid Build Coastguard Worker    if "string" == type_v  then
328*387f9dfdSAndroid Build Coastguard Worker        if keeponeline then v = v:gsub("\n", "\\n") end
329*387f9dfdSAndroid Build Coastguard Worker
330*387f9dfdSAndroid Build Coastguard Worker        -- use clever delimiters according to content:
331*387f9dfdSAndroid Build Coastguard Worker        -- enclose with single quotes if string contains ", but no '
332*387f9dfdSAndroid Build Coastguard Worker        if v:find('"', 1, true) and not v:find("'", 1, true) then
333*387f9dfdSAndroid Build Coastguard Worker            return "'" .. v .. "'"
334*387f9dfdSAndroid Build Coastguard Worker        end
335*387f9dfdSAndroid Build Coastguard Worker        -- use double quotes otherwise, escape embedded "
336*387f9dfdSAndroid Build Coastguard Worker        return '"' .. v:gsub('"', '\\"') .. '"'
337*387f9dfdSAndroid Build Coastguard Worker
338*387f9dfdSAndroid Build Coastguard Worker    elseif "table" == type_v then
339*387f9dfdSAndroid Build Coastguard Worker        --if v.__class__ then
340*387f9dfdSAndroid Build Coastguard Worker        --    return string.gsub( tostring(v), 'table', v.__class__ )
341*387f9dfdSAndroid Build Coastguard Worker        --end
342*387f9dfdSAndroid Build Coastguard Worker        return M.private._table_tostring(v, indentLevel, printTableRefs, recursionTable)
343*387f9dfdSAndroid Build Coastguard Worker    end
344*387f9dfdSAndroid Build Coastguard Worker
345*387f9dfdSAndroid Build Coastguard Worker    return tostring(v)
346*387f9dfdSAndroid Build Coastguard Workerend
347*387f9dfdSAndroid Build Coastguard Worker
348*387f9dfdSAndroid Build Coastguard Workerlocal function prettystr( v, keeponeline )
349*387f9dfdSAndroid Build Coastguard Worker    --[[ Better string conversion, to display nice variable content:
350*387f9dfdSAndroid Build Coastguard Worker    For strings, if keeponeline is set to true, string is displayed on one line, with visible \n
351*387f9dfdSAndroid Build Coastguard Worker    * string are enclosed with " by default, or with ' if string contains a "
352*387f9dfdSAndroid Build Coastguard Worker    * if table is a class, display class name
353*387f9dfdSAndroid Build Coastguard Worker    * tables are expanded
354*387f9dfdSAndroid Build Coastguard Worker    ]]--
355*387f9dfdSAndroid Build Coastguard Worker    local recursionTable = {}
356*387f9dfdSAndroid Build Coastguard Worker    local s = prettystr_sub(v, 1, keeponeline, M.PRINT_TABLE_REF_IN_ERROR_MSG, recursionTable)
357*387f9dfdSAndroid Build Coastguard Worker    if recursionTable.recursionDetected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then
358*387f9dfdSAndroid Build Coastguard Worker        -- some table contain recursive references,
359*387f9dfdSAndroid Build Coastguard Worker        -- so we must recompute the value by including all table references
360*387f9dfdSAndroid Build Coastguard Worker        -- else the result looks like crap
361*387f9dfdSAndroid Build Coastguard Worker        recursionTable = {}
362*387f9dfdSAndroid Build Coastguard Worker        s = prettystr_sub(v, 1, keeponeline, true, recursionTable)
363*387f9dfdSAndroid Build Coastguard Worker    end
364*387f9dfdSAndroid Build Coastguard Worker    return s
365*387f9dfdSAndroid Build Coastguard Workerend
366*387f9dfdSAndroid Build Coastguard WorkerM.prettystr = prettystr
367*387f9dfdSAndroid Build Coastguard Worker
368*387f9dfdSAndroid Build Coastguard Workerlocal function prettystrPadded(value1, value2, suffix_a, suffix_b)
369*387f9dfdSAndroid Build Coastguard Worker    --[[
370*387f9dfdSAndroid Build Coastguard Worker    This function helps with the recurring task of constructing the "expected
371*387f9dfdSAndroid Build Coastguard Worker    vs. actual" error messages. It takes two arbitrary values and formats
372*387f9dfdSAndroid Build Coastguard Worker    corresponding strings with prettystr().
373*387f9dfdSAndroid Build Coastguard Worker
374*387f9dfdSAndroid Build Coastguard Worker    To keep the (possibly complex) output more readable in case the resulting
375*387f9dfdSAndroid Build Coastguard Worker    strings contain line breaks, they get automatically prefixed with additional
376*387f9dfdSAndroid Build Coastguard Worker    newlines. Both suffixes are optional (default to empty strings), and get
377*387f9dfdSAndroid Build Coastguard Worker    appended to the "value1" string. "suffix_a" is used if line breaks were
378*387f9dfdSAndroid Build Coastguard Worker    encountered, "suffix_b" otherwise.
379*387f9dfdSAndroid Build Coastguard Worker
380*387f9dfdSAndroid Build Coastguard Worker    Returns the two formatted strings (including padding/newlines).
381*387f9dfdSAndroid Build Coastguard Worker    ]]
382*387f9dfdSAndroid Build Coastguard Worker    local str1, str2 = prettystr(value1), prettystr(value2)
383*387f9dfdSAndroid Build Coastguard Worker    if hasNewLine(str1) or hasNewLine(str2) then
384*387f9dfdSAndroid Build Coastguard Worker        -- line break(s) detected, add padding
385*387f9dfdSAndroid Build Coastguard Worker        return "\n" .. str1 .. (suffix_a or ""), "\n" .. str2
386*387f9dfdSAndroid Build Coastguard Worker    end
387*387f9dfdSAndroid Build Coastguard Worker    return str1 .. (suffix_b or ""), str2
388*387f9dfdSAndroid Build Coastguard Workerend
389*387f9dfdSAndroid Build Coastguard WorkerM.private.prettystrPadded = prettystrPadded
390*387f9dfdSAndroid Build Coastguard Worker
391*387f9dfdSAndroid Build Coastguard Workerlocal function _table_keytostring(k)
392*387f9dfdSAndroid Build Coastguard Worker    -- like prettystr but do not enclose with "" if the string is just alphanumerical
393*387f9dfdSAndroid Build Coastguard Worker    -- this is better for displaying table keys who are often simple strings
394*387f9dfdSAndroid Build Coastguard Worker    if "string" == type(k) and k:match("^[_%a][_%w]*$") then
395*387f9dfdSAndroid Build Coastguard Worker        return k
396*387f9dfdSAndroid Build Coastguard Worker    end
397*387f9dfdSAndroid Build Coastguard Worker    return prettystr(k)
398*387f9dfdSAndroid Build Coastguard Workerend
399*387f9dfdSAndroid Build Coastguard WorkerM.private._table_keytostring = _table_keytostring
400*387f9dfdSAndroid Build Coastguard Worker
401*387f9dfdSAndroid Build Coastguard Workerlocal TABLE_TOSTRING_SEP = ", "
402*387f9dfdSAndroid Build Coastguard Workerlocal TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP)
403*387f9dfdSAndroid Build Coastguard Worker
404*387f9dfdSAndroid Build Coastguard Workerlocal function _table_tostring( tbl, indentLevel, printTableRefs, recursionTable )
405*387f9dfdSAndroid Build Coastguard Worker    printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG
406*387f9dfdSAndroid Build Coastguard Worker    recursionTable = recursionTable or {}
407*387f9dfdSAndroid Build Coastguard Worker    recursionTable[tbl] = true
408*387f9dfdSAndroid Build Coastguard Worker
409*387f9dfdSAndroid Build Coastguard Worker    local result, dispOnMultLines = {}, false
410*387f9dfdSAndroid Build Coastguard Worker
411*387f9dfdSAndroid Build Coastguard Worker    local entry, count, seq_index = nil, 0, 1
412*387f9dfdSAndroid Build Coastguard Worker    for k, v in sortedPairs( tbl ) do
413*387f9dfdSAndroid Build Coastguard Worker        if k == seq_index then
414*387f9dfdSAndroid Build Coastguard Worker            -- for the sequential part of tables, we'll skip the "<key>=" output
415*387f9dfdSAndroid Build Coastguard Worker            entry = ''
416*387f9dfdSAndroid Build Coastguard Worker            seq_index = seq_index + 1
417*387f9dfdSAndroid Build Coastguard Worker        else
418*387f9dfdSAndroid Build Coastguard Worker            entry = _table_keytostring( k ) .. "="
419*387f9dfdSAndroid Build Coastguard Worker        end
420*387f9dfdSAndroid Build Coastguard Worker        if recursionTable[v] then -- recursion detected!
421*387f9dfdSAndroid Build Coastguard Worker            recursionTable.recursionDetected = true
422*387f9dfdSAndroid Build Coastguard Worker            entry = entry .. "<"..tostring(v)..">"
423*387f9dfdSAndroid Build Coastguard Worker        else
424*387f9dfdSAndroid Build Coastguard Worker            entry = entry ..
425*387f9dfdSAndroid Build Coastguard Worker                prettystr_sub( v, indentLevel+1, true, printTableRefs, recursionTable )
426*387f9dfdSAndroid Build Coastguard Worker        end
427*387f9dfdSAndroid Build Coastguard Worker        count = count + 1
428*387f9dfdSAndroid Build Coastguard Worker        result[count] = entry
429*387f9dfdSAndroid Build Coastguard Worker    end
430*387f9dfdSAndroid Build Coastguard Worker
431*387f9dfdSAndroid Build Coastguard Worker    -- set dispOnMultLines if the maximum LINE_LENGTH would be exceeded
432*387f9dfdSAndroid Build Coastguard Worker    local totalLength = 0
433*387f9dfdSAndroid Build Coastguard Worker    for k, v in ipairs( result ) do
434*387f9dfdSAndroid Build Coastguard Worker        totalLength = totalLength + string.len( v )
435*387f9dfdSAndroid Build Coastguard Worker        if totalLength >= M.LINE_LENGTH then
436*387f9dfdSAndroid Build Coastguard Worker            dispOnMultLines = true
437*387f9dfdSAndroid Build Coastguard Worker            break
438*387f9dfdSAndroid Build Coastguard Worker        end
439*387f9dfdSAndroid Build Coastguard Worker    end
440*387f9dfdSAndroid Build Coastguard Worker
441*387f9dfdSAndroid Build Coastguard Worker    if not dispOnMultLines then
442*387f9dfdSAndroid Build Coastguard Worker        -- adjust with length of separator(s):
443*387f9dfdSAndroid Build Coastguard Worker        -- two items need 1 sep, three items two seps, ... plus len of '{}'
444*387f9dfdSAndroid Build Coastguard Worker        if count > 0 then
445*387f9dfdSAndroid Build Coastguard Worker            totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (count - 1)
446*387f9dfdSAndroid Build Coastguard Worker        end
447*387f9dfdSAndroid Build Coastguard Worker        dispOnMultLines = totalLength + 2 >= M.LINE_LENGTH
448*387f9dfdSAndroid Build Coastguard Worker    end
449*387f9dfdSAndroid Build Coastguard Worker
450*387f9dfdSAndroid Build Coastguard Worker    -- now reformat the result table (currently holding element strings)
451*387f9dfdSAndroid Build Coastguard Worker    if dispOnMultLines then
452*387f9dfdSAndroid Build Coastguard Worker        local indentString = string.rep("    ", indentLevel - 1)
453*387f9dfdSAndroid Build Coastguard Worker        result = {"{\n    ", indentString,
454*387f9dfdSAndroid Build Coastguard Worker                  table.concat(result, ",\n    " .. indentString), "\n",
455*387f9dfdSAndroid Build Coastguard Worker                  indentString, "}"}
456*387f9dfdSAndroid Build Coastguard Worker    else
457*387f9dfdSAndroid Build Coastguard Worker        result = {"{", table.concat(result, TABLE_TOSTRING_SEP), "}"}
458*387f9dfdSAndroid Build Coastguard Worker    end
459*387f9dfdSAndroid Build Coastguard Worker    if printTableRefs then
460*387f9dfdSAndroid Build Coastguard Worker        table.insert(result, 1, "<"..tostring(tbl).."> ") -- prepend table ref
461*387f9dfdSAndroid Build Coastguard Worker    end
462*387f9dfdSAndroid Build Coastguard Worker    return table.concat(result)
463*387f9dfdSAndroid Build Coastguard Workerend
464*387f9dfdSAndroid Build Coastguard WorkerM.private._table_tostring = _table_tostring -- prettystr_sub() needs it
465*387f9dfdSAndroid Build Coastguard Worker
466*387f9dfdSAndroid Build Coastguard Workerlocal function _table_contains(t, element)
467*387f9dfdSAndroid Build Coastguard Worker    if t then
468*387f9dfdSAndroid Build Coastguard Worker        for _, value in pairs(t) do
469*387f9dfdSAndroid Build Coastguard Worker            if type(value) == type(element) then
470*387f9dfdSAndroid Build Coastguard Worker                if type(element) == 'table' then
471*387f9dfdSAndroid Build Coastguard Worker                    -- if we wanted recursive items content comparison, we could use
472*387f9dfdSAndroid Build Coastguard Worker                    -- _is_table_items_equals(v, expected) but one level of just comparing
473*387f9dfdSAndroid Build Coastguard Worker                    -- items is sufficient
474*387f9dfdSAndroid Build Coastguard Worker                    if M.private._is_table_equals( value, element ) then
475*387f9dfdSAndroid Build Coastguard Worker                        return true
476*387f9dfdSAndroid Build Coastguard Worker                    end
477*387f9dfdSAndroid Build Coastguard Worker                else
478*387f9dfdSAndroid Build Coastguard Worker                    if value == element then
479*387f9dfdSAndroid Build Coastguard Worker                        return true
480*387f9dfdSAndroid Build Coastguard Worker                    end
481*387f9dfdSAndroid Build Coastguard Worker                end
482*387f9dfdSAndroid Build Coastguard Worker            end
483*387f9dfdSAndroid Build Coastguard Worker        end
484*387f9dfdSAndroid Build Coastguard Worker    end
485*387f9dfdSAndroid Build Coastguard Worker    return false
486*387f9dfdSAndroid Build Coastguard Workerend
487*387f9dfdSAndroid Build Coastguard Worker
488*387f9dfdSAndroid Build Coastguard Workerlocal function _is_table_items_equals(actual, expected )
489*387f9dfdSAndroid Build Coastguard Worker    if (type(actual) == 'table') and (type(expected) == 'table') then
490*387f9dfdSAndroid Build Coastguard Worker        for k,v in pairs(actual) do
491*387f9dfdSAndroid Build Coastguard Worker            if not _table_contains(expected, v) then
492*387f9dfdSAndroid Build Coastguard Worker                return false
493*387f9dfdSAndroid Build Coastguard Worker            end
494*387f9dfdSAndroid Build Coastguard Worker        end
495*387f9dfdSAndroid Build Coastguard Worker        for k,v in pairs(expected) do
496*387f9dfdSAndroid Build Coastguard Worker            if not _table_contains(actual, v) then
497*387f9dfdSAndroid Build Coastguard Worker                return false
498*387f9dfdSAndroid Build Coastguard Worker            end
499*387f9dfdSAndroid Build Coastguard Worker        end
500*387f9dfdSAndroid Build Coastguard Worker        return true
501*387f9dfdSAndroid Build Coastguard Worker    elseif type(actual) ~= type(expected) then
502*387f9dfdSAndroid Build Coastguard Worker        return false
503*387f9dfdSAndroid Build Coastguard Worker    elseif actual == expected then
504*387f9dfdSAndroid Build Coastguard Worker        return true
505*387f9dfdSAndroid Build Coastguard Worker    end
506*387f9dfdSAndroid Build Coastguard Worker    return false
507*387f9dfdSAndroid Build Coastguard Workerend
508*387f9dfdSAndroid Build Coastguard Worker
509*387f9dfdSAndroid Build Coastguard Workerlocal function _is_table_equals(actual, expected)
510*387f9dfdSAndroid Build Coastguard Worker    if (type(actual) == 'table') and (type(expected) == 'table') then
511*387f9dfdSAndroid Build Coastguard Worker        if (#actual ~= #expected) then
512*387f9dfdSAndroid Build Coastguard Worker            return false
513*387f9dfdSAndroid Build Coastguard Worker        end
514*387f9dfdSAndroid Build Coastguard Worker
515*387f9dfdSAndroid Build Coastguard Worker        local actualTableKeys = {}
516*387f9dfdSAndroid Build Coastguard Worker        for k,v in pairs(actual) do
517*387f9dfdSAndroid Build Coastguard Worker            if M.TABLE_EQUALS_KEYBYCONTENT and type(k) == "table" then
518*387f9dfdSAndroid Build Coastguard Worker                -- If the keys are tables, things get a bit tricky here as we
519*387f9dfdSAndroid Build Coastguard Worker                -- can have _is_table_equals(k1, k2) and t[k1] ~= t[k2]. So we
520*387f9dfdSAndroid Build Coastguard Worker                -- collect actual's table keys, group them by length for
521*387f9dfdSAndroid Build Coastguard Worker                -- performance, and then for each table key in expected we look
522*387f9dfdSAndroid Build Coastguard Worker                -- it up in actualTableKeys.
523*387f9dfdSAndroid Build Coastguard Worker                if not actualTableKeys[#k] then actualTableKeys[#k] = {} end
524*387f9dfdSAndroid Build Coastguard Worker                table.insert(actualTableKeys[#k], k)
525*387f9dfdSAndroid Build Coastguard Worker            else
526*387f9dfdSAndroid Build Coastguard Worker                if not _is_table_equals(v, expected[k]) then
527*387f9dfdSAndroid Build Coastguard Worker                    return false
528*387f9dfdSAndroid Build Coastguard Worker                end
529*387f9dfdSAndroid Build Coastguard Worker            end
530*387f9dfdSAndroid Build Coastguard Worker        end
531*387f9dfdSAndroid Build Coastguard Worker
532*387f9dfdSAndroid Build Coastguard Worker        for k,v in pairs(expected) do
533*387f9dfdSAndroid Build Coastguard Worker            if M.TABLE_EQUALS_KEYBYCONTENT and type(k) == "table" then
534*387f9dfdSAndroid Build Coastguard Worker                local candidates = actualTableKeys[#k]
535*387f9dfdSAndroid Build Coastguard Worker                if not candidates then return false end
536*387f9dfdSAndroid Build Coastguard Worker                local found
537*387f9dfdSAndroid Build Coastguard Worker                for i, candidate in pairs(candidates) do
538*387f9dfdSAndroid Build Coastguard Worker                    if _is_table_equals(candidate, k) then
539*387f9dfdSAndroid Build Coastguard Worker                        found = candidate
540*387f9dfdSAndroid Build Coastguard Worker                        -- Remove the candidate we matched against from the list
541*387f9dfdSAndroid Build Coastguard Worker                        -- of candidates, so each key in actual can only match
542*387f9dfdSAndroid Build Coastguard Worker                        -- one key in expected.
543*387f9dfdSAndroid Build Coastguard Worker                        candidates[i] = nil
544*387f9dfdSAndroid Build Coastguard Worker                        break
545*387f9dfdSAndroid Build Coastguard Worker                    end
546*387f9dfdSAndroid Build Coastguard Worker                end
547*387f9dfdSAndroid Build Coastguard Worker                if not(found and _is_table_equals(actual[found], v)) then return false end
548*387f9dfdSAndroid Build Coastguard Worker            else
549*387f9dfdSAndroid Build Coastguard Worker                if not _is_table_equals(v, actual[k]) then
550*387f9dfdSAndroid Build Coastguard Worker                    return false
551*387f9dfdSAndroid Build Coastguard Worker                end
552*387f9dfdSAndroid Build Coastguard Worker            end
553*387f9dfdSAndroid Build Coastguard Worker        end
554*387f9dfdSAndroid Build Coastguard Worker
555*387f9dfdSAndroid Build Coastguard Worker        if M.TABLE_EQUALS_KEYBYCONTENT then
556*387f9dfdSAndroid Build Coastguard Worker            for _, keys in pairs(actualTableKeys) do
557*387f9dfdSAndroid Build Coastguard Worker                -- if there are any keys left in any actualTableKeys[i] then
558*387f9dfdSAndroid Build Coastguard Worker                -- that is a key in actual with no matching key in expected,
559*387f9dfdSAndroid Build Coastguard Worker                -- and so the tables aren't equal.
560*387f9dfdSAndroid Build Coastguard Worker                if next(keys) then return false end
561*387f9dfdSAndroid Build Coastguard Worker            end
562*387f9dfdSAndroid Build Coastguard Worker        end
563*387f9dfdSAndroid Build Coastguard Worker
564*387f9dfdSAndroid Build Coastguard Worker        return true
565*387f9dfdSAndroid Build Coastguard Worker    elseif type(actual) ~= type(expected) then
566*387f9dfdSAndroid Build Coastguard Worker        return false
567*387f9dfdSAndroid Build Coastguard Worker    elseif actual == expected then
568*387f9dfdSAndroid Build Coastguard Worker        return true
569*387f9dfdSAndroid Build Coastguard Worker    end
570*387f9dfdSAndroid Build Coastguard Worker    return false
571*387f9dfdSAndroid Build Coastguard Workerend
572*387f9dfdSAndroid Build Coastguard WorkerM.private._is_table_equals = _is_table_equals
573*387f9dfdSAndroid Build Coastguard Worker
574*387f9dfdSAndroid Build Coastguard Workerlocal function failure(msg, level)
575*387f9dfdSAndroid Build Coastguard Worker    -- raise an error indicating a test failure
576*387f9dfdSAndroid Build Coastguard Worker    -- for error() compatibility we adjust "level" here (by +1), to report the
577*387f9dfdSAndroid Build Coastguard Worker    -- calling context
578*387f9dfdSAndroid Build Coastguard Worker    error(M.FAILURE_PREFIX .. msg, (level or 1) + 1)
579*387f9dfdSAndroid Build Coastguard Workerend
580*387f9dfdSAndroid Build Coastguard Worker
581*387f9dfdSAndroid Build Coastguard Workerlocal function fail_fmt(level, ...)
582*387f9dfdSAndroid Build Coastguard Worker     -- failure with printf-style formatted message and given error level
583*387f9dfdSAndroid Build Coastguard Worker    failure(string.format(...), (level or 1) + 1)
584*387f9dfdSAndroid Build Coastguard Workerend
585*387f9dfdSAndroid Build Coastguard WorkerM.private.fail_fmt = fail_fmt
586*387f9dfdSAndroid Build Coastguard Worker
587*387f9dfdSAndroid Build Coastguard Workerlocal function error_fmt(level, ...)
588*387f9dfdSAndroid Build Coastguard Worker     -- printf-style error()
589*387f9dfdSAndroid Build Coastguard Worker    error(string.format(...), (level or 1) + 1)
590*387f9dfdSAndroid Build Coastguard Workerend
591*387f9dfdSAndroid Build Coastguard Worker
592*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
593*387f9dfdSAndroid Build Coastguard Worker--
594*387f9dfdSAndroid Build Coastguard Worker--                     assertions
595*387f9dfdSAndroid Build Coastguard Worker--
596*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
597*387f9dfdSAndroid Build Coastguard Worker
598*387f9dfdSAndroid Build Coastguard Workerlocal function errorMsgEquality(actual, expected)
599*387f9dfdSAndroid Build Coastguard Worker    if not M.ORDER_ACTUAL_EXPECTED then
600*387f9dfdSAndroid Build Coastguard Worker        expected, actual = actual, expected
601*387f9dfdSAndroid Build Coastguard Worker    end
602*387f9dfdSAndroid Build Coastguard Worker    if type(expected) == 'string' or type(expected) == 'table' then
603*387f9dfdSAndroid Build Coastguard Worker        expected, actual = prettystrPadded(expected, actual)
604*387f9dfdSAndroid Build Coastguard Worker        return string.format("expected: %s\nactual: %s", expected, actual)
605*387f9dfdSAndroid Build Coastguard Worker    end
606*387f9dfdSAndroid Build Coastguard Worker    return string.format("expected: %s, actual: %s",
607*387f9dfdSAndroid Build Coastguard Worker                         prettystr(expected), prettystr(actual))
608*387f9dfdSAndroid Build Coastguard Workerend
609*387f9dfdSAndroid Build Coastguard Worker
610*387f9dfdSAndroid Build Coastguard Workerfunction M.assertError(f, ...)
611*387f9dfdSAndroid Build Coastguard Worker    -- assert that calling f with the arguments will raise an error
612*387f9dfdSAndroid Build Coastguard Worker    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
613*387f9dfdSAndroid Build Coastguard Worker    if pcall( f, ... ) then
614*387f9dfdSAndroid Build Coastguard Worker        failure( "Expected an error when calling function but no error generated", 2 )
615*387f9dfdSAndroid Build Coastguard Worker    end
616*387f9dfdSAndroid Build Coastguard Workerend
617*387f9dfdSAndroid Build Coastguard Worker
618*387f9dfdSAndroid Build Coastguard Workerfunction M.assertTrue(value)
619*387f9dfdSAndroid Build Coastguard Worker    if not value then
620*387f9dfdSAndroid Build Coastguard Worker        failure("expected: true, actual: " ..prettystr(value), 2)
621*387f9dfdSAndroid Build Coastguard Worker    end
622*387f9dfdSAndroid Build Coastguard Workerend
623*387f9dfdSAndroid Build Coastguard Worker
624*387f9dfdSAndroid Build Coastguard Workerfunction M.assertFalse(value)
625*387f9dfdSAndroid Build Coastguard Worker    if value then
626*387f9dfdSAndroid Build Coastguard Worker        failure("expected: false, actual: " ..prettystr(value), 2)
627*387f9dfdSAndroid Build Coastguard Worker    end
628*387f9dfdSAndroid Build Coastguard Workerend
629*387f9dfdSAndroid Build Coastguard Worker
630*387f9dfdSAndroid Build Coastguard Workerfunction M.assertIsNil(value)
631*387f9dfdSAndroid Build Coastguard Worker    if value ~= nil then
632*387f9dfdSAndroid Build Coastguard Worker        failure("expected: nil, actual: " ..prettystr(value), 2)
633*387f9dfdSAndroid Build Coastguard Worker    end
634*387f9dfdSAndroid Build Coastguard Workerend
635*387f9dfdSAndroid Build Coastguard Worker
636*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotIsNil(value)
637*387f9dfdSAndroid Build Coastguard Worker    if value == nil then
638*387f9dfdSAndroid Build Coastguard Worker        failure("expected non nil value, received nil", 2)
639*387f9dfdSAndroid Build Coastguard Worker    end
640*387f9dfdSAndroid Build Coastguard Workerend
641*387f9dfdSAndroid Build Coastguard Worker
642*387f9dfdSAndroid Build Coastguard Workerfunction M.assertEquals(actual, expected)
643*387f9dfdSAndroid Build Coastguard Worker    if type(actual) == 'table' and type(expected) == 'table' then
644*387f9dfdSAndroid Build Coastguard Worker        if not _is_table_equals(actual, expected) then
645*387f9dfdSAndroid Build Coastguard Worker            failure( errorMsgEquality(actual, expected), 2 )
646*387f9dfdSAndroid Build Coastguard Worker        end
647*387f9dfdSAndroid Build Coastguard Worker    elseif type(actual) ~= type(expected) then
648*387f9dfdSAndroid Build Coastguard Worker        failure( errorMsgEquality(actual, expected), 2 )
649*387f9dfdSAndroid Build Coastguard Worker    elseif actual ~= expected then
650*387f9dfdSAndroid Build Coastguard Worker        failure( errorMsgEquality(actual, expected), 2 )
651*387f9dfdSAndroid Build Coastguard Worker    end
652*387f9dfdSAndroid Build Coastguard Workerend
653*387f9dfdSAndroid Build Coastguard Worker
654*387f9dfdSAndroid Build Coastguard Worker-- Help Lua in corner cases like almostEquals(1.1, 1.0, 0.1), which by default
655*387f9dfdSAndroid Build Coastguard Worker-- may not work. We need to give margin a small boost; EPSILON defines the
656*387f9dfdSAndroid Build Coastguard Worker-- default value to use for this:
657*387f9dfdSAndroid Build Coastguard Workerlocal EPSILON = 0.00000000001
658*387f9dfdSAndroid Build Coastguard Workerfunction M.almostEquals( actual, expected, margin, margin_boost )
659*387f9dfdSAndroid Build Coastguard Worker    if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then
660*387f9dfdSAndroid Build Coastguard Worker        error_fmt(3, 'almostEquals: must supply only number arguments.\nArguments supplied: %s, %s, %s',
661*387f9dfdSAndroid Build Coastguard Worker            prettystr(actual), prettystr(expected), prettystr(margin))
662*387f9dfdSAndroid Build Coastguard Worker    end
663*387f9dfdSAndroid Build Coastguard Worker    if margin <= 0 then
664*387f9dfdSAndroid Build Coastguard Worker        error('almostEquals: margin must be positive, current value is ' .. margin, 3)
665*387f9dfdSAndroid Build Coastguard Worker    end
666*387f9dfdSAndroid Build Coastguard Worker    local realmargin = margin + (margin_boost or EPSILON)
667*387f9dfdSAndroid Build Coastguard Worker    return math.abs(expected - actual) <= realmargin
668*387f9dfdSAndroid Build Coastguard Workerend
669*387f9dfdSAndroid Build Coastguard Worker
670*387f9dfdSAndroid Build Coastguard Workerfunction M.assertAlmostEquals( actual, expected, margin )
671*387f9dfdSAndroid Build Coastguard Worker    -- check that two floats are close by margin
672*387f9dfdSAndroid Build Coastguard Worker    if not M.almostEquals(actual, expected, margin) then
673*387f9dfdSAndroid Build Coastguard Worker        if not M.ORDER_ACTUAL_EXPECTED then
674*387f9dfdSAndroid Build Coastguard Worker            expected, actual = actual, expected
675*387f9dfdSAndroid Build Coastguard Worker        end
676*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Values are not almost equal\nExpected: %s with margin of %s, received: %s',
677*387f9dfdSAndroid Build Coastguard Worker                 expected, margin, actual)
678*387f9dfdSAndroid Build Coastguard Worker    end
679*387f9dfdSAndroid Build Coastguard Workerend
680*387f9dfdSAndroid Build Coastguard Worker
681*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotEquals(actual, expected)
682*387f9dfdSAndroid Build Coastguard Worker    if type(actual) ~= type(expected) then
683*387f9dfdSAndroid Build Coastguard Worker        return
684*387f9dfdSAndroid Build Coastguard Worker    end
685*387f9dfdSAndroid Build Coastguard Worker
686*387f9dfdSAndroid Build Coastguard Worker    if type(actual) == 'table' and type(expected) == 'table' then
687*387f9dfdSAndroid Build Coastguard Worker        if not _is_table_equals(actual, expected) then
688*387f9dfdSAndroid Build Coastguard Worker            return
689*387f9dfdSAndroid Build Coastguard Worker        end
690*387f9dfdSAndroid Build Coastguard Worker    elseif actual ~= expected then
691*387f9dfdSAndroid Build Coastguard Worker        return
692*387f9dfdSAndroid Build Coastguard Worker    end
693*387f9dfdSAndroid Build Coastguard Worker    fail_fmt(2, 'Received the not expected value: %s', prettystr(actual))
694*387f9dfdSAndroid Build Coastguard Workerend
695*387f9dfdSAndroid Build Coastguard Worker
696*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotAlmostEquals( actual, expected, margin )
697*387f9dfdSAndroid Build Coastguard Worker    -- check that two floats are not close by margin
698*387f9dfdSAndroid Build Coastguard Worker    if M.almostEquals(actual, expected, margin) then
699*387f9dfdSAndroid Build Coastguard Worker        if not M.ORDER_ACTUAL_EXPECTED then
700*387f9dfdSAndroid Build Coastguard Worker            expected, actual = actual, expected
701*387f9dfdSAndroid Build Coastguard Worker        end
702*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Values are almost equal\nExpected: %s with a difference above margin of %s, received: %s',
703*387f9dfdSAndroid Build Coastguard Worker                 expected, margin, actual)
704*387f9dfdSAndroid Build Coastguard Worker    end
705*387f9dfdSAndroid Build Coastguard Workerend
706*387f9dfdSAndroid Build Coastguard Worker
707*387f9dfdSAndroid Build Coastguard Workerfunction M.assertStrContains( str, sub, useRe )
708*387f9dfdSAndroid Build Coastguard Worker    -- this relies on lua string.find function
709*387f9dfdSAndroid Build Coastguard Worker    -- a string always contains the empty string
710*387f9dfdSAndroid Build Coastguard Worker    if not string.find(str, sub, 1, not useRe) then
711*387f9dfdSAndroid Build Coastguard Worker        sub, str = prettystrPadded(sub, str, '\n')
712*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error, %s %s was not found in string %s',
713*387f9dfdSAndroid Build Coastguard Worker                 useRe and 'regexp' or 'substring', sub, str)
714*387f9dfdSAndroid Build Coastguard Worker    end
715*387f9dfdSAndroid Build Coastguard Workerend
716*387f9dfdSAndroid Build Coastguard Worker
717*387f9dfdSAndroid Build Coastguard Workerfunction M.assertStrIContains( str, sub )
718*387f9dfdSAndroid Build Coastguard Worker    -- this relies on lua string.find function
719*387f9dfdSAndroid Build Coastguard Worker    -- a string always contains the empty string
720*387f9dfdSAndroid Build Coastguard Worker    if not string.find(str:lower(), sub:lower(), 1, true) then
721*387f9dfdSAndroid Build Coastguard Worker        sub, str = prettystrPadded(sub, str, '\n')
722*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error, substring %s was not found (case insensitively) in string %s',
723*387f9dfdSAndroid Build Coastguard Worker                 sub, str)
724*387f9dfdSAndroid Build Coastguard Worker    end
725*387f9dfdSAndroid Build Coastguard Workerend
726*387f9dfdSAndroid Build Coastguard Worker
727*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotStrContains( str, sub, useRe )
728*387f9dfdSAndroid Build Coastguard Worker    -- this relies on lua string.find function
729*387f9dfdSAndroid Build Coastguard Worker    -- a string always contains the empty string
730*387f9dfdSAndroid Build Coastguard Worker    if string.find(str, sub, 1, not useRe) then
731*387f9dfdSAndroid Build Coastguard Worker        sub, str = prettystrPadded(sub, str, '\n')
732*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error, %s %s was found in string %s',
733*387f9dfdSAndroid Build Coastguard Worker                 useRe and 'regexp' or 'substring', sub, str)
734*387f9dfdSAndroid Build Coastguard Worker    end
735*387f9dfdSAndroid Build Coastguard Workerend
736*387f9dfdSAndroid Build Coastguard Worker
737*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotStrIContains( str, sub )
738*387f9dfdSAndroid Build Coastguard Worker    -- this relies on lua string.find function
739*387f9dfdSAndroid Build Coastguard Worker    -- a string always contains the empty string
740*387f9dfdSAndroid Build Coastguard Worker    if string.find(str:lower(), sub:lower(), 1, true) then
741*387f9dfdSAndroid Build Coastguard Worker        sub, str = prettystrPadded(sub, str, '\n')
742*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error, substring %s was found (case insensitively) in string %s',
743*387f9dfdSAndroid Build Coastguard Worker                 sub, str)
744*387f9dfdSAndroid Build Coastguard Worker    end
745*387f9dfdSAndroid Build Coastguard Workerend
746*387f9dfdSAndroid Build Coastguard Worker
747*387f9dfdSAndroid Build Coastguard Workerfunction M.assertStrMatches( str, pattern, start, final )
748*387f9dfdSAndroid Build Coastguard Worker    -- Verify a full match for the string
749*387f9dfdSAndroid Build Coastguard Worker    -- for a partial match, simply use assertStrContains with useRe set to true
750*387f9dfdSAndroid Build Coastguard Worker    if not strMatch( str, pattern, start, final ) then
751*387f9dfdSAndroid Build Coastguard Worker        pattern, str = prettystrPadded(pattern, str, '\n')
752*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error, pattern %s was not matched by string %s',
753*387f9dfdSAndroid Build Coastguard Worker                 pattern, str)
754*387f9dfdSAndroid Build Coastguard Worker    end
755*387f9dfdSAndroid Build Coastguard Workerend
756*387f9dfdSAndroid Build Coastguard Worker
757*387f9dfdSAndroid Build Coastguard Workerfunction M.assertErrorMsgEquals( expectedMsg, func, ... )
758*387f9dfdSAndroid Build Coastguard Worker    -- assert that calling f with the arguments will raise an error
759*387f9dfdSAndroid Build Coastguard Worker    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
760*387f9dfdSAndroid Build Coastguard Worker    local no_error, error_msg = pcall( func, ... )
761*387f9dfdSAndroid Build Coastguard Worker    if no_error then
762*387f9dfdSAndroid Build Coastguard Worker        failure( 'No error generated when calling function but expected error: "'..expectedMsg..'"', 2 )
763*387f9dfdSAndroid Build Coastguard Worker    end
764*387f9dfdSAndroid Build Coastguard Worker    if error_msg ~= expectedMsg then
765*387f9dfdSAndroid Build Coastguard Worker        error_msg, expectedMsg = prettystrPadded(error_msg, expectedMsg)
766*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Exact error message expected: %s\nError message received: %s\n',
767*387f9dfdSAndroid Build Coastguard Worker                 expectedMsg, error_msg)
768*387f9dfdSAndroid Build Coastguard Worker    end
769*387f9dfdSAndroid Build Coastguard Workerend
770*387f9dfdSAndroid Build Coastguard Worker
771*387f9dfdSAndroid Build Coastguard Workerfunction M.assertErrorMsgContains( partialMsg, func, ... )
772*387f9dfdSAndroid Build Coastguard Worker    -- assert that calling f with the arguments will raise an error
773*387f9dfdSAndroid Build Coastguard Worker    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
774*387f9dfdSAndroid Build Coastguard Worker    local no_error, error_msg = pcall( func, ... )
775*387f9dfdSAndroid Build Coastguard Worker    if no_error then
776*387f9dfdSAndroid Build Coastguard Worker        failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), 2 )
777*387f9dfdSAndroid Build Coastguard Worker    end
778*387f9dfdSAndroid Build Coastguard Worker    if not string.find( error_msg, partialMsg, nil, true ) then
779*387f9dfdSAndroid Build Coastguard Worker        error_msg, partialMsg = prettystrPadded(error_msg, partialMsg)
780*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error message does not contain: %s\nError message received: %s\n',
781*387f9dfdSAndroid Build Coastguard Worker                 partialMsg, error_msg)
782*387f9dfdSAndroid Build Coastguard Worker    end
783*387f9dfdSAndroid Build Coastguard Workerend
784*387f9dfdSAndroid Build Coastguard Worker
785*387f9dfdSAndroid Build Coastguard Workerfunction M.assertErrorMsgMatches( expectedMsg, func, ... )
786*387f9dfdSAndroid Build Coastguard Worker    -- assert that calling f with the arguments will raise an error
787*387f9dfdSAndroid Build Coastguard Worker    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
788*387f9dfdSAndroid Build Coastguard Worker    local no_error, error_msg = pcall( func, ... )
789*387f9dfdSAndroid Build Coastguard Worker    if no_error then
790*387f9dfdSAndroid Build Coastguard Worker        failure( 'No error generated when calling function but expected error matching: "'..expectedMsg..'"', 2 )
791*387f9dfdSAndroid Build Coastguard Worker    end
792*387f9dfdSAndroid Build Coastguard Worker    if not strMatch( error_msg, expectedMsg ) then
793*387f9dfdSAndroid Build Coastguard Worker        expectedMsg, error_msg = prettystrPadded(expectedMsg, error_msg)
794*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Error message does not match: %s\nError message received: %s\n',
795*387f9dfdSAndroid Build Coastguard Worker                 expectedMsg, error_msg)
796*387f9dfdSAndroid Build Coastguard Worker    end
797*387f9dfdSAndroid Build Coastguard Workerend
798*387f9dfdSAndroid Build Coastguard Worker
799*387f9dfdSAndroid Build Coastguard Worker--[[
800*387f9dfdSAndroid Build Coastguard WorkerAdd type assertion functions to the module table M. Each of these functions
801*387f9dfdSAndroid Build Coastguard Workertakes a single parameter "value", and checks that its Lua type matches the
802*387f9dfdSAndroid Build Coastguard Workerexpected string (derived from the function name):
803*387f9dfdSAndroid Build Coastguard Worker
804*387f9dfdSAndroid Build Coastguard WorkerM.assertIsXxx(value) -> ensure that type(value) conforms to "xxx"
805*387f9dfdSAndroid Build Coastguard Worker]]
806*387f9dfdSAndroid Build Coastguard Workerfor _, funcName in ipairs(
807*387f9dfdSAndroid Build Coastguard Worker    {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean',
808*387f9dfdSAndroid Build Coastguard Worker     'assertIsFunction', 'assertIsUserdata', 'assertIsThread'}
809*387f9dfdSAndroid Build Coastguard Worker) do
810*387f9dfdSAndroid Build Coastguard Worker    local typeExpected = funcName:match("^assertIs([A-Z]%a*)$")
811*387f9dfdSAndroid Build Coastguard Worker    -- Lua type() always returns lowercase, also make sure the match() succeeded
812*387f9dfdSAndroid Build Coastguard Worker    typeExpected = typeExpected and typeExpected:lower()
813*387f9dfdSAndroid Build Coastguard Worker                   or error("bad function name '"..funcName.."' for type assertion")
814*387f9dfdSAndroid Build Coastguard Worker
815*387f9dfdSAndroid Build Coastguard Worker    M[funcName] = function(value)
816*387f9dfdSAndroid Build Coastguard Worker        if type(value) ~= typeExpected then
817*387f9dfdSAndroid Build Coastguard Worker            fail_fmt(2, 'Expected: a %s value, actual: type %s, value %s',
818*387f9dfdSAndroid Build Coastguard Worker                     typeExpected, type(value), prettystrPadded(value))
819*387f9dfdSAndroid Build Coastguard Worker        end
820*387f9dfdSAndroid Build Coastguard Worker    end
821*387f9dfdSAndroid Build Coastguard Workerend
822*387f9dfdSAndroid Build Coastguard Worker
823*387f9dfdSAndroid Build Coastguard Worker--[[
824*387f9dfdSAndroid Build Coastguard WorkerAdd non-type assertion functions to the module table M. Each of these functions
825*387f9dfdSAndroid Build Coastguard Workertakes a single parameter "value", and checks that its Lua type differs from the
826*387f9dfdSAndroid Build Coastguard Workerexpected string (derived from the function name):
827*387f9dfdSAndroid Build Coastguard Worker
828*387f9dfdSAndroid Build Coastguard WorkerM.assertNotIsXxx(value) -> ensure that type(value) is not "xxx"
829*387f9dfdSAndroid Build Coastguard Worker]]
830*387f9dfdSAndroid Build Coastguard Workerfor _, funcName in ipairs(
831*387f9dfdSAndroid Build Coastguard Worker    {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean',
832*387f9dfdSAndroid Build Coastguard Worker     'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'}
833*387f9dfdSAndroid Build Coastguard Worker) do
834*387f9dfdSAndroid Build Coastguard Worker    local typeUnexpected = funcName:match("^assertNotIs([A-Z]%a*)$")
835*387f9dfdSAndroid Build Coastguard Worker    -- Lua type() always returns lowercase, also make sure the match() succeeded
836*387f9dfdSAndroid Build Coastguard Worker    typeUnexpected = typeUnexpected and typeUnexpected:lower()
837*387f9dfdSAndroid Build Coastguard Worker                   or error("bad function name '"..funcName.."' for type assertion")
838*387f9dfdSAndroid Build Coastguard Worker
839*387f9dfdSAndroid Build Coastguard Worker    M[funcName] = function(value)
840*387f9dfdSAndroid Build Coastguard Worker        if type(value) == typeUnexpected then
841*387f9dfdSAndroid Build Coastguard Worker            fail_fmt(2, 'Not expected: a %s type, actual: value %s',
842*387f9dfdSAndroid Build Coastguard Worker                     typeUnexpected, prettystrPadded(value))
843*387f9dfdSAndroid Build Coastguard Worker        end
844*387f9dfdSAndroid Build Coastguard Worker    end
845*387f9dfdSAndroid Build Coastguard Workerend
846*387f9dfdSAndroid Build Coastguard Worker
847*387f9dfdSAndroid Build Coastguard Workerfunction M.assertIs(actual, expected)
848*387f9dfdSAndroid Build Coastguard Worker    if actual ~= expected then
849*387f9dfdSAndroid Build Coastguard Worker        if not M.ORDER_ACTUAL_EXPECTED then
850*387f9dfdSAndroid Build Coastguard Worker            actual, expected = expected, actual
851*387f9dfdSAndroid Build Coastguard Worker        end
852*387f9dfdSAndroid Build Coastguard Worker        expected, actual = prettystrPadded(expected, actual, '\n', ', ')
853*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Expected object and actual object are not the same\nExpected: %sactual: %s',
854*387f9dfdSAndroid Build Coastguard Worker                 expected, actual)
855*387f9dfdSAndroid Build Coastguard Worker    end
856*387f9dfdSAndroid Build Coastguard Workerend
857*387f9dfdSAndroid Build Coastguard Worker
858*387f9dfdSAndroid Build Coastguard Workerfunction M.assertNotIs(actual, expected)
859*387f9dfdSAndroid Build Coastguard Worker    if actual == expected then
860*387f9dfdSAndroid Build Coastguard Worker        if not M.ORDER_ACTUAL_EXPECTED then
861*387f9dfdSAndroid Build Coastguard Worker            expected = actual
862*387f9dfdSAndroid Build Coastguard Worker        end
863*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Expected object and actual object are the same object: %s',
864*387f9dfdSAndroid Build Coastguard Worker                 prettystrPadded(expected))
865*387f9dfdSAndroid Build Coastguard Worker    end
866*387f9dfdSAndroid Build Coastguard Workerend
867*387f9dfdSAndroid Build Coastguard Worker
868*387f9dfdSAndroid Build Coastguard Workerfunction M.assertItemsEquals(actual, expected)
869*387f9dfdSAndroid Build Coastguard Worker    -- checks that the items of table expected
870*387f9dfdSAndroid Build Coastguard Worker    -- are contained in table actual. Warning, this function
871*387f9dfdSAndroid Build Coastguard Worker    -- is at least O(n^2)
872*387f9dfdSAndroid Build Coastguard Worker    if not _is_table_items_equals(actual, expected ) then
873*387f9dfdSAndroid Build Coastguard Worker        expected, actual = prettystrPadded(expected, actual)
874*387f9dfdSAndroid Build Coastguard Worker        fail_fmt(2, 'Contents of the tables are not identical:\nExpected: %s\nActual: %s',
875*387f9dfdSAndroid Build Coastguard Worker                 expected, actual)
876*387f9dfdSAndroid Build Coastguard Worker    end
877*387f9dfdSAndroid Build Coastguard Workerend
878*387f9dfdSAndroid Build Coastguard Worker
879*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
880*387f9dfdSAndroid Build Coastguard Worker--                     Compatibility layer
881*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
882*387f9dfdSAndroid Build Coastguard Worker
883*387f9dfdSAndroid Build Coastguard Worker-- for compatibility with LuaUnit v2.x
884*387f9dfdSAndroid Build Coastguard Workerfunction M.wrapFunctions(...)
885*387f9dfdSAndroid Build Coastguard Worker    io.stderr:write( [[Use of WrapFunction() is no longer needed.
886*387f9dfdSAndroid Build Coastguard WorkerJust prefix your test function names with "test" or "Test" and they
887*387f9dfdSAndroid Build Coastguard Workerwill be picked up and run by LuaUnit.]] )
888*387f9dfdSAndroid Build Coastguard Worker    -- In LuaUnit version <= 2.1 , this function was necessary to include
889*387f9dfdSAndroid Build Coastguard Worker    -- a test function inside the global test suite. Nowadays, the functions
890*387f9dfdSAndroid Build Coastguard Worker    -- are simply run directly as part of the test discovery process.
891*387f9dfdSAndroid Build Coastguard Worker    -- so just do nothing !
892*387f9dfdSAndroid Build Coastguard Worker
893*387f9dfdSAndroid Build Coastguard Worker    --[[
894*387f9dfdSAndroid Build Coastguard Worker    local testClass, testFunction
895*387f9dfdSAndroid Build Coastguard Worker    testClass = {}
896*387f9dfdSAndroid Build Coastguard Worker    local function storeAsMethod(idx, testName)
897*387f9dfdSAndroid Build Coastguard Worker        testFunction = _G[testName]
898*387f9dfdSAndroid Build Coastguard Worker        testClass[testName] = testFunction
899*387f9dfdSAndroid Build Coastguard Worker    end
900*387f9dfdSAndroid Build Coastguard Worker    for i,v in ipairs({...}) do
901*387f9dfdSAndroid Build Coastguard Worker        storeAsMethod( i, v )
902*387f9dfdSAndroid Build Coastguard Worker    end
903*387f9dfdSAndroid Build Coastguard Worker
904*387f9dfdSAndroid Build Coastguard Worker    return testClass
905*387f9dfdSAndroid Build Coastguard Worker    ]]
906*387f9dfdSAndroid Build Coastguard Workerend
907*387f9dfdSAndroid Build Coastguard Worker
908*387f9dfdSAndroid Build Coastguard Workerlocal list_of_funcs = {
909*387f9dfdSAndroid Build Coastguard Worker    -- { official function name , alias }
910*387f9dfdSAndroid Build Coastguard Worker
911*387f9dfdSAndroid Build Coastguard Worker    -- general assertions
912*387f9dfdSAndroid Build Coastguard Worker    { 'assertEquals'            , 'assert_equals' },
913*387f9dfdSAndroid Build Coastguard Worker    { 'assertItemsEquals'       , 'assert_items_equals' },
914*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotEquals'         , 'assert_not_equals' },
915*387f9dfdSAndroid Build Coastguard Worker    { 'assertAlmostEquals'      , 'assert_almost_equals' },
916*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotAlmostEquals'   , 'assert_not_almost_equals' },
917*387f9dfdSAndroid Build Coastguard Worker    { 'assertTrue'              , 'assert_true' },
918*387f9dfdSAndroid Build Coastguard Worker    { 'assertFalse'             , 'assert_false' },
919*387f9dfdSAndroid Build Coastguard Worker    { 'assertStrContains'       , 'assert_str_contains' },
920*387f9dfdSAndroid Build Coastguard Worker    { 'assertStrIContains'      , 'assert_str_icontains' },
921*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotStrContains'    , 'assert_not_str_contains' },
922*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotStrIContains'   , 'assert_not_str_icontains' },
923*387f9dfdSAndroid Build Coastguard Worker    { 'assertStrMatches'        , 'assert_str_matches' },
924*387f9dfdSAndroid Build Coastguard Worker    { 'assertError'             , 'assert_error' },
925*387f9dfdSAndroid Build Coastguard Worker    { 'assertErrorMsgEquals'    , 'assert_error_msg_equals' },
926*387f9dfdSAndroid Build Coastguard Worker    { 'assertErrorMsgContains'  , 'assert_error_msg_contains' },
927*387f9dfdSAndroid Build Coastguard Worker    { 'assertErrorMsgMatches'   , 'assert_error_msg_matches' },
928*387f9dfdSAndroid Build Coastguard Worker    { 'assertIs'                , 'assert_is' },
929*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIs'             , 'assert_not_is' },
930*387f9dfdSAndroid Build Coastguard Worker    { 'wrapFunctions'           , 'WrapFunctions' },
931*387f9dfdSAndroid Build Coastguard Worker    { 'wrapFunctions'           , 'wrap_functions' },
932*387f9dfdSAndroid Build Coastguard Worker
933*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertIsXXX -> assert_is_xxx
934*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNumber'          , 'assert_is_number' },
935*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsString'          , 'assert_is_string' },
936*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsTable'           , 'assert_is_table' },
937*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsBoolean'         , 'assert_is_boolean' },
938*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNil'             , 'assert_is_nil' },
939*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsFunction'        , 'assert_is_function' },
940*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assert_is_thread' },
941*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsUserdata'        , 'assert_is_userdata' },
942*387f9dfdSAndroid Build Coastguard Worker
943*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertIsXXX -> assertXxx
944*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNumber'          , 'assertNumber' },
945*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsString'          , 'assertString' },
946*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsTable'           , 'assertTable' },
947*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsBoolean'         , 'assertBoolean' },
948*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNil'             , 'assertNil' },
949*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsFunction'        , 'assertFunction' },
950*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assertThread' },
951*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsUserdata'        , 'assertUserdata' },
952*387f9dfdSAndroid Build Coastguard Worker
953*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat)
954*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNumber'          , 'assert_number' },
955*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsString'          , 'assert_string' },
956*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsTable'           , 'assert_table' },
957*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsBoolean'         , 'assert_boolean' },
958*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsNil'             , 'assert_nil' },
959*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsFunction'        , 'assert_function' },
960*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assert_thread' },
961*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsUserdata'        , 'assert_userdata' },
962*387f9dfdSAndroid Build Coastguard Worker
963*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertNotIsXXX -> assert_not_is_xxx
964*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNumber'       , 'assert_not_is_number' },
965*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsString'       , 'assert_not_is_string' },
966*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsTable'        , 'assert_not_is_table' },
967*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsBoolean'      , 'assert_not_is_boolean' },
968*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNil'          , 'assert_not_is_nil' },
969*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsFunction'     , 'assert_not_is_function' },
970*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assert_not_is_thread' },
971*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsUserdata'     , 'assert_not_is_userdata' },
972*387f9dfdSAndroid Build Coastguard Worker
973*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat)
974*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNumber'       , 'assertNotNumber' },
975*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsString'       , 'assertNotString' },
976*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsTable'        , 'assertNotTable' },
977*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsBoolean'      , 'assertNotBoolean' },
978*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNil'          , 'assertNotNil' },
979*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsFunction'     , 'assertNotFunction' },
980*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assertNotThread' },
981*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsUserdata'     , 'assertNotUserdata' },
982*387f9dfdSAndroid Build Coastguard Worker
983*387f9dfdSAndroid Build Coastguard Worker    -- type assertions: assertNotIsXXX -> assert_not_xxx
984*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNumber'       , 'assert_not_number' },
985*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsString'       , 'assert_not_string' },
986*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsTable'        , 'assert_not_table' },
987*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsBoolean'      , 'assert_not_boolean' },
988*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsNil'          , 'assert_not_nil' },
989*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsFunction'     , 'assert_not_function' },
990*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assert_not_thread' },
991*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsUserdata'     , 'assert_not_userdata' },
992*387f9dfdSAndroid Build Coastguard Worker
993*387f9dfdSAndroid Build Coastguard Worker    -- all assertions with Coroutine duplicate Thread assertions
994*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assertIsCoroutine' },
995*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assertCoroutine' },
996*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assert_is_coroutine' },
997*387f9dfdSAndroid Build Coastguard Worker    { 'assertIsThread'          , 'assert_coroutine' },
998*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assertNotIsCoroutine' },
999*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assertNotCoroutine' },
1000*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assert_not_is_coroutine' },
1001*387f9dfdSAndroid Build Coastguard Worker    { 'assertNotIsThread'       , 'assert_not_coroutine' },
1002*387f9dfdSAndroid Build Coastguard Worker}
1003*387f9dfdSAndroid Build Coastguard Worker
1004*387f9dfdSAndroid Build Coastguard Worker-- Create all aliases in M
1005*387f9dfdSAndroid Build Coastguard Workerfor _,v in ipairs( list_of_funcs ) do
1006*387f9dfdSAndroid Build Coastguard Worker    funcname, alias = v[1], v[2]
1007*387f9dfdSAndroid Build Coastguard Worker    M[alias] = M[funcname]
1008*387f9dfdSAndroid Build Coastguard Worker
1009*387f9dfdSAndroid Build Coastguard Worker    if EXPORT_ASSERT_TO_GLOBALS then
1010*387f9dfdSAndroid Build Coastguard Worker        _G[funcname] = M[funcname]
1011*387f9dfdSAndroid Build Coastguard Worker        _G[alias] = M[funcname]
1012*387f9dfdSAndroid Build Coastguard Worker    end
1013*387f9dfdSAndroid Build Coastguard Workerend
1014*387f9dfdSAndroid Build Coastguard Worker
1015*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1016*387f9dfdSAndroid Build Coastguard Worker--
1017*387f9dfdSAndroid Build Coastguard Worker--                     Outputters
1018*387f9dfdSAndroid Build Coastguard Worker--
1019*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1020*387f9dfdSAndroid Build Coastguard Worker
1021*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1022*387f9dfdSAndroid Build Coastguard Worker--                     class TapOutput
1023*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1024*387f9dfdSAndroid Build Coastguard Worker
1025*387f9dfdSAndroid Build Coastguard Worker
1026*387f9dfdSAndroid Build Coastguard Workerlocal TapOutput = { __class__ = 'TapOutput' } -- class
1027*387f9dfdSAndroid Build Coastguard Workerlocal TapOutput_MT = { __index = TapOutput } -- metatable
1028*387f9dfdSAndroid Build Coastguard Worker
1029*387f9dfdSAndroid Build Coastguard Worker    -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html
1030*387f9dfdSAndroid Build Coastguard Worker
1031*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:new()
1032*387f9dfdSAndroid Build Coastguard Worker        return setmetatable( { verbosity = M.VERBOSITY_LOW }, TapOutput_MT)
1033*387f9dfdSAndroid Build Coastguard Worker    end
1034*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:startSuite()
1035*387f9dfdSAndroid Build Coastguard Worker        print("1.."..self.result.testCount)
1036*387f9dfdSAndroid Build Coastguard Worker        print('# Started on '..self.result.startDate)
1037*387f9dfdSAndroid Build Coastguard Worker    end
1038*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:startClass(className)
1039*387f9dfdSAndroid Build Coastguard Worker        if className ~= '[TestFunctions]' then
1040*387f9dfdSAndroid Build Coastguard Worker            print('# Starting class: '..className)
1041*387f9dfdSAndroid Build Coastguard Worker        end
1042*387f9dfdSAndroid Build Coastguard Worker    end
1043*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:startTest(testName) end
1044*387f9dfdSAndroid Build Coastguard Worker
1045*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:addFailure( node )
1046*387f9dfdSAndroid Build Coastguard Worker        io.stdout:write("not ok ", self.result.currentTestNumber, "\t", node.testName, "\n")
1047*387f9dfdSAndroid Build Coastguard Worker        if self.verbosity > M.VERBOSITY_LOW then
1048*387f9dfdSAndroid Build Coastguard Worker           print( prefixString( '    ', node.msg ) )
1049*387f9dfdSAndroid Build Coastguard Worker        end
1050*387f9dfdSAndroid Build Coastguard Worker        if self.verbosity > M.VERBOSITY_DEFAULT then
1051*387f9dfdSAndroid Build Coastguard Worker           print( prefixString( '    ', node.stackTrace ) )
1052*387f9dfdSAndroid Build Coastguard Worker        end
1053*387f9dfdSAndroid Build Coastguard Worker    end
1054*387f9dfdSAndroid Build Coastguard Worker    TapOutput.addError = TapOutput.addFailure
1055*387f9dfdSAndroid Build Coastguard Worker
1056*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:endTest( node )
1057*387f9dfdSAndroid Build Coastguard Worker        if node:isPassed() then
1058*387f9dfdSAndroid Build Coastguard Worker            io.stdout:write("ok     ", self.result.currentTestNumber, "\t", node.testName, "\n")
1059*387f9dfdSAndroid Build Coastguard Worker        end
1060*387f9dfdSAndroid Build Coastguard Worker    end
1061*387f9dfdSAndroid Build Coastguard Worker
1062*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:endClass() end
1063*387f9dfdSAndroid Build Coastguard Worker
1064*387f9dfdSAndroid Build Coastguard Worker    function TapOutput:endSuite()
1065*387f9dfdSAndroid Build Coastguard Worker        print( '# '..M.LuaUnit.statusLine( self.result ) )
1066*387f9dfdSAndroid Build Coastguard Worker        return self.result.notPassedCount
1067*387f9dfdSAndroid Build Coastguard Worker    end
1068*387f9dfdSAndroid Build Coastguard Worker
1069*387f9dfdSAndroid Build Coastguard Worker
1070*387f9dfdSAndroid Build Coastguard Worker-- class TapOutput end
1071*387f9dfdSAndroid Build Coastguard Worker
1072*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1073*387f9dfdSAndroid Build Coastguard Worker--                     class JUnitOutput
1074*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1075*387f9dfdSAndroid Build Coastguard Worker
1076*387f9dfdSAndroid Build Coastguard Worker-- See directory junitxml for more information about the junit format
1077*387f9dfdSAndroid Build Coastguard Workerlocal JUnitOutput = { __class__ = 'JUnitOutput' } -- class
1078*387f9dfdSAndroid Build Coastguard Workerlocal JUnitOutput_MT = { __index = JUnitOutput } -- metatable
1079*387f9dfdSAndroid Build Coastguard Worker
1080*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:new()
1081*387f9dfdSAndroid Build Coastguard Worker        return setmetatable(
1082*387f9dfdSAndroid Build Coastguard Worker            { testList = {}, verbosity = M.VERBOSITY_LOW }, JUnitOutput_MT)
1083*387f9dfdSAndroid Build Coastguard Worker    end
1084*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:startSuite()
1085*387f9dfdSAndroid Build Coastguard Worker
1086*387f9dfdSAndroid Build Coastguard Worker        -- open xml file early to deal with errors
1087*387f9dfdSAndroid Build Coastguard Worker        if self.fname == nil then
1088*387f9dfdSAndroid Build Coastguard Worker            error('With Junit, an output filename must be supplied with --name!')
1089*387f9dfdSAndroid Build Coastguard Worker        end
1090*387f9dfdSAndroid Build Coastguard Worker        if string.sub(self.fname,-4) ~= '.xml' then
1091*387f9dfdSAndroid Build Coastguard Worker            self.fname = self.fname..'.xml'
1092*387f9dfdSAndroid Build Coastguard Worker        end
1093*387f9dfdSAndroid Build Coastguard Worker        self.fd = io.open(self.fname, "w")
1094*387f9dfdSAndroid Build Coastguard Worker        if self.fd == nil then
1095*387f9dfdSAndroid Build Coastguard Worker            error("Could not open file for writing: "..self.fname)
1096*387f9dfdSAndroid Build Coastguard Worker        end
1097*387f9dfdSAndroid Build Coastguard Worker
1098*387f9dfdSAndroid Build Coastguard Worker        print('# XML output to '..self.fname)
1099*387f9dfdSAndroid Build Coastguard Worker        print('# Started on '..self.result.startDate)
1100*387f9dfdSAndroid Build Coastguard Worker    end
1101*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:startClass(className)
1102*387f9dfdSAndroid Build Coastguard Worker        if className ~= '[TestFunctions]' then
1103*387f9dfdSAndroid Build Coastguard Worker            print('# Starting class: '..className)
1104*387f9dfdSAndroid Build Coastguard Worker        end
1105*387f9dfdSAndroid Build Coastguard Worker    end
1106*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:startTest(testName)
1107*387f9dfdSAndroid Build Coastguard Worker        print('# Starting test: '..testName)
1108*387f9dfdSAndroid Build Coastguard Worker    end
1109*387f9dfdSAndroid Build Coastguard Worker
1110*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:addFailure( node )
1111*387f9dfdSAndroid Build Coastguard Worker        print('# Failure: ' .. node.msg)
1112*387f9dfdSAndroid Build Coastguard Worker        -- print('# ' .. node.stackTrace)
1113*387f9dfdSAndroid Build Coastguard Worker    end
1114*387f9dfdSAndroid Build Coastguard Worker
1115*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:addError( node )
1116*387f9dfdSAndroid Build Coastguard Worker        print('# Error: ' .. node.msg)
1117*387f9dfdSAndroid Build Coastguard Worker        -- print('# ' .. node.stackTrace)
1118*387f9dfdSAndroid Build Coastguard Worker    end
1119*387f9dfdSAndroid Build Coastguard Worker
1120*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:endTest( node )
1121*387f9dfdSAndroid Build Coastguard Worker    end
1122*387f9dfdSAndroid Build Coastguard Worker
1123*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:endClass()
1124*387f9dfdSAndroid Build Coastguard Worker    end
1125*387f9dfdSAndroid Build Coastguard Worker
1126*387f9dfdSAndroid Build Coastguard Worker    function JUnitOutput:endSuite()
1127*387f9dfdSAndroid Build Coastguard Worker        print( '# '..M.LuaUnit.statusLine(self.result))
1128*387f9dfdSAndroid Build Coastguard Worker
1129*387f9dfdSAndroid Build Coastguard Worker        -- XML file writing
1130*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('<?xml version="1.0" encoding="UTF-8" ?>\n')
1131*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('<testsuites>\n')
1132*387f9dfdSAndroid Build Coastguard Worker        self.fd:write(string.format(
1133*387f9dfdSAndroid Build Coastguard Worker            '    <testsuite name="LuaUnit" id="00001" package="" hostname="localhost" tests="%d" timestamp="%s" time="%0.3f" errors="%d" failures="%d">\n',
1134*387f9dfdSAndroid Build Coastguard Worker            self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount ))
1135*387f9dfdSAndroid Build Coastguard Worker        self.fd:write("        <properties>\n")
1136*387f9dfdSAndroid Build Coastguard Worker        self.fd:write(string.format('            <property name="Lua Version" value="%s"/>\n', _VERSION ) )
1137*387f9dfdSAndroid Build Coastguard Worker        self.fd:write(string.format('            <property name="LuaUnit Version" value="%s"/>\n', M.VERSION) )
1138*387f9dfdSAndroid Build Coastguard Worker        -- XXX please include system name and version if possible
1139*387f9dfdSAndroid Build Coastguard Worker        self.fd:write("        </properties>\n")
1140*387f9dfdSAndroid Build Coastguard Worker
1141*387f9dfdSAndroid Build Coastguard Worker        for i,node in ipairs(self.result.tests) do
1142*387f9dfdSAndroid Build Coastguard Worker            self.fd:write(string.format('        <testcase classname="%s" name="%s" time="%0.3f">\n',
1143*387f9dfdSAndroid Build Coastguard Worker                node.className, node.testName, node.duration ) )
1144*387f9dfdSAndroid Build Coastguard Worker            if node:isNotPassed() then
1145*387f9dfdSAndroid Build Coastguard Worker                self.fd:write(node:statusXML())
1146*387f9dfdSAndroid Build Coastguard Worker            end
1147*387f9dfdSAndroid Build Coastguard Worker            self.fd:write('        </testcase>\n')
1148*387f9dfdSAndroid Build Coastguard Worker        end
1149*387f9dfdSAndroid Build Coastguard Worker
1150*387f9dfdSAndroid Build Coastguard Worker        -- Next two lines are needed to validate junit ANT xsd, but really not useful in general:
1151*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('    <system-out/>\n')
1152*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('    <system-err/>\n')
1153*387f9dfdSAndroid Build Coastguard Worker
1154*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('    </testsuite>\n')
1155*387f9dfdSAndroid Build Coastguard Worker        self.fd:write('</testsuites>\n')
1156*387f9dfdSAndroid Build Coastguard Worker        self.fd:close()
1157*387f9dfdSAndroid Build Coastguard Worker        return self.result.notPassedCount
1158*387f9dfdSAndroid Build Coastguard Worker    end
1159*387f9dfdSAndroid Build Coastguard Worker
1160*387f9dfdSAndroid Build Coastguard Worker
1161*387f9dfdSAndroid Build Coastguard Worker-- class TapOutput end
1162*387f9dfdSAndroid Build Coastguard Worker
1163*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1164*387f9dfdSAndroid Build Coastguard Worker--                     class TextOutput
1165*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1166*387f9dfdSAndroid Build Coastguard Worker
1167*387f9dfdSAndroid Build Coastguard Worker--[[
1168*387f9dfdSAndroid Build Coastguard Worker
1169*387f9dfdSAndroid Build Coastguard Worker-- Python Non verbose:
1170*387f9dfdSAndroid Build Coastguard Worker
1171*387f9dfdSAndroid Build Coastguard WorkerFor each test: . or F or E
1172*387f9dfdSAndroid Build Coastguard Worker
1173*387f9dfdSAndroid Build Coastguard WorkerIf some failed tests:
1174*387f9dfdSAndroid Build Coastguard Worker    ==============
1175*387f9dfdSAndroid Build Coastguard Worker    ERROR / FAILURE: TestName (testfile.testclass)
1176*387f9dfdSAndroid Build Coastguard Worker    ---------
1177*387f9dfdSAndroid Build Coastguard Worker    Stack trace
1178*387f9dfdSAndroid Build Coastguard Worker
1179*387f9dfdSAndroid Build Coastguard Worker
1180*387f9dfdSAndroid Build Coastguard Workerthen --------------
1181*387f9dfdSAndroid Build Coastguard Workerthen "Ran x tests in 0.000s"
1182*387f9dfdSAndroid Build Coastguard Workerthen OK or FAILED (failures=1, error=1)
1183*387f9dfdSAndroid Build Coastguard Worker
1184*387f9dfdSAndroid Build Coastguard Worker-- Python Verbose:
1185*387f9dfdSAndroid Build Coastguard Workertestname (filename.classname) ... ok
1186*387f9dfdSAndroid Build Coastguard Workertestname (filename.classname) ... FAIL
1187*387f9dfdSAndroid Build Coastguard Workertestname (filename.classname) ... ERROR
1188*387f9dfdSAndroid Build Coastguard Worker
1189*387f9dfdSAndroid Build Coastguard Workerthen --------------
1190*387f9dfdSAndroid Build Coastguard Workerthen "Ran x tests in 0.000s"
1191*387f9dfdSAndroid Build Coastguard Workerthen OK or FAILED (failures=1, error=1)
1192*387f9dfdSAndroid Build Coastguard Worker
1193*387f9dfdSAndroid Build Coastguard Worker-- Ruby:
1194*387f9dfdSAndroid Build Coastguard WorkerStarted
1195*387f9dfdSAndroid Build Coastguard Worker .
1196*387f9dfdSAndroid Build Coastguard Worker Finished in 0.002695 seconds.
1197*387f9dfdSAndroid Build Coastguard Worker
1198*387f9dfdSAndroid Build Coastguard Worker 1 tests, 2 assertions, 0 failures, 0 errors
1199*387f9dfdSAndroid Build Coastguard Worker
1200*387f9dfdSAndroid Build Coastguard Worker-- Ruby:
1201*387f9dfdSAndroid Build Coastguard Worker>> ruby tc_simple_number2.rb
1202*387f9dfdSAndroid Build Coastguard WorkerLoaded suite tc_simple_number2
1203*387f9dfdSAndroid Build Coastguard WorkerStarted
1204*387f9dfdSAndroid Build Coastguard WorkerF..
1205*387f9dfdSAndroid Build Coastguard WorkerFinished in 0.038617 seconds.
1206*387f9dfdSAndroid Build Coastguard Worker
1207*387f9dfdSAndroid Build Coastguard Worker  1) Failure:
1208*387f9dfdSAndroid Build Coastguard Workertest_failure(TestSimpleNumber) [tc_simple_number2.rb:16]:
1209*387f9dfdSAndroid Build Coastguard WorkerAdding doesn't work.
1210*387f9dfdSAndroid Build Coastguard Worker<3> expected but was
1211*387f9dfdSAndroid Build Coastguard Worker<4>.
1212*387f9dfdSAndroid Build Coastguard Worker
1213*387f9dfdSAndroid Build Coastguard Worker3 tests, 4 assertions, 1 failures, 0 errors
1214*387f9dfdSAndroid Build Coastguard Worker
1215*387f9dfdSAndroid Build Coastguard Worker-- Java Junit
1216*387f9dfdSAndroid Build Coastguard Worker.......F.
1217*387f9dfdSAndroid Build Coastguard WorkerTime: 0,003
1218*387f9dfdSAndroid Build Coastguard WorkerThere was 1 failure:
1219*387f9dfdSAndroid Build Coastguard Worker1) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError
1220*387f9dfdSAndroid Build Coastguard Worker    at junit.samples.VectorTest.testCapacity(VectorTest.java:87)
1221*387f9dfdSAndroid Build Coastguard Worker    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
1222*387f9dfdSAndroid Build Coastguard Worker    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
1223*387f9dfdSAndroid Build Coastguard Worker    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
1224*387f9dfdSAndroid Build Coastguard Worker
1225*387f9dfdSAndroid Build Coastguard WorkerFAILURES!!!
1226*387f9dfdSAndroid Build Coastguard WorkerTests run: 8,  Failures: 1,  Errors: 0
1227*387f9dfdSAndroid Build Coastguard Worker
1228*387f9dfdSAndroid Build Coastguard Worker
1229*387f9dfdSAndroid Build Coastguard Worker-- Maven
1230*387f9dfdSAndroid Build Coastguard Worker
1231*387f9dfdSAndroid Build Coastguard Worker# mvn test
1232*387f9dfdSAndroid Build Coastguard Worker-------------------------------------------------------
1233*387f9dfdSAndroid Build Coastguard Worker T E S T S
1234*387f9dfdSAndroid Build Coastguard Worker-------------------------------------------------------
1235*387f9dfdSAndroid Build Coastguard WorkerRunning math.AdditionTest
1236*387f9dfdSAndroid Build Coastguard WorkerTests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed:
1237*387f9dfdSAndroid Build Coastguard Worker0.03 sec <<< FAILURE!
1238*387f9dfdSAndroid Build Coastguard Worker
1239*387f9dfdSAndroid Build Coastguard WorkerResults :
1240*387f9dfdSAndroid Build Coastguard Worker
1241*387f9dfdSAndroid Build Coastguard WorkerFailed tests:
1242*387f9dfdSAndroid Build Coastguard Worker  testLireSymbole(math.AdditionTest)
1243*387f9dfdSAndroid Build Coastguard Worker
1244*387f9dfdSAndroid Build Coastguard WorkerTests run: 2, Failures: 1, Errors: 0, Skipped: 0
1245*387f9dfdSAndroid Build Coastguard Worker
1246*387f9dfdSAndroid Build Coastguard Worker
1247*387f9dfdSAndroid Build Coastguard Worker-- LuaUnit
1248*387f9dfdSAndroid Build Coastguard Worker---- non verbose
1249*387f9dfdSAndroid Build Coastguard Worker* display . or F or E when running tests
1250*387f9dfdSAndroid Build Coastguard Worker---- verbose
1251*387f9dfdSAndroid Build Coastguard Worker* display test name + ok/fail
1252*387f9dfdSAndroid Build Coastguard Worker----
1253*387f9dfdSAndroid Build Coastguard Worker* blank line
1254*387f9dfdSAndroid Build Coastguard Worker* number) ERROR or FAILURE: TestName
1255*387f9dfdSAndroid Build Coastguard Worker   Stack trace
1256*387f9dfdSAndroid Build Coastguard Worker* blank line
1257*387f9dfdSAndroid Build Coastguard Worker* number) ERROR or FAILURE: TestName
1258*387f9dfdSAndroid Build Coastguard Worker   Stack trace
1259*387f9dfdSAndroid Build Coastguard Worker
1260*387f9dfdSAndroid Build Coastguard Workerthen --------------
1261*387f9dfdSAndroid Build Coastguard Workerthen "Ran x tests in 0.000s (%d not selected, %d skipped)"
1262*387f9dfdSAndroid Build Coastguard Workerthen OK or FAILED (failures=1, error=1)
1263*387f9dfdSAndroid Build Coastguard Worker
1264*387f9dfdSAndroid Build Coastguard Worker
1265*387f9dfdSAndroid Build Coastguard Worker]]
1266*387f9dfdSAndroid Build Coastguard Worker
1267*387f9dfdSAndroid Build Coastguard Workerlocal TextOutput = { __class__ = 'TextOutput' } -- class
1268*387f9dfdSAndroid Build Coastguard Workerlocal TextOutput_MT = { __index = TextOutput } -- metatable
1269*387f9dfdSAndroid Build Coastguard Worker
1270*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:new()
1271*387f9dfdSAndroid Build Coastguard Worker        return setmetatable(
1272*387f9dfdSAndroid Build Coastguard Worker            { errorList = {}, verbosity = M.VERBOSITY_DEFAULT }, TextOutput_MT )
1273*387f9dfdSAndroid Build Coastguard Worker    end
1274*387f9dfdSAndroid Build Coastguard Worker
1275*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:startSuite()
1276*387f9dfdSAndroid Build Coastguard Worker        if self.verbosity > M.VERBOSITY_DEFAULT then
1277*387f9dfdSAndroid Build Coastguard Worker            print( 'Started on '.. self.result.startDate )
1278*387f9dfdSAndroid Build Coastguard Worker        end
1279*387f9dfdSAndroid Build Coastguard Worker    end
1280*387f9dfdSAndroid Build Coastguard Worker
1281*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:startClass(className)
1282*387f9dfdSAndroid Build Coastguard Worker        -- display nothing when starting a new class
1283*387f9dfdSAndroid Build Coastguard Worker    end
1284*387f9dfdSAndroid Build Coastguard Worker
1285*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:startTest(testName)
1286*387f9dfdSAndroid Build Coastguard Worker        if self.verbosity > M.VERBOSITY_DEFAULT then
1287*387f9dfdSAndroid Build Coastguard Worker            io.stdout:write( "    ", self.result.currentNode.testName, " ... " )
1288*387f9dfdSAndroid Build Coastguard Worker        end
1289*387f9dfdSAndroid Build Coastguard Worker    end
1290*387f9dfdSAndroid Build Coastguard Worker
1291*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:addFailure( node )
1292*387f9dfdSAndroid Build Coastguard Worker        -- nothing
1293*387f9dfdSAndroid Build Coastguard Worker    end
1294*387f9dfdSAndroid Build Coastguard Worker
1295*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:addError( node )
1296*387f9dfdSAndroid Build Coastguard Worker        -- nothing
1297*387f9dfdSAndroid Build Coastguard Worker    end
1298*387f9dfdSAndroid Build Coastguard Worker
1299*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:endTest( node )
1300*387f9dfdSAndroid Build Coastguard Worker        if node:isPassed() then
1301*387f9dfdSAndroid Build Coastguard Worker            if self.verbosity > M.VERBOSITY_DEFAULT then
1302*387f9dfdSAndroid Build Coastguard Worker                io.stdout:write("Ok\n")
1303*387f9dfdSAndroid Build Coastguard Worker            else
1304*387f9dfdSAndroid Build Coastguard Worker                io.stdout:write(".")
1305*387f9dfdSAndroid Build Coastguard Worker            end
1306*387f9dfdSAndroid Build Coastguard Worker        else
1307*387f9dfdSAndroid Build Coastguard Worker            if self.verbosity > M.VERBOSITY_DEFAULT then
1308*387f9dfdSAndroid Build Coastguard Worker                print( node.status )
1309*387f9dfdSAndroid Build Coastguard Worker                print( node.msg )
1310*387f9dfdSAndroid Build Coastguard Worker                --[[
1311*387f9dfdSAndroid Build Coastguard Worker                -- find out when to do this:
1312*387f9dfdSAndroid Build Coastguard Worker                if self.verbosity > M.VERBOSITY_DEFAULT then
1313*387f9dfdSAndroid Build Coastguard Worker                    print( node.stackTrace )
1314*387f9dfdSAndroid Build Coastguard Worker                end
1315*387f9dfdSAndroid Build Coastguard Worker                ]]
1316*387f9dfdSAndroid Build Coastguard Worker            else
1317*387f9dfdSAndroid Build Coastguard Worker                -- write only the first character of status
1318*387f9dfdSAndroid Build Coastguard Worker                io.stdout:write(string.sub(node.status, 1, 1))
1319*387f9dfdSAndroid Build Coastguard Worker            end
1320*387f9dfdSAndroid Build Coastguard Worker        end
1321*387f9dfdSAndroid Build Coastguard Worker    end
1322*387f9dfdSAndroid Build Coastguard Worker
1323*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:endClass()
1324*387f9dfdSAndroid Build Coastguard Worker        -- nothing
1325*387f9dfdSAndroid Build Coastguard Worker    end
1326*387f9dfdSAndroid Build Coastguard Worker
1327*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:displayOneFailedTest( index, failure )
1328*387f9dfdSAndroid Build Coastguard Worker        print(index..") "..failure.testName )
1329*387f9dfdSAndroid Build Coastguard Worker        print( failure.msg )
1330*387f9dfdSAndroid Build Coastguard Worker        print( failure.stackTrace )
1331*387f9dfdSAndroid Build Coastguard Worker        print()
1332*387f9dfdSAndroid Build Coastguard Worker    end
1333*387f9dfdSAndroid Build Coastguard Worker
1334*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:displayFailedTests()
1335*387f9dfdSAndroid Build Coastguard Worker        if self.result.notPassedCount == 0 then return end
1336*387f9dfdSAndroid Build Coastguard Worker        print("Failed tests:")
1337*387f9dfdSAndroid Build Coastguard Worker        print("-------------")
1338*387f9dfdSAndroid Build Coastguard Worker        for i,v in ipairs(self.result.notPassed) do
1339*387f9dfdSAndroid Build Coastguard Worker            self:displayOneFailedTest( i, v )
1340*387f9dfdSAndroid Build Coastguard Worker        end
1341*387f9dfdSAndroid Build Coastguard Worker    end
1342*387f9dfdSAndroid Build Coastguard Worker
1343*387f9dfdSAndroid Build Coastguard Worker    function TextOutput:endSuite()
1344*387f9dfdSAndroid Build Coastguard Worker        if self.verbosity > M.VERBOSITY_DEFAULT then
1345*387f9dfdSAndroid Build Coastguard Worker            print("=========================================================")
1346*387f9dfdSAndroid Build Coastguard Worker        else
1347*387f9dfdSAndroid Build Coastguard Worker            print()
1348*387f9dfdSAndroid Build Coastguard Worker        end
1349*387f9dfdSAndroid Build Coastguard Worker        self:displayFailedTests()
1350*387f9dfdSAndroid Build Coastguard Worker        print( M.LuaUnit.statusLine( self.result ) )
1351*387f9dfdSAndroid Build Coastguard Worker        local ignoredString = ""
1352*387f9dfdSAndroid Build Coastguard Worker        if self.result.notPassedCount == 0 then
1353*387f9dfdSAndroid Build Coastguard Worker            print('OK')
1354*387f9dfdSAndroid Build Coastguard Worker        end
1355*387f9dfdSAndroid Build Coastguard Worker    end
1356*387f9dfdSAndroid Build Coastguard Worker
1357*387f9dfdSAndroid Build Coastguard Worker-- class TextOutput end
1358*387f9dfdSAndroid Build Coastguard Worker
1359*387f9dfdSAndroid Build Coastguard Worker
1360*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1361*387f9dfdSAndroid Build Coastguard Worker--                     class NilOutput
1362*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1363*387f9dfdSAndroid Build Coastguard Worker
1364*387f9dfdSAndroid Build Coastguard Workerlocal function nopCallable()
1365*387f9dfdSAndroid Build Coastguard Worker    --print(42)
1366*387f9dfdSAndroid Build Coastguard Worker    return nopCallable
1367*387f9dfdSAndroid Build Coastguard Workerend
1368*387f9dfdSAndroid Build Coastguard Worker
1369*387f9dfdSAndroid Build Coastguard Workerlocal NilOutput = { __class__ = 'NilOuptut' } -- class
1370*387f9dfdSAndroid Build Coastguard Workerlocal NilOutput_MT = { __index = nopCallable } -- metatable
1371*387f9dfdSAndroid Build Coastguard Worker
1372*387f9dfdSAndroid Build Coastguard Workerfunction NilOutput:new()
1373*387f9dfdSAndroid Build Coastguard Worker    return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT )
1374*387f9dfdSAndroid Build Coastguard Workerend
1375*387f9dfdSAndroid Build Coastguard Worker
1376*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1377*387f9dfdSAndroid Build Coastguard Worker--
1378*387f9dfdSAndroid Build Coastguard Worker--                     class LuaUnit
1379*387f9dfdSAndroid Build Coastguard Worker--
1380*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1381*387f9dfdSAndroid Build Coastguard Worker
1382*387f9dfdSAndroid Build Coastguard WorkerM.LuaUnit = {
1383*387f9dfdSAndroid Build Coastguard Worker    outputType = TextOutput,
1384*387f9dfdSAndroid Build Coastguard Worker    verbosity = M.VERBOSITY_DEFAULT,
1385*387f9dfdSAndroid Build Coastguard Worker    __class__ = 'LuaUnit'
1386*387f9dfdSAndroid Build Coastguard Worker}
1387*387f9dfdSAndroid Build Coastguard Workerlocal LuaUnit_MT = { __index = M.LuaUnit }
1388*387f9dfdSAndroid Build Coastguard Worker
1389*387f9dfdSAndroid Build Coastguard Workerif EXPORT_ASSERT_TO_GLOBALS then
1390*387f9dfdSAndroid Build Coastguard Worker    LuaUnit = M.LuaUnit
1391*387f9dfdSAndroid Build Coastguard Workerend
1392*387f9dfdSAndroid Build Coastguard Worker
1393*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:new()
1394*387f9dfdSAndroid Build Coastguard Worker        return setmetatable( {}, LuaUnit_MT )
1395*387f9dfdSAndroid Build Coastguard Worker    end
1396*387f9dfdSAndroid Build Coastguard Worker
1397*387f9dfdSAndroid Build Coastguard Worker    -----------------[[ Utility methods ]]---------------------
1398*387f9dfdSAndroid Build Coastguard Worker
1399*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.asFunction(aObject)
1400*387f9dfdSAndroid Build Coastguard Worker        -- return "aObject" if it is a function, and nil otherwise
1401*387f9dfdSAndroid Build Coastguard Worker        if 'function' == type(aObject) then return aObject end
1402*387f9dfdSAndroid Build Coastguard Worker    end
1403*387f9dfdSAndroid Build Coastguard Worker
1404*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.isClassMethod(aName)
1405*387f9dfdSAndroid Build Coastguard Worker        -- return true if aName contains a class + a method name in the form class:method
1406*387f9dfdSAndroid Build Coastguard Worker        return string.find(aName, '.', nil, true) ~= nil
1407*387f9dfdSAndroid Build Coastguard Worker    end
1408*387f9dfdSAndroid Build Coastguard Worker
1409*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.splitClassMethod(someName)
1410*387f9dfdSAndroid Build Coastguard Worker        -- return a pair className, methodName for a name in the form class:method
1411*387f9dfdSAndroid Build Coastguard Worker        -- return nil if not a class + method name
1412*387f9dfdSAndroid Build Coastguard Worker        -- name is class + method
1413*387f9dfdSAndroid Build Coastguard Worker        local hasMethod, methodName, className
1414*387f9dfdSAndroid Build Coastguard Worker        hasMethod = string.find(someName, '.', nil, true )
1415*387f9dfdSAndroid Build Coastguard Worker        if not hasMethod then return nil end
1416*387f9dfdSAndroid Build Coastguard Worker        methodName = string.sub(someName, hasMethod+1)
1417*387f9dfdSAndroid Build Coastguard Worker        className = string.sub(someName,1,hasMethod-1)
1418*387f9dfdSAndroid Build Coastguard Worker        return className, methodName
1419*387f9dfdSAndroid Build Coastguard Worker    end
1420*387f9dfdSAndroid Build Coastguard Worker
1421*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.isMethodTestName( s )
1422*387f9dfdSAndroid Build Coastguard Worker        -- return true is the name matches the name of a test method
1423*387f9dfdSAndroid Build Coastguard Worker        -- default rule is that is starts with 'Test' or with 'test'
1424*387f9dfdSAndroid Build Coastguard Worker        return string.sub(s, 1, 4):lower() == 'test'
1425*387f9dfdSAndroid Build Coastguard Worker    end
1426*387f9dfdSAndroid Build Coastguard Worker
1427*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.isTestName( s )
1428*387f9dfdSAndroid Build Coastguard Worker        -- return true is the name matches the name of a test
1429*387f9dfdSAndroid Build Coastguard Worker        -- default rule is that is starts with 'Test' or with 'test'
1430*387f9dfdSAndroid Build Coastguard Worker        return string.sub(s, 1, 4):lower() == 'test'
1431*387f9dfdSAndroid Build Coastguard Worker    end
1432*387f9dfdSAndroid Build Coastguard Worker
1433*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.collectTests()
1434*387f9dfdSAndroid Build Coastguard Worker        -- return a list of all test names in the global namespace
1435*387f9dfdSAndroid Build Coastguard Worker        -- that match LuaUnit.isTestName
1436*387f9dfdSAndroid Build Coastguard Worker
1437*387f9dfdSAndroid Build Coastguard Worker        local testNames = {}
1438*387f9dfdSAndroid Build Coastguard Worker        for k, v in pairs(_G) do
1439*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.isTestName( k ) then
1440*387f9dfdSAndroid Build Coastguard Worker                table.insert( testNames , k )
1441*387f9dfdSAndroid Build Coastguard Worker            end
1442*387f9dfdSAndroid Build Coastguard Worker        end
1443*387f9dfdSAndroid Build Coastguard Worker        table.sort( testNames )
1444*387f9dfdSAndroid Build Coastguard Worker        return testNames
1445*387f9dfdSAndroid Build Coastguard Worker    end
1446*387f9dfdSAndroid Build Coastguard Worker
1447*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.parseCmdLine( cmdLine )
1448*387f9dfdSAndroid Build Coastguard Worker        -- parse the command line
1449*387f9dfdSAndroid Build Coastguard Worker        -- Supported command line parameters:
1450*387f9dfdSAndroid Build Coastguard Worker        -- --verbose, -v: increase verbosity
1451*387f9dfdSAndroid Build Coastguard Worker        -- --quiet, -q: silence output
1452*387f9dfdSAndroid Build Coastguard Worker        -- --error, -e: treat errors as fatal (quit program)
1453*387f9dfdSAndroid Build Coastguard Worker        -- --output, -o, + name: select output type
1454*387f9dfdSAndroid Build Coastguard Worker        -- --pattern, -p, + pattern: run test matching pattern, may be repeated
1455*387f9dfdSAndroid Build Coastguard Worker        -- --name, -n, + fname: name of output file for junit, default to stdout
1456*387f9dfdSAndroid Build Coastguard Worker        -- [testnames, ...]: run selected test names
1457*387f9dfdSAndroid Build Coastguard Worker        --
1458*387f9dfdSAndroid Build Coastguard Worker        -- Returns a table with the following fields:
1459*387f9dfdSAndroid Build Coastguard Worker        -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE
1460*387f9dfdSAndroid Build Coastguard Worker        -- output: nil, 'tap', 'junit', 'text', 'nil'
1461*387f9dfdSAndroid Build Coastguard Worker        -- testNames: nil or a list of test names to run
1462*387f9dfdSAndroid Build Coastguard Worker        -- pattern: nil or a list of patterns
1463*387f9dfdSAndroid Build Coastguard Worker
1464*387f9dfdSAndroid Build Coastguard Worker        local result = {}
1465*387f9dfdSAndroid Build Coastguard Worker        local state = nil
1466*387f9dfdSAndroid Build Coastguard Worker        local SET_OUTPUT = 1
1467*387f9dfdSAndroid Build Coastguard Worker        local SET_PATTERN = 2
1468*387f9dfdSAndroid Build Coastguard Worker        local SET_FNAME = 3
1469*387f9dfdSAndroid Build Coastguard Worker
1470*387f9dfdSAndroid Build Coastguard Worker        if cmdLine == nil then
1471*387f9dfdSAndroid Build Coastguard Worker            return result
1472*387f9dfdSAndroid Build Coastguard Worker        end
1473*387f9dfdSAndroid Build Coastguard Worker
1474*387f9dfdSAndroid Build Coastguard Worker        local function parseOption( option )
1475*387f9dfdSAndroid Build Coastguard Worker            if option == '--help' or option == '-h' then
1476*387f9dfdSAndroid Build Coastguard Worker                result['help'] = true
1477*387f9dfdSAndroid Build Coastguard Worker                return
1478*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--version' then
1479*387f9dfdSAndroid Build Coastguard Worker                result['version'] = true
1480*387f9dfdSAndroid Build Coastguard Worker                return
1481*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--verbose' or option == '-v' then
1482*387f9dfdSAndroid Build Coastguard Worker                result['verbosity'] = M.VERBOSITY_VERBOSE
1483*387f9dfdSAndroid Build Coastguard Worker                return
1484*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--quiet' or option == '-q' then
1485*387f9dfdSAndroid Build Coastguard Worker                result['verbosity'] = M.VERBOSITY_QUIET
1486*387f9dfdSAndroid Build Coastguard Worker                return
1487*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--error' or option == '-e' then
1488*387f9dfdSAndroid Build Coastguard Worker                result['quitOnError'] = true
1489*387f9dfdSAndroid Build Coastguard Worker                return
1490*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--failure' or option == '-f' then
1491*387f9dfdSAndroid Build Coastguard Worker                result['quitOnFailure'] = true
1492*387f9dfdSAndroid Build Coastguard Worker                return
1493*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--output' or option == '-o' then
1494*387f9dfdSAndroid Build Coastguard Worker                state = SET_OUTPUT
1495*387f9dfdSAndroid Build Coastguard Worker                return state
1496*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--name' or option == '-n' then
1497*387f9dfdSAndroid Build Coastguard Worker                state = SET_FNAME
1498*387f9dfdSAndroid Build Coastguard Worker                return state
1499*387f9dfdSAndroid Build Coastguard Worker            elseif option == '--pattern' or option == '-p' then
1500*387f9dfdSAndroid Build Coastguard Worker                state = SET_PATTERN
1501*387f9dfdSAndroid Build Coastguard Worker                return state
1502*387f9dfdSAndroid Build Coastguard Worker            end
1503*387f9dfdSAndroid Build Coastguard Worker            error('Unknown option: '..option,3)
1504*387f9dfdSAndroid Build Coastguard Worker        end
1505*387f9dfdSAndroid Build Coastguard Worker
1506*387f9dfdSAndroid Build Coastguard Worker        local function setArg( cmdArg, state )
1507*387f9dfdSAndroid Build Coastguard Worker            if state == SET_OUTPUT then
1508*387f9dfdSAndroid Build Coastguard Worker                result['output'] = cmdArg
1509*387f9dfdSAndroid Build Coastguard Worker                return
1510*387f9dfdSAndroid Build Coastguard Worker            elseif state == SET_FNAME then
1511*387f9dfdSAndroid Build Coastguard Worker                result['fname'] = cmdArg
1512*387f9dfdSAndroid Build Coastguard Worker                return
1513*387f9dfdSAndroid Build Coastguard Worker            elseif state == SET_PATTERN then
1514*387f9dfdSAndroid Build Coastguard Worker                if result['pattern'] then
1515*387f9dfdSAndroid Build Coastguard Worker                    table.insert( result['pattern'], cmdArg )
1516*387f9dfdSAndroid Build Coastguard Worker                else
1517*387f9dfdSAndroid Build Coastguard Worker                    result['pattern'] = { cmdArg }
1518*387f9dfdSAndroid Build Coastguard Worker                end
1519*387f9dfdSAndroid Build Coastguard Worker                return
1520*387f9dfdSAndroid Build Coastguard Worker            end
1521*387f9dfdSAndroid Build Coastguard Worker            error('Unknown parse state: '.. state)
1522*387f9dfdSAndroid Build Coastguard Worker        end
1523*387f9dfdSAndroid Build Coastguard Worker
1524*387f9dfdSAndroid Build Coastguard Worker
1525*387f9dfdSAndroid Build Coastguard Worker        for i, cmdArg in ipairs(cmdLine) do
1526*387f9dfdSAndroid Build Coastguard Worker            if state ~= nil then
1527*387f9dfdSAndroid Build Coastguard Worker                setArg( cmdArg, state, result )
1528*387f9dfdSAndroid Build Coastguard Worker                state = nil
1529*387f9dfdSAndroid Build Coastguard Worker            else
1530*387f9dfdSAndroid Build Coastguard Worker                if cmdArg:sub(1,1) == '-' then
1531*387f9dfdSAndroid Build Coastguard Worker                    state = parseOption( cmdArg )
1532*387f9dfdSAndroid Build Coastguard Worker                else
1533*387f9dfdSAndroid Build Coastguard Worker                    if result['testNames'] then
1534*387f9dfdSAndroid Build Coastguard Worker                        table.insert( result['testNames'], cmdArg )
1535*387f9dfdSAndroid Build Coastguard Worker                    else
1536*387f9dfdSAndroid Build Coastguard Worker                        result['testNames'] = { cmdArg }
1537*387f9dfdSAndroid Build Coastguard Worker                    end
1538*387f9dfdSAndroid Build Coastguard Worker                end
1539*387f9dfdSAndroid Build Coastguard Worker            end
1540*387f9dfdSAndroid Build Coastguard Worker        end
1541*387f9dfdSAndroid Build Coastguard Worker
1542*387f9dfdSAndroid Build Coastguard Worker        if result['help'] then
1543*387f9dfdSAndroid Build Coastguard Worker            M.LuaUnit.help()
1544*387f9dfdSAndroid Build Coastguard Worker        end
1545*387f9dfdSAndroid Build Coastguard Worker
1546*387f9dfdSAndroid Build Coastguard Worker        if result['version'] then
1547*387f9dfdSAndroid Build Coastguard Worker            M.LuaUnit.version()
1548*387f9dfdSAndroid Build Coastguard Worker        end
1549*387f9dfdSAndroid Build Coastguard Worker
1550*387f9dfdSAndroid Build Coastguard Worker        if state ~= nil then
1551*387f9dfdSAndroid Build Coastguard Worker            error('Missing argument after '..cmdLine[ #cmdLine ],2 )
1552*387f9dfdSAndroid Build Coastguard Worker        end
1553*387f9dfdSAndroid Build Coastguard Worker
1554*387f9dfdSAndroid Build Coastguard Worker        return result
1555*387f9dfdSAndroid Build Coastguard Worker    end
1556*387f9dfdSAndroid Build Coastguard Worker
1557*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.help()
1558*387f9dfdSAndroid Build Coastguard Worker        print(M.USAGE)
1559*387f9dfdSAndroid Build Coastguard Worker        os.exit(0)
1560*387f9dfdSAndroid Build Coastguard Worker    end
1561*387f9dfdSAndroid Build Coastguard Worker
1562*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.version()
1563*387f9dfdSAndroid Build Coastguard Worker        print('LuaUnit v'..M.VERSION..' by Philippe Fremy <[email protected]>')
1564*387f9dfdSAndroid Build Coastguard Worker        os.exit(0)
1565*387f9dfdSAndroid Build Coastguard Worker    end
1566*387f9dfdSAndroid Build Coastguard Worker
1567*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.patternInclude( patternFilter, expr )
1568*387f9dfdSAndroid Build Coastguard Worker        -- check if any of patternFilter is contained in expr. If so, return true.
1569*387f9dfdSAndroid Build Coastguard Worker        -- return false if None of the patterns are contained in expr
1570*387f9dfdSAndroid Build Coastguard Worker        -- if patternFilter is nil, return true (no filtering)
1571*387f9dfdSAndroid Build Coastguard Worker        if patternFilter == nil then
1572*387f9dfdSAndroid Build Coastguard Worker            return true
1573*387f9dfdSAndroid Build Coastguard Worker        end
1574*387f9dfdSAndroid Build Coastguard Worker
1575*387f9dfdSAndroid Build Coastguard Worker        for i,pattern in ipairs(patternFilter) do
1576*387f9dfdSAndroid Build Coastguard Worker            if string.find(expr, pattern) then
1577*387f9dfdSAndroid Build Coastguard Worker                return true
1578*387f9dfdSAndroid Build Coastguard Worker            end
1579*387f9dfdSAndroid Build Coastguard Worker        end
1580*387f9dfdSAndroid Build Coastguard Worker
1581*387f9dfdSAndroid Build Coastguard Worker        return false
1582*387f9dfdSAndroid Build Coastguard Worker    end
1583*387f9dfdSAndroid Build Coastguard Worker
1584*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1585*387f9dfdSAndroid Build Coastguard Worker--                     class NodeStatus
1586*387f9dfdSAndroid Build Coastguard Worker----------------------------------------------------------------
1587*387f9dfdSAndroid Build Coastguard Worker
1588*387f9dfdSAndroid Build Coastguard Worker    local NodeStatus = { __class__ = 'NodeStatus' } -- class
1589*387f9dfdSAndroid Build Coastguard Worker    local NodeStatus_MT = { __index = NodeStatus } -- metatable
1590*387f9dfdSAndroid Build Coastguard Worker    M.NodeStatus = NodeStatus
1591*387f9dfdSAndroid Build Coastguard Worker
1592*387f9dfdSAndroid Build Coastguard Worker    -- values of status
1593*387f9dfdSAndroid Build Coastguard Worker    NodeStatus.PASS  = 'PASS'
1594*387f9dfdSAndroid Build Coastguard Worker    NodeStatus.FAIL  = 'FAIL'
1595*387f9dfdSAndroid Build Coastguard Worker    NodeStatus.ERROR = 'ERROR'
1596*387f9dfdSAndroid Build Coastguard Worker
1597*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:new( number, testName, className )
1598*387f9dfdSAndroid Build Coastguard Worker        local t = { number = number, testName = testName, className = className }
1599*387f9dfdSAndroid Build Coastguard Worker        setmetatable( t, NodeStatus_MT )
1600*387f9dfdSAndroid Build Coastguard Worker        t:pass()
1601*387f9dfdSAndroid Build Coastguard Worker        return t
1602*387f9dfdSAndroid Build Coastguard Worker    end
1603*387f9dfdSAndroid Build Coastguard Worker
1604*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:pass()
1605*387f9dfdSAndroid Build Coastguard Worker        self.status = self.PASS
1606*387f9dfdSAndroid Build Coastguard Worker        -- useless but we know it's the field we want to use
1607*387f9dfdSAndroid Build Coastguard Worker        self.msg = nil
1608*387f9dfdSAndroid Build Coastguard Worker        self.stackTrace = nil
1609*387f9dfdSAndroid Build Coastguard Worker    end
1610*387f9dfdSAndroid Build Coastguard Worker
1611*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:fail(msg, stackTrace)
1612*387f9dfdSAndroid Build Coastguard Worker        self.status = self.FAIL
1613*387f9dfdSAndroid Build Coastguard Worker        self.msg = msg
1614*387f9dfdSAndroid Build Coastguard Worker        self.stackTrace = stackTrace
1615*387f9dfdSAndroid Build Coastguard Worker    end
1616*387f9dfdSAndroid Build Coastguard Worker
1617*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:error(msg, stackTrace)
1618*387f9dfdSAndroid Build Coastguard Worker        self.status = self.ERROR
1619*387f9dfdSAndroid Build Coastguard Worker        self.msg = msg
1620*387f9dfdSAndroid Build Coastguard Worker        self.stackTrace = stackTrace
1621*387f9dfdSAndroid Build Coastguard Worker    end
1622*387f9dfdSAndroid Build Coastguard Worker
1623*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:isPassed()
1624*387f9dfdSAndroid Build Coastguard Worker        return self.status == NodeStatus.PASS
1625*387f9dfdSAndroid Build Coastguard Worker    end
1626*387f9dfdSAndroid Build Coastguard Worker
1627*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:isNotPassed()
1628*387f9dfdSAndroid Build Coastguard Worker        -- print('hasFailure: '..prettystr(self))
1629*387f9dfdSAndroid Build Coastguard Worker        return self.status ~= NodeStatus.PASS
1630*387f9dfdSAndroid Build Coastguard Worker    end
1631*387f9dfdSAndroid Build Coastguard Worker
1632*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:isFailure()
1633*387f9dfdSAndroid Build Coastguard Worker        return self.status == NodeStatus.FAIL
1634*387f9dfdSAndroid Build Coastguard Worker    end
1635*387f9dfdSAndroid Build Coastguard Worker
1636*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:isError()
1637*387f9dfdSAndroid Build Coastguard Worker        return self.status == NodeStatus.ERROR
1638*387f9dfdSAndroid Build Coastguard Worker    end
1639*387f9dfdSAndroid Build Coastguard Worker
1640*387f9dfdSAndroid Build Coastguard Worker    function NodeStatus:statusXML()
1641*387f9dfdSAndroid Build Coastguard Worker        if self:isError() then
1642*387f9dfdSAndroid Build Coastguard Worker            return table.concat(
1643*387f9dfdSAndroid Build Coastguard Worker                {'            <error type="', xmlEscape(self.msg), '">\n',
1644*387f9dfdSAndroid Build Coastguard Worker                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),
1645*387f9dfdSAndroid Build Coastguard Worker                 ']]></error>\n'})
1646*387f9dfdSAndroid Build Coastguard Worker        elseif self:isFailure() then
1647*387f9dfdSAndroid Build Coastguard Worker            return table.concat(
1648*387f9dfdSAndroid Build Coastguard Worker                {'            <failure type="', xmlEscape(self.msg), '">\n',
1649*387f9dfdSAndroid Build Coastguard Worker                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),
1650*387f9dfdSAndroid Build Coastguard Worker                 ']]></failure>\n'})
1651*387f9dfdSAndroid Build Coastguard Worker        end
1652*387f9dfdSAndroid Build Coastguard Worker        return '            <passed/>\n' -- (not XSD-compliant! normally shouldn't get here)
1653*387f9dfdSAndroid Build Coastguard Worker    end
1654*387f9dfdSAndroid Build Coastguard Worker
1655*387f9dfdSAndroid Build Coastguard Worker    --------------[[ Output methods ]]-------------------------
1656*387f9dfdSAndroid Build Coastguard Worker
1657*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.statusLine(result)
1658*387f9dfdSAndroid Build Coastguard Worker        -- return status line string according to results
1659*387f9dfdSAndroid Build Coastguard Worker        local s = string.format('Ran %d tests in %0.3f seconds, %d successes',
1660*387f9dfdSAndroid Build Coastguard Worker            result.runCount, result.duration, result.passedCount )
1661*387f9dfdSAndroid Build Coastguard Worker        if result.notPassedCount > 0 then
1662*387f9dfdSAndroid Build Coastguard Worker            if result.failureCount > 0 then
1663*387f9dfdSAndroid Build Coastguard Worker                s = s..string.format(', %d failures', result.failureCount )
1664*387f9dfdSAndroid Build Coastguard Worker            end
1665*387f9dfdSAndroid Build Coastguard Worker            if result.errorCount > 0 then
1666*387f9dfdSAndroid Build Coastguard Worker                s = s..string.format(', %d errors', result.errorCount )
1667*387f9dfdSAndroid Build Coastguard Worker            end
1668*387f9dfdSAndroid Build Coastguard Worker        else
1669*387f9dfdSAndroid Build Coastguard Worker            s = s..', 0 failures'
1670*387f9dfdSAndroid Build Coastguard Worker        end
1671*387f9dfdSAndroid Build Coastguard Worker        if result.nonSelectedCount > 0 then
1672*387f9dfdSAndroid Build Coastguard Worker            s = s..string.format(", %d non-selected", result.nonSelectedCount )
1673*387f9dfdSAndroid Build Coastguard Worker        end
1674*387f9dfdSAndroid Build Coastguard Worker        return s
1675*387f9dfdSAndroid Build Coastguard Worker    end
1676*387f9dfdSAndroid Build Coastguard Worker
1677*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:startSuite(testCount, nonSelectedCount)
1678*387f9dfdSAndroid Build Coastguard Worker        self.result = {}
1679*387f9dfdSAndroid Build Coastguard Worker        self.result.testCount = testCount
1680*387f9dfdSAndroid Build Coastguard Worker        self.result.nonSelectedCount = nonSelectedCount
1681*387f9dfdSAndroid Build Coastguard Worker        self.result.passedCount = 0
1682*387f9dfdSAndroid Build Coastguard Worker        self.result.runCount = 0
1683*387f9dfdSAndroid Build Coastguard Worker        self.result.currentTestNumber = 0
1684*387f9dfdSAndroid Build Coastguard Worker        self.result.currentClassName = ""
1685*387f9dfdSAndroid Build Coastguard Worker        self.result.currentNode = nil
1686*387f9dfdSAndroid Build Coastguard Worker        self.result.suiteStarted = true
1687*387f9dfdSAndroid Build Coastguard Worker        self.result.startTime = os.clock()
1688*387f9dfdSAndroid Build Coastguard Worker        self.result.startDate = os.date(os.getenv('LUAUNIT_DATEFMT'))
1689*387f9dfdSAndroid Build Coastguard Worker        self.result.startIsodate = os.date('%Y-%m-%dT%H:%M:%S')
1690*387f9dfdSAndroid Build Coastguard Worker        self.result.patternFilter = self.patternFilter
1691*387f9dfdSAndroid Build Coastguard Worker        self.result.tests = {}
1692*387f9dfdSAndroid Build Coastguard Worker        self.result.failures = {}
1693*387f9dfdSAndroid Build Coastguard Worker        self.result.errors = {}
1694*387f9dfdSAndroid Build Coastguard Worker        self.result.notPassed = {}
1695*387f9dfdSAndroid Build Coastguard Worker
1696*387f9dfdSAndroid Build Coastguard Worker        self.outputType = self.outputType or TextOutput
1697*387f9dfdSAndroid Build Coastguard Worker        self.output = self.outputType:new()
1698*387f9dfdSAndroid Build Coastguard Worker        self.output.runner = self
1699*387f9dfdSAndroid Build Coastguard Worker        self.output.result = self.result
1700*387f9dfdSAndroid Build Coastguard Worker        self.output.verbosity = self.verbosity
1701*387f9dfdSAndroid Build Coastguard Worker        self.output.fname = self.fname
1702*387f9dfdSAndroid Build Coastguard Worker        self.output:startSuite()
1703*387f9dfdSAndroid Build Coastguard Worker    end
1704*387f9dfdSAndroid Build Coastguard Worker
1705*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:startClass( className )
1706*387f9dfdSAndroid Build Coastguard Worker        self.result.currentClassName = className
1707*387f9dfdSAndroid Build Coastguard Worker        self.output:startClass( className )
1708*387f9dfdSAndroid Build Coastguard Worker    end
1709*387f9dfdSAndroid Build Coastguard Worker
1710*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:startTest( testName  )
1711*387f9dfdSAndroid Build Coastguard Worker        self.result.currentTestNumber = self.result.currentTestNumber + 1
1712*387f9dfdSAndroid Build Coastguard Worker        self.result.runCount = self.result.runCount + 1
1713*387f9dfdSAndroid Build Coastguard Worker        self.result.currentNode = NodeStatus:new(
1714*387f9dfdSAndroid Build Coastguard Worker            self.result.currentTestNumber,
1715*387f9dfdSAndroid Build Coastguard Worker            testName,
1716*387f9dfdSAndroid Build Coastguard Worker            self.result.currentClassName
1717*387f9dfdSAndroid Build Coastguard Worker        )
1718*387f9dfdSAndroid Build Coastguard Worker        self.result.currentNode.startTime = os.clock()
1719*387f9dfdSAndroid Build Coastguard Worker        table.insert( self.result.tests, self.result.currentNode )
1720*387f9dfdSAndroid Build Coastguard Worker        self.output:startTest( testName )
1721*387f9dfdSAndroid Build Coastguard Worker    end
1722*387f9dfdSAndroid Build Coastguard Worker
1723*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:addStatus( err )
1724*387f9dfdSAndroid Build Coastguard Worker        -- "err" is expected to be a table / result from protectedCall()
1725*387f9dfdSAndroid Build Coastguard Worker        if err.status == NodeStatus.PASS then return end
1726*387f9dfdSAndroid Build Coastguard Worker
1727*387f9dfdSAndroid Build Coastguard Worker        local node = self.result.currentNode
1728*387f9dfdSAndroid Build Coastguard Worker
1729*387f9dfdSAndroid Build Coastguard Worker        --[[ As a first approach, we will report only one error or one failure for one test.
1730*387f9dfdSAndroid Build Coastguard Worker
1731*387f9dfdSAndroid Build Coastguard Worker        However, we can have the case where the test is in failure, and the teardown is in error.
1732*387f9dfdSAndroid Build Coastguard Worker        In such case, it's a good idea to report both a failure and an error in the test suite. This is
1733*387f9dfdSAndroid Build Coastguard Worker        what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for
1734*387f9dfdSAndroid Build Coastguard Worker        example, there could be more (failures + errors) count that tests. What happens to the current node ?
1735*387f9dfdSAndroid Build Coastguard Worker
1736*387f9dfdSAndroid Build Coastguard Worker        We will do this more intelligent version later.
1737*387f9dfdSAndroid Build Coastguard Worker        ]]
1738*387f9dfdSAndroid Build Coastguard Worker
1739*387f9dfdSAndroid Build Coastguard Worker        -- if the node is already in failure/error, just don't report the new error (see above)
1740*387f9dfdSAndroid Build Coastguard Worker        if node.status ~= NodeStatus.PASS then return end
1741*387f9dfdSAndroid Build Coastguard Worker
1742*387f9dfdSAndroid Build Coastguard Worker        table.insert( self.result.notPassed, node )
1743*387f9dfdSAndroid Build Coastguard Worker
1744*387f9dfdSAndroid Build Coastguard Worker        if err.status == NodeStatus.FAIL then
1745*387f9dfdSAndroid Build Coastguard Worker            node:fail( err.msg, err.trace )
1746*387f9dfdSAndroid Build Coastguard Worker            table.insert( self.result.failures, node )
1747*387f9dfdSAndroid Build Coastguard Worker            self.output:addFailure( node )
1748*387f9dfdSAndroid Build Coastguard Worker        elseif err.status == NodeStatus.ERROR then
1749*387f9dfdSAndroid Build Coastguard Worker            node:error( err.msg, err.trace )
1750*387f9dfdSAndroid Build Coastguard Worker            table.insert( self.result.errors, node )
1751*387f9dfdSAndroid Build Coastguard Worker            self.output:addError( node )
1752*387f9dfdSAndroid Build Coastguard Worker        end
1753*387f9dfdSAndroid Build Coastguard Worker    end
1754*387f9dfdSAndroid Build Coastguard Worker
1755*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:endTest()
1756*387f9dfdSAndroid Build Coastguard Worker        local node = self.result.currentNode
1757*387f9dfdSAndroid Build Coastguard Worker        -- print( 'endTest() '..prettystr(node))
1758*387f9dfdSAndroid Build Coastguard Worker        -- print( 'endTest() '..prettystr(node:isNotPassed()))
1759*387f9dfdSAndroid Build Coastguard Worker        node.duration = os.clock() - node.startTime
1760*387f9dfdSAndroid Build Coastguard Worker        node.startTime = nil
1761*387f9dfdSAndroid Build Coastguard Worker        self.output:endTest( node )
1762*387f9dfdSAndroid Build Coastguard Worker
1763*387f9dfdSAndroid Build Coastguard Worker        if node:isPassed() then
1764*387f9dfdSAndroid Build Coastguard Worker            self.result.passedCount = self.result.passedCount + 1
1765*387f9dfdSAndroid Build Coastguard Worker        elseif node:isError() then
1766*387f9dfdSAndroid Build Coastguard Worker            if self.quitOnError or self.quitOnFailure then
1767*387f9dfdSAndroid Build Coastguard Worker                -- Runtime error - abort test execution as requested by
1768*387f9dfdSAndroid Build Coastguard Worker                -- "--error" option. This is done by setting a special
1769*387f9dfdSAndroid Build Coastguard Worker                -- flag that gets handled in runSuiteByInstances().
1770*387f9dfdSAndroid Build Coastguard Worker                print("\nERROR during LuaUnit test execution:\n" .. node.msg)
1771*387f9dfdSAndroid Build Coastguard Worker                self.result.aborted = true
1772*387f9dfdSAndroid Build Coastguard Worker            end
1773*387f9dfdSAndroid Build Coastguard Worker        elseif node:isFailure() then
1774*387f9dfdSAndroid Build Coastguard Worker            if self.quitOnFailure then
1775*387f9dfdSAndroid Build Coastguard Worker                -- Failure - abort test execution as requested by
1776*387f9dfdSAndroid Build Coastguard Worker                -- "--failure" option. This is done by setting a special
1777*387f9dfdSAndroid Build Coastguard Worker                -- flag that gets handled in runSuiteByInstances().
1778*387f9dfdSAndroid Build Coastguard Worker                print("\nFailure during LuaUnit test execution:\n" .. node.msg)
1779*387f9dfdSAndroid Build Coastguard Worker                self.result.aborted = true
1780*387f9dfdSAndroid Build Coastguard Worker            end
1781*387f9dfdSAndroid Build Coastguard Worker        end
1782*387f9dfdSAndroid Build Coastguard Worker        self.result.currentNode = nil
1783*387f9dfdSAndroid Build Coastguard Worker    end
1784*387f9dfdSAndroid Build Coastguard Worker
1785*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:endClass()
1786*387f9dfdSAndroid Build Coastguard Worker        self.output:endClass()
1787*387f9dfdSAndroid Build Coastguard Worker    end
1788*387f9dfdSAndroid Build Coastguard Worker
1789*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:endSuite()
1790*387f9dfdSAndroid Build Coastguard Worker        if self.result.suiteStarted == false then
1791*387f9dfdSAndroid Build Coastguard Worker            error('LuaUnit:endSuite() -- suite was already ended' )
1792*387f9dfdSAndroid Build Coastguard Worker        end
1793*387f9dfdSAndroid Build Coastguard Worker        self.result.duration = os.clock()-self.result.startTime
1794*387f9dfdSAndroid Build Coastguard Worker        self.result.suiteStarted = false
1795*387f9dfdSAndroid Build Coastguard Worker
1796*387f9dfdSAndroid Build Coastguard Worker        -- Expose test counts for outputter's endSuite(). This could be managed
1797*387f9dfdSAndroid Build Coastguard Worker        -- internally instead, but unit tests (and existing use cases) might
1798*387f9dfdSAndroid Build Coastguard Worker        -- rely on these fields being present.
1799*387f9dfdSAndroid Build Coastguard Worker        self.result.notPassedCount = #self.result.notPassed
1800*387f9dfdSAndroid Build Coastguard Worker        self.result.failureCount = #self.result.failures
1801*387f9dfdSAndroid Build Coastguard Worker        self.result.errorCount = #self.result.errors
1802*387f9dfdSAndroid Build Coastguard Worker
1803*387f9dfdSAndroid Build Coastguard Worker        self.output:endSuite()
1804*387f9dfdSAndroid Build Coastguard Worker    end
1805*387f9dfdSAndroid Build Coastguard Worker
1806*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:setOutputType(outputType)
1807*387f9dfdSAndroid Build Coastguard Worker        -- default to text
1808*387f9dfdSAndroid Build Coastguard Worker        -- tap produces results according to TAP format
1809*387f9dfdSAndroid Build Coastguard Worker        if outputType:upper() == "NIL" then
1810*387f9dfdSAndroid Build Coastguard Worker            self.outputType = NilOutput
1811*387f9dfdSAndroid Build Coastguard Worker            return
1812*387f9dfdSAndroid Build Coastguard Worker        end
1813*387f9dfdSAndroid Build Coastguard Worker        if outputType:upper() == "TAP" then
1814*387f9dfdSAndroid Build Coastguard Worker            self.outputType = TapOutput
1815*387f9dfdSAndroid Build Coastguard Worker            return
1816*387f9dfdSAndroid Build Coastguard Worker        end
1817*387f9dfdSAndroid Build Coastguard Worker        if outputType:upper() == "JUNIT" then
1818*387f9dfdSAndroid Build Coastguard Worker            self.outputType = JUnitOutput
1819*387f9dfdSAndroid Build Coastguard Worker            return
1820*387f9dfdSAndroid Build Coastguard Worker        end
1821*387f9dfdSAndroid Build Coastguard Worker        if outputType:upper() == "TEXT" then
1822*387f9dfdSAndroid Build Coastguard Worker            self.outputType = TextOutput
1823*387f9dfdSAndroid Build Coastguard Worker            return
1824*387f9dfdSAndroid Build Coastguard Worker        end
1825*387f9dfdSAndroid Build Coastguard Worker        error( 'No such format: '..outputType,2)
1826*387f9dfdSAndroid Build Coastguard Worker    end
1827*387f9dfdSAndroid Build Coastguard Worker
1828*387f9dfdSAndroid Build Coastguard Worker    --------------[[ Runner ]]-----------------
1829*387f9dfdSAndroid Build Coastguard Worker
1830*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName)
1831*387f9dfdSAndroid Build Coastguard Worker        -- if classInstance is nil, this is just a function call
1832*387f9dfdSAndroid Build Coastguard Worker        -- else, it's method of a class being called.
1833*387f9dfdSAndroid Build Coastguard Worker
1834*387f9dfdSAndroid Build Coastguard Worker        local function err_handler(e)
1835*387f9dfdSAndroid Build Coastguard Worker            -- transform error into a table, adding the traceback information
1836*387f9dfdSAndroid Build Coastguard Worker            return {
1837*387f9dfdSAndroid Build Coastguard Worker                status = NodeStatus.ERROR,
1838*387f9dfdSAndroid Build Coastguard Worker                msg = e,
1839*387f9dfdSAndroid Build Coastguard Worker                trace = string.sub(debug.traceback("", 3), 2)
1840*387f9dfdSAndroid Build Coastguard Worker            }
1841*387f9dfdSAndroid Build Coastguard Worker        end
1842*387f9dfdSAndroid Build Coastguard Worker
1843*387f9dfdSAndroid Build Coastguard Worker        local ok, err
1844*387f9dfdSAndroid Build Coastguard Worker        if classInstance then
1845*387f9dfdSAndroid Build Coastguard Worker            -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround
1846*387f9dfdSAndroid Build Coastguard Worker            ok, err = xpcall( function () methodInstance(classInstance) end, err_handler )
1847*387f9dfdSAndroid Build Coastguard Worker        else
1848*387f9dfdSAndroid Build Coastguard Worker            ok, err = xpcall( function () methodInstance() end, err_handler )
1849*387f9dfdSAndroid Build Coastguard Worker        end
1850*387f9dfdSAndroid Build Coastguard Worker        if ok then
1851*387f9dfdSAndroid Build Coastguard Worker            return {status = NodeStatus.PASS}
1852*387f9dfdSAndroid Build Coastguard Worker        end
1853*387f9dfdSAndroid Build Coastguard Worker
1854*387f9dfdSAndroid Build Coastguard Worker        -- determine if the error was a failed test:
1855*387f9dfdSAndroid Build Coastguard Worker        -- We do this by stripping the failure prefix from the error message,
1856*387f9dfdSAndroid Build Coastguard Worker        -- while keeping track of the gsub() count. A non-zero value -> failure
1857*387f9dfdSAndroid Build Coastguard Worker        local failed
1858*387f9dfdSAndroid Build Coastguard Worker        err.msg, failed = err.msg:gsub(M.FAILURE_PREFIX, "", 1)
1859*387f9dfdSAndroid Build Coastguard Worker        if failed > 0 then
1860*387f9dfdSAndroid Build Coastguard Worker            err.status = NodeStatus.FAIL
1861*387f9dfdSAndroid Build Coastguard Worker        end
1862*387f9dfdSAndroid Build Coastguard Worker
1863*387f9dfdSAndroid Build Coastguard Worker        -- reformat / improve the stack trace
1864*387f9dfdSAndroid Build Coastguard Worker        if prettyFuncName then -- we do have the real method name
1865*387f9dfdSAndroid Build Coastguard Worker            err.trace = err.trace:gsub("in (%a+) 'methodInstance'", "in %1 '"..prettyFuncName.."'")
1866*387f9dfdSAndroid Build Coastguard Worker        end
1867*387f9dfdSAndroid Build Coastguard Worker        if STRIP_LUAUNIT_FROM_STACKTRACE then
1868*387f9dfdSAndroid Build Coastguard Worker            err.trace = stripLuaunitTrace(err.trace)
1869*387f9dfdSAndroid Build Coastguard Worker        end
1870*387f9dfdSAndroid Build Coastguard Worker
1871*387f9dfdSAndroid Build Coastguard Worker        return err -- return the error "object" (table)
1872*387f9dfdSAndroid Build Coastguard Worker    end
1873*387f9dfdSAndroid Build Coastguard Worker
1874*387f9dfdSAndroid Build Coastguard Worker
1875*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance)
1876*387f9dfdSAndroid Build Coastguard Worker        -- When executing a test function, className and classInstance must be nil
1877*387f9dfdSAndroid Build Coastguard Worker        -- When executing a class method, all parameters must be set
1878*387f9dfdSAndroid Build Coastguard Worker
1879*387f9dfdSAndroid Build Coastguard Worker        if type(methodInstance) ~= 'function' then
1880*387f9dfdSAndroid Build Coastguard Worker            error( tostring(methodName)..' must be a function, not '..type(methodInstance))
1881*387f9dfdSAndroid Build Coastguard Worker        end
1882*387f9dfdSAndroid Build Coastguard Worker
1883*387f9dfdSAndroid Build Coastguard Worker        local prettyFuncName
1884*387f9dfdSAndroid Build Coastguard Worker        if className == nil then
1885*387f9dfdSAndroid Build Coastguard Worker            className = '[TestFunctions]'
1886*387f9dfdSAndroid Build Coastguard Worker            prettyFuncName = methodName
1887*387f9dfdSAndroid Build Coastguard Worker        else
1888*387f9dfdSAndroid Build Coastguard Worker            prettyFuncName = className..'.'..methodName
1889*387f9dfdSAndroid Build Coastguard Worker        end
1890*387f9dfdSAndroid Build Coastguard Worker
1891*387f9dfdSAndroid Build Coastguard Worker        if self.lastClassName ~= className then
1892*387f9dfdSAndroid Build Coastguard Worker            if self.lastClassName ~= nil then
1893*387f9dfdSAndroid Build Coastguard Worker                self:endClass()
1894*387f9dfdSAndroid Build Coastguard Worker            end
1895*387f9dfdSAndroid Build Coastguard Worker            self:startClass( className )
1896*387f9dfdSAndroid Build Coastguard Worker            self.lastClassName = className
1897*387f9dfdSAndroid Build Coastguard Worker        end
1898*387f9dfdSAndroid Build Coastguard Worker
1899*387f9dfdSAndroid Build Coastguard Worker        self:startTest(prettyFuncName)
1900*387f9dfdSAndroid Build Coastguard Worker
1901*387f9dfdSAndroid Build Coastguard Worker        -- run setUp first (if any)
1902*387f9dfdSAndroid Build Coastguard Worker        if classInstance then
1903*387f9dfdSAndroid Build Coastguard Worker            local func = self.asFunction( classInstance.setUp )
1904*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.Setup )
1905*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.setup )
1906*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.SetUp )
1907*387f9dfdSAndroid Build Coastguard Worker            if func then
1908*387f9dfdSAndroid Build Coastguard Worker                self:addStatus(self:protectedCall(classInstance, func, className..'.setUp'))
1909*387f9dfdSAndroid Build Coastguard Worker            end
1910*387f9dfdSAndroid Build Coastguard Worker        end
1911*387f9dfdSAndroid Build Coastguard Worker
1912*387f9dfdSAndroid Build Coastguard Worker        -- run testMethod()
1913*387f9dfdSAndroid Build Coastguard Worker        if self.result.currentNode:isPassed() then
1914*387f9dfdSAndroid Build Coastguard Worker            self:addStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName))
1915*387f9dfdSAndroid Build Coastguard Worker        end
1916*387f9dfdSAndroid Build Coastguard Worker
1917*387f9dfdSAndroid Build Coastguard Worker        -- lastly, run tearDown (if any)
1918*387f9dfdSAndroid Build Coastguard Worker        if classInstance then
1919*387f9dfdSAndroid Build Coastguard Worker            local func = self.asFunction( classInstance.tearDown )
1920*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.TearDown )
1921*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.teardown )
1922*387f9dfdSAndroid Build Coastguard Worker                         or self.asFunction( classInstance.Teardown )
1923*387f9dfdSAndroid Build Coastguard Worker            if func then
1924*387f9dfdSAndroid Build Coastguard Worker                self:addStatus(self:protectedCall(classInstance, func, className..'.tearDown'))
1925*387f9dfdSAndroid Build Coastguard Worker            end
1926*387f9dfdSAndroid Build Coastguard Worker        end
1927*387f9dfdSAndroid Build Coastguard Worker
1928*387f9dfdSAndroid Build Coastguard Worker        self:endTest()
1929*387f9dfdSAndroid Build Coastguard Worker    end
1930*387f9dfdSAndroid Build Coastguard Worker
1931*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.expandOneClass( result, className, classInstance )
1932*387f9dfdSAndroid Build Coastguard Worker        -- add all test methods of classInstance to result
1933*387f9dfdSAndroid Build Coastguard Worker        for methodName, methodInstance in sortedPairs(classInstance) do
1934*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then
1935*387f9dfdSAndroid Build Coastguard Worker                table.insert( result, { className..'.'..methodName, classInstance } )
1936*387f9dfdSAndroid Build Coastguard Worker            end
1937*387f9dfdSAndroid Build Coastguard Worker        end
1938*387f9dfdSAndroid Build Coastguard Worker    end
1939*387f9dfdSAndroid Build Coastguard Worker
1940*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.expandClasses( listOfNameAndInst )
1941*387f9dfdSAndroid Build Coastguard Worker        -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance}
1942*387f9dfdSAndroid Build Coastguard Worker        -- functions and methods remain untouched
1943*387f9dfdSAndroid Build Coastguard Worker        local result = {}
1944*387f9dfdSAndroid Build Coastguard Worker
1945*387f9dfdSAndroid Build Coastguard Worker        for i,v in ipairs( listOfNameAndInst ) do
1946*387f9dfdSAndroid Build Coastguard Worker            local name, instance = v[1], v[2]
1947*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.asFunction(instance) then
1948*387f9dfdSAndroid Build Coastguard Worker                table.insert( result, { name, instance } )
1949*387f9dfdSAndroid Build Coastguard Worker            else
1950*387f9dfdSAndroid Build Coastguard Worker                if type(instance) ~= 'table' then
1951*387f9dfdSAndroid Build Coastguard Worker                    error( 'Instance must be a table or a function, not a '..type(instance)..', value '..prettystr(instance))
1952*387f9dfdSAndroid Build Coastguard Worker                end
1953*387f9dfdSAndroid Build Coastguard Worker                if M.LuaUnit.isClassMethod( name ) then
1954*387f9dfdSAndroid Build Coastguard Worker                    local className, methodName = M.LuaUnit.splitClassMethod( name )
1955*387f9dfdSAndroid Build Coastguard Worker                    local methodInstance = instance[methodName]
1956*387f9dfdSAndroid Build Coastguard Worker                    if methodInstance == nil then
1957*387f9dfdSAndroid Build Coastguard Worker                        error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) )
1958*387f9dfdSAndroid Build Coastguard Worker                    end
1959*387f9dfdSAndroid Build Coastguard Worker                    table.insert( result, { name, instance } )
1960*387f9dfdSAndroid Build Coastguard Worker                else
1961*387f9dfdSAndroid Build Coastguard Worker                    M.LuaUnit.expandOneClass( result, name, instance )
1962*387f9dfdSAndroid Build Coastguard Worker                end
1963*387f9dfdSAndroid Build Coastguard Worker            end
1964*387f9dfdSAndroid Build Coastguard Worker        end
1965*387f9dfdSAndroid Build Coastguard Worker
1966*387f9dfdSAndroid Build Coastguard Worker        return result
1967*387f9dfdSAndroid Build Coastguard Worker    end
1968*387f9dfdSAndroid Build Coastguard Worker
1969*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.applyPatternFilter( patternFilter, listOfNameAndInst )
1970*387f9dfdSAndroid Build Coastguard Worker        local included, excluded = {}, {}
1971*387f9dfdSAndroid Build Coastguard Worker        for i, v in ipairs( listOfNameAndInst ) do
1972*387f9dfdSAndroid Build Coastguard Worker            -- local name, instance = v[1], v[2]
1973*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.patternInclude( patternFilter, v[1] ) then
1974*387f9dfdSAndroid Build Coastguard Worker                table.insert( included, v )
1975*387f9dfdSAndroid Build Coastguard Worker            else
1976*387f9dfdSAndroid Build Coastguard Worker                table.insert( excluded, v )
1977*387f9dfdSAndroid Build Coastguard Worker            end
1978*387f9dfdSAndroid Build Coastguard Worker        end
1979*387f9dfdSAndroid Build Coastguard Worker        return included, excluded
1980*387f9dfdSAndroid Build Coastguard Worker    end
1981*387f9dfdSAndroid Build Coastguard Worker
1982*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:runSuiteByInstances( listOfNameAndInst )
1983*387f9dfdSAndroid Build Coastguard Worker        -- Run an explicit list of tests. All test instances and names must be supplied.
1984*387f9dfdSAndroid Build Coastguard Worker        -- each test must be one of:
1985*387f9dfdSAndroid Build Coastguard Worker        --   * { function name, function instance }
1986*387f9dfdSAndroid Build Coastguard Worker        --   * { class name, class instance }
1987*387f9dfdSAndroid Build Coastguard Worker        --   * { class.method name, class instance }
1988*387f9dfdSAndroid Build Coastguard Worker
1989*387f9dfdSAndroid Build Coastguard Worker        local expandedList, filteredList, filteredOutList, className, methodName, methodInstance
1990*387f9dfdSAndroid Build Coastguard Worker        expandedList = self.expandClasses( listOfNameAndInst )
1991*387f9dfdSAndroid Build Coastguard Worker
1992*387f9dfdSAndroid Build Coastguard Worker        filteredList, filteredOutList = self.applyPatternFilter( self.patternFilter, expandedList )
1993*387f9dfdSAndroid Build Coastguard Worker
1994*387f9dfdSAndroid Build Coastguard Worker        self:startSuite( #filteredList, #filteredOutList )
1995*387f9dfdSAndroid Build Coastguard Worker
1996*387f9dfdSAndroid Build Coastguard Worker        for i,v in ipairs( filteredList ) do
1997*387f9dfdSAndroid Build Coastguard Worker            local name, instance = v[1], v[2]
1998*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.asFunction(instance) then
1999*387f9dfdSAndroid Build Coastguard Worker                self:execOneFunction( nil, name, nil, instance )
2000*387f9dfdSAndroid Build Coastguard Worker            else
2001*387f9dfdSAndroid Build Coastguard Worker                if type(instance) ~= 'table' then
2002*387f9dfdSAndroid Build Coastguard Worker                    error( 'Instance must be a table or a function, not a '..type(instance)..', value '..prettystr(instance))
2003*387f9dfdSAndroid Build Coastguard Worker                else
2004*387f9dfdSAndroid Build Coastguard Worker                    assert( M.LuaUnit.isClassMethod( name ) )
2005*387f9dfdSAndroid Build Coastguard Worker                    className, methodName = M.LuaUnit.splitClassMethod( name )
2006*387f9dfdSAndroid Build Coastguard Worker                    methodInstance = instance[methodName]
2007*387f9dfdSAndroid Build Coastguard Worker                    if methodInstance == nil then
2008*387f9dfdSAndroid Build Coastguard Worker                        error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) )
2009*387f9dfdSAndroid Build Coastguard Worker                    end
2010*387f9dfdSAndroid Build Coastguard Worker                    self:execOneFunction( className, methodName, instance, methodInstance )
2011*387f9dfdSAndroid Build Coastguard Worker                end
2012*387f9dfdSAndroid Build Coastguard Worker            end
2013*387f9dfdSAndroid Build Coastguard Worker            if self.result.aborted then break end -- "--error" or "--failure" option triggered
2014*387f9dfdSAndroid Build Coastguard Worker        end
2015*387f9dfdSAndroid Build Coastguard Worker
2016*387f9dfdSAndroid Build Coastguard Worker        if self.lastClassName ~= nil then
2017*387f9dfdSAndroid Build Coastguard Worker            self:endClass()
2018*387f9dfdSAndroid Build Coastguard Worker        end
2019*387f9dfdSAndroid Build Coastguard Worker
2020*387f9dfdSAndroid Build Coastguard Worker        self:endSuite()
2021*387f9dfdSAndroid Build Coastguard Worker
2022*387f9dfdSAndroid Build Coastguard Worker        if self.result.aborted then
2023*387f9dfdSAndroid Build Coastguard Worker            print("LuaUnit ABORTED (as requested by --error or --failure option)")
2024*387f9dfdSAndroid Build Coastguard Worker            os.exit(-2)
2025*387f9dfdSAndroid Build Coastguard Worker        end
2026*387f9dfdSAndroid Build Coastguard Worker    end
2027*387f9dfdSAndroid Build Coastguard Worker
2028*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:runSuiteByNames( listOfName )
2029*387f9dfdSAndroid Build Coastguard Worker        -- Run an explicit list of test names
2030*387f9dfdSAndroid Build Coastguard Worker
2031*387f9dfdSAndroid Build Coastguard Worker        local  className, methodName, instanceName, instance, methodInstance
2032*387f9dfdSAndroid Build Coastguard Worker        local listOfNameAndInst = {}
2033*387f9dfdSAndroid Build Coastguard Worker
2034*387f9dfdSAndroid Build Coastguard Worker        for i,name in ipairs( listOfName ) do
2035*387f9dfdSAndroid Build Coastguard Worker            if M.LuaUnit.isClassMethod( name ) then
2036*387f9dfdSAndroid Build Coastguard Worker                className, methodName = M.LuaUnit.splitClassMethod( name )
2037*387f9dfdSAndroid Build Coastguard Worker                instanceName = className
2038*387f9dfdSAndroid Build Coastguard Worker                instance = _G[instanceName]
2039*387f9dfdSAndroid Build Coastguard Worker
2040*387f9dfdSAndroid Build Coastguard Worker                if instance == nil then
2041*387f9dfdSAndroid Build Coastguard Worker                    error( "No such name in global space: "..instanceName )
2042*387f9dfdSAndroid Build Coastguard Worker                end
2043*387f9dfdSAndroid Build Coastguard Worker
2044*387f9dfdSAndroid Build Coastguard Worker                if type(instance) ~= 'table' then
2045*387f9dfdSAndroid Build Coastguard Worker                    error( 'Instance of '..instanceName..' must be a table, not '..type(instance))
2046*387f9dfdSAndroid Build Coastguard Worker                end
2047*387f9dfdSAndroid Build Coastguard Worker
2048*387f9dfdSAndroid Build Coastguard Worker                methodInstance = instance[methodName]
2049*387f9dfdSAndroid Build Coastguard Worker                if methodInstance == nil then
2050*387f9dfdSAndroid Build Coastguard Worker                    error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) )
2051*387f9dfdSAndroid Build Coastguard Worker                end
2052*387f9dfdSAndroid Build Coastguard Worker
2053*387f9dfdSAndroid Build Coastguard Worker            else
2054*387f9dfdSAndroid Build Coastguard Worker                -- for functions and classes
2055*387f9dfdSAndroid Build Coastguard Worker                instanceName = name
2056*387f9dfdSAndroid Build Coastguard Worker                instance = _G[instanceName]
2057*387f9dfdSAndroid Build Coastguard Worker            end
2058*387f9dfdSAndroid Build Coastguard Worker
2059*387f9dfdSAndroid Build Coastguard Worker            if instance == nil then
2060*387f9dfdSAndroid Build Coastguard Worker                error( "No such name in global space: "..instanceName )
2061*387f9dfdSAndroid Build Coastguard Worker            end
2062*387f9dfdSAndroid Build Coastguard Worker
2063*387f9dfdSAndroid Build Coastguard Worker            if (type(instance) ~= 'table' and type(instance) ~= 'function') then
2064*387f9dfdSAndroid Build Coastguard Worker                error( 'Name must match a function or a table: '..instanceName )
2065*387f9dfdSAndroid Build Coastguard Worker            end
2066*387f9dfdSAndroid Build Coastguard Worker
2067*387f9dfdSAndroid Build Coastguard Worker            table.insert( listOfNameAndInst, { name, instance } )
2068*387f9dfdSAndroid Build Coastguard Worker        end
2069*387f9dfdSAndroid Build Coastguard Worker
2070*387f9dfdSAndroid Build Coastguard Worker        self:runSuiteByInstances( listOfNameAndInst )
2071*387f9dfdSAndroid Build Coastguard Worker    end
2072*387f9dfdSAndroid Build Coastguard Worker
2073*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit.run(...)
2074*387f9dfdSAndroid Build Coastguard Worker        -- Run some specific test classes.
2075*387f9dfdSAndroid Build Coastguard Worker        -- If no arguments are passed, run the class names specified on the
2076*387f9dfdSAndroid Build Coastguard Worker        -- command line. If no class name is specified on the command line
2077*387f9dfdSAndroid Build Coastguard Worker        -- run all classes whose name starts with 'Test'
2078*387f9dfdSAndroid Build Coastguard Worker        --
2079*387f9dfdSAndroid Build Coastguard Worker        -- If arguments are passed, they must be strings of the class names
2080*387f9dfdSAndroid Build Coastguard Worker        -- that you want to run or generic command line arguments (-o, -p, -v, ...)
2081*387f9dfdSAndroid Build Coastguard Worker
2082*387f9dfdSAndroid Build Coastguard Worker        local runner = M.LuaUnit.new()
2083*387f9dfdSAndroid Build Coastguard Worker        return runner:runSuite(...)
2084*387f9dfdSAndroid Build Coastguard Worker    end
2085*387f9dfdSAndroid Build Coastguard Worker
2086*387f9dfdSAndroid Build Coastguard Worker    function M.LuaUnit:runSuite( ... )
2087*387f9dfdSAndroid Build Coastguard Worker
2088*387f9dfdSAndroid Build Coastguard Worker        local args = {...}
2089*387f9dfdSAndroid Build Coastguard Worker        if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then
2090*387f9dfdSAndroid Build Coastguard Worker            -- run was called with the syntax M.LuaUnit:runSuite()
2091*387f9dfdSAndroid Build Coastguard Worker            -- we support both M.LuaUnit.run() and M.LuaUnit:run()
2092*387f9dfdSAndroid Build Coastguard Worker            -- strip out the first argument
2093*387f9dfdSAndroid Build Coastguard Worker            table.remove(args,1)
2094*387f9dfdSAndroid Build Coastguard Worker        end
2095*387f9dfdSAndroid Build Coastguard Worker
2096*387f9dfdSAndroid Build Coastguard Worker        if #args == 0 then
2097*387f9dfdSAndroid Build Coastguard Worker            args = cmdline_argv
2098*387f9dfdSAndroid Build Coastguard Worker        end
2099*387f9dfdSAndroid Build Coastguard Worker
2100*387f9dfdSAndroid Build Coastguard Worker        local no_error, val = pcall( M.LuaUnit.parseCmdLine, args )
2101*387f9dfdSAndroid Build Coastguard Worker        if not no_error then
2102*387f9dfdSAndroid Build Coastguard Worker            print(val) -- error message
2103*387f9dfdSAndroid Build Coastguard Worker            print()
2104*387f9dfdSAndroid Build Coastguard Worker            print(M.USAGE)
2105*387f9dfdSAndroid Build Coastguard Worker            os.exit(-1)
2106*387f9dfdSAndroid Build Coastguard Worker        end
2107*387f9dfdSAndroid Build Coastguard Worker
2108*387f9dfdSAndroid Build Coastguard Worker        local options = val
2109*387f9dfdSAndroid Build Coastguard Worker
2110*387f9dfdSAndroid Build Coastguard Worker        -- We expect these option fields to be either `nil` or contain
2111*387f9dfdSAndroid Build Coastguard Worker        -- valid values, so it's safe to always copy them directly.
2112*387f9dfdSAndroid Build Coastguard Worker        self.verbosity     = options.verbosity
2113*387f9dfdSAndroid Build Coastguard Worker        self.quitOnError   = options.quitOnError
2114*387f9dfdSAndroid Build Coastguard Worker        self.quitOnFailure = options.quitOnFailure
2115*387f9dfdSAndroid Build Coastguard Worker        self.fname         = options.fname
2116*387f9dfdSAndroid Build Coastguard Worker        self.patternFilter = options.pattern
2117*387f9dfdSAndroid Build Coastguard Worker
2118*387f9dfdSAndroid Build Coastguard Worker        if options.output and options.output:lower() == 'junit' and options.fname == nil then
2119*387f9dfdSAndroid Build Coastguard Worker            print('With junit output, a filename must be supplied with -n or --name')
2120*387f9dfdSAndroid Build Coastguard Worker            os.exit(-1)
2121*387f9dfdSAndroid Build Coastguard Worker        end
2122*387f9dfdSAndroid Build Coastguard Worker
2123*387f9dfdSAndroid Build Coastguard Worker        if options.output then
2124*387f9dfdSAndroid Build Coastguard Worker            no_error, val = pcall(self.setOutputType, self, options.output)
2125*387f9dfdSAndroid Build Coastguard Worker            if not no_error then
2126*387f9dfdSAndroid Build Coastguard Worker                print(val) -- error message
2127*387f9dfdSAndroid Build Coastguard Worker                print()
2128*387f9dfdSAndroid Build Coastguard Worker                print(M.USAGE)
2129*387f9dfdSAndroid Build Coastguard Worker                os.exit(-1)
2130*387f9dfdSAndroid Build Coastguard Worker            end
2131*387f9dfdSAndroid Build Coastguard Worker        end
2132*387f9dfdSAndroid Build Coastguard Worker
2133*387f9dfdSAndroid Build Coastguard Worker        self:runSuiteByNames( options.testNames or M.LuaUnit.collectTests() )
2134*387f9dfdSAndroid Build Coastguard Worker
2135*387f9dfdSAndroid Build Coastguard Worker        return self.result.notPassedCount
2136*387f9dfdSAndroid Build Coastguard Worker    end
2137*387f9dfdSAndroid Build Coastguard Worker-- class LuaUnit
2138*387f9dfdSAndroid Build Coastguard Worker
2139*387f9dfdSAndroid Build Coastguard Worker-- For compatibility with LuaUnit v2
2140*387f9dfdSAndroid Build Coastguard WorkerM.run = M.LuaUnit.run
2141*387f9dfdSAndroid Build Coastguard WorkerM.Run = M.LuaUnit.run
2142*387f9dfdSAndroid Build Coastguard Worker
2143*387f9dfdSAndroid Build Coastguard Workerfunction M:setVerbosity( verbosity )
2144*387f9dfdSAndroid Build Coastguard Worker    M.LuaUnit.verbosity = verbosity
2145*387f9dfdSAndroid Build Coastguard Workerend
2146*387f9dfdSAndroid Build Coastguard WorkerM.set_verbosity = M.setVerbosity
2147*387f9dfdSAndroid Build Coastguard WorkerM.SetVerbosity = M.setVerbosity
2148*387f9dfdSAndroid Build Coastguard Worker
2149*387f9dfdSAndroid Build Coastguard Worker
2150*387f9dfdSAndroid Build Coastguard Workerreturn M
2151