Zum Inhalt springen

„Modul:Test“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
[gesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
Keine Bearbeitungszusammenfassung
Markierung: Zurückgesetzt
K Änderungen von 2A02:908:2214:A100:98E3:C0FF:FE50:40C0 (Diskussion) auf die letzte Version von Vollbracht zurückgesetzt
Markierung: Zurücksetzung
Zeile 23: Zeile 23:
an object representating whatever is necessary to call a function
an object representating whatever is necessary to call a function
constructors:
constructors:
new(frame, action, data) testcase
new(frame) an invoke call of a Test function
invoker(frame, action, data) testcase callable by invoke
requirer(frame, action, data) testcase callable by require
suggester(frame, test, result) an invoke call of a Test function
suggester(frame, test, result) an invoke call of a Test function
fields:
fields:
frame object copy of the calling objects frame
wikitext string available for invoke only
wikitext string available for invoke only
luatext string
luatext string
modulename string
modulename string
moduleprefix string 'Module' or 'Modul'
moduleprefix string 'Module' or 'Modul'
Module required module by this.moduleprefix:this.modulename
functionpath array of strings
functionpath array of strings
parameters array
parameters array
Zeile 38: Zeile 38:
unwraped luatext with its particular parameters
unwraped luatext with its particular parameters
toCode() returns <code>-wraped toString()
toCode() returns <code>-wraped toString()
getModule() returns required module by this name
extensionTag(element, core, attributes) copy of frames extensionTag
]]
]]
local Call = {
local Call = {
Zeile 73: Zeile 75:
action string representing the module and its directly exported
action string representing the module and its directly exported
function, which is to be tested
function, which is to be tested
possible formats:
"<module>/<function path>"
{modulename=<module name>, functionpath=<function path>}
{ moduleprefix=<module prefix>, modulename=<module name>,
functionpath=<function path> }
Call
data all parameters remaining in test case, which are handed over to
data all parameters remaining in test case, which are handed over to
the client module and function that is to be tested
the client module and function that is to be tested
returns the new call object
returns the new call object
]]
]]
function Call:new(frame, action, data)
function Call:invoker(frame, action, data)
if not frame or not frame.extensionTag or not action then return nil end
if not frame or not frame.extensionTag or not action then return nil end
if type(action) == 'string' then
action = mw.text.split(action, '$s*%/%s*')
end
if type(action) ~= 'table' or #action < 2 then return nil end
local result = {
local result = {
frame = frame,
frame = frame,
functionpath = { action[2] },
parameters = data
parameters = data
}
}
result.moduleprefix, result.modulename = action[1]:match('(Modul[e]?):(.*)')
local set, success, extendSearch = false
if not result.modulename then result.modulename = action[1] end
if type(action) == 'string' then
result.Module = nil
result.functionpath = {}
set = {action}
if not action:find('^Modul') then extendSearch = true end
while not result.Module do
set = {set[1]:match('(.*)%/(%a%w*)^')}
if not set[1] then return nil end
result.functionpath = {set[2], unpack(result.functionpath)}
success, result.Module = pcall(require, set[1])
if not success and extendSearch then
success, result.Module = pcall(require, 'Modul:' .. set[1])
if result.Module then
result.modulename = set[1]
result.moduleprefix = 'Modul:'
else
success, result.Module = pcall(require, 'Module:' .. set[1])
if result.Module then
result.modulename = set[1]
result.moduleprefix = 'Module:'
end
end
end
end
elseif type(action) == 'table' then
if not action.modulename then return nil end
if not action.functionpath then return nil end
result.modulename = action.modulename
result.functionpath = functionpath
if action.Module then
result.Module = action.Module
if action.moduleprefix then
result.moduleprefix = action.moduleprefix
end
else
if action.moduleprefix then
result.moduleprefix = action.moduleprefix
success, result.Module = pcall(require,
result.moduleprefix .. result.modulename)
if not success then return nil end
else
success, result.Module = pcall(require, result.modulename)
if not success then
local mn = 'Modul:' .. result.modulename
success, result.Module = pcall(require, mn)
if success then result.moduleprefix = 'Modul:'
else
local mn = 'Module:' .. result.modulename
success, result.Module = pcall(require, mn)
if success then result.moduleprefix = 'Module:'
else return nil end
end
end
end
end
end
local luadata = ''
local luadata = ''
local wtData = ''
local wtData = ''
Zeile 158: Zeile 104:
if luadata == '' then
if luadata == '' then
result.luatext = action[2] .. '()'
result.luatext = action[2] .. '()'
if #result.functionpath == 1 then
result.wikitext = '{{#invoke:' .. result.modulename .. ' |'
.. action[2] .. '}}'
result.wikitext = '{{#invoke:' .. result.modulename .. ' |'
.. action[2] .. '}}'
end
else
else
result.luatext = action[2] .. '{' .. luadata:sub(3) .. '}'
result.luatext = action[2] .. '{' .. luadata:sub(3) .. '}'
if #result.functionpath == 1 then
result.wikitext = '{{#invoke:' .. result.modulename .. ' |'
result.wikitext = '{{#invoke:' .. result.modulename .. ' |'
.. action[2] .. wtData:gsub('%s*$', '') .. '}}'
.. action[2] .. wtData:gsub('%s*$', '') .. '}}'
end
end
end
setmetatable(result, self)
setmetatable(result, self)
Zeile 174: Zeile 116:
end
end


