<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://ml.sayahna.org/index.php?action=history&amp;feed=atom&amp;title=Module%3AConvert%2Ftester</id>
	<title>Module:Convert/tester - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://ml.sayahna.org/index.php?action=history&amp;feed=atom&amp;title=Module%3AConvert%2Ftester"/>
	<link rel="alternate" type="text/html" href="http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;action=history"/>
	<updated>2026-04-23T20:36:56Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.31.5</generator>
	<entry>
		<id>http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=12745&amp;oldid=prev</id>
		<title>Cvr: 1 revision</title>
		<link rel="alternate" type="text/html" href="http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=12745&amp;oldid=prev"/>
		<updated>2014-08-22T07:50:43Z</updated>

		<summary type="html">&lt;p&gt;1 revision&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 07:50, 22 August 2014&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Cvr</name></author>
		
	</entry>
	<entry>
		<id>http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=12744&amp;oldid=prev</id>
		<title>Cvr: 1 revision</title>
		<link rel="alternate" type="text/html" href="http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=12744&amp;oldid=prev"/>
		<updated>2014-04-10T10:02:52Z</updated>

		<summary type="html">&lt;p&gt;1 revision&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 10:02, 10 April 2014&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Cvr</name></author>
		
	</entry>
	<entry>
		<id>http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=6091&amp;oldid=prev</id>
		<title>Admin: 1 revision</title>
		<link rel="alternate" type="text/html" href="http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=6091&amp;oldid=prev"/>
		<updated>2014-04-07T07:24:19Z</updated>

		<summary type="html">&lt;p&gt;1 revision&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 07:24, 7 April 2014&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
		
	</entry>
	<entry>
		<id>http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=6090&amp;oldid=prev</id>
		<title>Trappist the monk: Add Citation/CS1/Date validation</title>
		<link rel="alternate" type="text/html" href="http://ml.sayahna.org/index.php?title=Module:Convert/tester&amp;diff=6090&amp;oldid=prev"/>
		<updated>2014-03-10T14:58:17Z</updated>

		<summary type="html">&lt;p&gt;Add Citation/CS1/Date validation&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Test the output from a template by comparing it with fixed text.&lt;br /&gt;
