မဝ်ဂျူ:transliteration module testcases

Returns a function that creates testcases for a single romanization function.

Can be used by placing the following code in the testcases module:

return require("Module:transliteration module testcases") {
	module = "module name", -- for instance, "grc-translit"; module must export a tr function
    examples = {
    	{ example1, expected1 },
        { example2, expected2, script_if_different_from_below },
        { example2, expected2, script_if_different_from_below, lang_if_different_from_below },
        -- and so on
        -- Strings may be inserted to act as subheaders in the table of results.
    },
    sc = "script_code", lang = "language_code",
    -- options, e.g. nolink = true
}

The 'module' argument may be the transliteration function itself if so desired.

Positional arguments are supported and still used. There are then four required and one optional argument. The required arguments are module, examples, sc and lang as above. The fifth and optional argument is options, which gather the options as above together.

Options:

nolink
If true, the items in the "test" column will not be linked.
func_before_link
A function to process the text before it is linked or language-tagged. It receives and should return a string. For even greater flexibilty, it is actually passed the first four field of the example. (Omitted fields will be received as nil. This returned string is then treated as the name of a page in Wiktionary, unless option nolink is set.
func_with_link
A function to process the text, including the linking and language-tagging. It receives as argument the first four fields of each example, with omitted fields being nil. It returns the string to be output in the text field.
output_display
This is the display option as passed to function equals of Module:UnitTests.

As with Module:UnitTests, the documentation page should have {{#invoke:testcases module name here|run_tests}}.


local library_util = require('libraryUtil')
local check_type = library_util.checkType
	
-- Allow examples to be a string in a sort of TSV format,
-- consisting of one or more lines with two or three non-empty arguments
-- separated by one or more tabs. Single-line Lua comments are stripped, as well as
-- whitespace at the beginning and end of each line.
-- Empty lines and lines consisting of whitespace are skipped.
local function examples_string_to_array(examples)
	examples = examples:gsub("%s*%-%-[^\n]*", "")
	local array = {}
	for line in examples:gmatch("[^\n]+") do
		line = mw.text.trim(line)
		if line ~= "" then
			local original, expected, sc = line:match("^([^\t]+)\t+([^\t]+)\t-([^\t]-)$")
			if not original then
				error("The following line did not consist of two or three arguments separated by tabs:\n"
					.. line)
			end
			if sc == "" then sc = nil end
			table.insert(array, { original, expected, sc })
		end
	end
	return array
end

local function _check(funcName, expectType)
	return function(argIndex, arg, expectType, nilOk)
		check_type(funcName, argIndex, arg, expectType, nilOk)
	end
end

local function get_translit_function(module_name)
	local success, translit_module = pcall(require, 'Module:' .. module_name)
	if not success then
		error('Error requiring Module:' .. module_name .. ': ' .. tostring(translit_module))
	end
	translit_function = translit_module.tr
	if type(translit_function) ~= 'function' then
		error('Module:' .. module_name .. ' does not contain a function \'tr\'.')
	end
	return translit_function
end

return function(translit_function, examples, sc_code, lang_code, options)
	local module_name
	if type(translit_function) == 'table' then
		local args = translit_function
		module_name, examples, sc_code, lang_code
			= args.module, args.examples, args.sc, args.lang
		
		options = {}
		for k, v in pairs(args) do
			if not (k == 'module' or k == 'examples' or k == 'sc' or k == 'lang') then
				options[k] = v
			end
		end
	elseif type(translit_function) == 'string' then
		module_name, translit_function = translit_function, nil
	end
	
	if module_name then
		translit_function = get_translit_function(module_name)
	end
	
	--  C H E C K   P A R A M E T E R S  --
	local check_type = _check('translit_module_testcases')
	check_type(1, translit_function, 'function')
	check_type(3, sc_code, 'string')
	check_type(4, lang_code, 'string')
	check_type(5, options, 'table', true)
	
	options = options or {}
	
	if type(examples) == "table" then
		if not require('Module:table').isArray(examples) then
			error('Third argument or "examples" argument to translit_module_testcases should be an array.')
		end
	elseif type(examples) == "string" then
		examples = examples_string_to_array(examples)
	else
		error('Third argument or "examples" argument to translit_module_testcases should be an array or string.')
	end
	
	-- Get canonical name and validate language code.
	local lang_object = require('Module:languages').getByCode(lang_code)
		or error('The language code ' .. lang_code .. ' is not valid.')
	local canonical_name = lang_object:getCanonicalName()
	
	-- Validate script code.
	if not mw.loadData('Module:scripts/data')[sc_code] then
		error('The script code ' .. sc_code .. ' is not valid.')
	end
	
	
	--  M A K E   T E S T   F U N C T I O N  --
	local tests = require('Module:UnitTests')
	
	local normalize = mw.ustring.toNFD
	
	local opening_tag = '<span class="' .. sc_code .. '" lang="' .. lang_code .. '">'
	local template
	if options.nolink then
		template = opening_tag .. '&</span>'
	else
		template = opening_tag .. '[[&#' .. canonical_name .. '|&]]</span>'
	end
	
	if options.func_with_link then
		format_text = options.func_with_link
		if type(format_text) ~= "function" then
			error("func_with_link in options table should be a function.")
		end
	elseif options.func_before_link then
		local func = options.func_before_link
		if type(func) ~= "function" then
			error("func_before_link in options table should be a function.")
		end
		function format_text(text)
			return template:gsub('&', (func(text)))
		end
	else
		function format_text(text)
			return template:gsub('&', text)
		end
	end
	
	local self_equals_options = { display = options.output_display }
	function tests:check(example, expected, manual_sc, manual_lang)
		ex_tr = translit_function(example, manual_lang or lang_code, manual_sc or sc_code)
				or 'NIL' -- Handle transliteration function returning nil, a valid function value.
		self:equals(format_text(example, expected, manual_sc, manual_lang),
			normalize(ex_tr),
			expected and normalize(expected),
			self_equals_options
		)
	end
	
	function tests:test()
		self:iterate(examples, 'check')
	end
	
	if module_name then
		tests["testcases for <code>tr</code> function in [[Module:" .. module_name .. "]]"], tests.test
			= tests.test, nil
	end
	
	return tests
end