Module:Convert/tester explained

-- Test the output from a template by comparing it with fixed text.-- The expected text must be in a single line, but can include-- "\n" (two characters) to indicate that a newline is expected.-- Tests are run (or created) by setting p.tests (string or table), or-- by setting page=PAGE_TITLE (and optionally section=SECTION_TITLE),-- then executing run_tests (or make_tests).

local Collection = Collection.__index = Collectiondo function Collection:add(item) if item ~= nil then self.n = self.n + 1 self[self.n] = item end end function Collection:join(sep) return table.concat(self, sep) end function Collection.new return setmetatable(Collection) endend

local function empty(text) -- Return true if text is nil or empty (assuming a string). return text

nil or text

end

local function strip(text) -- Return text with no leading/trailing whitespace. return text:match("^%s*(.-)%s*$")end

local function normalize(text) -- Return text with any strip markers normalized by replacing the -- unique number with a fixed value so comparisons work. return text:gsub('(\127[^\127]*UNIQ[^\127]*%-)(%x+)(-QINU[^\127]*\127)', '%100000000%3')end

local function status_box(stats, expected, actual, iscomment) local label, bgcolor, align, isfail if iscomment then actual = align = 'center' bgcolor = 'silver' label = 'Cmnt' elseif expected

then stats.ignored = stats.ignored + 1 return , actual elseif normalize(expected)

normalize(actual) then stats.pass = stats.pass + 1 actual = align = 'center' bgcolor = 'green' label = 'Pass' else stats.fail = stats.fail + 1 align = 'center' bgcolor = 'red' label = 'Fail' isfail = true end local sbox = 'style="text-align:' .. align .. ';color:white;background:' .. bgcolor .. ';" | ' .. label return sbox, actual, isfailend

local function status_text(stats) local bgcolor, ignored_text, msg, ttext if stats.template then ttext = "Using : " else ttext = end if stats.fail

0 then if stats.pass

0 then bgcolor = 'salmon' msg = 'No tests performed' else bgcolor = 'green' msg = string.format('All %d tests passed', stats.pass) end else bgcolor = 'darkred' msg = string.format('%d test%s failed', stats.fail, stats.fail

1 and or 's') end if stats.ignored

0 then ignored_text = else bgcolor = 'salmon' ignored_text = string.format(', %d test%s ignored because expected text is blank', stats.ignored, stats.ignored

1 and or 's') end return ttext .. '

' .. msg .. ignored_text .. '.'end

local function run_template(frame, template, args, collapse_multiline) -- Template "" -- gives xargs . if template:sub(1, 2)

'' then template = template:sub(3, -3) .. '|' -- append sentinel to get last field else return '(invalid template)' end local xargs = local index = 1 local templatename local function put_arg(k, v) -- Kludge: Module:Val uses Module:Arguments which trims arguments and -- omits blank arguments. Simulate that here. -- LATER Need a parameter to control this. if templatename:sub(1, 3)

'val' then v = strip(v) if v

then return end end xargs[k] = v end template = template:gsub('(%[%[[^%[%]]-)|(.-%]%])', '%1\0%2') -- replace pipe in piped link with a zero byte for field in template:gmatch('(.-)|') do field = field:gsub('%z', '|') -- restore pipe in piped link if templatename

nil then templatename = args.template or strip(field) if templatename

then return '(invalid template)' end else local k, eq, v = field:match("^(.-)(=)(.*)$") if eq then k, v = strip(k), strip(v) -- k and/or v can be empty local i = tonumber(k) if i and i > 0 and string.match(k, '^%d+$') then put_arg(i, v) else put_arg(k, v) end else while xargs[index] ~= nil do -- Skip any explicit numbered parameters like "|5=five". index = index + 1 end put_arg(index, field) end end end if args.test and not xargs.test then -- For convert, allow test=preview or test=nopreview to be injected into -- the convert under test, if it does not already use that parameter. -- That allows, for example, a preview of make_tests to show nopreview results. xargs.test = args.test end local function expand(t) return frame:expandTemplate(t) end local ok, result = pcall(expand,) if not ok then result = 'Error: ' .. result end if collapse_multiline then result = result:gsub('\n', '\\n') end return resultend

local function _make_tests(frame, all_tests, args) local maxlen = 38 for _, item in ipairs(all_tests) do local template = item[1] if template then local templen = mw.ustring.len(template) item.templen = templen if maxlen < templen and templen <= 70 then maxlen = templen end end end local result = Collection.new for _, item in ipairs(all_tests) do local template = item[1] if template then local actual = run_template(frame, template, args, true) local pad = string.rep(' ', maxlen - item.templen) .. ' ' result:add(template .. pad .. actual) else local text = item.text if text then result:add(text) end end end -- Pre tags returned by a module are html tags, not like wikitext

...
. return '
\n' .. mw.text.nowiki(result:join('\n')) .. '\n
'end

local function _run_tests(frame, all_tests, args) local function safe_cell(text, multiline) -- For testing, want wikitext like 'kg' to be unchanged -- so the link works and so the displayed text is short (just "kg" in example). text = text:gsub('(%[%[[^%[%]]-)|(.-%]%])', '%1\0%2') -- replace pipe in piped link with a zero byte text = text:gsub('