Mô đun:Convert/tester

-- 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 function collection()
-- Return a table to hold lines of text.
return {
n = 0,
add = function (self, s)
self.n = self.n + 1
self[self.n] = s
end,
join = function (self, sep)
return table.concat(self, sep or '\n')
end,
}
end

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 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 expected == actual then
stats.pass = stats.pass + 1
actual =
align = 'center'
bgcolor = 'green'
label = 'Được'
else
stats.fail = stats.fail + 1
align = 'center'
bgcolor = 'red'
label = 'Hỏng'
isfail = true
end
return actual, 'style="text-align:' .. align .. ';color:white;background:' .. bgcolor .. ';" | ' .. label, isfail
end

local function status_text(stats)
local bgcolor, ignored_text, msg
if stats.fail 0 then
if stats.pass 0 then
bgcolor = 'salmon'
msg = 'Không kiểm thử trường hợp nào'
else
bgcolor = 'green'
msg = string.format('Tất cả %d trường hợp qua được', stats.pass)
end
else
bgcolor = 'darkred'
msg = string.format('%d trường hợp thị thất bại', stats.fail)
end
if stats.ignored == 0 then
ignored_text =
else
bgcolor = 'salmon'
ignored_text = string.format(', đã bỏ qua %d trường hợp vì không có văn bản mong đợi', stats.ignored)
end
return .. msg .. ignored_text .. '.'
end

local function run_template(frame, template, forcename, collapse_multiline)
-- Template ""
-- gives args { " abc ", "def", name = "ghi jkl" }.
if template:sub(1, 2) == then
template = template:sub(3, -3) .. '|' -- append sentinel to get last field
else
return '(invalid template)'
end
local args = {}
local index = 1
local templatename
for field in template:gmatch('(.-)|') do
if templatename == nil then
templatename = forcename 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
args[i] = v
else
args[k] = v
end
else
while args[index] ~= nil do
-- Skip any explicit numbered parameters like "|5=five".
index = index + 1
end
args[index] = field
end
end
end
local function expand(t)
return frame:expandTemplate(t)
end
local ok, result = pcall(expand, { title = templatename, args = args })
if not ok then
result = 'Error: ' .. result
end
if collapse_multiline then
result = result:gsub('\n', '\\n')
end
return result
end

local function _make_tests(frame, all_tests, forcename)
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 ....
return '\n' .. mw.text.nowiki(result:join()) .. '\n\n'
end

local function _run_tests(frame, all_tests, forcename)
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('{', '&123;'):gsub('|', '&124;') -- escape '{' and '|'
text = text:gsub('%z', '|') -- restore pipe in piped link
if multiline then
text = text:gsub('\\n', )
end
return text
end
local function nowiki_cell(text, multiline)
text = mw.text.nowiki(text)
if multiline then
text = text:gsub('\\n', )
end
return text
end
local stats = { pass = 0, fail = 0, ignored = 0 }
local result = collection()
result:add()
return status_text(stats) .. '\n\n' .. result:join()
end

local function get_page_content(page_title)
local t = mw.title.new(page_title)
if t then
local content = t:getContent()
if content then
if content:sub(-1) ~= '\n' then
content = content .. '\n'
end
return content
end
end
error('Could not read wikitext from "' .. page_title .. '".', 0)
end

local function _compare(frame, page_pairs)
local function diff_link(title1, title2)
return '[' ..
tostring(mw.uri.fullUrl('Special:ComparePages',
{ page1 = title1, page2 = title2 })) ..
' diff]'
end
local function link(title)
return .. title ..
end
local function message(text, isgood)
local color = isgood and 'green' or 'darkred'
return .. text ..
end
local result = collection()
for _, item in ipairs(page_pairs) do
local label
local title1 = item[1]
local title2 = item[2]
if title1 == title2 then
label = message('same title', false)
else
local content1 = get_page_content(title1)
local content2 = get_page_content(title2)
if content1 == content2 then
label = message('same content', true)
else
label = message('different', false) .. ' (' .. diff_link(title1, title2) .. ')'
end
end
result:add('' .. link(title1) .. ' • ' .. link(title2) .. ' • ' .. label)
end
return result:join() .. '\n'
end

local function sections(text)
return {
first = 1, -- just after the newline at the end of the last heading
this_section = 1,
next_heading = function(self)
local first = self.first
while first Error\n\n' .. result
end

local modules = {
-- For convenience, a key defined here can be used to refer to the
-- corresponding list of modules.
convert = {
'Convert',
'Convert/data',
'Convert/text',
'Convert/extra',
},
cs1 = {
'Citation/CS1',
'Citation/CS1/Configuration',
},
cs1all = {
'Citation/CS1',
'Citation/CS1/Configuration',
'Citation/CS1/Whitelist',
},
}

local p = {}

function p.compare(frame)
local pairs = p.pairs
if not pairs then
local args = frame.args
if not args[2] then
local builtins = modules[args[1] or 'convert']
if builtins then
args = builtins
end
end
pairs = {}
for i, title in ipairs(args) do
if not title:find(':', 1, true) then
title = 'Module:' .. title
end
pairs[i] = { title, title .. '/sandbox' }
end
end
local ok, result = pcall(_compare, frame, pairs)
if ok then
return result
end
return 'Error\n\n' .. result
end

p.check_sandbox = p.compare

function p.make_tests(frame)
return main(frame, p, _make_tests)
end

function p.run_tests(frame)
return main(frame, p, _run_tests)
end

return p