--[[
requirer(frame, action, data)
constructor for a client call, i. e. a test case
parameters:
frame environmental frame of the test providing call
action string representing the module and its directly exported
function, which is to be tested
data all parameters remaining in test case, which are handed over to
the client module and function that is to be tested
returns the new call object
]]
function Call:requirer(frame, action, data)
if not frame or not frame.extensionTag or not action then return nil end
if type(action) == 'string' then
action = mw.text.split(action, '$s*%/%s*')
end
if type(action) ~= 'table' or #action < 2 then return nil end
local result = {
frame = frame,
functionpath = {},
parameters = data,
wikitext = nil
}
for i = 2, #action do table.insert(result.functionpath, action[i]) end
result.moduleprefix, result.modulename = action[1]:match('(Modul[e]?):(.*)')
if not result.modulename then result.modulename = action[1] end
local luadata = ''
for i, v in ipairs(data) do
luadata = luadata .. ', [' .. i .. '] = "' .. v .. '"'
end
for k, v in ipairs(data) do
luadata = luadata .. ', ' .. k .. ' = "' .. v .. '"'
end
if luadata == '' then
result.luatext = action[2] .. '()'
else
result.luatext = action[2] .. '{' .. luadata:sub(3) .. '}'
end
setmetatable(result, self)
self.__index = self
return result
end


