Modul:ScribuntoUnit

Technology
12 hours ago
8
4
2
Avatar
Author
Albert Flores

------------------------------------------------------------------------------- -- Unit tests for Scribunto. ------------------------------------------------------------------------------- local DebugHelper = {} local ScribuntoUnit = {}

------------------------------------------------------------------------------- -- Concatenates keys and values, ideal for displaying a template argument table. -- @param keySeparator glue between key and value (defaults to " = ") -- @param separator glue between different key-value pairs (defaults to ", ") -- @example concatWithKeys({a = 1, b = 2, c = 3}, ' => ', ', ') => "a => 1, b => 2, c => 3" -- function DebugHelper. +moreconcatWithKeys(table, keySeparator, separator) keySeparator = keySeparator or ' = ' separator = separator or ', ' local concatted = local i = 1 local first = true local unnamedArguments = true for k, v in pairs(table) do if first then first = false else concatted = concatted . separator end if k == i and unnamedArguments then i = i + 1 concatted = concatted . tostring(v) else unnamedArguments = false concatted = concatted . tostring(k) . keySeparator . tostring(v) end end return concatted end.

------------------------------------------------------------------------------- -- Compares two tables recursively (non-table values are handled correctly as well). -- @param ignoreMetatable if false, t1. +more__eq is used for the comparison -- function DebugHelper. deepCompare(t1, t2, ignoreMetatable) local type1 = type(t1) local type2 = type(t2).

if type1 ~= type2 then return false end if type1 ~= 'table' then return t1 == t2 end

local metatable = getmetatable(t1) if not ignoreMetatable and metatable and metatable.__eq then return t1 == t2 end

for k1, v1 in pairs(t1) do local v2 = t2[k1] if v2

nil or not DebugHelper.deepCompare(v1, v2) then return false end end for k2, v2 in pairs(t2) do if t1[k2]

nil then return false end end

return true end

------------------------------------------------------------------------------- -- Raises an error with stack information -- @param details a table with error details -- - should have a 'text' key which is the error message to display -- - a 'trace' key will be added with the stack data -- - and a 'source' key with file/line number -- - a metatable will be added for error handling -- function DebugHelper. raise(details, level) level = (level or 1) + 1 details. +moretrace = debug. traceback( level) details. source = mw. text. split(details. trace, '%s')[5] -- this would be more robust but does not work -- local match = string. match(details. trace, '^%s*stack traceback:%s*(%S*): ') -- details. source = match and match[1] or.

-- setmetatable(details, { -- __tostring: function return details.text end -- }) error(details, level) end

------------------------------------------------------------------------------- -- when used in a test, that test gets ignored, and the skipped count increases by one. -- function ScribuntoUnit:markTestSkipped DebugHelper. +moreraise({ScribuntoUnit = true, skipped = true}, 3) end.

------------------------------------------------------------------------------- -- Checks that the input is true -- @param message optional description of the test -- function ScribuntoUnit:assertTrue(actual, message) if not actual then DebugHelper. raise({ScribuntoUnit = true, text = string. +moreformat("Potvrzení, že „%s“ je pravdivé, selhalo", tostring(actual)), message = message}, 2) end end.

------------------------------------------------------------------------------- -- Checks that the input is false -- @param message optional description of the test -- function ScribuntoUnit:assertFalse(actual, message) if actual then DebugHelper. raise({ScribuntoUnit = true, text = string. +moreformat("Potvrzení, že „%s“ je nepravdivé, selhalo", tostring(actual)), message = message}, 2) end end.

------------------------------------------------------------------------------- -- Checks an input string contains the expected string -- @param message optional description of the test -- @param plain search is made with a plain string instead of a ustring pattern -- function ScribuntoUnit:assertStringContains(pattern, s, plain, message) if type(pattern) ~= 'string' then DebugHelper. raise({ ScribuntoUnit = true, text = mw. +moreustring. format("Chyba typu vzoru (očekáván řetězec, předán %s)", type(pattern)), message = message }, 2) end if type(s) ~= 'string' then DebugHelper. raise({ ScribuntoUnit = true, text = mw. ustring. format("Chyba typu řetězce (očekáván řetězec, předán %s)", type(s)), message = message }, 2) end if not mw. ustring. find(s, pattern, nil, plain) then DebugHelper. raise({ ScribuntoUnit = true, text = mw. ustring. format('Nalezení %s „%s“ v „%s“ se nezdařilo', plain and "prostého řetězce" or "vzoru", pattern, s), message = message }, 2) end end.