-- The expected text must be in a single line, but can include&lt;br /&gt;
-- &amp;quot;\n&amp;quot; (two characters) to indicate that a newline is expected.&lt;br /&gt;
-- Tests are run (or created) by setting p.tests (string or table), or&lt;br /&gt;
-- by setting page=PAGE_TITLE (and optionally section=SECTION_TITLE),&lt;br /&gt;
-- then executing run_tests (or make_tests).&lt;br /&gt;
&lt;br /&gt;
local function collection()&lt;br /&gt;
	-- Return a table to hold lines of text.&lt;br /&gt;
	return {&lt;br /&gt;
		n = 0,&lt;br /&gt;
		add = function (self, s)&lt;br /&gt;
			self.n = self.n + 1&lt;br /&gt;
			self[self.n] = s&lt;br /&gt;
		end,&lt;br /&gt;
		join = function (self, sep)&lt;br /&gt;
			return table.concat(self, sep or '\n')&lt;br /&gt;
		end,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function empty(text)&lt;br /&gt;
	-- Return true if text is nil or empty (assuming a string).&lt;br /&gt;
	return text == nil or text == ''&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function strip(text)&lt;br /&gt;
	-- Return text with no leading/trailing whitespace.&lt;br /&gt;
	return text:match(&amp;quot;^%s*(.-)%s*$&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function status_box(stats, expected, actual, iscomment)&lt;br /&gt;
	local label, bgcolor, align, isfail&lt;br /&gt;
	if iscomment then&lt;br /&gt;
		actual = ''&lt;br /&gt;
		align = 'center'&lt;br /&gt;
		bgcolor = 'silver'&lt;br /&gt;
		label = 'Cmnt'&lt;br /&gt;
	elseif expected == '' then&lt;br /&gt;
		stats.ignored = stats.ignored + 1&lt;br /&gt;
		return actual, ''&lt;br /&gt;
	elseif expected == actual then&lt;br /&gt;
		stats.pass = stats.pass + 1&lt;br /&gt;
		actual = ''&lt;br /&gt;
		align = 'center'&lt;br /&gt;
		bgcolor = 'green'&lt;br /&gt;
		label = 'Pass'&lt;br /&gt;
	else&lt;br /&gt;
		stats.fail = stats.fail + 1&lt;br /&gt;
		align = 'center'&lt;br /&gt;
		bgcolor = 'red'&lt;br /&gt;
		label = 'Fail'&lt;br /&gt;
		isfail = true&lt;br /&gt;
	end&lt;br /&gt;
	return actual, 'style=&amp;quot;text-align:' .. align .. ';color:white;background:' .. bgcolor .. ';&amp;quot; | ' .. label, isfail&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function status_text(stats)&lt;br /&gt;
	local bgcolor, ignored_text, msg&lt;br /&gt;
	if stats.fail == 0 then&lt;br /&gt;
		if stats.pass == 0 then&lt;br /&gt;
			bgcolor = 'salmon'&lt;br /&gt;
			msg = 'No tests performed'&lt;br /&gt;
		else&lt;br /&gt;
			bgcolor = 'green'&lt;br /&gt;
			msg = string.format('All %d tests passed', stats.pass)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		bgcolor = 'darkred'&lt;br /&gt;
		msg = string.format('%d test%s failed', stats.fail, stats.fail == 1 and '' or 's')&lt;br /&gt;
	end&lt;br /&gt;
	if stats.ignored == 0 then&lt;br /&gt;
		ignored_text = ''&lt;br /&gt;
	else&lt;br /&gt;
		bgcolor = 'salmon'&lt;br /&gt;
		ignored_text = string.format(', %d test%s ignored because expected text is blank', stats.ignored, stats.ignored == 1 and '' or 's')&lt;br /&gt;
	end&lt;br /&gt;
	return '&amp;lt;span style=&amp;quot;font-size:120%;color:white;background-color:' .. bgcolor .. ';&amp;quot;&amp;gt;' .. msg .. ignored_text .. '.&amp;lt;/span&amp;gt;'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function run_template(frame, template, forcename, collapse_multiline)&lt;br /&gt;
	-- Template &amp;quot;{{ example |  2  =  def  |  abc  |  name  =  ghi jkl  }}&amp;quot;&lt;br /&gt;
	-- gives args { &amp;quot;  abc  &amp;quot;, &amp;quot;def&amp;quot;, name = &amp;quot;ghi jkl&amp;quot; }.&lt;br /&gt;
	if template:sub(1, 2) == '{{' and template:sub(-2, -1) == '}}' then&lt;br /&gt;
		template = template:sub(3, -3) .. '|'  -- append sentinel to get last field&lt;br /&gt;
	else&lt;br /&gt;
		return '(invalid template)'&lt;br /&gt;
	end&lt;br /&gt;
	local args = {}&lt;br /&gt;
	local index = 1&lt;br /&gt;
	local templatename&lt;br /&gt;
	for field in template:gmatch('(.-)|') do&lt;br /&gt;
		if templatename == nil then&lt;br /&gt;
			templatename = forcename or strip(field)&lt;br /&gt;
			if templatename == '' then&lt;br /&gt;
				return '(invalid template)'&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			local k, eq, v = field:match(&amp;quot;^(.-)(=)(.*)$&amp;quot;)&lt;br /&gt;
			if eq then&lt;br /&gt;
				k, v = strip(k), strip(v)  -- k and/or v can be empty&lt;br /&gt;
				local i = tonumber(k)&lt;br /&gt;
				if i and i &amp;gt; 0 and string.match(k, '^%d+$') then&lt;br /&gt;
					args[i] = v&lt;br /&gt;
				else&lt;br /&gt;
					args[k] = v&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				while args[index] ~= nil do&lt;br /&gt;
					-- Skip any explicit numbered parameters like &amp;quot;|5=five&amp;quot;.&lt;br /&gt;
					index = index + 1&lt;br /&gt;
				end&lt;br /&gt;
				args[index] = field&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local function expand(t)&lt;br /&gt;
		return frame:expandTemplate(t)&lt;br /&gt;
	end&lt;br /&gt;
	local ok, result = pcall(expand, { title = templatename, args = args })&lt;br /&gt;
	if not ok then&lt;br /&gt;
		result = 'Error: ' .. result&lt;br /&gt;
	end&lt;br /&gt;
	if collapse_multiline then&lt;br /&gt;
		result = result:gsub('\n', '\\n')&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _make_tests(frame, all_tests, forcename)&lt;br /&gt;
	local maxlen = 38&lt;br /&gt;
	for _, item in ipairs(all_tests) do&lt;br /&gt;
		local template = item[1]&lt;br /&gt;
		if template then&lt;br /&gt;
			local templen = mw.ustring.len(template)&lt;br /&gt;
			item.templen = templen&lt;br /&gt;
			if maxlen &amp;lt; templen and templen &amp;lt;= 70 then&lt;br /&gt;
				maxlen = templen&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local result = collection()&lt;br /&gt;
	for _, item in ipairs(all_tests) do&lt;br /&gt;
		local template = item[1]&lt;br /&gt;
		if template then&lt;br /&gt;
			local actual = run_template(frame, template, forcename, true)&lt;br /&gt;
			local pad = string.rep(' ', maxlen - item.templen) .. '  '&lt;br /&gt;
			result:add(template .. pad .. actual)&lt;br /&gt;
		else&lt;br /&gt;
			local text = item.text&lt;br /&gt;
			if text then&lt;br /&gt;
				result:add(text)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- Pre tags returned by a module are html tags, not like wikitext &amp;lt;pre&amp;gt;...&amp;lt;/pre&amp;gt;.&lt;br /&gt;
	return '&amp;lt;pre&amp;gt;\n' .. mw.text.nowiki(result:join()) .. '\n&amp;lt;/pre&amp;gt;\n'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _run_tests(frame, all_tests, forcename)&lt;br /&gt;
	local function safe_cell(text, multiline)&lt;br /&gt;
		-- For testing {{convert}}, want wikitext like '[[kilogram|kg]]' to be unchanged&lt;br /&gt;
		-- so the link works and so the displayed text is short (just &amp;quot;kg&amp;quot; in example).&lt;br /&gt;
		text = text:gsub('(%[%[[^%[%]]-)|(.-%]%])', '%1\0%2')  -- replace pipe in piped link with a zero byte&lt;br /&gt;
		text = text:gsub('{', '&amp;amp;#123;'):gsub('|', '&amp;amp;#124;')    -- escape '{' and '|'&lt;br /&gt;
		text = text:gsub('%z', '|')                            -- restore pipe in piped link&lt;br /&gt;
		if multiline then&lt;br /&gt;
			text = text:gsub('\\n', '&amp;lt;br /&amp;gt;')&lt;br /&gt;
		end&lt;br /&gt;
		return text&lt;br /&gt;
	end&lt;br /&gt;
	local function nowiki_cell(text, multiline)&lt;br /&gt;
		text = mw.text.nowiki(text)&lt;br /&gt;
		if multiline then&lt;br /&gt;
			text = text:gsub('\\n', '&amp;lt;br /&amp;gt;')&lt;br /&gt;
		end&lt;br /&gt;
		return text&lt;br /&gt;
	end&lt;br /&gt;
	local stats = { pass = 0, fail = 0, ignored = 0 }&lt;br /&gt;
	local result = collection()&lt;br /&gt;
	result:add('{| class=&amp;quot;wikitable&amp;quot;')&lt;br /&gt;
	result:add('! Template !! Expected !! Actual, if different !! Status')&lt;br /&gt;
	for _, item in ipairs(all_tests) do&lt;br /&gt;
		local template, expected = item[1], item[2] or ''&lt;br /&gt;
		if template then&lt;br /&gt;
			local actual = run_template(frame, template, forcename, true)&lt;br /&gt;
			local sbox, isfail&lt;br /&gt;
			actual, sbox, isfail = status_box(stats, expected, actual)&lt;br /&gt;
			result:add('|-')&lt;br /&gt;
			result:add('| ' .. safe_cell(template))&lt;br /&gt;
			result:add('| ' .. safe_cell(expected, true))&lt;br /&gt;
			result:add('| ' .. safe_cell(actual, true))&lt;br /&gt;
			result:add('| ' .. sbox)&lt;br /&gt;
			if isfail then&lt;br /&gt;
				result:add('|-')&lt;br /&gt;
				result:add('| align=&amp;quot;center&amp;quot;| (above, nowiki)')&lt;br /&gt;
				result:add('| ' .. nowiki_cell(expected, true))&lt;br /&gt;
				result:add('| ' .. nowiki_cell(actual, true))&lt;br /&gt;
				result:add('|')&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			local text = item.text&lt;br /&gt;
			if text and text:sub(1, 3) == '---' then&lt;br /&gt;
				actual, sbox, isfail = status_box(stats, '', '', true)&lt;br /&gt;
				result:add('|-')&lt;br /&gt;
				result:add('| colspan=&amp;quot;3&amp;quot; style=&amp;quot;color:white;background:silver;&amp;quot; | ' .. safe_cell(strip(text:sub(4)), true))&lt;br /&gt;
				result:add('| ' .. sbox)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	result:add('|}')&lt;br /&gt;
	return status_text(stats) .. '\n\n' .. result:join()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_page_content(page_title)&lt;br /&gt;
	local t = mw.title.new(page_title)&lt;br /&gt;
	if t then&lt;br /&gt;
		local content = t:getContent()&lt;br /&gt;
		if content then&lt;br /&gt;
			if content:sub(-1) ~= '\n' then&lt;br /&gt;
				content = content .. '\n'&lt;br /&gt;
			end&lt;br /&gt;
			return content&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	error('Could not read wikitext from &amp;quot;[[' .. page_title .. ']]&amp;quot;.', 0)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _compare(frame, page_pairs)&lt;br /&gt;
	local function diff_link(title1, title2)&lt;br /&gt;
		return '&amp;lt;span class=&amp;quot;plainlinks&amp;quot;&amp;gt;[' ..&lt;br /&gt;
			tostring(mw.uri.fullUrl('Special:ComparePages',&lt;br /&gt;
				{ page1 = title1, page2 = title2 })) ..&lt;br /&gt;
			' diff]&amp;lt;/span&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
	local function link(title)&lt;br /&gt;
		return '[[' .. title .. ']]'&lt;br /&gt;
	end&lt;br /&gt;
	local function message(text, isgood)&lt;br /&gt;
		local color = isgood and 'green' or 'darkred'&lt;br /&gt;
		return '&amp;lt;span style=&amp;quot;color:' .. color .. ';&amp;quot;&amp;gt;' .. text .. '&amp;lt;/span&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
	local result = collection()&lt;br /&gt;
	for _, item in ipairs(page_pairs) do&lt;br /&gt;
		local label&lt;br /&gt;
		local title1 = item[1]&lt;br /&gt;
		local title2 = item[2]&lt;br /&gt;
		if title1 == title2 then&lt;br /&gt;
			label = message('same title', false)&lt;br /&gt;
		else&lt;br /&gt;
			local content1 = get_page_content(title1)&lt;br /&gt;
			local content2 = get_page_content(title2)&lt;br /&gt;
			if content1 == content2 then&lt;br /&gt;
				label = message('same content', true)&lt;br /&gt;
			else&lt;br /&gt;
				label = message('different', false) .. ' (' .. diff_link(title1, title2) .. ')'&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		result:add('*' .. link(title1) .. ' • ' .. link(title2) .. ' • ' .. label)&lt;br /&gt;
	end&lt;br /&gt;
	return result:join() .. '\n'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function sections(text)&lt;br /&gt;
	return {&lt;br /&gt;
		first = 1,  -- just after the newline at the end of the last heading&lt;br /&gt;
		this_section = 1,&lt;br /&gt;
		next_heading = function(self)&lt;br /&gt;
			local first = self.first&lt;br /&gt;
			while first &amp;lt;= #text do&lt;br /&gt;
				local last, heading&lt;br /&gt;
				first, last, heading = text:find('==+[\t ]*([^\n]-)[\t ]*==+[\t\r ]*\n', first)&lt;br /&gt;
				if first then&lt;br /&gt;
					if first == 1 or text:sub(first - 1, first - 1) == '\n' then&lt;br /&gt;
						self.this_section = first&lt;br /&gt;
						self.first = last + 1&lt;br /&gt;
						return heading&lt;br /&gt;
					end&lt;br /&gt;
					first = last + 1&lt;br /&gt;
				else&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			self.first = #text + 1&lt;br /&gt;
			return nil&lt;br /&gt;
		end,&lt;br /&gt;
		current_section = function(self)&lt;br /&gt;
			local first = self.this_section&lt;br /&gt;
			local last = text:find('\n==[^\n]-==[\t\r ]*\n', first)&lt;br /&gt;
			if not last then&lt;br /&gt;
				last = -1&lt;br /&gt;
			end&lt;br /&gt;
			return text:sub(first, last)&lt;br /&gt;
		end,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_tests(frame, tests)&lt;br /&gt;
	local args = frame.args&lt;br /&gt;
	local page_title, section_title = args.page, args.section&lt;br /&gt;
	local show_all = (args.show == 'all')&lt;br /&gt;
	if not empty(page_title) then&lt;br /&gt;
		if not empty(tests) then&lt;br /&gt;
			error('Invoke must not set &amp;quot;page=' .. page_title .. '&amp;quot; if also setting p.tests.', 0)&lt;br /&gt;
		end&lt;br /&gt;
		if page_title:sub(1, 2) == '[[' and page_title:sub(-2) == ']]' then&lt;br /&gt;
			page_title = strip(page_title:sub(3, -3))&lt;br /&gt;
		end&lt;br /&gt;
		tests = get_page_content(page_title)&lt;br /&gt;
		if not empty(section_title) then&lt;br /&gt;
			local s = sections(tests)&lt;br /&gt;
			while true do&lt;br /&gt;
				local heading = s:next_heading()&lt;br /&gt;
				if heading then&lt;br /&gt;
					if heading == section_title then&lt;br /&gt;
						tests = s:current_section()&lt;br /&gt;
						break&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					error('Section &amp;quot;' .. section_title .. '&amp;quot; not found in page [[' .. page_title .. ']].', 0)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if type(tests) ~= 'string' then&lt;br /&gt;
		if type(tests) == 'table' then&lt;br /&gt;
			return tests&lt;br /&gt;
		end&lt;br /&gt;
		error('No tests were specified; see [[Module:Convert/tester/doc]].', 0)&lt;br /&gt;
	end&lt;br /&gt;
	if tests:sub(-1) ~= '\n' then&lt;br /&gt;
		tests = tests .. '\n'&lt;br /&gt;
	end&lt;br /&gt;
	local template_count = 0&lt;br /&gt;
	local all_tests = collection()&lt;br /&gt;
	for line in (tests):gmatch('([^\n]-)[\t\r ]*\n') do&lt;br /&gt;
		local template, expected = line:match('^({{.-}})%s*(.-)%s*$')&lt;br /&gt;
		if template then&lt;br /&gt;
			template_count = template_count + 1&lt;br /&gt;
			all_tests:add({ template, expected })&lt;br /&gt;
		elseif show_all then&lt;br /&gt;
			all_tests:add({ text = line })&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if template_count == 0 then&lt;br /&gt;
		error('No templates found; see [[Module:Convert/tester/doc]].', 0)&lt;br /&gt;
	end&lt;br /&gt;
	return all_tests&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function main(frame, p, worker)&lt;br /&gt;
	local args = frame.args&lt;br /&gt;
	local ok, result = pcall(get_tests, frame, p.tests)&lt;br /&gt;
	if ok then&lt;br /&gt;
		ok, result = pcall(worker, frame, result, args.template)&lt;br /&gt;
		if ok then&lt;br /&gt;
			return result&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return '&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Error&amp;lt;/strong&amp;gt;\n\n' .. result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local modules = {&lt;br /&gt;
	-- For convenience, a key defined here can be used to refer to the&lt;br /&gt;
	-- corresponding list of modules.&lt;br /&gt;
	convert = {&lt;br /&gt;
		'Convert',&lt;br /&gt;
		'Convert/data',&lt;br /&gt;
		'Convert/text',&lt;br /&gt;
		'Convert/extra',&lt;br /&gt;
	},&lt;br /&gt;
	cs1 = {&lt;br /&gt;
		'Citation/CS1',&lt;br /&gt;
		'Citation/CS1/Configuration',&lt;br /&gt;
	},&lt;br /&gt;
	cs1all = {&lt;br /&gt;
		'Citation/CS1',&lt;br /&gt;
		'Citation/CS1/Configuration',&lt;br /&gt;
		'Citation/CS1/Whitelist',&lt;br /&gt;
		'Citation/CS1/Date validation',&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p.compare(frame)&lt;br /&gt;
	local pairs = p.pairs&lt;br /&gt;
	if not pairs then&lt;br /&gt;
		local args = frame.args&lt;br /&gt;
		if not args[2] then&lt;br /&gt;
			local builtins = modules[args[1] or 'convert']&lt;br /&gt;
			if builtins then&lt;br /&gt;
				args = builtins&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		pairs = {}&lt;br /&gt;
		for i, title in ipairs(args) do&lt;br /&gt;
			if not title:find(':', 1, true) then&lt;br /&gt;
				title = 'Module:' .. title&lt;br /&gt;
			end&lt;br /&gt;
			pairs[i] = { title, title .. '/sandbox' }&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local ok, result = pcall(_compare, frame, pairs)&lt;br /&gt;
	if ok then&lt;br /&gt;
		return result&lt;br /&gt;
	end&lt;br /&gt;
	return '&amp;lt;strong class=&amp;quot;error&amp;quot;&amp;gt;Error&amp;lt;/strong&amp;gt;\n\n' .. result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.check_sandbox = p.compare&lt;br /&gt;
&lt;br /&gt;
function p.make_tests(frame)&lt;br /&gt;
	return main(frame, p, _make_tests)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.run_tests(frame)&lt;br /&gt;
	return main(frame, p, _run_tests)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Trappist the monk</name></author>
		
	</entry>
</feed>