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