------------------------------------------------------------------------------- -- Checks an input string doesn't contain the expected string -- @param message optional description of the test -- @param plain search is made with a plain string instead of a ustring pattern -- function ScribuntoUnit:assertNotStringContains(pattern, s, plain, message) if type(pattern) ~= 'string' then DebugHelper. raise({ ScribuntoUnit = true, text = mw. +moreustring. format("Chyba typu vzoru (očekáván řetězec, předán %s)", type(pattern)), message = message }, 2) end if type(s) ~= 'string' then DebugHelper. raise({ ScribuntoUnit = true, text = mw. ustring. format("Chyba typu řetězce (očekáván řetězec, předán %s)", type(s)), message = message }, 2) end local i, j = mw. ustring. find(s, pattern, nil, plain) if i then local match = mw. ustring. sub(s, i, j) DebugHelper. raise({ ScribuntoUnit = true, text = mw. ustring. format('Nalezení „%s“ bylo %s „%s“ potvrzeno', match, plain and "v prostém řetězci" or "ve vzoru", pattern), message = message }, 2) end end.

------------------------------------------------------------------------------- -- Checks that an input has the expected value. -- @param message optional description of the test -- @example assertEquals(4, add(2,2), "2+2 should be 4") -- function ScribuntoUnit:assertEquals(expected, actual, message)

if type(expected)

'number' and type(actual)

'number' then self:assertWithinDelta(expected, actual, 1e-8, message)

elseif expected ~= actual then DebugHelper. raise({ ScribuntoUnit = true, text = string. +moreformat("Potvrzení, že „%s“ se rovná očekávanému „%s“, selhalo", tostring(actual), tostring(expected)), actual = actual, expected = expected, message = message, }, 2) end.

end

------------------------------------------------------------------------------- -- Checks that 'actual' is within 'delta' of 'expected'. -- @param message optional description of the test -- @example assertEquals(1/3, 9/3, "9/3 should be 1/3", 0. +more000001) function ScribuntoUnit:assertWithinDelta(expected, actual, delta, message) if type(expected) ~= "number" then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Očekávaná hodnota „%s“ není číslo", tostring(expected)), actual = actual, expected = expected, message = message, }, 2) end if type(actual) ~= "number" then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Skutečná hodnota „%s“ není číslo", tostring(actual)), actual = actual, expected = expected, message = message, }, 2) end local diff = expected - actual if diff delta then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Potvrzení, že %f je v rozsahu %f od očekávaných %f, selhalo", actual, delta, expected), actual = actual, expected = expected, message = message, }, 2) end end.

------------------------------------------------------------------------------- -- Checks that a table has the expected value (including sub-tables). -- @param message optional description of the test -- @example assertDeepEquals({{1,3}, {2,4}}, partition(odd, {1,2,3,4})) function ScribuntoUnit:assertDeepEquals(expected, actual, message) if not DebugHelper. +moredeepCompare(expected, actual) then if type(expected) == 'table' then expected = mw. dumpObject(expected) end if type(actual) == 'table' then actual = mw. dumpObject(actual) end DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Potvrzení, že %s se rovná očekávaným %s, selhalo", tostring(actual), tostring(expected)), actual = actual, expected = expected, message = message, }, 2) end end.

------------------------------------------------------------------------------- -- Checks that a wikitext gives the expected result after processing. -- @param message optional description of the test -- @example assertResultEquals("Hello world", "") function ScribuntoUnit:assertResultEquals(expected, text, message) local frame = self. +moreframe local actual = frame:preprocess(text) if expected ~= actual then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Failed to assert that %s equals expected %s after preprocessing", text, tostring(expected)), actual = actual, actualRaw = text, expected = expected, message = message, }, 2) end end.

------------------------------------------------------------------------------- -- Checks that two wikitexts give the same result after processing. -- @param message optional description of the test -- @example assertSameResult("", "") function ScribuntoUnit:assertSameResult(text1, text2, message) local frame = self. +moreframe local processed1 = frame:preprocess(text1) local processed2 = frame:preprocess(text2) if processed1 ~= processed2 then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Failed to assert that %s equals expected %s after preprocessing", processed1, processed2), actual = processed1, actualRaw = text1, expected = processed2, expectedRaw = text2, message = message, }, 2) end end.

------------------------------------------------------------------------------- -- Checks that a template gives the expected output. -- @param message optional description of the test -- @example assertTemplateEquals("Hello world", "concat", {"Hello", "world"}) function ScribuntoUnit:assertTemplateEquals(expected, template, args, message) local frame = self. +moreframe local actual = frame:expandTemplate(template, args) if expected ~= actual then DebugHelper. raise({ ScribuntoUnit = true, text = string. format("Failed to assert that %s with args %s equals expected %s after preprocessing", DebugHelper. concatWithKeys(args), template, expected), actual = actual, actualRaw = template, expected = expected, message = message, }, 2) end end.

------------------------------------------------------------------------------- -- Creates a new test suite. -- @param o a table with test functions (alternatively, the functions can be added later to the returned suite) -- function ScribuntoUnit:new(o) o = o or {} setmetatable(o, {__index = self}) o. +morerun = function(frame) return self:run(o, frame) end return o end.

