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