1--[[
2        luaunit.lua
3
4Description: A unit testing framework
5Homepage: https://github.com/bluebird75/luaunit
6Development by Philippe Fremy <phil@freehackers.org>
7Based on initial work of Ryu, Gwang (http://www.gpgstudy.com/gpgiki/LuaUnit)
8License: BSD License, see LICENSE.txt
9]]--
10
11require("math")
12local M={}
13
14-- private exported functions (for testing)
15M.private = {}
16
17M.VERSION='3.4-dev'
18M._VERSION=M.VERSION -- For LuaUnit v2 compatibility
19
20-- a version which distinguish between regular Lua and LuaJit
21M._LUAVERSION = (jit and jit.version) or _VERSION
22
23--[[ Some people like assertEquals( actual, expected ) and some people prefer
24assertEquals( expected, actual ).
25]]--
26M.ORDER_ACTUAL_EXPECTED = true
27M.PRINT_TABLE_REF_IN_ERROR_MSG = false
28M.LINE_LENGTH = 80
29M.TABLE_DIFF_ANALYSIS_THRESHOLD = 10    -- display deep analysis for more than 10 items
30M.LIST_DIFF_ANALYSIS_THRESHOLD  = 10    -- display deep analysis for more than 10 items
31
32--[[ EPS is meant to help with Lua's floating point math in simple corner
33cases like almostEquals(1.1-0.1, 1), which may not work as-is (e.g. on numbers
34with rational binary representation) if the user doesn't provide some explicit
35error margin.
36
37The default margin used by almostEquals() in such cases is EPS; and since
38Lua may be compiled with different numeric precisions (single vs. double), we
39try to select a useful default for it dynamically. Note: If the initial value
40is not acceptable, it can be changed by the user to better suit specific needs.
41
42See also: https://en.wikipedia.org/wiki/Machine_epsilon
43]]
44M.EPS = 2^-52 -- = machine epsilon for "double", ~2.22E-16
45if math.abs(1.1 - 1 - 0.1) > M.EPS then
46    -- rounding error is above EPS, assume single precision
47    M.EPS = 2^-23 -- = machine epsilon for "float", ~1.19E-07
48end
49
50-- set this to false to debug luaunit
51local STRIP_LUAUNIT_FROM_STACKTRACE = true
52
53M.VERBOSITY_DEFAULT = 10
54M.VERBOSITY_LOW     = 1
55M.VERBOSITY_QUIET   = 0
56M.VERBOSITY_VERBOSE = 20
57M.DEFAULT_DEEP_ANALYSIS = nil
58M.FORCE_DEEP_ANALYSIS   = true
59M.DISABLE_DEEP_ANALYSIS = false
60
61-- set EXPORT_ASSERT_TO_GLOBALS to have all asserts visible as global values
62-- EXPORT_ASSERT_TO_GLOBALS = true
63
64-- we need to keep a copy of the script args before it is overriden
65local cmdline_argv = rawget(_G, "arg")
66
67M.FAILURE_PREFIX = 'LuaUnit test FAILURE: ' -- prefix string for failed tests
68M.SUCCESS_PREFIX = 'LuaUnit test SUCCESS: ' -- prefix string for successful tests finished early
69M.SKIP_PREFIX    = 'LuaUnit test SKIP:    ' -- prefix string for skipped tests
70
71
72
73M.USAGE=[[Usage: lua <your_test_suite.lua> [options] [testname1 [testname2] ... ]
74Options:
75  -h, --help:             Print this help
76  --version:              Print version information
77  -v, --verbose:          Increase verbosity
78  -q, --quiet:            Set verbosity to minimum
79  -e, --error:            Stop on first error
80  -f, --failure:          Stop on first failure or error
81  -s, --shuffle:          Shuffle tests before running them
82  -o, --output OUTPUT:    Set output type to OUTPUT
83                          Possible values: text, tap, junit, nil
84  -n, --name NAME:        For junit only, mandatory name of xml file
85  -r, --repeat NUM:       Execute all tests NUM times, e.g. to trig the JIT
86  -p, --pattern PATTERN:  Execute all test names matching the Lua PATTERN
87                          May be repeated to include several patterns
88                          Make sure you escape magic chars like +? with %
89  -x, --exclude PATTERN:  Exclude all test names matching the Lua PATTERN
90                          May be repeated to exclude several patterns
91                          Make sure you escape magic chars like +? with %
92  testname1, testname2, ... : tests to run in the form of testFunction,
93                              TestClass or TestClass.testMethod
94]]
95
96local is_equal -- defined here to allow calling from mismatchFormattingPureList
97
98----------------------------------------------------------------
99--
100--                 general utility functions
101--
102----------------------------------------------------------------
103
104local function pcall_or_abort(func, ...)
105    -- unpack is a global function for Lua 5.1, otherwise use table.unpack
106    local unpack = rawget(_G, "unpack") or table.unpack
107    local result = {pcall(func, ...)}
108    if not result[1] then
109        -- an error occurred
110        print(result[2]) -- error message
111        print()
112        print(M.USAGE)
113        os.exit(-1)
114    end
115    return unpack(result, 2)
116end
117
118local crossTypeOrdering = {
119    number = 1, boolean = 2, string = 3, table = 4, other = 5
120}
121local crossTypeComparison = {
122    number = function(a, b) return a < b end,
123    string = function(a, b) return a < b end,
124    other = function(a, b) return tostring(a) < tostring(b) end,
125}
126
127local function crossTypeSort(a, b)
128    local type_a, type_b = type(a), type(b)
129    if type_a == type_b then
130        local func = crossTypeComparison[type_a] or crossTypeComparison.other
131        return func(a, b)
132    end
133    type_a = crossTypeOrdering[type_a] or crossTypeOrdering.other
134    type_b = crossTypeOrdering[type_b] or crossTypeOrdering.other
135    return type_a < type_b
136end
137
138local function __genSortedIndex( t )
139    -- Returns a sequence consisting of t's keys, sorted.
140    local sortedIndex = {}
141
142    for key,_ in pairs(t) do
143        table.insert(sortedIndex, key)
144    end
145
146    table.sort(sortedIndex, crossTypeSort)
147    return sortedIndex
148end
149M.private.__genSortedIndex = __genSortedIndex
150
151local function sortedNext(state, control)
152    -- Equivalent of the next() function of table iteration, but returns the
153    -- keys in sorted order (see __genSortedIndex and crossTypeSort).
154    -- The state is a temporary variable during iteration and contains the
155    -- sorted key table (state.sortedIdx). It also stores the last index (into
156    -- the keys) used by the iteration, to find the next one quickly.
157    local key
158
159    --print("sortedNext: control = "..tostring(control) )
160    if control == nil then
161        -- start of iteration
162        state.count = #state.sortedIdx
163        state.lastIdx = 1
164        key = state.sortedIdx[1]
165        return key, state.t[key]
166    end
167
168    -- normally, we expect the control variable to match the last key used
169    if control ~= state.sortedIdx[state.lastIdx] then
170        -- strange, we have to find the next value by ourselves
171        -- the key table is sorted in crossTypeSort() order! -> use bisection
172        local lower, upper = 1, state.count
173        repeat
174            state.lastIdx = math.modf((lower + upper) / 2)
175            key = state.sortedIdx[state.lastIdx]
176            if key == control then
177                break -- key found (and thus prev index)
178            end
179            if crossTypeSort(key, control) then
180                -- key < control, continue search "right" (towards upper bound)
181                lower = state.lastIdx + 1
182            else
183                -- key > control, continue search "left" (towards lower bound)
184                upper = state.lastIdx - 1
185            end
186        until lower > upper
187        if lower > upper then -- only true if the key wasn't found, ...
188            state.lastIdx = state.count -- ... so ensure no match in code below
189        end
190    end
191
192    -- proceed by retrieving the next value (or nil) from the sorted keys
193    state.lastIdx = state.lastIdx + 1
194    key = state.sortedIdx[state.lastIdx]
195    if key then
196        return key, state.t[key]
197    end
198
199    -- getting here means returning `nil`, which will end the iteration
200end
201
202local function sortedPairs(tbl)
203    -- Equivalent of the pairs() function on tables. Allows to iterate in
204    -- sorted order. As required by "generic for" loops, this will return the
205    -- iterator (function), an "invariant state", and the initial control value.
206    -- (see http://www.lua.org/pil/7.2.html)
207    return sortedNext, {t = tbl, sortedIdx = __genSortedIndex(tbl)}, nil
208end
209M.private.sortedPairs = sortedPairs
210
211-- seed the random with a strongly varying seed
212math.randomseed(math.floor(os.clock()*1E11))
213
214local function randomizeTable( t )
215    -- randomize the item orders of the table t
216    for i = #t, 2, -1 do
217        local j = math.random(i)
218        if i ~= j then
219            t[i], t[j] = t[j], t[i]
220        end
221    end
222end
223M.private.randomizeTable = randomizeTable
224
225local function strsplit(delimiter, text)
226-- Split text into a list consisting of the strings in text, separated
227-- by strings matching delimiter (which may _NOT_ be a pattern).
228-- Example: strsplit(", ", "Anna, Bob, Charlie, Dolores")
229    if delimiter == "" or delimiter == nil then -- this would result in endless loops
230        error("delimiter is nil or empty string!")
231    end
232    if text == nil then
233        return nil
234    end
235
236    local list, pos, first, last = {}, 1
237    while true do
238        first, last = text:find(delimiter, pos, true)
239        if first then -- found?
240            table.insert(list, text:sub(pos, first - 1))
241            pos = last + 1
242        else
243            table.insert(list, text:sub(pos))
244            break
245        end
246    end
247    return list
248end
249M.private.strsplit = strsplit
250
251local function hasNewLine( s )
252    -- return true if s has a newline
253    return (string.find(s, '\n', 1, true) ~= nil)
254end
255M.private.hasNewLine = hasNewLine
256
257local function prefixString( prefix, s )
258    -- Prefix all the lines of s with prefix
259    return prefix .. string.gsub(s, '\n', '\n' .. prefix)
260end
261M.private.prefixString = prefixString
262
263local function strMatch(s, pattern, start, final )
264    -- return true if s matches completely the pattern from index start to index end
265    -- return false in every other cases
266    -- if start is nil, matches from the beginning of the string
267    -- if final is nil, matches to the end of the string
268    start = start or 1
269    final = final or string.len(s)
270
271    local foundStart, foundEnd = string.find(s, pattern, start, false)
272    return foundStart == start and foundEnd == final
273end
274M.private.strMatch = strMatch
275
276local function patternFilter(patterns, expr)
277    -- Run `expr` through the inclusion and exclusion rules defined in patterns
278    -- and return true if expr shall be included, false for excluded.
279    -- Inclusion pattern are defined as normal patterns, exclusions
280    -- patterns start with `!` and are followed by a normal pattern
281
282    -- result: nil = UNKNOWN (not matched yet), true = ACCEPT, false = REJECT
283    -- default: true if no explicit "include" is found, set to false otherwise
284    local default, result = true, nil
285
286    if patterns ~= nil then
287        for _, pattern in ipairs(patterns) do
288            local exclude = pattern:sub(1,1) == '!'
289            if exclude then
290                pattern = pattern:sub(2)
291            else
292                -- at least one include pattern specified, a match is required
293                default = false
294            end
295            -- print('pattern: ',pattern)
296            -- print('exclude: ',exclude)
297            -- print('default: ',default)
298
299            if string.find(expr, pattern) then
300                -- set result to false when excluding, true otherwise
301                result = not exclude
302            end
303        end
304    end
305
306    if result ~= nil then
307        return result
308    end
309    return default
310end
311M.private.patternFilter = patternFilter
312
313local function xmlEscape( s )
314    -- Return s escaped for XML attributes
315    -- escapes table:
316    -- "   &quot;
317    -- '   &apos;
318    -- <   &lt;
319    -- >   &gt;
320    -- &   &amp;
321
322    return string.gsub( s, '.', {
323        ['&'] = "&amp;",
324        ['"'] = "&quot;",
325        ["'"] = "&apos;",
326        ['<'] = "&lt;",
327        ['>'] = "&gt;",
328    } )
329end
330M.private.xmlEscape = xmlEscape
331
332local function xmlCDataEscape( s )
333    -- Return s escaped for CData section, escapes: "]]>"
334    return string.gsub( s, ']]>', ']]&gt;' )
335end
336M.private.xmlCDataEscape = xmlCDataEscape
337
338local function stripLuaunitTrace( stackTrace )
339    --[[
340    -- Example of  a traceback:
341    <<stack traceback:
342        example_with_luaunit.lua:130: in function 'test2_withFailure'
343        ./luaunit.lua:1449: in function <./luaunit.lua:1449>
344        [C]: in function 'xpcall'
345        ./luaunit.lua:1449: in function 'protectedCall'
346        ./luaunit.lua:1508: in function 'execOneFunction'
347        ./luaunit.lua:1596: in function 'runSuiteByInstances'
348        ./luaunit.lua:1660: in function 'runSuiteByNames'
349        ./luaunit.lua:1736: in function 'runSuite'
350        example_with_luaunit.lua:140: in main chunk
351        [C]: in ?>>
352
353        Other example:
354    <<stack traceback:
355        ./luaunit.lua:545: in function 'assertEquals'
356        example_with_luaunit.lua:58: in function 'TestToto.test7'
357        ./luaunit.lua:1517: in function <./luaunit.lua:1517>
358        [C]: in function 'xpcall'
359        ./luaunit.lua:1517: in function 'protectedCall'
360        ./luaunit.lua:1578: in function 'execOneFunction'
361        ./luaunit.lua:1677: in function 'runSuiteByInstances'
362        ./luaunit.lua:1730: in function 'runSuiteByNames'
363        ./luaunit.lua:1806: in function 'runSuite'
364        example_with_luaunit.lua:140: in main chunk
365        [C]: in ?>>
366
367    <<stack traceback:
368        luaunit2/example_with_luaunit.lua:124: in function 'test1_withFailure'
369        luaunit2/luaunit.lua:1532: in function <luaunit2/luaunit.lua:1532>
370        [C]: in function 'xpcall'
371        luaunit2/luaunit.lua:1532: in function 'protectedCall'
372        luaunit2/luaunit.lua:1591: in function 'execOneFunction'
373        luaunit2/luaunit.lua:1679: in function 'runSuiteByInstances'
374        luaunit2/luaunit.lua:1743: in function 'runSuiteByNames'
375        luaunit2/luaunit.lua:1819: in function 'runSuite'
376        luaunit2/example_with_luaunit.lua:140: in main chunk
377        [C]: in ?>>
378
379
380    -- first line is "stack traceback": KEEP
381    -- next line may be luaunit line: REMOVE
382    -- next lines are call in the program under testOk: REMOVE
383    -- next lines are calls from luaunit to call the program under test: KEEP
384
385    -- Strategy:
386    -- keep first line
387    -- remove lines that are part of luaunit
388    -- kepp lines until we hit a luaunit line
389    ]]
390
391    local function isLuaunitInternalLine( s )
392        -- return true if line of stack trace comes from inside luaunit
393        return s:find('[/\\]luaunit%.lua:%d+: ') ~= nil
394    end
395
396    -- print( '<<'..stackTrace..'>>' )
397
398    local t = strsplit( '\n', stackTrace )
399    -- print( prettystr(t) )
400
401    local idx = 2
402
403    -- remove lines that are still part of luaunit
404    while t[idx] and isLuaunitInternalLine( t[idx] ) do
405        -- print('Removing : '..t[idx] )
406        table.remove(t, idx)
407    end
408
409    -- keep lines until we hit luaunit again
410    while t[idx] and (not isLuaunitInternalLine(t[idx])) do
411        -- print('Keeping : '..t[idx] )
412        idx = idx + 1
413    end
414
415    -- remove remaining luaunit lines
416    while t[idx] do
417        -- print('Removing : '..t[idx] )
418        table.remove(t, idx)
419    end
420
421    -- print( prettystr(t) )
422    return table.concat( t, '\n')
423
424end
425M.private.stripLuaunitTrace = stripLuaunitTrace
426
427
428local function prettystr_sub(v, indentLevel, printTableRefs, cycleDetectTable )
429    local type_v = type(v)
430    if "string" == type_v  then
431        -- use clever delimiters according to content:
432        -- enclose with single quotes if string contains ", but no '
433        if v:find('"', 1, true) and not v:find("'", 1, true) then
434            return "'" .. v .. "'"
435        end
436        -- use double quotes otherwise, escape embedded "
437        return '"' .. v:gsub('"', '\\"') .. '"'
438
439    elseif "table" == type_v then
440        --if v.__class__ then
441        --    return string.gsub( tostring(v), 'table', v.__class__ )
442        --end
443        return M.private._table_tostring(v, indentLevel, printTableRefs, cycleDetectTable)
444
445    elseif "number" == type_v then
446        -- eliminate differences in formatting between various Lua versions
447        if v ~= v then
448            return "#NaN" -- "not a number"
449        end
450        if v == math.huge then
451            return "#Inf" -- "infinite"
452        end
453        if v == -math.huge then
454            return "-#Inf"
455        end
456        if _VERSION == "Lua 5.3" then
457            local i = math.tointeger(v)
458            if i then
459                return tostring(i)
460            end
461        end
462    end
463
464    return tostring(v)
465end
466
467local function prettystr( v )
468    --[[ Pretty string conversion, to display the full content of a variable of any type.
469
470    * string are enclosed with " by default, or with ' if string contains a "
471    * tables are expanded to show their full content, with indentation in case of nested tables
472    ]]--
473    local cycleDetectTable = {}
474    local s = prettystr_sub(v, 1, M.PRINT_TABLE_REF_IN_ERROR_MSG, cycleDetectTable)
475    if cycleDetectTable.detected and not M.PRINT_TABLE_REF_IN_ERROR_MSG then
476        -- some table contain recursive references,
477        -- so we must recompute the value by including all table references
478        -- else the result looks like crap
479        cycleDetectTable = {}
480        s = prettystr_sub(v, 1, true, cycleDetectTable)
481    end
482    return s
483end
484M.prettystr = prettystr
485
486function M.adjust_err_msg_with_iter( err_msg, iter_msg )
487    --[[ Adjust the error message err_msg: trim the FAILURE_PREFIX or SUCCESS_PREFIX information if needed,
488    add the iteration message if any and return the result.
489
490    err_msg:  string, error message captured with pcall
491    iter_msg: a string describing the current iteration ("iteration N") or nil
492              if there is no iteration in this test.
493
494    Returns: (new_err_msg, test_status)
495        new_err_msg: string, adjusted error message, or nil in case of success
496        test_status: M.NodeStatus.FAIL, SUCCESS or ERROR according to the information
497                     contained in the error message.
498    ]]
499    if iter_msg then
500        iter_msg = iter_msg..', '
501    else
502        iter_msg = ''
503    end
504
505    local RE_FILE_LINE = '.*:%d+: '
506
507    -- error message is not necessarily a string,
508    -- so convert the value to string with prettystr()
509    if type( err_msg ) ~= 'string' then
510        err_msg = prettystr( err_msg )
511    end
512
513    if (err_msg:find( M.SUCCESS_PREFIX ) == 1) or err_msg:match( '('..RE_FILE_LINE..')' .. M.SUCCESS_PREFIX .. ".*" ) then
514        -- test finished early with success()
515        return nil, M.NodeStatus.SUCCESS
516    end
517
518    if (err_msg:find( M.SKIP_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.SKIP_PREFIX .. ".*" ) ~= nil) then
519        -- substitute prefix by iteration message
520        err_msg = err_msg:gsub('.*'..M.SKIP_PREFIX, iter_msg, 1)
521        -- print("failure detected")
522        return err_msg, M.NodeStatus.SKIP
523    end
524
525    if (err_msg:find( M.FAILURE_PREFIX ) == 1) or (err_msg:match( '('..RE_FILE_LINE..')' .. M.FAILURE_PREFIX .. ".*" ) ~= nil) then
526        -- substitute prefix by iteration message
527        err_msg = err_msg:gsub(M.FAILURE_PREFIX, iter_msg, 1)
528        -- print("failure detected")
529        return err_msg, M.NodeStatus.FAIL
530    end
531
532
533
534    -- print("error detected")
535    -- regular error, not a failure
536    if iter_msg then
537        local match
538        -- "./test\\test_luaunit.lua:2241: some error msg
539        match = err_msg:match( '(.*:%d+: ).*' )
540        if match then
541            err_msg = err_msg:gsub( match, match .. iter_msg )
542        else
543            -- no file:line: infromation, just add the iteration info at the beginning of the line
544            err_msg = iter_msg .. err_msg
545        end
546    end
547    return err_msg, M.NodeStatus.ERROR
548end
549
550local function tryMismatchFormatting( table_a, table_b, doDeepAnalysis )
551    --[[
552    Prepares a nice error message when comparing tables, performing a deeper
553    analysis.
554
555    Arguments:
556    * table_a, table_b: tables to be compared
557    * doDeepAnalysis:
558        M.DEFAULT_DEEP_ANALYSIS: (the default if not specified) perform deep analysis only for big lists and big dictionnaries
559        M.FORCE_DEEP_ANALYSIS  : always perform deep analysis
560        M.DISABLE_DEEP_ANALYSIS: never perform deep analysis
561
562    Returns: {success, result}
563    * success: false if deep analysis could not be performed
564               in this case, just use standard assertion message
565    * result: if success is true, a multi-line string with deep analysis of the two lists
566    ]]
567
568    -- check if table_a & table_b are suitable for deep analysis
569    if type(table_a) ~= 'table' or type(table_b) ~= 'table' then
570        return false
571    end
572
573    if doDeepAnalysis == M.DISABLE_DEEP_ANALYSIS then
574        return false
575    end
576
577    local len_a, len_b, isPureList = #table_a, #table_b, true
578
579    for k1, v1 in pairs(table_a) do
580        if type(k1) ~= 'number' or k1 > len_a then
581            -- this table a mapping
582            isPureList = false
583            break
584        end
585    end
586
587    if isPureList then
588        for k2, v2 in pairs(table_b) do
589            if type(k2) ~= 'number' or k2 > len_b then
590                -- this table a mapping
591                isPureList = false
592                break
593            end
594        end
595    end
596
597    if isPureList and math.min(len_a, len_b) < M.LIST_DIFF_ANALYSIS_THRESHOLD then
598        if not (doDeepAnalysis == M.FORCE_DEEP_ANALYSIS) then
599            return false
600        end
601    end
602
603    if isPureList then
604        return M.private.mismatchFormattingPureList( table_a, table_b )
605    else
606        -- only work on mapping for the moment
607        -- return M.private.mismatchFormattingMapping( table_a, table_b, doDeepAnalysis )
608        return false
609    end
610end
611M.private.tryMismatchFormatting = tryMismatchFormatting
612
613local function getTaTbDescr()
614    if not M.ORDER_ACTUAL_EXPECTED then
615        return 'expected', 'actual'
616    end
617    return 'actual', 'expected'
618end
619
620local function extendWithStrFmt( res, ... )
621    table.insert( res, string.format( ... ) )
622end
623
624local function mismatchFormattingMapping( table_a, table_b, doDeepAnalysis )
625    --[[
626    Prepares a nice error message when comparing tables which are not pure lists, performing a deeper
627    analysis.
628
629    Returns: {success, result}
630    * success: false if deep analysis could not be performed
631               in this case, just use standard assertion message
632    * result: if success is true, a multi-line string with deep analysis of the two lists
633    ]]
634
635    -- disable for the moment
636    --[[
637    local result = {}
638    local descrTa, descrTb = getTaTbDescr()
639
640    local keysCommon = {}
641    local keysOnlyTa = {}
642    local keysOnlyTb = {}
643    local keysDiffTaTb = {}
644
645    local k, v
646
647    for k,v in pairs( table_a ) do
648        if is_equal( v, table_b[k] ) then
649            table.insert( keysCommon, k )
650        else
651            if table_b[k] == nil then
652                table.insert( keysOnlyTa, k )
653            else
654                table.insert( keysDiffTaTb, k )
655            end
656        end
657    end
658
659    for k,v in pairs( table_b ) do
660        if not is_equal( v, table_a[k] ) and table_a[k] == nil then
661            table.insert( keysOnlyTb, k )
662        end
663    end
664
665    local len_a = #keysCommon + #keysDiffTaTb + #keysOnlyTa
666    local len_b = #keysCommon + #keysDiffTaTb + #keysOnlyTb
667    local limited_display = (len_a < 5 or len_b < 5)
668
669    if math.min(len_a, len_b) < M.TABLE_DIFF_ANALYSIS_THRESHOLD then
670        return false
671    end
672
673    if not limited_display then
674        if len_a == len_b then
675            extendWithStrFmt( result, 'Table A (%s) and B (%s) both have %d items', descrTa, descrTb, len_a )
676        else
677            extendWithStrFmt( result, 'Table A (%s) has %d items and table B (%s) has %d items', descrTa, len_a, descrTb, len_b )
678            end
679
680        if #keysCommon == 0 and #keysDiffTaTb == 0 then
681            table.insert( result, 'Table A and B have no keys in common, they are totally different')
682        else
683            local s_other = 'other '
684            if #keysCommon then
685                extendWithStrFmt( result, 'Table A and B have %d identical items', #keysCommon )
686            else
687                table.insert( result, 'Table A and B have no identical items' )
688                s_other = ''
689            end
690
691            if #keysDiffTaTb ~= 0 then
692                result[#result] = string.format( '%s and %d items differing present in both tables', result[#result], #keysDiffTaTb)
693            else
694                result[#result] = string.format( '%s and no %sitems differing present in both tables', result[#result], s_other, #keysDiffTaTb)
695            end
696        end
697
698        extendWithStrFmt( result, 'Table A has %d keys not present in table B and table B has %d keys not present in table A', #keysOnlyTa, #keysOnlyTb )
699    end
700
701    local function keytostring(k)
702        if "string" == type(k) and k:match("^[_%a][_%w]*$") then
703            return k
704        end
705        return prettystr(k)
706    end
707
708    if #keysDiffTaTb ~= 0 then
709        table.insert( result, 'Items differing in A and B:')
710        for k,v in sortedPairs( keysDiffTaTb ) do
711            extendWithStrFmt( result, '  - A[%s]: %s', keytostring(v), prettystr(table_a[v]) )
712            extendWithStrFmt( result, '  + B[%s]: %s', keytostring(v), prettystr(table_b[v]) )
713        end
714    end
715
716    if #keysOnlyTa ~= 0 then
717        table.insert( result, 'Items only in table A:' )
718        for k,v in sortedPairs( keysOnlyTa ) do
719            extendWithStrFmt( result, '  - A[%s]: %s', keytostring(v), prettystr(table_a[v]) )
720        end
721    end
722
723    if #keysOnlyTb ~= 0 then
724        table.insert( result, 'Items only in table B:' )
725        for k,v in sortedPairs( keysOnlyTb ) do
726            extendWithStrFmt( result, '  + B[%s]: %s', keytostring(v), prettystr(table_b[v]) )
727        end
728    end
729
730    if #keysCommon ~= 0 then
731        table.insert( result, 'Items common to A and B:')
732        for k,v in sortedPairs( keysCommon ) do
733            extendWithStrFmt( result, '  = A and B [%s]: %s', keytostring(v), prettystr(table_a[v]) )
734        end
735    end
736
737    return true, table.concat( result, '\n')
738    ]]
739end
740M.private.mismatchFormattingMapping = mismatchFormattingMapping
741
742local function mismatchFormattingPureList( table_a, table_b )
743    --[[
744    Prepares a nice error message when comparing tables which are lists, performing a deeper
745    analysis.
746
747    Returns: {success, result}
748    * success: false if deep analysis could not be performed
749               in this case, just use standard assertion message
750    * result: if success is true, a multi-line string with deep analysis of the two lists
751    ]]
752    local result, descrTa, descrTb = {}, getTaTbDescr()
753
754    local len_a, len_b, refa, refb = #table_a, #table_b, '', ''
755    if M.PRINT_TABLE_REF_IN_ERROR_MSG then
756        refa, refb = string.format( '<%s> ', M.private.table_ref(table_a)), string.format('<%s> ', M.private.table_ref(table_b) )
757    end
758    local longest, shortest = math.max(len_a, len_b), math.min(len_a, len_b)
759    local deltalv  = longest - shortest
760
761    local commonUntil = shortest
762    for i = 1, shortest do
763        if not is_equal(table_a[i], table_b[i]) then
764            commonUntil = i - 1
765            break
766        end
767    end
768
769    local commonBackTo = shortest - 1
770    for i = 0, shortest - 1 do
771        if not is_equal(table_a[len_a-i], table_b[len_b-i]) then
772            commonBackTo = i - 1
773            break
774        end
775    end
776
777
778    table.insert( result, 'List difference analysis:' )
779    if len_a == len_b then
780        -- TODO: handle expected/actual naming
781        extendWithStrFmt( result, '* lists %sA (%s) and %sB (%s) have the same size', refa, descrTa, refb, descrTb )
782    else
783        extendWithStrFmt( result, '* list sizes differ: list %sA (%s) has %d items, list %sB (%s) has %d items', refa, descrTa, len_a, refb, descrTb, len_b )
784    end
785
786    extendWithStrFmt( result, '* lists A and B start differing at index %d', commonUntil+1 )
787    if commonBackTo >= 0 then
788        if deltalv > 0 then
789            extendWithStrFmt( result, '* lists A and B are equal again from index %d for A, %d for B', len_a-commonBackTo, len_b-commonBackTo )
790        else
791            extendWithStrFmt( result, '* lists A and B are equal again from index %d', len_a-commonBackTo )
792        end
793    end
794
795    local function insertABValue(ai, bi)
796        bi = bi or ai
797        if is_equal( table_a[ai], table_b[bi]) then
798            return extendWithStrFmt( result, '  = A[%d], B[%d]: %s', ai, bi, prettystr(table_a[ai]) )
799        else
800            extendWithStrFmt( result, '  - A[%d]: %s', ai, prettystr(table_a[ai]))
801            extendWithStrFmt( result, '  + B[%d]: %s', bi, prettystr(table_b[bi]))
802        end
803    end
804
805    -- common parts to list A & B, at the beginning
806    if commonUntil > 0 then
807        table.insert( result, '* Common parts:' )
808        for i = 1, commonUntil do
809            insertABValue( i )
810        end
811    end
812
813    -- diffing parts to list A & B
814    if commonUntil < shortest - commonBackTo - 1 then
815        table.insert( result, '* Differing parts:' )
816        for i = commonUntil + 1, shortest - commonBackTo - 1 do
817            insertABValue( i )
818        end
819    end
820
821    -- display indexes of one list, with no match on other list
822    if shortest - commonBackTo <= longest - commonBackTo - 1 then
823        table.insert( result, '* Present only in one list:' )
824        for i = shortest - commonBackTo, longest - commonBackTo - 1 do
825            if len_a > len_b then
826                extendWithStrFmt( result, '  - A[%d]: %s', i, prettystr(table_a[i]) )
827                -- table.insert( result, '+ (no matching B index)')
828            else
829                -- table.insert( result, '- no matching A index')
830                extendWithStrFmt( result, '  + B[%d]: %s', i, prettystr(table_b[i]) )
831            end
832        end
833    end
834
835    -- common parts to list A & B, at the end
836    if commonBackTo >= 0 then
837        table.insert( result, '* Common parts at the end of the lists' )
838        for i = longest - commonBackTo, longest do
839            if len_a > len_b then
840                insertABValue( i, i-deltalv )
841            else
842                insertABValue( i-deltalv, i )
843            end
844        end
845    end
846
847    return true, table.concat( result, '\n')
848end
849M.private.mismatchFormattingPureList = mismatchFormattingPureList
850
851local function prettystrPairs(value1, value2, suffix_a, suffix_b)
852    --[[
853    This function helps with the recurring task of constructing the "expected
854    vs. actual" error messages. It takes two arbitrary values and formats
855    corresponding strings with prettystr().
856
857    To keep the (possibly complex) output more readable in case the resulting
858    strings contain line breaks, they get automatically prefixed with additional
859    newlines. Both suffixes are optional (default to empty strings), and get
860    appended to the "value1" string. "suffix_a" is used if line breaks were
861    encountered, "suffix_b" otherwise.
862
863    Returns the two formatted strings (including padding/newlines).
864    ]]
865    local str1, str2 = prettystr(value1), prettystr(value2)
866    if hasNewLine(str1) or hasNewLine(str2) then
867        -- line break(s) detected, add padding
868        return "\n" .. str1 .. (suffix_a or ""), "\n" .. str2
869    end
870    return str1 .. (suffix_b or ""), str2
871end
872M.private.prettystrPairs = prettystrPairs
873
874local UNKNOWN_REF = 'table 00-unknown ref'
875local ref_generator = { value=1, [UNKNOWN_REF]=0 }
876
877local function table_ref( t )
878    -- return the default tostring() for tables, with the table ID, even if the table has a metatable
879    -- with the __tostring converter
880    local ref = ''
881    local mt = getmetatable( t )
882    if mt == nil then
883        ref = tostring(t)
884    else
885        local success, result
886        success, result = pcall(setmetatable, t, nil)
887        if not success then
888            -- protected table, if __tostring is defined, we can
889            -- not get the reference. And we can not know in advance.
890            ref = tostring(t)
891            if not ref:match( 'table: 0?x?[%x]+' ) then
892                return UNKNOWN_REF
893            end
894        else
895            ref = tostring(t)
896            setmetatable( t, mt )
897        end
898    end
899    -- strip the "table: " part
900    ref = ref:sub(8)
901    if ref ~= UNKNOWN_REF and ref_generator[ref] == nil then
902        -- Create a new reference number
903        ref_generator[ref] = ref_generator.value
904        ref_generator.value = ref_generator.value+1
905    end
906    if M.PRINT_TABLE_REF_IN_ERROR_MSG then
907        return string.format('table %02d-%s', ref_generator[ref], ref)
908    else
909        return string.format('table %02d', ref_generator[ref])
910    end
911end
912M.private.table_ref = table_ref
913
914local TABLE_TOSTRING_SEP = ", "
915local TABLE_TOSTRING_SEP_LEN = string.len(TABLE_TOSTRING_SEP)
916
917local function _table_tostring( tbl, indentLevel, printTableRefs, cycleDetectTable )
918    printTableRefs = printTableRefs or M.PRINT_TABLE_REF_IN_ERROR_MSG
919    cycleDetectTable = cycleDetectTable or {}
920    cycleDetectTable[tbl] = true
921
922    local result, dispOnMultLines = {}, false
923
924    -- like prettystr but do not enclose with "" if the string is just alphanumerical
925    -- this is better for displaying table keys who are often simple strings
926    local function keytostring(k)
927        if "string" == type(k) and k:match("^[_%a][_%w]*$") then
928            return k
929        end
930        return prettystr_sub(k, indentLevel+1, printTableRefs, cycleDetectTable)
931    end
932
933    local mt = getmetatable( tbl )
934
935    if mt and mt.__tostring then
936        -- if table has a __tostring() function in its metatable, use it to display the table
937        -- else, compute a regular table
938        result = tostring(tbl)
939        if type(result) ~= 'string' then
940            return string.format( '<invalid tostring() result: "%s" >', prettystr(result) )
941        end
942        result = strsplit( '\n', result )
943        return M.private._table_tostring_format_multiline_string( result, indentLevel )
944
945    else
946        -- no metatable, compute the table representation
947
948        local entry, count, seq_index = nil, 0, 1
949        for k, v in sortedPairs( tbl ) do
950
951            -- key part
952            if k == seq_index then
953                -- for the sequential part of tables, we'll skip the "<key>=" output
954                entry = ''
955                seq_index = seq_index + 1
956            elseif cycleDetectTable[k] then
957                -- recursion in the key detected
958                cycleDetectTable.detected = true
959                entry = "<"..table_ref(k)..">="
960            else
961                entry = keytostring(k) .. "="
962            end
963
964            -- value part
965            if cycleDetectTable[v] then
966                -- recursion in the value detected!
967                cycleDetectTable.detected = true
968                entry = entry .. "<"..table_ref(v)..">"
969            else
970                entry = entry ..
971                    prettystr_sub( v, indentLevel+1, printTableRefs, cycleDetectTable )
972            end
973            count = count + 1
974            result[count] = entry
975        end
976        return M.private._table_tostring_format_result( tbl, result, indentLevel, printTableRefs )
977    end
978
979end
980M.private._table_tostring = _table_tostring -- prettystr_sub() needs it
981
982local function _table_tostring_format_multiline_string( tbl_str, indentLevel )
983    local indentString = '\n'..string.rep("    ", indentLevel - 1)
984    return table.concat( tbl_str, indentString )
985
986end
987M.private._table_tostring_format_multiline_string = _table_tostring_format_multiline_string
988
989
990local function _table_tostring_format_result( tbl, result, indentLevel, printTableRefs )
991    -- final function called in _table_to_string() to format the resulting list of
992    -- string describing the table.
993
994    local dispOnMultLines = false
995
996    -- set dispOnMultLines to true if the maximum LINE_LENGTH would be exceeded with the values
997    local totalLength = 0
998    for k, v in ipairs( result ) do
999        totalLength = totalLength + string.len( v )
1000        if totalLength >= M.LINE_LENGTH then
1001            dispOnMultLines = true
1002            break
1003        end
1004    end
1005
1006    -- set dispOnMultLines to true if the max LINE_LENGTH would be exceeded
1007    -- with the values and the separators.
1008    if not dispOnMultLines then
1009        -- adjust with length of separator(s):
1010        -- two items need 1 sep, three items two seps, ... plus len of '{}'
1011        if #result > 0 then
1012            totalLength = totalLength + TABLE_TOSTRING_SEP_LEN * (#result - 1)
1013        end
1014        dispOnMultLines = (totalLength + 2 >= M.LINE_LENGTH)
1015    end
1016
1017    -- now reformat the result table (currently holding element strings)
1018    if dispOnMultLines then
1019        local indentString = string.rep("    ", indentLevel - 1)
1020        result = {
1021                    "{\n    ",
1022                    indentString,
1023                    table.concat(result, ",\n    " .. indentString),
1024                    "\n",
1025                    indentString,
1026                    "}"
1027                }
1028    else
1029        result = {"{", table.concat(result, TABLE_TOSTRING_SEP), "}"}
1030    end
1031    if printTableRefs then
1032        table.insert(result, 1, "<"..table_ref(tbl).."> ") -- prepend table ref
1033    end
1034    return table.concat(result)
1035end
1036M.private._table_tostring_format_result = _table_tostring_format_result -- prettystr_sub() needs it
1037
1038local function table_findkeyof(t, element)
1039    -- Return the key k of the given element in table t, so that t[k] == element
1040    -- (or `nil` if element is not present within t). Note that we use our
1041    -- 'general' is_equal comparison for matching, so this function should
1042    -- handle table-type elements gracefully and consistently.
1043    if type(t) == "table" then
1044        for k, v in pairs(t) do
1045            if is_equal(v, element) then
1046                return k
1047            end
1048        end
1049    end
1050    return nil
1051end
1052
1053local function _is_table_items_equals(actual, expected )
1054    local type_a, type_e = type(actual), type(expected)
1055
1056    if type_a ~= type_e then
1057        return false
1058
1059    elseif (type_a == 'table') --[[and (type_e == 'table')]] then
1060        for k, v in pairs(actual) do
1061            if table_findkeyof(expected, v) == nil then
1062                return false -- v not contained in expected
1063            end
1064        end
1065        for k, v in pairs(expected) do
1066            if table_findkeyof(actual, v) == nil then
1067                return false -- v not contained in actual
1068            end
1069        end
1070        return true
1071
1072    elseif actual ~= expected then
1073        return false
1074    end
1075
1076    return true
1077end
1078
1079--[[
1080This is a specialized metatable to help with the bookkeeping of recursions
1081in _is_table_equals(). It provides an __index table that implements utility
1082functions for easier management of the table. The "cached" method queries
1083the state of a specific (actual,expected) pair; and the "store" method sets
1084this state to the given value. The state of pairs not "seen" / visited is
1085assumed to be `nil`.
1086]]
1087local _recursion_cache_MT = {
1088    __index = {
1089        -- Return the cached value for an (actual,expected) pair (or `nil`)
1090        cached = function(t, actual, expected)
1091            local subtable = t[actual] or {}
1092            return subtable[expected]
1093        end,
1094
1095        -- Store cached value for a specific (actual,expected) pair.
1096        -- Returns the value, so it's easy to use for a "tailcall" (return ...).
1097        store = function(t, actual, expected, value, asymmetric)
1098            local subtable = t[actual]
1099            if not subtable then
1100                subtable = {}
1101                t[actual] = subtable
1102            end
1103            subtable[expected] = value
1104
1105            -- Unless explicitly marked "asymmetric": Consider the recursion
1106            -- on (expected,actual) to be equivalent to (actual,expected) by
1107            -- default, and thus cache the value for both.
1108            if not asymmetric then
1109                t:store(expected, actual, value, true)
1110            end
1111
1112            return value
1113        end
1114    }
1115}
1116
1117local function _is_table_equals(actual, expected, cycleDetectTable)
1118    local type_a, type_e = type(actual), type(expected)
1119
1120    if type_a ~= type_e then
1121        return false -- different types won't match
1122    end
1123
1124    if type_a ~= 'table' then
1125        -- other typtes compare directly
1126        return actual == expected
1127    end
1128
1129    -- print('_is_table_equals( \n     '..prettystr(actual)..'\n      , '..prettystr(expected)..'\n     , '..prettystr(recursions)..' \n )')
1130
1131    cycleDetectTable = cycleDetectTable or { actual={}, expected={} }
1132    if cycleDetectTable.actual[ actual ] then
1133        -- oh, we hit a cycle in actual
1134        if cycleDetectTable.expected[ expected ] then
1135            -- uh, we hit a cycle at the same time in expected
1136            -- so the two tables have similar structure
1137            return true
1138        end
1139
1140        -- cycle was hit only in actual, the structure differs from expected
1141        return false
1142    end
1143
1144    if cycleDetectTable.expected[ expected ] then
1145        -- no cycle in actual, but cycle in expected
1146        -- the structure differ
1147        return false
1148    end
1149
1150    -- at this point, no table cycle detected, we are
1151    -- seeing this table for the first time
1152
1153    -- mark the cycle detection
1154    cycleDetectTable.actual[ actual ] = true
1155    cycleDetectTable.expected[ expected ] = true
1156
1157
1158    local actualKeysMatched = {}
1159    for k, v in pairs(actual) do
1160        actualKeysMatched[k] = true -- Keep track of matched keys
1161        if not _is_table_equals(v, expected[k], cycleDetectTable) then
1162            -- table differs on this key
1163            -- clear the cycle detection before returning
1164            cycleDetectTable.actual[ actual ] = nil
1165            cycleDetectTable.expected[ expected ] = nil
1166            return false
1167        end
1168    end
1169
1170    for k, v in pairs(expected) do
1171        if not actualKeysMatched[k] then
1172            -- Found a key that we did not see in "actual" -> mismatch
1173            -- clear the cycle detection before returning
1174            cycleDetectTable.actual[ actual ] = nil
1175            cycleDetectTable.expected[ expected ] = nil
1176            return false
1177        end
1178        -- Otherwise actual[k] was already matched against v = expected[k].
1179    end
1180
1181    -- all key match, we have a match !
1182    cycleDetectTable.actual[ actual ] = nil
1183    cycleDetectTable.expected[ expected ] = nil
1184    return true
1185end
1186M.private._is_table_equals = _is_table_equals
1187is_equal = _is_table_equals
1188
1189local function failure(main_msg, extra_msg_or_nil, level)
1190    -- raise an error indicating a test failure
1191    -- for error() compatibility we adjust "level" here (by +1), to report the
1192    -- calling context
1193    local msg
1194    if type(extra_msg_or_nil) == 'string' and extra_msg_or_nil:len() > 0 then
1195        msg = extra_msg_or_nil .. '\n' .. main_msg
1196    else
1197        msg = main_msg
1198    end
1199    error(M.FAILURE_PREFIX .. msg, (level or 1) + 1)
1200end
1201
1202local function fail_fmt(level, extra_msg_or_nil, ...)
1203     -- failure with printf-style formatted message and given error level
1204    failure(string.format(...), extra_msg_or_nil, (level or 1) + 1)
1205end
1206M.private.fail_fmt = fail_fmt
1207
1208local function error_fmt(level, ...)
1209     -- printf-style error()
1210    error(string.format(...), (level or 1) + 1)
1211end
1212
1213----------------------------------------------------------------
1214--
1215--                     assertions
1216--
1217----------------------------------------------------------------
1218
1219local function errorMsgEquality(actual, expected, doDeepAnalysis)
1220
1221    if not M.ORDER_ACTUAL_EXPECTED then
1222        expected, actual = actual, expected
1223    end
1224    if type(expected) == 'string' or type(expected) == 'table' then
1225        local strExpected, strActual = prettystrPairs(expected, actual)
1226        local result = string.format("expected: %s\nactual: %s", strExpected, strActual)
1227
1228        -- extend with mismatch analysis if possible:
1229        local success, mismatchResult
1230        success, mismatchResult = tryMismatchFormatting( actual, expected, doDeepAnalysis )
1231        if success then
1232            result = table.concat( { result, mismatchResult }, '\n' )
1233        end
1234        return result
1235    end
1236    return string.format("expected: %s, actual: %s",
1237                         prettystr(expected), prettystr(actual))
1238end
1239
1240function M.assertError(f, ...)
1241    -- assert that calling f with the arguments will raise an error
1242    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
1243    if pcall( f, ... ) then
1244        failure( "Expected an error when calling function but no error generated", nil, 2 )
1245    end
1246end
1247
1248function M.fail( msg )
1249    -- stops a test due to a failure
1250    failure( msg, nil, 2 )
1251end
1252
1253function M.failIf( cond, msg )
1254    -- Fails a test with "msg" if condition is true
1255    if cond then
1256        failure( msg, nil, 2 )
1257    end
1258end
1259
1260function M.skip(msg)
1261    -- skip a running test
1262    error(M.SKIP_PREFIX .. msg, 2)
1263end
1264
1265function M.skipIf( cond, msg )
1266    -- skip a running test if condition is met
1267    if cond then
1268        error(M.SKIP_PREFIX .. msg, 2)
1269    end
1270end
1271
1272function M.runOnlyIf( cond, msg )
1273    -- continue a running test if condition is met, else skip it
1274    if not cond then
1275        error(M.SKIP_PREFIX .. prettystr(msg), 2)
1276    end
1277end
1278
1279function M.success()
1280    -- stops a test with a success
1281    error(M.SUCCESS_PREFIX, 2)
1282end
1283
1284function M.successIf( cond )
1285    -- stops a test with a success if condition is met
1286    if cond then
1287        error(M.SUCCESS_PREFIX, 2)
1288    end
1289end
1290
1291
1292------------------------------------------------------------------
1293--                  Equality assertions
1294------------------------------------------------------------------
1295
1296function M.assertEquals(actual, expected, extra_msg_or_nil, doDeepAnalysis)
1297    if type(actual) == 'table' and type(expected) == 'table' then
1298        if not _is_table_equals(actual, expected) then
1299            failure( errorMsgEquality(actual, expected, doDeepAnalysis), extra_msg_or_nil, 2 )
1300        end
1301    elseif type(actual) ~= type(expected) then
1302        failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 )
1303    elseif actual ~= expected then
1304        failure( errorMsgEquality(actual, expected), extra_msg_or_nil, 2 )
1305    end
1306end
1307
1308function M.almostEquals( actual, expected, margin )
1309    if type(actual) ~= 'number' or type(expected) ~= 'number' or type(margin) ~= 'number' then
1310        error_fmt(3, 'almostEquals: must supply only number arguments.\nArguments supplied: %s, %s, %s',
1311            prettystr(actual), prettystr(expected), prettystr(margin))
1312    end
1313    if margin < 0 then
1314        error('almostEquals: margin must not be negative, current value is ' .. margin, 3)
1315    end
1316    return math.abs(expected - actual) <= margin
1317end
1318
1319function M.assertAlmostEquals( actual, expected, margin, extra_msg_or_nil )
1320    -- check that two floats are close by margin
1321    margin = margin or M.EPS
1322    if not M.almostEquals(actual, expected, margin) then
1323        if not M.ORDER_ACTUAL_EXPECTED then
1324            expected, actual = actual, expected
1325        end
1326        local delta = math.abs(actual - expected)
1327        fail_fmt(2, extra_msg_or_nil, 'Values are not almost equal\n' ..
1328                    'Actual: %s, expected: %s, delta %s above margin of %s',
1329                    actual, expected, delta, margin)
1330    end
1331end
1332
1333function M.assertNotEquals(actual, expected, extra_msg_or_nil)
1334    if type(actual) ~= type(expected) then
1335        return
1336    end
1337
1338    if type(actual) == 'table' and type(expected) == 'table' then
1339        if not _is_table_equals(actual, expected) then
1340            return
1341        end
1342    elseif actual ~= expected then
1343        return
1344    end
1345    fail_fmt(2, extra_msg_or_nil, 'Received the not expected value: %s', prettystr(actual))
1346end
1347
1348function M.assertNotAlmostEquals( actual, expected, margin, extra_msg_or_nil )
1349    -- check that two floats are not close by margin
1350    margin = margin or M.EPS
1351    if M.almostEquals(actual, expected, margin) then
1352        if not M.ORDER_ACTUAL_EXPECTED then
1353            expected, actual = actual, expected
1354        end
1355        local delta = math.abs(actual - expected)
1356        fail_fmt(2, extra_msg_or_nil, 'Values are almost equal\nActual: %s, expected: %s' ..
1357                    ', delta %s below margin of %s',
1358                    actual, expected, delta, margin)
1359    end
1360end
1361
1362function M.assertItemsEquals(actual, expected, extra_msg_or_nil)
1363    -- checks that the items of table expected
1364    -- are contained in table actual. Warning, this function
1365    -- is at least O(n^2)
1366    if not _is_table_items_equals(actual, expected ) then
1367        expected, actual = prettystrPairs(expected, actual)
1368        fail_fmt(2, extra_msg_or_nil, 'Content of the tables are not identical:\nExpected: %s\nActual: %s',
1369                 expected, actual)
1370    end
1371end
1372
1373------------------------------------------------------------------
1374--                  String assertion
1375------------------------------------------------------------------
1376
1377function M.assertStrContains( str, sub, isPattern, extra_msg_or_nil )
1378    -- this relies on lua string.find function
1379    -- a string always contains the empty string
1380    -- assert( type(str) == 'string', 'Argument 1 of assertStrContains() should be a string.' ) )
1381    -- assert( type(sub) == 'string', 'Argument 2 of assertStrContains() should be a string.' ) )
1382    if not string.find(str, sub, 1, not isPattern) then
1383        sub, str = prettystrPairs(sub, str, '\n')
1384        fail_fmt(2, extra_msg_or_nil, 'Could not find %s %s in string %s',
1385                 isPattern and 'pattern' or 'substring', sub, str)
1386    end
1387end
1388
1389function M.assertStrIContains( str, sub, extra_msg_or_nil )
1390    -- this relies on lua string.find function
1391    -- a string always contains the empty string
1392    if not string.find(str:lower(), sub:lower(), 1, true) then
1393        sub, str = prettystrPairs(sub, str, '\n')
1394        fail_fmt(2, extra_msg_or_nil, 'Could not find (case insensitively) substring %s in string %s',
1395                 sub, str)
1396    end
1397end
1398
1399function M.assertNotStrContains( str, sub, isPattern, extra_msg_or_nil )
1400    -- this relies on lua string.find function
1401    -- a string always contains the empty string
1402    if string.find(str, sub, 1, not isPattern) then
1403        sub, str = prettystrPairs(sub, str, '\n')
1404        fail_fmt(2, extra_msg_or_nil, 'Found the not expected %s %s in string %s',
1405                 isPattern and 'pattern' or 'substring', sub, str)
1406    end
1407end
1408
1409function M.assertNotStrIContains( str, sub, extra_msg_or_nil )
1410    -- this relies on lua string.find function
1411    -- a string always contains the empty string
1412    if string.find(str:lower(), sub:lower(), 1, true) then
1413        sub, str = prettystrPairs(sub, str, '\n')
1414        fail_fmt(2, extra_msg_or_nil, 'Found (case insensitively) the not expected substring %s in string %s',
1415                 sub, str)
1416    end
1417end
1418
1419function M.assertStrMatches( str, pattern, start, final, extra_msg_or_nil )
1420    -- Verify a full match for the string
1421    if not strMatch( str, pattern, start, final ) then
1422        pattern, str = prettystrPairs(pattern, str, '\n')
1423        fail_fmt(2, extra_msg_or_nil, 'Could not match pattern %s with string %s',
1424                 pattern, str)
1425    end
1426end
1427
1428local function _assertErrorMsgEquals( stripFileAndLine, expectedMsg, func, ... )
1429    local no_error, error_msg = pcall( func, ... )
1430    if no_error then
1431        failure( 'No error generated when calling function but expected error: '..M.prettystr(expectedMsg), nil, 3 )
1432    end
1433    if type(expectedMsg) == "string" and type(error_msg) ~= "string" then
1434        -- table are converted to string automatically
1435        error_msg = tostring(error_msg)
1436    end
1437    local differ = false
1438    if stripFileAndLine then
1439        if error_msg:gsub("^.+:%d+: ", "") ~= expectedMsg then
1440            differ = true
1441        end
1442    else
1443        if error_msg ~= expectedMsg then
1444            local tr = type(error_msg)
1445            local te = type(expectedMsg)
1446            if te == 'table' then
1447                if tr ~= 'table' then
1448                    differ = true
1449                else
1450                     local ok = pcall(M.assertItemsEquals, error_msg, expectedMsg)
1451                     if not ok then
1452                         differ = true
1453                     end
1454                end
1455            else
1456               differ = true
1457            end
1458        end
1459    end
1460
1461    if differ then
1462        error_msg, expectedMsg = prettystrPairs(error_msg, expectedMsg)
1463        fail_fmt(3, nil, 'Error message expected: %s\nError message received: %s\n',
1464                 expectedMsg, error_msg)
1465    end
1466end
1467
1468function M.assertErrorMsgEquals( expectedMsg, func, ... )
1469    -- assert that calling f with the arguments will raise an error
1470    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
1471    _assertErrorMsgEquals(false, expectedMsg, func, ...)
1472end
1473
1474function M.assertErrorMsgContentEquals(expectedMsg, func, ...)
1475     _assertErrorMsgEquals(true, expectedMsg, func, ...)
1476end
1477
1478function M.assertErrorMsgContains( partialMsg, func, ... )
1479    -- assert that calling f with the arguments will raise an error
1480    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
1481    local no_error, error_msg = pcall( func, ... )
1482    if no_error then
1483        failure( 'No error generated when calling function but expected error containing: '..prettystr(partialMsg), nil, 2 )
1484    end
1485    if type(error_msg) ~= "string" then
1486        error_msg = tostring(error_msg)
1487    end
1488    if not string.find( error_msg, partialMsg, nil, true ) then
1489        error_msg, partialMsg = prettystrPairs(error_msg, partialMsg)
1490        fail_fmt(2, nil, 'Error message does not contain: %s\nError message received: %s\n',
1491                 partialMsg, error_msg)
1492    end
1493end
1494
1495function M.assertErrorMsgMatches( expectedMsg, func, ... )
1496    -- assert that calling f with the arguments will raise an error
1497    -- example: assertError( f, 1, 2 ) => f(1,2) should generate an error
1498    local no_error, error_msg = pcall( func, ... )
1499    if no_error then
1500        failure( 'No error generated when calling function but expected error matching: "'..expectedMsg..'"', nil, 2 )
1501    end
1502    if type(error_msg) ~= "string" then
1503        error_msg = tostring(error_msg)
1504    end
1505    if not strMatch( error_msg, expectedMsg ) then
1506        expectedMsg, error_msg = prettystrPairs(expectedMsg, error_msg)
1507        fail_fmt(2, nil, 'Error message does not match pattern: %s\nError message received: %s\n',
1508                 expectedMsg, error_msg)
1509    end
1510end
1511
1512------------------------------------------------------------------
1513--              Type assertions
1514------------------------------------------------------------------
1515
1516function M.assertEvalToTrue(value, extra_msg_or_nil)
1517    if not value then
1518        failure("expected: a value evaluating to true, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1519    end
1520end
1521
1522function M.assertEvalToFalse(value, extra_msg_or_nil)
1523    if value then
1524        failure("expected: false or nil, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1525    end
1526end
1527
1528function M.assertIsTrue(value, extra_msg_or_nil)
1529    if value ~= true then
1530        failure("expected: true, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1531    end
1532end
1533
1534function M.assertNotIsTrue(value, extra_msg_or_nil)
1535    if value == true then
1536        failure("expected: not true, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1537    end
1538end
1539
1540function M.assertIsFalse(value, extra_msg_or_nil)
1541    if value ~= false then
1542        failure("expected: false, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1543    end
1544end
1545
1546function M.assertNotIsFalse(value, extra_msg_or_nil)
1547    if value == false then
1548        failure("expected: not false, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1549    end
1550end
1551
1552function M.assertIsNil(value, extra_msg_or_nil)
1553    if value ~= nil then
1554        failure("expected: nil, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1555    end
1556end
1557
1558function M.assertNotIsNil(value, extra_msg_or_nil)
1559    if value == nil then
1560        failure("expected: not nil, actual: nil", extra_msg_or_nil, 2)
1561    end
1562end
1563
1564--[[
1565Add type assertion functions to the module table M. Each of these functions
1566takes a single parameter "value", and checks that its Lua type matches the
1567expected string (derived from the function name):
1568
1569M.assertIsXxx(value) -> ensure that type(value) conforms to "xxx"
1570]]
1571for _, funcName in ipairs(
1572    {'assertIsNumber', 'assertIsString', 'assertIsTable', 'assertIsBoolean',
1573     'assertIsFunction', 'assertIsUserdata', 'assertIsThread'}
1574) do
1575    local typeExpected = funcName:match("^assertIs([A-Z]%a*)$")
1576    -- Lua type() always returns lowercase, also make sure the match() succeeded
1577    typeExpected = typeExpected and typeExpected:lower()
1578                   or error("bad function name '"..funcName.."' for type assertion")
1579
1580    M[funcName] = function(value, extra_msg_or_nil)
1581        if type(value) ~= typeExpected then
1582            if type(value) == 'nil' then
1583                fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: nil',
1584                         typeExpected, type(value), prettystrPairs(value))
1585            else
1586                fail_fmt(2, extra_msg_or_nil, 'expected: a %s value, actual: type %s, value %s',
1587                         typeExpected, type(value), prettystrPairs(value))
1588            end
1589        end
1590    end
1591end
1592
1593--[[
1594Add shortcuts for verifying type of a variable, without failure (luaunit v2 compatibility)
1595M.isXxx(value) -> returns true if type(value) conforms to "xxx"
1596]]
1597for _, typeExpected in ipairs(
1598    {'Number', 'String', 'Table', 'Boolean',
1599     'Function', 'Userdata', 'Thread', 'Nil' }
1600) do
1601    local typeExpectedLower = typeExpected:lower()
1602    local isType = function(value)
1603        return (type(value) == typeExpectedLower)
1604    end
1605    M['is'..typeExpected] = isType
1606    M['is_'..typeExpectedLower] = isType
1607end
1608
1609--[[
1610Add non-type assertion functions to the module table M. Each of these functions
1611takes a single parameter "value", and checks that its Lua type differs from the
1612expected string (derived from the function name):
1613
1614M.assertNotIsXxx(value) -> ensure that type(value) is not "xxx"
1615]]
1616for _, funcName in ipairs(
1617    {'assertNotIsNumber', 'assertNotIsString', 'assertNotIsTable', 'assertNotIsBoolean',
1618     'assertNotIsFunction', 'assertNotIsUserdata', 'assertNotIsThread'}
1619) do
1620    local typeUnexpected = funcName:match("^assertNotIs([A-Z]%a*)$")
1621    -- Lua type() always returns lowercase, also make sure the match() succeeded
1622    typeUnexpected = typeUnexpected and typeUnexpected:lower()
1623                   or error("bad function name '"..funcName.."' for type assertion")
1624
1625    M[funcName] = function(value, extra_msg_or_nil)
1626        if type(value) == typeUnexpected then
1627            fail_fmt(2, extra_msg_or_nil, 'expected: not a %s type, actual: value %s',
1628                     typeUnexpected, prettystrPairs(value))
1629        end
1630    end
1631end
1632
1633function M.assertIs(actual, expected, extra_msg_or_nil)
1634    if actual ~= expected then
1635        if not M.ORDER_ACTUAL_EXPECTED then
1636            actual, expected = expected, actual
1637        end
1638        local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG
1639        M.PRINT_TABLE_REF_IN_ERROR_MSG = true
1640        expected, actual = prettystrPairs(expected, actual, '\n', '')
1641        M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg
1642        fail_fmt(2, extra_msg_or_nil, 'expected and actual object should not be different\nExpected: %s\nReceived: %s',
1643                 expected, actual)
1644    end
1645end
1646
1647function M.assertNotIs(actual, expected, extra_msg_or_nil)
1648    if actual == expected then
1649        local old_print_table_ref_in_error_msg = M.PRINT_TABLE_REF_IN_ERROR_MSG
1650        M.PRINT_TABLE_REF_IN_ERROR_MSG = true
1651        local s_expected
1652        if not M.ORDER_ACTUAL_EXPECTED then
1653            s_expected = prettystrPairs(actual)
1654        else
1655            s_expected = prettystrPairs(expected)
1656        end
1657        M.PRINT_TABLE_REF_IN_ERROR_MSG = old_print_table_ref_in_error_msg
1658        fail_fmt(2, extra_msg_or_nil, 'expected and actual object should be different: %s', s_expected )
1659    end
1660end
1661
1662
1663------------------------------------------------------------------
1664--              Scientific assertions
1665------------------------------------------------------------------
1666
1667
1668function M.assertIsNaN(value, extra_msg_or_nil)
1669    if type(value) ~= "number" or value == value then
1670        failure("expected: NaN, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1671    end
1672end
1673
1674function M.assertNotIsNaN(value, extra_msg_or_nil)
1675    if type(value) == "number" and value ~= value then
1676        failure("expected: not NaN, actual: NaN", extra_msg_or_nil, 2)
1677    end
1678end
1679
1680function M.assertIsInf(value, extra_msg_or_nil)
1681    if type(value) ~= "number" or math.abs(value) ~= math.huge then
1682        failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1683    end
1684end
1685
1686function M.assertIsPlusInf(value, extra_msg_or_nil)
1687    if type(value) ~= "number" or value ~= math.huge then
1688        failure("expected: #Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1689    end
1690end
1691
1692function M.assertIsMinusInf(value, extra_msg_or_nil)
1693    if type(value) ~= "number" or value ~= -math.huge then
1694        failure("expected: -#Inf, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1695    end
1696end
1697
1698function M.assertNotIsPlusInf(value, extra_msg_or_nil)
1699    if type(value) == "number" and value == math.huge then
1700        failure("expected: not #Inf, actual: #Inf", extra_msg_or_nil, 2)
1701    end
1702end
1703
1704function M.assertNotIsMinusInf(value, extra_msg_or_nil)
1705    if type(value) == "number" and value == -math.huge then
1706        failure("expected: not -#Inf, actual: -#Inf", extra_msg_or_nil, 2)
1707    end
1708end
1709
1710function M.assertNotIsInf(value, extra_msg_or_nil)
1711    if type(value) == "number" and math.abs(value) == math.huge then
1712        failure("expected: not infinity, actual: " .. prettystr(value), extra_msg_or_nil, 2)
1713    end
1714end
1715
1716function M.assertIsPlusZero(value, extra_msg_or_nil)
1717    if type(value) ~= 'number' or value ~= 0 then
1718        failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1719    else if (1/value == -math.huge) then
1720            -- more precise error diagnosis
1721            failure("expected: +0.0, actual: -0.0", extra_msg_or_nil, 2)
1722        else if (1/value ~= math.huge) then
1723                -- strange, case should have already been covered
1724                failure("expected: +0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1725            end
1726        end
1727    end
1728end
1729
1730function M.assertIsMinusZero(value, extra_msg_or_nil)
1731    if type(value) ~= 'number' or value ~= 0 then
1732        failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1733    else if (1/value == math.huge) then
1734            -- more precise error diagnosis
1735            failure("expected: -0.0, actual: +0.0", extra_msg_or_nil, 2)
1736        else if (1/value ~= -math.huge) then
1737                -- strange, case should have already been covered
1738                failure("expected: -0.0, actual: " ..prettystr(value), extra_msg_or_nil, 2)
1739            end
1740        end
1741    end
1742end
1743
1744function M.assertNotIsPlusZero(value, extra_msg_or_nil)
1745    if type(value) == 'number' and (1/value == math.huge) then
1746        failure("expected: not +0.0, actual: +0.0", extra_msg_or_nil, 2)
1747    end
1748end
1749
1750function M.assertNotIsMinusZero(value, extra_msg_or_nil)
1751    if type(value) == 'number' and (1/value == -math.huge) then
1752        failure("expected: not -0.0, actual: -0.0", extra_msg_or_nil, 2)
1753    end
1754end
1755
1756function M.assertTableContains(t, expected)
1757    -- checks that table t contains the expected element
1758    if table_findkeyof(t, expected) == nil then
1759        t, expected = prettystrPairs(t, expected)
1760        fail_fmt(2, 'Table %s does NOT contain the expected element %s',
1761                 t, expected)
1762    end
1763end
1764
1765function M.assertNotTableContains(t, expected)
1766    -- checks that table t doesn't contain the expected element
1767    local k = table_findkeyof(t, expected)
1768    if k ~= nil then
1769        t, expected = prettystrPairs(t, expected)
1770        fail_fmt(2, 'Table %s DOES contain the unwanted element %s (at key %s)',
1771                 t, expected, prettystr(k))
1772    end
1773end
1774
1775----------------------------------------------------------------
1776--                     Compatibility layer
1777----------------------------------------------------------------
1778
1779-- for compatibility with LuaUnit v2.x
1780function M.wrapFunctions()
1781    -- In LuaUnit version <= 2.1 , this function was necessary to include
1782    -- a test function inside the global test suite. Nowadays, the functions
1783    -- are simply run directly as part of the test discovery process.
1784    -- so just do nothing !
1785    io.stderr:write[[Use of WrapFunctions() is no longer needed.
1786Just prefix your test function names with "test" or "Test" and they
1787will be picked up and run by LuaUnit.
1788]]
1789end
1790
1791local list_of_funcs = {
1792    -- { official function name , alias }
1793
1794    -- general assertions
1795    { 'assertEquals'            , 'assert_equals' },
1796    { 'assertItemsEquals'       , 'assert_items_equals' },
1797    { 'assertNotEquals'         , 'assert_not_equals' },
1798    { 'assertAlmostEquals'      , 'assert_almost_equals' },
1799    { 'assertNotAlmostEquals'   , 'assert_not_almost_equals' },
1800    { 'assertEvalToTrue'        , 'assert_eval_to_true' },
1801    { 'assertEvalToFalse'       , 'assert_eval_to_false' },
1802    { 'assertStrContains'       , 'assert_str_contains' },
1803    { 'assertStrIContains'      , 'assert_str_icontains' },
1804    { 'assertNotStrContains'    , 'assert_not_str_contains' },
1805    { 'assertNotStrIContains'   , 'assert_not_str_icontains' },
1806    { 'assertStrMatches'        , 'assert_str_matches' },
1807    { 'assertError'             , 'assert_error' },
1808    { 'assertErrorMsgEquals'    , 'assert_error_msg_equals' },
1809    { 'assertErrorMsgContains'  , 'assert_error_msg_contains' },
1810    { 'assertErrorMsgMatches'   , 'assert_error_msg_matches' },
1811    { 'assertErrorMsgContentEquals', 'assert_error_msg_content_equals' },
1812    { 'assertIs'                , 'assert_is' },
1813    { 'assertNotIs'             , 'assert_not_is' },
1814    { 'assertTableContains'     , 'assert_table_contains' },
1815    { 'assertNotTableContains'  , 'assert_not_table_contains' },
1816    { 'wrapFunctions'           , 'WrapFunctions' },
1817    { 'wrapFunctions'           , 'wrap_functions' },
1818
1819    -- type assertions: assertIsXXX -> assert_is_xxx
1820    { 'assertIsNumber'          , 'assert_is_number' },
1821    { 'assertIsString'          , 'assert_is_string' },
1822    { 'assertIsTable'           , 'assert_is_table' },
1823    { 'assertIsBoolean'         , 'assert_is_boolean' },
1824    { 'assertIsNil'             , 'assert_is_nil' },
1825    { 'assertIsTrue'            , 'assert_is_true' },
1826    { 'assertIsFalse'           , 'assert_is_false' },
1827    { 'assertIsNaN'             , 'assert_is_nan' },
1828    { 'assertIsInf'             , 'assert_is_inf' },
1829    { 'assertIsPlusInf'         , 'assert_is_plus_inf' },
1830    { 'assertIsMinusInf'        , 'assert_is_minus_inf' },
1831    { 'assertIsPlusZero'        , 'assert_is_plus_zero' },
1832    { 'assertIsMinusZero'       , 'assert_is_minus_zero' },
1833    { 'assertIsFunction'        , 'assert_is_function' },
1834    { 'assertIsThread'          , 'assert_is_thread' },
1835    { 'assertIsUserdata'        , 'assert_is_userdata' },
1836
1837    -- type assertions: assertIsXXX -> assertXxx
1838    { 'assertIsNumber'          , 'assertNumber' },
1839    { 'assertIsString'          , 'assertString' },
1840    { 'assertIsTable'           , 'assertTable' },
1841    { 'assertIsBoolean'         , 'assertBoolean' },
1842    { 'assertIsNil'             , 'assertNil' },
1843    { 'assertIsTrue'            , 'assertTrue' },
1844    { 'assertIsFalse'           , 'assertFalse' },
1845    { 'assertIsNaN'             , 'assertNaN' },
1846    { 'assertIsInf'             , 'assertInf' },
1847    { 'assertIsPlusInf'         , 'assertPlusInf' },
1848    { 'assertIsMinusInf'        , 'assertMinusInf' },
1849    { 'assertIsPlusZero'        , 'assertPlusZero' },
1850    { 'assertIsMinusZero'       , 'assertMinusZero'},
1851    { 'assertIsFunction'        , 'assertFunction' },
1852    { 'assertIsThread'          , 'assertThread' },
1853    { 'assertIsUserdata'        , 'assertUserdata' },
1854
1855    -- type assertions: assertIsXXX -> assert_xxx (luaunit v2 compat)
1856    { 'assertIsNumber'          , 'assert_number' },
1857    { 'assertIsString'          , 'assert_string' },
1858    { 'assertIsTable'           , 'assert_table' },
1859    { 'assertIsBoolean'         , 'assert_boolean' },
1860    { 'assertIsNil'             , 'assert_nil' },
1861    { 'assertIsTrue'            , 'assert_true' },
1862    { 'assertIsFalse'           , 'assert_false' },
1863    { 'assertIsNaN'             , 'assert_nan' },
1864    { 'assertIsInf'             , 'assert_inf' },
1865    { 'assertIsPlusInf'         , 'assert_plus_inf' },
1866    { 'assertIsMinusInf'        , 'assert_minus_inf' },
1867    { 'assertIsPlusZero'        , 'assert_plus_zero' },
1868    { 'assertIsMinusZero'       , 'assert_minus_zero' },
1869    { 'assertIsFunction'        , 'assert_function' },
1870    { 'assertIsThread'          , 'assert_thread' },
1871    { 'assertIsUserdata'        , 'assert_userdata' },
1872
1873    -- type assertions: assertNotIsXXX -> assert_not_is_xxx
1874    { 'assertNotIsNumber'       , 'assert_not_is_number' },
1875    { 'assertNotIsString'       , 'assert_not_is_string' },
1876    { 'assertNotIsTable'        , 'assert_not_is_table' },
1877    { 'assertNotIsBoolean'      , 'assert_not_is_boolean' },
1878    { 'assertNotIsNil'          , 'assert_not_is_nil' },
1879    { 'assertNotIsTrue'         , 'assert_not_is_true' },
1880    { 'assertNotIsFalse'        , 'assert_not_is_false' },
1881    { 'assertNotIsNaN'          , 'assert_not_is_nan' },
1882    { 'assertNotIsInf'          , 'assert_not_is_inf' },
1883    { 'assertNotIsPlusInf'      , 'assert_not_plus_inf' },
1884    { 'assertNotIsMinusInf'     , 'assert_not_minus_inf' },
1885    { 'assertNotIsPlusZero'     , 'assert_not_plus_zero' },
1886    { 'assertNotIsMinusZero'    , 'assert_not_minus_zero' },
1887    { 'assertNotIsFunction'     , 'assert_not_is_function' },
1888    { 'assertNotIsThread'       , 'assert_not_is_thread' },
1889    { 'assertNotIsUserdata'     , 'assert_not_is_userdata' },
1890
1891    -- type assertions: assertNotIsXXX -> assertNotXxx (luaunit v2 compat)
1892    { 'assertNotIsNumber'       , 'assertNotNumber' },
1893    { 'assertNotIsString'       , 'assertNotString' },
1894    { 'assertNotIsTable'        , 'assertNotTable' },
1895    { 'assertNotIsBoolean'      , 'assertNotBoolean' },
1896    { 'assertNotIsNil'          , 'assertNotNil' },
1897    { 'assertNotIsTrue'         , 'assertNotTrue' },
1898    { 'assertNotIsFalse'        , 'assertNotFalse' },
1899    { 'assertNotIsNaN'          , 'assertNotNaN' },
1900    { 'assertNotIsInf'          , 'assertNotInf' },
1901    { 'assertNotIsPlusInf'      , 'assertNotPlusInf' },
1902    { 'assertNotIsMinusInf'     , 'assertNotMinusInf' },
1903    { 'assertNotIsPlusZero'     , 'assertNotPlusZero' },
1904    { 'assertNotIsMinusZero'    , 'assertNotMinusZero' },
1905    { 'assertNotIsFunction'     , 'assertNotFunction' },
1906    { 'assertNotIsThread'       , 'assertNotThread' },
1907    { 'assertNotIsUserdata'     , 'assertNotUserdata' },
1908
1909    -- type assertions: assertNotIsXXX -> assert_not_xxx
1910    { 'assertNotIsNumber'       , 'assert_not_number' },
1911    { 'assertNotIsString'       , 'assert_not_string' },
1912    { 'assertNotIsTable'        , 'assert_not_table' },
1913    { 'assertNotIsBoolean'      , 'assert_not_boolean' },
1914    { 'assertNotIsNil'          , 'assert_not_nil' },
1915    { 'assertNotIsTrue'         , 'assert_not_true' },
1916    { 'assertNotIsFalse'        , 'assert_not_false' },
1917    { 'assertNotIsNaN'          , 'assert_not_nan' },
1918    { 'assertNotIsInf'          , 'assert_not_inf' },
1919    { 'assertNotIsPlusInf'      , 'assert_not_plus_inf' },
1920    { 'assertNotIsMinusInf'     , 'assert_not_minus_inf' },
1921    { 'assertNotIsPlusZero'     , 'assert_not_plus_zero' },
1922    { 'assertNotIsMinusZero'    , 'assert_not_minus_zero' },
1923    { 'assertNotIsFunction'     , 'assert_not_function' },
1924    { 'assertNotIsThread'       , 'assert_not_thread' },
1925    { 'assertNotIsUserdata'     , 'assert_not_userdata' },
1926
1927    -- all assertions with Coroutine duplicate Thread assertions
1928    { 'assertIsThread'          , 'assertIsCoroutine' },
1929    { 'assertIsThread'          , 'assertCoroutine' },
1930    { 'assertIsThread'          , 'assert_is_coroutine' },
1931    { 'assertIsThread'          , 'assert_coroutine' },
1932    { 'assertNotIsThread'       , 'assertNotIsCoroutine' },
1933    { 'assertNotIsThread'       , 'assertNotCoroutine' },
1934    { 'assertNotIsThread'       , 'assert_not_is_coroutine' },
1935    { 'assertNotIsThread'       , 'assert_not_coroutine' },
1936}
1937
1938-- Create all aliases in M
1939for _,v in ipairs( list_of_funcs ) do
1940    local funcname, alias = v[1], v[2]
1941    M[alias] = M[funcname]
1942
1943    if EXPORT_ASSERT_TO_GLOBALS then
1944        _G[funcname] = M[funcname]
1945        _G[alias] = M[funcname]
1946    end
1947end
1948
1949----------------------------------------------------------------
1950--
1951--                     Outputters
1952--
1953----------------------------------------------------------------
1954
1955-- A common "base" class for outputters
1956-- For concepts involved (class inheritance) see http://www.lua.org/pil/16.2.html
1957
1958local genericOutput = { __class__ = 'genericOutput' } -- class
1959local genericOutput_MT = { __index = genericOutput } -- metatable
1960M.genericOutput = genericOutput -- publish, so that custom classes may derive from it
1961
1962function genericOutput.new(runner, default_verbosity)
1963    -- runner is the "parent" object controlling the output, usually a LuaUnit instance
1964    local t = { runner = runner }
1965    if runner then
1966        t.result = runner.result
1967        t.verbosity = runner.verbosity or default_verbosity
1968        t.fname = runner.fname
1969    else
1970        t.verbosity = default_verbosity
1971    end
1972    return setmetatable( t, genericOutput_MT)
1973end
1974
1975-- abstract ("empty") methods
1976function genericOutput:startSuite()
1977    -- Called once, when the suite is started
1978end
1979
1980function genericOutput:startClass(className)
1981    -- Called each time a new test class is started
1982end
1983
1984function genericOutput:startTest(testName)
1985    -- called each time a new test is started, right before the setUp()
1986    -- the current test status node is already created and available in: self.result.currentNode
1987end
1988
1989function genericOutput:updateStatus(node)
1990    -- called with status failed or error as soon as the error/failure is encountered
1991    -- this method is NOT called for a successful test because a test is marked as successful by default
1992    -- and does not need to be updated
1993end
1994
1995function genericOutput:endTest(node)
1996    -- called when the test is finished, after the tearDown() method
1997end
1998
1999function genericOutput:endClass()
2000    -- called when executing the class is finished, before moving on to the next class of at the end of the test execution
2001end
2002
2003function genericOutput:endSuite()
2004    -- called at the end of the test suite execution
2005end
2006
2007
2008----------------------------------------------------------------
2009--                     class TapOutput
2010----------------------------------------------------------------
2011
2012local TapOutput = genericOutput.new() -- derived class
2013local TapOutput_MT = { __index = TapOutput } -- metatable
2014TapOutput.__class__ = 'TapOutput'
2015
2016    -- For a good reference for TAP format, check: http://testanything.org/tap-specification.html
2017
2018    function TapOutput.new(runner)
2019        local t = genericOutput.new(runner, M.VERBOSITY_LOW)
2020        return setmetatable( t, TapOutput_MT)
2021    end
2022    function TapOutput:startSuite()
2023        print("1.."..self.result.selectedCount)
2024        print('# Started on '..self.result.startDate)
2025    end
2026    function TapOutput:startClass(className)
2027        if className ~= '[TestFunctions]' then
2028            print('# Starting class: '..className)
2029        end
2030    end
2031
2032    function TapOutput:updateStatus( node )
2033        if node:isSkipped() then
2034            io.stdout:write("ok ", self.result.currentTestNumber, "\t# SKIP ", node.msg, "\n" )
2035            return
2036        end
2037
2038        io.stdout:write("not ok ", self.result.currentTestNumber, "\t", node.testName, "\n")
2039        if self.verbosity > M.VERBOSITY_LOW then
2040           print( prefixString( '#   ', node.msg ) )
2041        end
2042        if (node:isFailure() or node:isError()) and self.verbosity > M.VERBOSITY_DEFAULT then
2043           print( prefixString( '#   ', node.stackTrace ) )
2044        end
2045    end
2046
2047    function TapOutput:endTest( node )
2048        if node:isSuccess() then
2049            io.stdout:write("ok     ", self.result.currentTestNumber, "\t", node.testName, "\n")
2050        end
2051    end
2052
2053    function TapOutput:endSuite()
2054        print( '# '..M.LuaUnit.statusLine( self.result ) )
2055        return self.result.notSuccessCount
2056    end
2057
2058
2059-- class TapOutput end
2060
2061----------------------------------------------------------------
2062--                     class JUnitOutput
2063----------------------------------------------------------------
2064
2065-- See directory junitxml for more information about the junit format
2066local JUnitOutput = genericOutput.new() -- derived class
2067local JUnitOutput_MT = { __index = JUnitOutput } -- metatable
2068JUnitOutput.__class__ = 'JUnitOutput'
2069
2070    function JUnitOutput.new(runner)
2071        local t = genericOutput.new(runner, M.VERBOSITY_LOW)
2072        t.testList = {}
2073        return setmetatable( t, JUnitOutput_MT )
2074    end
2075
2076    function JUnitOutput:startSuite()
2077        -- open xml file early to deal with errors
2078        if self.fname == nil then
2079            error('With Junit, an output filename must be supplied with --name!')
2080        end
2081        if string.sub(self.fname,-4) ~= '.xml' then
2082            self.fname = self.fname..'.xml'
2083        end
2084        self.fd = io.open(self.fname, "w")
2085        if self.fd == nil then
2086            error("Could not open file for writing: "..self.fname)
2087        end
2088
2089        print('# XML output to '..self.fname)
2090        print('# Started on '..self.result.startDate)
2091    end
2092    function JUnitOutput:startClass(className)
2093        if className ~= '[TestFunctions]' then
2094            print('# Starting class: '..className)
2095        end
2096    end
2097    function JUnitOutput:startTest(testName)
2098        print('# Starting test: '..testName)
2099    end
2100
2101    function JUnitOutput:updateStatus( node )
2102        if node:isFailure() then
2103            print( '#   Failure: ' .. prefixString( '#   ', node.msg ):sub(4, nil) )
2104            -- print('# ' .. node.stackTrace)
2105        elseif node:isError() then
2106            print( '#   Error: ' .. prefixString( '#   '  , node.msg ):sub(4, nil) )
2107            -- print('# ' .. node.stackTrace)
2108        end
2109    end
2110
2111    function JUnitOutput:endSuite()
2112        print( '# '..M.LuaUnit.statusLine(self.result))
2113
2114        -- XML file writing
2115        self.fd:write('<?xml version="1.0" encoding="UTF-8" ?>\n')
2116        self.fd:write('<testsuites>\n')
2117        self.fd:write(string.format(
2118            '    <testsuite name="LuaUnit" id="00001" package="" hostname="localhost" tests="%d" timestamp="%s" time="%0.3f" errors="%d" failures="%d" skipped="%d">\n',
2119            self.result.runCount, self.result.startIsodate, self.result.duration, self.result.errorCount, self.result.failureCount, self.result.skippedCount ))
2120        self.fd:write("        <properties>\n")
2121        self.fd:write(string.format('            <property name="Lua Version" value="%s"/>\n', _VERSION ) )
2122        self.fd:write(string.format('            <property name="LuaUnit Version" value="%s"/>\n', M.VERSION) )
2123        -- XXX please include system name and version if possible
2124        self.fd:write("        </properties>\n")
2125
2126        for i,node in ipairs(self.result.allTests) do
2127            self.fd:write(string.format('        <testcase classname="%s" name="%s" time="%0.3f">\n',
2128                node.className, node.testName, node.duration ) )
2129            if node:isNotSuccess() then
2130                self.fd:write(node:statusXML())
2131            end
2132            self.fd:write('        </testcase>\n')
2133        end
2134
2135        -- Next two lines are needed to validate junit ANT xsd, but really not useful in general:
2136        self.fd:write('    <system-out/>\n')
2137        self.fd:write('    <system-err/>\n')
2138
2139        self.fd:write('    </testsuite>\n')
2140        self.fd:write('</testsuites>\n')
2141        self.fd:close()
2142        return self.result.notSuccessCount
2143    end
2144
2145
2146-- class TapOutput end
2147
2148----------------------------------------------------------------
2149--                     class TextOutput
2150----------------------------------------------------------------
2151
2152--[[    Example of other unit-tests suite text output
2153
2154-- Python Non verbose:
2155
2156For each test: . or F or E
2157
2158If some failed tests:
2159    ==============
2160    ERROR / FAILURE: TestName (testfile.testclass)
2161    ---------
2162    Stack trace
2163
2164
2165then --------------
2166then "Ran x tests in 0.000s"
2167then OK or FAILED (failures=1, error=1)
2168
2169-- Python Verbose:
2170testname (filename.classname) ... ok
2171testname (filename.classname) ... FAIL
2172testname (filename.classname) ... ERROR
2173
2174then --------------
2175then "Ran x tests in 0.000s"
2176then OK or FAILED (failures=1, error=1)
2177
2178-- Ruby:
2179Started
2180 .
2181 Finished in 0.002695 seconds.
2182
2183 1 tests, 2 assertions, 0 failures, 0 errors
2184
2185-- Ruby:
2186>> ruby tc_simple_number2.rb
2187Loaded suite tc_simple_number2
2188Started
2189F..
2190Finished in 0.038617 seconds.
2191
2192  1) Failure:
2193test_failure(TestSimpleNumber) [tc_simple_number2.rb:16]:
2194Adding doesn't work.
2195<3> expected but was
2196<4>.
2197
21983 tests, 4 assertions, 1 failures, 0 errors
2199
2200-- Java Junit
2201.......F.
2202Time: 0,003
2203There was 1 failure:
22041) testCapacity(junit.samples.VectorTest)junit.framework.AssertionFailedError
2205    at junit.samples.VectorTest.testCapacity(VectorTest.java:87)
2206    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2207    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
2208    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
2209
2210FAILURES!!!
2211Tests run: 8,  Failures: 1,  Errors: 0
2212
2213
2214-- Maven
2215
2216# mvn test
2217-------------------------------------------------------
2218 T E S T S
2219-------------------------------------------------------
2220Running math.AdditionTest
2221Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed:
22220.03 sec <<< FAILURE!
2223
2224Results :
2225
2226Failed tests:
2227  testLireSymbole(math.AdditionTest)
2228
2229Tests run: 2, Failures: 1, Errors: 0, Skipped: 0
2230
2231
2232-- LuaUnit
2233---- non verbose
2234* display . or F or E when running tests
2235---- verbose
2236* display test name + ok/fail
2237----
2238* blank line
2239* number) ERROR or FAILURE: TestName
2240   Stack trace
2241* blank line
2242* number) ERROR or FAILURE: TestName
2243   Stack trace
2244
2245then --------------
2246then "Ran x tests in 0.000s (%d not selected, %d skipped)"
2247then OK or FAILED (failures=1, error=1)
2248
2249
2250]]
2251
2252local TextOutput = genericOutput.new() -- derived class
2253local TextOutput_MT = { __index = TextOutput } -- metatable
2254TextOutput.__class__ = 'TextOutput'
2255
2256    function TextOutput.new(runner)
2257        local t = genericOutput.new(runner, M.VERBOSITY_DEFAULT)
2258        t.errorList = {}
2259        return setmetatable( t, TextOutput_MT )
2260    end
2261
2262    function TextOutput:startSuite()
2263        if self.verbosity > M.VERBOSITY_DEFAULT then
2264            print( 'Started on '.. self.result.startDate )
2265        end
2266    end
2267
2268    function TextOutput:startTest(testName)
2269        if self.verbosity > M.VERBOSITY_DEFAULT then
2270            io.stdout:write( "    ", self.result.currentNode.testName, " ... " )
2271        end
2272    end
2273
2274    function TextOutput:endTest( node )
2275        if node:isSuccess() then
2276            if self.verbosity > M.VERBOSITY_DEFAULT then
2277                io.stdout:write("Ok\n")
2278            else
2279                io.stdout:write(".")
2280                io.stdout:flush()
2281            end
2282        else
2283            if self.verbosity > M.VERBOSITY_DEFAULT then
2284                print( node.status )
2285                print( node.msg )
2286                --[[
2287                -- find out when to do this:
2288                if self.verbosity > M.VERBOSITY_DEFAULT then
2289                    print( node.stackTrace )
2290                end
2291                ]]
2292            else
2293                -- write only the first character of status E, F or S
2294                io.stdout:write(string.sub(node.status, 1, 1))
2295                io.stdout:flush()
2296            end
2297        end
2298    end
2299
2300    function TextOutput:displayOneFailedTest( index, fail )
2301        print(index..") "..fail.testName )
2302        print( fail.msg )
2303        print( fail.stackTrace )
2304        print()
2305    end
2306
2307    function TextOutput:displayErroredTests()
2308        if #self.result.errorTests ~= 0 then
2309            print("Tests with errors:")
2310            print("------------------")
2311            for i, v in ipairs(self.result.errorTests) do
2312                self:displayOneFailedTest(i, v)
2313            end
2314        end
2315    end
2316
2317    function TextOutput:displayFailedTests()
2318        if #self.result.failedTests ~= 0 then
2319            print("Failed tests:")
2320            print("-------------")
2321            for i, v in ipairs(self.result.failedTests) do
2322                self:displayOneFailedTest(i, v)
2323            end
2324        end
2325    end
2326
2327    function TextOutput:endSuite()
2328        if self.verbosity > M.VERBOSITY_DEFAULT then
2329            print("=========================================================")
2330        else
2331            print()
2332        end
2333        self:displayErroredTests()
2334        self:displayFailedTests()
2335        print( M.LuaUnit.statusLine( self.result ) )
2336        if self.result.notSuccessCount == 0 then
2337            print('OK')
2338        end
2339    end
2340
2341-- class TextOutput end
2342
2343
2344----------------------------------------------------------------
2345--                     class NilOutput
2346----------------------------------------------------------------
2347
2348local function nopCallable()
2349    --print(42)
2350    return nopCallable
2351end
2352
2353local NilOutput = { __class__ = 'NilOuptut' } -- class
2354local NilOutput_MT = { __index = nopCallable } -- metatable
2355
2356function NilOutput.new(runner)
2357    return setmetatable( { __class__ = 'NilOutput' }, NilOutput_MT )
2358end
2359
2360----------------------------------------------------------------
2361--
2362--                     class LuaUnit
2363--
2364----------------------------------------------------------------
2365
2366M.LuaUnit = {
2367    outputType = TextOutput,
2368    verbosity = M.VERBOSITY_DEFAULT,
2369    __class__ = 'LuaUnit'
2370}
2371local LuaUnit_MT = { __index = M.LuaUnit }
2372
2373if EXPORT_ASSERT_TO_GLOBALS then
2374    LuaUnit = M.LuaUnit
2375end
2376
2377    function M.LuaUnit.new()
2378        return setmetatable( {}, LuaUnit_MT )
2379    end
2380
2381    -----------------[[ Utility methods ]]---------------------
2382
2383    function M.LuaUnit.asFunction(aObject)
2384        -- return "aObject" if it is a function, and nil otherwise
2385        if 'function' == type(aObject) then
2386            return aObject
2387        end
2388    end
2389
2390    function M.LuaUnit.splitClassMethod(someName)
2391        --[[
2392        Return a pair of className, methodName strings for a name in the form
2393        "class.method". If no class part (or separator) is found, will return
2394        nil, someName instead (the latter being unchanged).
2395
2396        This convention thus also replaces the older isClassMethod() test:
2397        You just have to check for a non-nil className (return) value.
2398        ]]
2399        local separator = string.find(someName, '.', 1, true)
2400        if separator then
2401            return someName:sub(1, separator - 1), someName:sub(separator + 1)
2402        end
2403        return nil, someName
2404    end
2405
2406    function M.LuaUnit.isMethodTestName( s )
2407        -- return true is the name matches the name of a test method
2408        -- default rule is that is starts with 'Test' or with 'test'
2409        return string.sub(s, 1, 4):lower() == 'test'
2410    end
2411
2412    function M.LuaUnit.isTestName( s )
2413        -- return true is the name matches the name of a test
2414        -- default rule is that is starts with 'Test' or with 'test'
2415        return string.sub(s, 1, 4):lower() == 'test'
2416    end
2417
2418    function M.LuaUnit.collectTests()
2419        -- return a list of all test names in the global namespace
2420        -- that match LuaUnit.isTestName
2421
2422        local testNames = {}
2423        for k, _ in pairs(_G) do
2424            if type(k) == "string" and M.LuaUnit.isTestName( k ) then
2425                table.insert( testNames , k )
2426            end
2427        end
2428        table.sort( testNames )
2429        return testNames
2430    end
2431
2432    function M.LuaUnit.parseCmdLine( cmdLine )
2433        -- parse the command line
2434        -- Supported command line parameters:
2435        -- --verbose, -v: increase verbosity
2436        -- --quiet, -q: silence output
2437        -- --error, -e: treat errors as fatal (quit program)
2438        -- --output, -o, + name: select output type
2439        -- --pattern, -p, + pattern: run test matching pattern, may be repeated
2440        -- --exclude, -x, + pattern: run test not matching pattern, may be repeated
2441        -- --shuffle, -s, : shuffle tests before reunning them
2442        -- --name, -n, + fname: name of output file for junit, default to stdout
2443        -- --repeat, -r, + num: number of times to execute each test
2444        -- [testnames, ...]: run selected test names
2445        --
2446        -- Returns a table with the following fields:
2447        -- verbosity: nil, M.VERBOSITY_DEFAULT, M.VERBOSITY_QUIET, M.VERBOSITY_VERBOSE
2448        -- output: nil, 'tap', 'junit', 'text', 'nil'
2449        -- testNames: nil or a list of test names to run
2450        -- exeRepeat: num or 1
2451        -- pattern: nil or a list of patterns
2452        -- exclude: nil or a list of patterns
2453
2454        local result, state = {}, nil
2455        local SET_OUTPUT = 1
2456        local SET_PATTERN = 2
2457        local SET_EXCLUDE = 3
2458        local SET_FNAME = 4
2459        local SET_REPEAT = 5
2460
2461        if cmdLine == nil then
2462            return result
2463        end
2464
2465        local function parseOption( option )
2466            if option == '--help' or option == '-h' then
2467                result['help'] = true
2468                return
2469            elseif option == '--version' then
2470                result['version'] = true
2471                return
2472            elseif option == '--verbose' or option == '-v' then
2473                result['verbosity'] = M.VERBOSITY_VERBOSE
2474                return
2475            elseif option == '--quiet' or option == '-q' then
2476                result['verbosity'] = M.VERBOSITY_QUIET
2477                return
2478            elseif option == '--error' or option == '-e' then
2479                result['quitOnError'] = true
2480                return
2481            elseif option == '--failure' or option == '-f' then
2482                result['quitOnFailure'] = true
2483                return
2484            elseif option == '--shuffle' or option == '-s' then
2485                result['shuffle'] = true
2486                return
2487            elseif option == '--output' or option == '-o' then
2488                state = SET_OUTPUT
2489                return state
2490            elseif option == '--name' or option == '-n' then
2491                state = SET_FNAME
2492                return state
2493            elseif option == '--repeat' or option == '-r' then
2494                state = SET_REPEAT
2495                return state
2496            elseif option == '--pattern' or option == '-p' then
2497                state = SET_PATTERN
2498                return state
2499            elseif option == '--exclude' or option == '-x' then
2500                state = SET_EXCLUDE
2501                return state
2502            end
2503            error('Unknown option: '..option,3)
2504        end
2505
2506        local function setArg( cmdArg, state )
2507            if state == SET_OUTPUT then
2508                result['output'] = cmdArg
2509                return
2510            elseif state == SET_FNAME then
2511                result['fname'] = cmdArg
2512                return
2513            elseif state == SET_REPEAT then
2514                result['exeRepeat'] = tonumber(cmdArg)
2515                                     or error('Malformed -r argument: '..cmdArg)
2516                return
2517            elseif state == SET_PATTERN then
2518                if result['pattern'] then
2519                    table.insert( result['pattern'], cmdArg )
2520                else
2521                    result['pattern'] = { cmdArg }
2522                end
2523                return
2524            elseif state == SET_EXCLUDE then
2525                local notArg = '!'..cmdArg
2526                if result['pattern'] then
2527                    table.insert( result['pattern'],  notArg )
2528                else
2529                    result['pattern'] = { notArg }
2530                end
2531                return
2532            end
2533            error('Unknown parse state: '.. state)
2534        end
2535
2536
2537        for i, cmdArg in ipairs(cmdLine) do
2538            if state ~= nil then
2539                setArg( cmdArg, state, result )
2540                state = nil
2541            else
2542                if cmdArg:sub(1,1) == '-' then
2543                    state = parseOption( cmdArg )
2544                else
2545                    if result['testNames'] then
2546                        table.insert( result['testNames'], cmdArg )
2547                    else
2548                        result['testNames'] = { cmdArg }
2549                    end
2550                end
2551            end
2552        end
2553
2554        if result['help'] then
2555            M.LuaUnit.help()
2556        end
2557
2558        if result['version'] then
2559            M.LuaUnit.version()
2560        end
2561
2562        if state ~= nil then
2563            error('Missing argument after '..cmdLine[ #cmdLine ],2 )
2564        end
2565
2566        return result
2567    end
2568
2569    function M.LuaUnit.help()
2570        print(M.USAGE)
2571        os.exit(0)
2572    end
2573
2574    function M.LuaUnit.version()
2575        print('LuaUnit v'..M.VERSION..' by Philippe Fremy <phil@freehackers.org>')
2576        os.exit(0)
2577    end
2578
2579----------------------------------------------------------------
2580--                     class NodeStatus
2581----------------------------------------------------------------
2582
2583    local NodeStatus = { __class__ = 'NodeStatus' } -- class
2584    local NodeStatus_MT = { __index = NodeStatus } -- metatable
2585    M.NodeStatus = NodeStatus
2586
2587    -- values of status
2588    NodeStatus.SUCCESS  = 'SUCCESS'
2589    NodeStatus.SKIP     = 'SKIP'
2590    NodeStatus.FAIL     = 'FAIL'
2591    NodeStatus.ERROR    = 'ERROR'
2592
2593    function NodeStatus.new( number, testName, className )
2594        -- default constructor, test are PASS by default
2595        local t = { number = number, testName = testName, className = className }
2596        setmetatable( t, NodeStatus_MT )
2597        t:success()
2598        return t
2599    end
2600
2601    function NodeStatus:success()
2602        self.status = self.SUCCESS
2603        -- useless because lua does this for us, but it helps me remembering the relevant field names
2604        self.msg = nil
2605        self.stackTrace = nil
2606    end
2607
2608    function NodeStatus:skip(msg)
2609        self.status = self.SKIP
2610        self.msg = msg
2611        self.stackTrace = nil
2612    end
2613
2614    function NodeStatus:fail(msg, stackTrace)
2615        self.status = self.FAIL
2616        self.msg = msg
2617        self.stackTrace = stackTrace
2618    end
2619
2620    function NodeStatus:error(msg, stackTrace)
2621        self.status = self.ERROR
2622        self.msg = msg
2623        self.stackTrace = stackTrace
2624    end
2625
2626    function NodeStatus:isSuccess()
2627        return self.status == NodeStatus.SUCCESS
2628    end
2629
2630    function NodeStatus:isNotSuccess()
2631        -- Return true if node is either failure or error or skip
2632        return (self.status == NodeStatus.FAIL or self.status == NodeStatus.ERROR or self.status == NodeStatus.SKIP)
2633    end
2634
2635    function NodeStatus:isSkipped()
2636        return self.status == NodeStatus.SKIP
2637    end
2638
2639    function NodeStatus:isFailure()
2640        return self.status == NodeStatus.FAIL
2641    end
2642
2643    function NodeStatus:isError()
2644        return self.status == NodeStatus.ERROR
2645    end
2646
2647    function NodeStatus:statusXML()
2648        if self:isError() then
2649            return table.concat(
2650                {'            <error type="', xmlEscape(self.msg), '">\n',
2651                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),
2652                 ']]></error>\n'})
2653        elseif self:isFailure() then
2654            return table.concat(
2655                {'            <failure type="', xmlEscape(self.msg), '">\n',
2656                 '                <![CDATA[', xmlCDataEscape(self.stackTrace),
2657                 ']]></failure>\n'})
2658        elseif self:isSkipped() then
2659            return table.concat({'            <skipped>', xmlEscape(self.msg),'</skipped>\n' } )
2660        end
2661        return '            <passed/>\n' -- (not XSD-compliant! normally shouldn't get here)
2662    end
2663
2664    --------------[[ Output methods ]]-------------------------
2665
2666    local function conditional_plural(number, singular)
2667        -- returns a grammatically well-formed string "%d <singular/plural>"
2668        local suffix = ''
2669        if number ~= 1 then -- use plural
2670            suffix = (singular:sub(-2) == 'ss') and 'es' or 's'
2671        end
2672        return string.format('%d %s%s', number, singular, suffix)
2673    end
2674
2675    function M.LuaUnit.statusLine(result)
2676        -- return status line string according to results
2677        local s = {
2678            string.format('Ran %d tests in %0.3f seconds',
2679                          result.runCount, result.duration),
2680            conditional_plural(result.successCount, 'success'),
2681        }
2682        if result.notSuccessCount > 0 then
2683            if result.failureCount > 0 then
2684                table.insert(s, conditional_plural(result.failureCount, 'failure'))
2685            end
2686            if result.errorCount > 0 then
2687                table.insert(s, conditional_plural(result.errorCount, 'error'))
2688            end
2689        else
2690            table.insert(s, '0 failures')
2691        end
2692        if result.skippedCount > 0 then
2693            table.insert(s, string.format("%d skipped", result.skippedCount))
2694        end
2695        if result.nonSelectedCount > 0 then
2696            table.insert(s, string.format("%d non-selected", result.nonSelectedCount))
2697        end
2698        return table.concat(s, ', ')
2699    end
2700
2701    function M.LuaUnit:startSuite(selectedCount, nonSelectedCount)
2702        self.result = {
2703            selectedCount = selectedCount,
2704            nonSelectedCount = nonSelectedCount,
2705            successCount = 0,
2706            runCount = 0,
2707            currentTestNumber = 0,
2708            currentClassName = "",
2709            currentNode = nil,
2710            suiteStarted = true,
2711            startTime = os.clock(),
2712            startDate = os.date(os.getenv('LUAUNIT_DATEFMT')),
2713            startIsodate = os.date('%Y-%m-%dT%H:%M:%S'),
2714            patternIncludeFilter = self.patternIncludeFilter,
2715
2716            -- list of test node status
2717            allTests = {},
2718            failedTests = {},
2719            errorTests = {},
2720            skippedTests = {},
2721
2722            failureCount = 0,
2723            errorCount = 0,
2724            notSuccessCount = 0,
2725            skippedCount = 0,
2726        }
2727
2728        self.outputType = self.outputType or TextOutput
2729        self.output = self.outputType.new(self)
2730        self.output:startSuite()
2731    end
2732
2733    function M.LuaUnit:startClass( className )
2734        self.result.currentClassName = className
2735        self.output:startClass( className )
2736    end
2737
2738    function M.LuaUnit:startTest( testName  )
2739        self.result.currentTestNumber = self.result.currentTestNumber + 1
2740        self.result.runCount = self.result.runCount + 1
2741        self.result.currentNode = NodeStatus.new(
2742            self.result.currentTestNumber,
2743            testName,
2744            self.result.currentClassName
2745        )
2746        self.result.currentNode.startTime = os.clock()
2747        table.insert( self.result.allTests, self.result.currentNode )
2748        self.output:startTest( testName )
2749    end
2750
2751    function M.LuaUnit:updateStatus( err )
2752        -- "err" is expected to be a table / result from protectedCall()
2753        if err.status == NodeStatus.SUCCESS then
2754            return
2755        end
2756
2757        local node = self.result.currentNode
2758
2759        --[[ As a first approach, we will report only one error or one failure for one test.
2760
2761        However, we can have the case where the test is in failure, and the teardown is in error.
2762        In such case, it's a good idea to report both a failure and an error in the test suite. This is
2763        what Python unittest does for example. However, it mixes up counts so need to be handled carefully: for
2764        example, there could be more (failures + errors) count that tests. What happens to the current node ?
2765
2766        We will do this more intelligent version later.
2767        ]]
2768
2769        -- if the node is already in failure/error, just don't report the new error (see above)
2770        if node.status ~= NodeStatus.SUCCESS then
2771            return
2772        end
2773
2774        if err.status == NodeStatus.FAIL then
2775            node:fail( err.msg, err.trace )
2776            table.insert( self.result.failedTests, node )
2777        elseif err.status == NodeStatus.ERROR then
2778            node:error( err.msg, err.trace )
2779            table.insert( self.result.errorTests, node )
2780        elseif err.status == NodeStatus.SKIP then
2781            node:skip( err.msg )
2782            table.insert( self.result.skippedTests, node )
2783        else
2784            error('No such status: ' .. prettystr(err.status))
2785        end
2786
2787        self.output:updateStatus( node )
2788    end
2789
2790    function M.LuaUnit:endTest()
2791        local node = self.result.currentNode
2792        -- print( 'endTest() '..prettystr(node))
2793        -- print( 'endTest() '..prettystr(node:isNotSuccess()))
2794        node.duration = os.clock() - node.startTime
2795        node.startTime = nil
2796        self.output:endTest( node )
2797
2798        if node:isSuccess() then
2799            self.result.successCount = self.result.successCount + 1
2800        elseif node:isError() then
2801            if self.quitOnError or self.quitOnFailure then
2802                -- Runtime error - abort test execution as requested by
2803                -- "--error" option. This is done by setting a special
2804                -- flag that gets handled in runSuiteByInstances().
2805                print("\nERROR during LuaUnit test execution:\n" .. node.msg)
2806                self.result.aborted = true
2807            end
2808        elseif node:isFailure() then
2809            if self.quitOnFailure then
2810                -- Failure - abort test execution as requested by
2811                -- "--failure" option. This is done by setting a special
2812                -- flag that gets handled in runSuiteByInstances().
2813                print("\nFailure during LuaUnit test execution:\n" .. node.msg)
2814                self.result.aborted = true
2815            end
2816        elseif node:isSkipped() then
2817            self.result.runCount = self.result.runCount - 1
2818        else
2819            error('No such node status: ' .. prettystr(node.status))
2820        end
2821        self.result.currentNode = nil
2822    end
2823
2824    function M.LuaUnit:endClass()
2825        self.output:endClass()
2826    end
2827
2828    function M.LuaUnit:endSuite()
2829        if self.result.suiteStarted == false then
2830            error('LuaUnit:endSuite() -- suite was already ended' )
2831        end
2832        self.result.duration = os.clock()-self.result.startTime
2833        self.result.suiteStarted = false
2834
2835        -- Expose test counts for outputter's endSuite(). This could be managed
2836        -- internally instead by using the length of the lists of failed tests
2837        -- but unit tests rely on these fields being present.
2838        self.result.failureCount = #self.result.failedTests
2839        self.result.errorCount = #self.result.errorTests
2840        self.result.notSuccessCount = self.result.failureCount + self.result.errorCount
2841        self.result.skippedCount = #self.result.skippedTests
2842
2843        self.output:endSuite()
2844    end
2845
2846    function M.LuaUnit:setOutputType(outputType, fname)
2847        -- Configures LuaUnit runner output
2848        -- outputType is one of: NIL, TAP, JUNIT, TEXT
2849        -- when outputType is junit, the additional argument fname is used to set the name of junit output file
2850        -- for other formats, fname is ignored
2851        if outputType:upper() == "NIL" then
2852            self.outputType = NilOutput
2853            return
2854        end
2855        if outputType:upper() == "TAP" then
2856            self.outputType = TapOutput
2857            return
2858        end
2859        if outputType:upper() == "JUNIT" then
2860            self.outputType = JUnitOutput
2861            if fname then
2862                self.fname = fname
2863            end
2864            return
2865        end
2866        if outputType:upper() == "TEXT" then
2867            self.outputType = TextOutput
2868            return
2869        end
2870        error( 'No such format: '..outputType,2)
2871    end
2872
2873    --------------[[ Runner ]]-----------------
2874
2875    function M.LuaUnit:protectedCall(classInstance, methodInstance, prettyFuncName)
2876        -- if classInstance is nil, this is just a function call
2877        -- else, it's method of a class being called.
2878
2879        local function err_handler(e)
2880            -- transform error into a table, adding the traceback information
2881            return {
2882                status = NodeStatus.ERROR,
2883                msg = e,
2884                trace = string.sub(debug.traceback("", 3), 2)
2885            }
2886        end
2887
2888        local ok, err
2889        if classInstance then
2890            -- stupid Lua < 5.2 does not allow xpcall with arguments so let's use a workaround
2891            ok, err = xpcall( function () methodInstance(classInstance) end, err_handler )
2892        else
2893            ok, err = xpcall( function () methodInstance() end, err_handler )
2894        end
2895        if ok then
2896            return {status = NodeStatus.SUCCESS}
2897        end
2898
2899        local iter_msg
2900        iter_msg = self.exeRepeat and 'iteration '..self.currentCount
2901
2902        err.msg, err.status = M.adjust_err_msg_with_iter( err.msg, iter_msg )
2903
2904        if err.status == NodeStatus.SUCCESS or err.status == NodeStatus.SKIP then
2905            err.trace = nil
2906            return err
2907        end
2908
2909        -- reformat / improve the stack trace
2910        if prettyFuncName then -- we do have the real method name
2911            err.trace = err.trace:gsub("in (%a+) 'methodInstance'", "in %1 '"..prettyFuncName.."'")
2912        end
2913        if STRIP_LUAUNIT_FROM_STACKTRACE then
2914            err.trace = stripLuaunitTrace(err.trace)
2915        end
2916
2917        return err -- return the error "object" (table)
2918    end
2919
2920
2921    function M.LuaUnit:execOneFunction(className, methodName, classInstance, methodInstance)
2922        -- When executing a test function, className and classInstance must be nil
2923        -- When executing a class method, all parameters must be set
2924
2925        if type(methodInstance) ~= 'function' then
2926            error( tostring(methodName)..' must be a function, not '..type(methodInstance))
2927        end
2928
2929        local prettyFuncName
2930        if className == nil then
2931            className = '[TestFunctions]'
2932            prettyFuncName = methodName
2933        else
2934            prettyFuncName = className..'.'..methodName
2935        end
2936
2937        if self.lastClassName ~= className then
2938            if self.lastClassName ~= nil then
2939                self:endClass()
2940            end
2941            self:startClass( className )
2942            self.lastClassName = className
2943        end
2944
2945        self:startTest(prettyFuncName)
2946
2947        local node = self.result.currentNode
2948        for iter_n = 1, self.exeRepeat or 1 do
2949            if node:isNotSuccess() then
2950                break
2951            end
2952            self.currentCount = iter_n
2953
2954            -- run setUp first (if any)
2955            if classInstance then
2956                local func = self.asFunction( classInstance.setUp ) or
2957                             self.asFunction( classInstance.Setup ) or
2958                             self.asFunction( classInstance.setup ) or
2959                             self.asFunction( classInstance.SetUp )
2960                if func then
2961                    self:updateStatus(self:protectedCall(classInstance, func, className..'.setUp'))
2962                end
2963            end
2964
2965            -- run testMethod()
2966            if node:isSuccess() then
2967                self:updateStatus(self:protectedCall(classInstance, methodInstance, prettyFuncName))
2968            end
2969
2970            -- lastly, run tearDown (if any)
2971            if classInstance then
2972                local func = self.asFunction( classInstance.tearDown ) or
2973                             self.asFunction( classInstance.TearDown ) or
2974                             self.asFunction( classInstance.teardown ) or
2975                             self.asFunction( classInstance.Teardown )
2976                if func then
2977                    self:updateStatus(self:protectedCall(classInstance, func, className..'.tearDown'))
2978                end
2979            end
2980        end
2981
2982        self:endTest()
2983    end
2984
2985    function M.LuaUnit.expandOneClass( result, className, classInstance )
2986        --[[
2987        Input: a list of { name, instance }, a class name, a class instance
2988        Ouptut: modify result to add all test method instance in the form:
2989        { className.methodName, classInstance }
2990        ]]
2991        for methodName, methodInstance in sortedPairs(classInstance) do
2992            if M.LuaUnit.asFunction(methodInstance) and M.LuaUnit.isMethodTestName( methodName ) then
2993                table.insert( result, { className..'.'..methodName, classInstance } )
2994            end
2995        end
2996    end
2997
2998    function M.LuaUnit.expandClasses( listOfNameAndInst )
2999        --[[
3000        -- expand all classes (provided as {className, classInstance}) to a list of {className.methodName, classInstance}
3001        -- functions and methods remain untouched
3002
3003        Input: a list of { name, instance }
3004
3005        Output:
3006        * { function name, function instance } : do nothing
3007        * { class.method name, class instance }: do nothing
3008        * { class name, class instance } : add all method names in the form of (className.methodName, classInstance)
3009        ]]
3010        local result = {}
3011
3012        for i,v in ipairs( listOfNameAndInst ) do
3013            local name, instance = v[1], v[2]
3014            if M.LuaUnit.asFunction(instance) then
3015                table.insert( result, { name, instance } )
3016            else
3017                if type(instance) ~= 'table' then
3018                    error( 'Instance must be a table or a function, not a '..type(instance)..' with value '..prettystr(instance))
3019                end
3020                local className, methodName = M.LuaUnit.splitClassMethod( name )
3021                if className then
3022                    local methodInstance = instance[methodName]
3023                    if methodInstance == nil then
3024                        error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) )
3025                    end
3026                    table.insert( result, { name, instance } )
3027                else
3028                    M.LuaUnit.expandOneClass( result, name, instance )
3029                end
3030            end
3031        end
3032
3033        return result
3034    end
3035
3036    function M.LuaUnit.applyPatternFilter( patternIncFilter, listOfNameAndInst )
3037        local included, excluded = {}, {}
3038        for i, v in ipairs( listOfNameAndInst ) do
3039            -- local name, instance = v[1], v[2]
3040            if  patternFilter( patternIncFilter, v[1] ) then
3041                table.insert( included, v )
3042            else
3043                table.insert( excluded, v )
3044            end
3045        end
3046        return included, excluded
3047    end
3048
3049    function M.LuaUnit:runSuiteByInstances( listOfNameAndInst )
3050        --[[ Run an explicit list of tests. Each item of the list must be one of:
3051        * { function name, function instance }
3052        * { class name, class instance }
3053        * { class.method name, class instance }
3054        ]]
3055
3056        local expandedList = self.expandClasses( listOfNameAndInst )
3057        if self.shuffle then
3058            randomizeTable( expandedList )
3059        end
3060        local filteredList, filteredOutList = self.applyPatternFilter(
3061            self.patternIncludeFilter, expandedList )
3062
3063        self:startSuite( #filteredList, #filteredOutList )
3064
3065        for i,v in ipairs( filteredList ) do
3066            local name, instance = v[1], v[2]
3067            if M.LuaUnit.asFunction(instance) then
3068                self:execOneFunction( nil, name, nil, instance )
3069            else
3070                -- expandClasses() should have already taken care of sanitizing the input
3071                assert( type(instance) == 'table' )
3072                local className, methodName = M.LuaUnit.splitClassMethod( name )
3073                assert( className ~= nil )
3074                local methodInstance = instance[methodName]
3075                assert(methodInstance ~= nil)
3076                self:execOneFunction( className, methodName, instance, methodInstance )
3077            end
3078            if self.result.aborted then
3079                break -- "--error" or "--failure" option triggered
3080            end
3081        end
3082
3083        if self.lastClassName ~= nil then
3084            self:endClass()
3085        end
3086
3087        self:endSuite()
3088
3089        if self.result.aborted then
3090            print("LuaUnit ABORTED (as requested by --error or --failure option)")
3091            os.exit(-2)
3092        end
3093    end
3094
3095    function M.LuaUnit:runSuiteByNames( listOfName )
3096        --[[ Run LuaUnit with a list of generic names, coming either from command-line or from global
3097            namespace analysis. Convert the list into a list of (name, valid instances (table or function))
3098            and calls runSuiteByInstances.
3099        ]]
3100
3101        local instanceName, instance
3102        local listOfNameAndInst = {}
3103
3104        for i,name in ipairs( listOfName ) do
3105            local className, methodName = M.LuaUnit.splitClassMethod( name )
3106            if className then
3107                instanceName = className
3108                instance = _G[instanceName]
3109
3110                if instance == nil then
3111                    error( "No such name in global space: "..instanceName )
3112                end
3113
3114                if type(instance) ~= 'table' then
3115                    error( 'Instance of '..instanceName..' must be a table, not '..type(instance))
3116                end
3117
3118                local methodInstance = instance[methodName]
3119                if methodInstance == nil then
3120                    error( "Could not find method in class "..tostring(className).." for method "..tostring(methodName) )
3121                end
3122
3123            else
3124                -- for functions and classes
3125                instanceName = name
3126                instance = _G[instanceName]
3127            end
3128
3129            if instance == nil then
3130                error( "No such name in global space: "..instanceName )
3131            end
3132
3133            if (type(instance) ~= 'table' and type(instance) ~= 'function') then
3134                error( 'Name must match a function or a table: '..instanceName )
3135            end
3136
3137            table.insert( listOfNameAndInst, { name, instance } )
3138        end
3139
3140        self:runSuiteByInstances( listOfNameAndInst )
3141    end
3142
3143    function M.LuaUnit.run(...)
3144        -- Run some specific test classes.
3145        -- If no arguments are passed, run the class names specified on the
3146        -- command line. If no class name is specified on the command line
3147        -- run all classes whose name starts with 'Test'
3148        --
3149        -- If arguments are passed, they must be strings of the class names
3150        -- that you want to run or generic command line arguments (-o, -p, -v, ...)
3151
3152        local runner = M.LuaUnit.new()
3153        return runner:runSuite(...)
3154    end
3155
3156    function M.LuaUnit:runSuite( ... )
3157
3158        local args = {...}
3159        if type(args[1]) == 'table' and args[1].__class__ == 'LuaUnit' then
3160            -- run was called with the syntax M.LuaUnit:runSuite()
3161            -- we support both M.LuaUnit.run() and M.LuaUnit:run()
3162            -- strip out the first argument
3163            table.remove(args,1)
3164        end
3165
3166        if #args == 0 then
3167            args = cmdline_argv
3168        end
3169
3170        local options = pcall_or_abort( M.LuaUnit.parseCmdLine, args )
3171
3172        -- We expect these option fields to be either `nil` or contain
3173        -- valid values, so it's safe to always copy them directly.
3174        self.verbosity     = options.verbosity
3175        self.quitOnError   = options.quitOnError
3176        self.quitOnFailure = options.quitOnFailure
3177
3178        self.exeRepeat            = options.exeRepeat
3179        self.patternIncludeFilter = options.pattern
3180        self.shuffle              = options.shuffle
3181
3182        if options.output then
3183            if options.output:lower() == 'junit' and options.fname == nil then
3184                print('With junit output, a filename must be supplied with -n or --name')
3185                os.exit(-1)
3186            end
3187            pcall_or_abort(self.setOutputType, self, options.output, options.fname)
3188        end
3189
3190        self:runSuiteByNames( options.testNames or M.LuaUnit.collectTests() )
3191
3192        return self.result.notSuccessCount
3193    end
3194-- class LuaUnit
3195
3196-- For compatbility with LuaUnit v2
3197M.run = M.LuaUnit.run
3198M.Run = M.LuaUnit.run
3199
3200function M:setVerbosity( verbosity )
3201    M.LuaUnit.verbosity = verbosity
3202end
3203M.set_verbosity = M.setVerbosity
3204M.SetVerbosity = M.setVerbosity
3205
3206
3207return M
3208