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
			selfself.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'
		label = 'Cmnt'
	elseif expected ==  then
		stats.ignored = stats.ignored + 1
		return actual, 
	elseif expected == actual then
		stats.pass = stats.pass + 1
		actual = 
		align = 'center'
		label = 'Được'
	else
		stats.fail = stats.fail + 1
		align = 'center'
		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
			msg = 'Không kiểm thử trường hợp nào'
		else
			msg = string.format('Tất cả %d trường hợp qua được', stats.pass)
		end
	else
		msg = string.format('%d trường hợp thị thất bại', stats.fail)
	end
	if stats.ignored == 0 then
		ignored_text = 
	else
		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 "  name  =  ghi jkl  "
	-- 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
					argsi = v
				else
					argsk = v
				end
			else
				while argsindex ~= nil do
					-- Skip any explicit numbered parameters like "|5=five".
					index = index + 1
				end
				argsindex = 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 = item1
		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('{', '{'):gsub('|', '|')    -- 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 = item1
		local title2 = item2
		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 args2 then
			local builtins = modulesargs1 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
			pairsi = { 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