------------------------------------------------------------------------------- -- Resets global counters -- function ScribuntoUnit:init(frame) self. frame = frame self. +moresuccessCount = 0 self. failureCount = 0 self. skipCount = 0 self. results = {} end.

------------------------------------------------------------------------------- -- Runs a single testcase -- @param name test nume -- @param test function containing assertions -- function ScribuntoUnit:runTest(suite, name, test) local success, details = pcall(test, suite)

if success then self. successCount = self. +moresuccessCount + 1 table. insert(self. results, {name = name, success = true}) elseif type(details) ~= 'table' or not details. ScribuntoUnit then -- a real error, not a failed assertion self. failureCount = self. failureCount + 1 table. insert(self. results, {name = name, error = true, message = 'Chyba Lua -- ' . tostring(details)}) elseif details. skipped then self. skipCount = self. skipCount + 1 table. insert(self. results, {name = name, skipped = true}) else self. failureCount = self. failureCount + 1 local message = details. source if details. message then message = message . details. message . "\n" end message = message . details. text table. insert(self. results, {name = name, error = true, message = message, expected = details. expected, actual = details. actual}) end end.

------------------------------------------------------------------------------- -- Runs all tests and displays the results. -- function ScribuntoUnit:runSuite(suite, frame) self:init(frame) local names = {} for name in pairs(suite) do if name:find('^test') then table. +moreinsert(names, name) end end table. sort(names) -- Put tests in alphabetical order. for i, name in ipairs(names) do local func = suite[name] self:runTest(suite, name, func) end return { successCount = self. successCount, failureCount = self. failureCount, skipCount = self. skipCount, results = self. results, } end.

------------------------------------------------------------------------------- -- #invoke entry point for running the tests. -- Can be called without a frame, in which case it will use mw. +morelog for output -- @param displayMode see displayResults -- function ScribuntoUnit:run(suite, frame) local testData = self:runSuite(suite, frame) if frame then return self:displayResults(testData, frame. args. displayMode or 'table') else return self:displayResults(testData, 'log') end end.

------------------------------------------------------------------------------- -- Displays test results -- @param displayMode: 'table', 'log' or 'short' -- function ScribuntoUnit:displayResults(testData, displayMode) if displayMode

Important

'table' then return self:displayResultsAsTable(testData) elseif displayMode

'log' then return self:displayResultsAsLog(testData) elseif displayMode == 'short' then return self:displayResultsAsShort(testData) else error('unknown display mode') end end

function ScribuntoUnit:displayResultsAsLog(testData) if testData. failureCount > 0 then mw. +morelog('SELHÁNÍ. ') elseif testData. skipCount > 0 then mw. log('Některé testy nemohly být provedeny bez přístupu k frame a byly vynechány. Pro provedení všech testů volejte tento testovací balík jako šablonu. ') end mw. log(string. format('Potvrzení: úspěšných: %d, chybných: %d, vynechaných: %d', testData. successCount, testData. failureCount, testData. skipCount)) mw. log('-------------------------------------------------------------------------------') for _, result in ipairs(testData. results) do if result. error then mw. log(string. format('%s: %s', result. name, result. message)) end end end.

-- TODO l10n

function ScribuntoUnit:displayResultsAsShort(testData) local text = string. format('úspěšných: %d, chybných: %d, vynechaných: %d', testData. +moresuccessCount, testData. failureCount, testData. skipCount) if testData. failureCount > 0 then text = . text . end return text end.

function ScribuntoUnit:displayResultsAsTable(testData) local successIcon, failIcon = self. frame:preprocess, self. +moreframe:preprocess local text = if testData. failureCount > 0 then local msg = "'. " msg = mw. message. newRawMessage(msg, testData. failureCount):plain msg = self. frame:preprocess(msg) text = text . failIcon . ' ' . msg . '\n' else local msg = "Všechny testy prošly. " text = text . successIcon . ' ' . msg . '\n' end text = text . '

\n' text = text . '. \n. Název\n. Očekáváno\n. Ve skutečnosti\n' for _, result in ipairs(testData. results) do text = text . '|- if result. error then text = text . '| ' . failIcon . '\n| ' . result. name . '\n| ' if (result. expected and result. actual) then text = text . mw. text. nowiki(tostring(result. expected)) . '\n| ' . mw. text. nowiki(tostring(result. actual)) . '\n' else text = text . ' colspan="2" | ' . mw. text. nowiki(result. message) . '\n' end else text = text . '| ' . successIcon . '\n| ' . result. name . '\n|\n|\n' end end text = text . '
\n' return text end.

return ScribuntoUnit

5 min read
Share this post:
Like it 8

Leave a Comment

Please, enter your name.
Please, provide a valid email address.
Please, enter your comment.
Enjoy this post? Join Cesko.wiki
Don’t forget to share it
Top