--[[
--[[
Zeile 380: Zeile 364:
}
}
end
end
action = mw.text.split(action, '%/')
-- 2.: generate string representation from action
-- 2.: generate string representation from action
local e = ""
result.call = Call:new(frame, action, data)
if #action > 2 then
local e = ''
if not result.call then
result.call = Call:requirer(frame, action, data)
elseif #action == 2 then
e = 'Inappropriate argument "action" is "' .. action
result.call = Call:invoker(frame, action, data)
else
e = 'Inappropriate argument "action" is "' .. action[1]
.. '" but should be "<module name>/<function name>" instead.'
.. '" but should be "<module name>/<function name>" instead.'
return { Error = e }
return { Error = e }
Zeile 390: Zeile 378:
mw.logObject(result, 'result')
mw.logObject(result, 'result')
-- 3.: retreive executable function from action
-- 3.: retreive executable function from action
local Function = result.call.Module
local Function = result.call:getModule()
if not Function then
if not Function then
e = 'Inappropriate module name "' .. result.call.modulename
e = 'Inappropriate module name "' .. result.call.modulename

Version vom 26. Dezember 2022, 19:43 Uhr

Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus


--[=[  Test 2022-12-10
	test a module
	
	Autor: Vollbracht
	
* test.single(frame)
  {{#invoke:Test|single	|module=<module name> |expected=<html or wikitext>
						|func=<function to be tested>
						|<key=value> |<key=value> ... }}
  {{#invoke:Test|single	|module=<module name> |expected=<html or wikitext>
						|func=<function to be tested>
						|unnamed=<value>{!}<value>{!} ... }}
]=]

local p = {}

local USAGE =
[[<code>{{#invoke:Test|single|module=<module name> |expected=<html or wikitext>
|func=<function to be tested> |<key=value> |<key=value> ... }}</code>]]

--[[
	Call
	an object representating whatever is necessary to call a function
	constructors:
		new(frame)						an invoke call of a Test function
		invoker(frame, action, data)	testcase callable by invoke
		requirer(frame, action, data)	testcase callable by require
		suggester(frame, test, result)	an invoke call of a Test function
	fields:
		wikitext		string	available for invoke only
		luatext			string
		modulename		string
		moduleprefix	string	'Module' or 'Modul'
		functionpath	array of strings
		parameters		array
	methods:
		toString()		returns <nowiki>-wraped wikitext or (if inavailable)
						unwraped luatext with its particular parameters
		toCode()		returns <code>-wraped toString()
		getModule()		returns required module by this name
		extensionTag(element, core, attributes) copy of frames extensionTag
]]
local Call = {
	properties = {module=1, func=2, expected=4, unnamed=8, export=16},
	toString = function(this) -- ####
		if this.wikitext then
			return this.frame:extensionTag('nowiki', this.wikitext, {})
		end
		if this.wraper then return this.wraper end
		return this.luatext
	end,
	getModule = function(this)
		local name = this.modulename
		if not name or name == '' then return nil end
		if	this.wikitext and
			(not this.moduleprefix or this.moduleprefix == '') then
			local _, result = pcall(require, 'Module:' .. name)
			if not result then _, result = pcall(require, 'Modul:' .. name) end
			return result
		end
		if not this.moduleprefix or this.moduleprefix == '' then return nil end
		local _, result = pcall(require, this.moduleprefix .. ':' .. name)
		return result
	end,
	toCode = function(this)
		return this.frame:extensionTag('code', this:toString(), {})
	end,
}

--[[
	invoker(frame, action, data)
	constructor for a client call, i. e. a test case
	parameters:
		frame	environmental frame of the test providing call
		action	string representing the module and its directly exported
				function, which is to be tested
		data	all parameters remaining in test case, which are handed over to
				the client module and function that is to be tested
	returns the new call object
]]
function Call:invoker(frame, action, data)
	if not frame or not frame.extensionTag or not action then return nil end
	if type(action) == 'string' then
		action = mw.text.split(action, '$s*%/%s*')
	end
	if type(action) ~= 'table' or #action < 2 then return nil end
	local result = {
		frame = frame,
		functionpath = { action[2] },
		parameters = data
	}
	result.moduleprefix, result.modulename = action[1]:match('(Modul[e]?):(.*)')
	if not result.modulename then result.modulename = action[1] end
	local luadata = ''
	local wtData = ''
	for i, v in ipairs(data) do
		luadata = luadata .. ', [' .. i .. '] = "' .. v .. '"'
		wtData = wtData .. '|' .. v
	end
	for k, v in pairs(data) do
		luadata = luadata .. ', ' .. k .. ' = "' .. v .. '"'
		wtData = wtData .. '|' .. k .. '=' .. v .. ' '
	end
	if luadata == '' then
		result.luatext = action[2] .. '()'
		result.wikitext =	'{{#invoke:' .. result.modulename .. ' |'
						..	action[2] .. '}}'
	else
		result.luatext = action[2] .. '{' .. luadata:sub(3) .. '}'
		result.wikitext =	'{{#invoke:' .. result.modulename .. ' |'
						..	action[2] .. wtData:gsub('%s*$', '') .. '}}'
	end
	setmetatable(result, self)
	self.__index = self
	return result
end

--[[
	requirer(frame, action, data)
	constructor for a client call, i. e. a test case
	parameters:
		frame	environmental frame of the test providing call
		action	string representing the module and its directly exported
				function, which is to be tested
		data	all parameters remaining in test case, which are handed over to
				the client module and function that is to be tested
	returns the new call object
]]
function Call:requirer(frame, action, data)
	if not frame or not frame.extensionTag or not action then return nil end
	if type(action) == 'string' then
		action = mw.text.split(action, '$s*%/%s*')
	end
	if type(action) ~= 'table' or #action < 2 then return nil end
	local result = {
		frame = frame,
		functionpath = {},
		parameters = data,
		wikitext = nil
	}
	for i = 2, #action do table.insert(result.functionpath, action[i]) end
	result.moduleprefix, result.modulename = action[1]:match('(Modul[e]?):(.*)')
	if not result.modulename then result.modulename = action[1] end
	local luadata = ''
	for i, v in ipairs(data) do
		luadata = luadata .. ', [' .. i .. '] = "' .. v .. '"'
	end
	for k, v in ipairs(data) do
		luadata = luadata .. ', ' .. k .. ' = "' .. v .. '"'
	end
	if luadata == '' then
		result.luatext = action[2] .. '()'
	else
		result.luatext = action[2] .. '{' .. luadata:sub(3) .. '}'
	end
	setmetatable(result, self)
	self.__index = self
	return result
end

--[[
	suggester(frame, test, suggestion)
	constructor for a test call
	parameters:
		frame	environmental frame of the test providing call
		test	name of the used test function in Test module
		suggestion	suggested result for the tested function
	returns the new call object
]]
function Call:suggester(frame, test, suggestion)
	local result = {
		frame = frame,
		modulename = 'Test',
		functionpath = { test },
		parameters = frame.args
	}
	local luadata = ''
	local wtData = ''
	for i, v in ipairs(frame.args) do
		luadata = luadata .. ', [' .. i .. '] = "' .. v .. '"'
		wtData = wtData .. '|' .. v
	end
	for k, v in pairs(frame.args) do
		luadata = luadata .. ', ' .. k .. ' = "' .. v .. '"'
		wtData = wtData .. '|' .. k .. '=' .. v .. ' '
	end
	luadata = luadata .. ', expected = "' .. suggestion .. '"'
	wtData = wtData .. '|expected=' .. suggestion
	result.luatext = test .. '{' .. luadata:sub(3) .. '}'
	result.wikitext =	'{{#invoke:Test |' .. test .. ' ' .. wtData .. ' }}'
	setmetatable(result, self)
	self.__index = self
	return result
end

--[[
	Counter
	Object: value set of prefix, number and postfix with an optional key name
	
]]
local Counter = {
	use = function(this)
		local result = this.prefix .. tostring(this.value) .. this.postfix
		this.value = this.value + 1
		return result
	end,
	keyValue = function(this, index)
		return index + 1, this.prefix .. index .. this.postfix
	end
}
function Counter:new(source, key)
	if not source then return nil end
	local result = {}
	if key then result.key = key end
	if type(source) == 'string' then
		result.prefix, result.value, result.postfix
			= source:match('(.*)%<counter>(%d*)%<%/counter%>(.*)')
		if result.value then
			if result.value == '' then result.value = 0
			else result.value = tonumber(result.value) end
		else
			result.prefix, result.postfix
				= source:match('(.*)%<counter%s?%/%>(.*)')
			if not result.prefix then return nil end
			result.value = 0
		end
	end
	if type(source) == 'table' then result = source end
	if not result.prefix or not result.value or not result.postfix then
		return nil
	end
	setmetatable(result, self)
	self.__index = self
	return result
end

--[[
	Client
	a test type object
	constructor:
		new(frame)
	fields:
		call		a Call object
		expected	string for comparison
		actual		string or an object with a __tostring method
		(	Following fields are for internal use only, because they aren't
			balanced. Use get<Field>() instead!	)
		accordance1	wikitext - do not get
		accordance2	wikitext - do not get
		missedMark	wikitext - get includes accordances
		variation	wikitext - get includes accordances
	methods:
		getVariation()	colored wikitext output of variation
						including accordances
		getMissedMark()	colored wikitext output of missedMark
						including accordances
]]
local Client = {
	getVariation = function(this)
		if not this.variation then return '' end
		local result = this.accordance1
		if result then
			if result ~= '' then
				result = this.call.frame:extensionTag('span',
						this.call.frame:extensionTag('nowiki', result, {}),
						{ style="background-color:#bfa;" })
			end
		else result = '' end
		result = result
			..	this.call.frame:extensionTag('span',
					this.call.frame:extensionTag('nowiki', this.variation, {}),
				{ style="background-color:#fba;" })
		if not this.accordance2 or this.accordance2 == '' then
			return this.call.frame:extensionTag('code', result, {}) end
		return this.call.frame:extensionTag('code', result
			..	this.call.frame:extensionTag('span',
					this.call.frame:extensionTag(	'nowiki', this.accordance2,
													{}),
				{ style="background-color:#bfa;" }), {})
	end,
	getMissedMark = function(this)
		if not this.missedMark or this.missedMark == '' then
			return this.call.frame:extensionTag('code', 
				this.call.frame:extensionTag('span',
				this.call.frame:extensionTag('nowiki', this.expected, {}),
				{ style="background-color:#bfa;" }), {})
		end
		local result = this.accordance1
		if result then
			if result ~= '' then
				result = this.call.frame:extensionTag('span',
					this.call.frame:extensionTag('nowiki', result, {}),
					{ style="background-color:#bfa;" })
			end
		else result = '' end
		result =	result
			..	this.call.frame:extensionTag('nowiki', this.missedMark, {})
		if not this.accordance2 or this.accordance2 == '' then
			return this.call.frame:extensionTag('code', result, {}) end
		return this.call.frame:extensionTag('code', result
			..	this.call.frame:extensionTag('span',
					this.call.frame:extensionTag(	'nowiki', this.accordance2,
													{}),
				{ style="background-color:#bfa;" }), {})
	end,
	toString = function(this) -- ####
		if this.variation then return this:getVariation() end
		return this.actual
	end
}

function Client:new(frame)
	local result = {}
	local data = {}
	if #frame.args > 0 then data = {unpack(frame.args)} end
	local action = nil
	-- 1.: unwrap action and expectation from frame.args
	for k, v in pairs(frame.args) do
		if k == 'action' then
			-- extract 1st action only; actions may be delimited by '//'
			v = v:gsub('^%/*', '')
			local i = v:find('//')
			if i then
				action = v:sub(1, i-1)
				if #v > i+2 then v = v:sub(i+2)
				else v = nil end
			else
				action = v
				v = nil
			end
		elseif k == 'expected' then
			-- extract 1st expectation only;
			-- expectations may be delimited by '<split></split>'
			local i, j = v:find('.%<split%>%<%/split%>.')
			if i then
				result.expected = v:sub(1, i - 1)
				if #v > j+1 then v = v:sub(j + 1)
				else v = nil end
			else
				result.expected = v
				v = nil
			end
		else
			local s, c = v:gsub('%<inTestSeries%s*%>?%<?%/%>?', '')
			if c > 0 then
				s = s:gsub('inTestSeries%>', '')
				if not result.testSeries then result.testSeries = {} end
				table.insert(result.testSeries, k)
				v = s
			elseif v:find('%<counter') then
				if not result.counters then result.counters = {} end
				local c = Counter:new(v, k)
				if c then
					table.insert(result.counters, c)
					v = c:use()
				end
			end
		end
		if v then data[k] = v end
	end
	if not action then
		return {
			Error = 'Missing argument "action=<module name>/<function name>".'
		}
	end
	action = mw.text.split(action, '%/')
	-- 2.: generate string representation from action
	local e = ""
	if #action > 2 then
		result.call = Call:requirer(frame, action, data)
	elseif #action == 2 then
		result.call = Call:invoker(frame, action, data)
	else
		e =	'Inappropriate argument "action" is "' .. action[1]
		..	'" but should be "<module name>/<function name>" instead.'
		return { Error = e }
	end
	mw.logObject(result, 'result')
	-- 3.: retreive executable function from action
	local Function = result.call:getModule()
	if not Function then
		e =	'Inappropriate module name "' .. result.call.modulename
		..	'" in test call.'
		if not result.call.moduleprefix then
			if result.call.wikitext then
				e = e .. ' (Even with "Module:" or "Modul:" prefix!)'
			else
				e = e .. ' Try providing a "Module:" or "Modul:" prefix!'
			end
		end
		return { Error = e }
	end
	for _, v in ipairs(result.call.functionpath) do
		Function = Function[v]
		if not Function then
			e =	'Inavailable export "' .. v
			..	'" in ' .. result.call.luatext .. ' call.'
			return { Error = e }
		end
	end
	-- 4.: get actual result of execution of this function
	local handler = function(err)
		e = err
		mw.logObject(e, 'error in execution of ' .. result.call.luatext)
	end
	local success = false
	if result.call.wikitext then
		mw.logObject(result, 'try wikitext')
		local c = result.call
		local f =  frame:newChild({ title = c.modulename, args=c.parameters })
		fkt = function()
			return Function(f)
		end
		success, result.actual = xpcall(fkt, handler, {})
	end
	if not success then
		mw.logObject(result, 'try without wikitext')
		success, result.actual = xpcall(Function, handler, unpack(data))
	end
	if not success or e and e ~= '' then return {Error = e} end
	-- 5.: tostring actual result
	if type(result.actual) == 'table' and result.actual.toString then
		result.actual = result.actual:toString()
	elseif type(result.actual) ~= 'string' then
		handler = function(err)
			e = err
			mw.logObject(e, 'tostring error in result of '
						..	action[#action] .. ' function')
		end
		fkt = function()
			return tostring(result.actual)
		end
		success, result.actual = xpcall(fkt, handler, {})
		if not success or e and e ~= '' then return {Error = e} end
	end
	-- 6.: get dif
	mw.logObject(result, 'client at get dif')
	if not result.expected then
		
	elseif result.actual == result.expected then
		result.accordance1 = result.actual
	else
		local aList = mw.text.split(result.actual, '')
		local eList = mw.text.split(result.expected, '')
		local i = 1
		result.accordance1 = ""
		while i <= #aList and i <= #eList and aList[i] == eList[i] do
			result.accordance1 = result.accordance1 .. aList[i]
			i = i + 1
		end
		result.accordance1 = result.accordance1:gsub('&', '&amp;')
		local ia = #aList
		local ie = #eList
		result.accordance2 = ""
		while i <= ia and i <= ie and aList[ia] == eList[ie] do
			result.accordance2 = aList[ia] .. result.accordance2
			ia = ia - 1
			ie = ie - 1
		end
		result.accordance2 = result.accordance2:gsub('&', '&amp;')
		if i > ia then
			result.variation = '[..]'
		else
			result.variation = ''
			while i <= ia do
				result.variation = aList[ia] .. result.variation
				ia = ia - 1
			end
		end
		result.variation = result.variation:gsub('&', '&amp;')
		result.missedMark = ''
		while i <= ie do
			result.missedMark = eList[ie] .. result.missedMark
			ie = ie - 1
		end
		result.missedMark = result.missedMark:gsub('&', '&amp;')
	end
	setmetatable(result, self)
	self.__index = self
	return result
end

p.single = function(frame)
	if not frame.args then
		return	'First usage: ' .. frame:extensionTag('syntaxhighlight',
			'{{#invoke:Test|single|action=<module name>/<exported function>|...'
		..	'}}', { lang="html+handlebars"}) .. ' with whatever params your fun'
			..	'ction might need in addition'
	end
	local client = Client:new(frame)
	if client.Error then return client.Error end
	local result =	'<table class="wikitable"><tr><th>call</th><td>'
				..	frame:extensionTag('code', client.call:toString(), {})
				..	'</td></tr><tr><th>actual value</th><td>'
				..	client.actual .. '</td></tr>'
	if not client.expected then
		local call = Call:suggester(frame, 'single', client.actual)
		return	result .. '<tr><td colspan="2">following call whould indica'
			..	'te success:</td></tr><tr><td colspan="2">'
			..	frame:extensionTag('syntaxhighlight', call.wikitext, {
					lang="html+handlebars"
				}) .. '</td></tr></table>'
	end
	if client.variation then
		return	result .. '<tr><th>expected value></th><td>' ..	client.expected
			..	'</td></tr><tr><td colspan="2">' ..	client:getVariation()
			..	'</td></tr><tr><td colspan="2">'
			..	client:getMissedMark() .. '</td></tr></table>'
	end
	return result .. '</table>'
end

p.testTable = function(frame)
	
end

return p