Module:Unitest Scribunto

From Guild of Archivists
Revision as of 07:26, 30 October 2015 by Alahmnat (talk | contribs) (1 revision imported)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
documentation view / edit
  • This module run some tests about Scribunto or Lua.
  • unitest_wwwww : function giving environment to document other tests, Where When What We Work.
  • Wiki and page under test.
  • Mediawiki version and revision universal time.
  • Universal runtime and namespaces.
  • doctable_frame_args, doctable_frame, doctable_mw : functions to list the contents of tables
  • levelmaxi : parameter to limit the number of levels of sub-tables. (default 99)
  • exclude1 exclude2, exclude3 : parameters to exclude the sub-tables if exclude1... is in there names.

unitest_wwwww

{{#invoke:Unitest Scribunto | unitest_wwwww }}

Page tested = https://archive.guildofarchivists.org/wiki/Module:Unitest_Scribunto
Mediawiki 1.39.7 - UTC revision time 2015-10-30 07:26:39 - Lua 5.1
Server runtime UTC 2024-11-25 09:17:45, namespace=Module

unitest

{{#invoke:Unitest Scribunto | unitest }}
* Init of unit tests. test_list = table
* Begin of unit tests :

Page tested = https://archive.guildofarchivists.org/wiki/Module:Unitest_Scribunto
Mediawiki 1.39.7 - UTC revision time 2015-10-30 07:26:39 - Lua 5.1
Server runtime UTC 2024-11-25 09:17:45, namespace=Module
1 frame.args Test OK : frame.args.lastname = "error".
2 sub table write OK Test OK : sub table write OK. / / / result OKOK  ; exec_ok=true ; value_ok=true ; var_tested=td.C.X ; value_before=xx ; value_after=okvalue ; A=aa ; Y=yy .
3 sub table write XX Test OK : sub table write OK. / / / result OKOK  ; exec_ok=true ; value_ok=true ; var_tested=td.C.X ; value_before=okvalue ; value_after=okvalue ; A=aa ; Y=yy .
  • End of unit tests. Statistic count : Nerr=0 / Ntests=3

doctable_frame_args

{{#invoke:Unitest Scribunto | doctable_frame_args | exclude1=space }}

  • Content of the frame_args table, begin : levelmaxi=99 exclude1=space exclude2=nil exclude3=nil
  • Table .frame_args vars : string - exclude1 = space,
  • Table .frame_args no functions.
  • Table .frame_args no sub-tables.
  • Content of the frame_args table, end.

doctable_frame

{{#invoke:Unitest Scribunto | doctable_frame | levelmaxi=2 | exclude1=space }}

  • Content of the frame table, begin : levelmaxi=2 exclude1=space exclude2=nil exclude3=nil
  • Table .frame no vars.
  • Table .frame functions : newTemplateParserValue, getParent, argumentPairs, extensionTag, callParserFunction, preprocess, getTitle, newParserValue, expandTemplate, getArgument, newChild,
  • Table .frame tables list : args,
    • Table .frame.args vars : string - levelmaxi = 2, string - exclude1 = space,
    • Table .frame.args no functions.
    • Table .frame.args no sub-tables.
  • Content of the frame table, end.

doctable_mw without (name)space(s)

{{#invoke:Unitest Scribunto | doctable_mw | levelmaxi= | exclude1=space }}

  • Content of the mw table, begin : levelmaxi=99 exclude1=space exclude2=nil exclude3=nil
  • Table .mw no vars.
  • Table .mw functions : incrementExpensiveFunctionCount, addWarning, getLanguage, getCurrentFrame, loadJsonData, allToString, executeFunction, logObject, loadData, clone, isSubsting, dumpObject, log, getContentLanguage,
  • Table .mw tables list : ext, smw, hash, title, uri, text, language, message, site, ustring, html,
    • Table .mw.ext no vars.
    • Table .mw.ext no functions.
    • Table .mw.ext tables list : TitleBlacklist, ParserFunctions, cargo,
      • Table .mw.ext.TitleBlacklist no vars.
      • Table .mw.ext.TitleBlacklist functions : test,
      • Table .mw.ext.TitleBlacklist no sub-tables.
      • Table .mw.ext.ParserFunctions no vars.
      • Table .mw.ext.ParserFunctions functions : expr,
      • Table .mw.ext.ParserFunctions no sub-tables.
      • Table .mw.ext.cargo no vars.
      • Table .mw.ext.cargo functions : declare, formatTable, store, query,
      • Table .mw.ext.cargo no sub-tables.
    • Table .mw.smw no vars.
    • Table .mw.smw functions : subobject, ask, info, set, getPropertyType, getQueryResult,
    • Table .mw.smw no sub-tables.
    • Table .mw.hash no vars.
    • Table .mw.hash functions : hashValue, listAlgorithms, setupInterface,
    • Table .mw.hash no sub-tables.
    • Table .mw.title no vars.
    • Table .mw.title functions : makeTitle, compare, equals, getCurrentTitle, new,
    • Table .mw.title no sub-tables.
    • Table .mw.uri no vars.
    • Table .mw.uri functions : parseQueryString, encode, fullUrl, canonicalUrl, buildQueryString, anchorEncode, validate, localUrl, decode, new,
    • Table .mw.uri no sub-tables.
    • Table .mw.text vars : number - JSON_PRETTY = 4, number - JSON_PRESERVE_KEYS = 1, number - JSON_TRY_FIXING = 2,
    • Table .mw.text functions : listToText, gsplit, nowiki, encode, decode, jsonEncode, truncate, trim, killMarkers, tag, unstripNoWiki, split, jsonDecode, unstrip,
    • Table .mw.text no sub-tables.
    • Table .mw.language no vars.
    • Table .mw.language functions : isValidCode, getFallbacksFor, isSupportedLanguage, new, fetchLanguageNames, isValidBuiltInCode, fetchLanguageName, isKnownLanguageTag, getContentLanguage,
    • Table .mw.language no sub-tables.
    • Table .mw.message no vars.
    • Table .mw.message functions : numParam, getDefaultLanguage, rawParam, newFallbackSequence, newRawMessage, new,
    • Table .mw.message no sub-tables.
    • Table .mw.site vars : string - siteName = Guild of Archivists, string - currentVersion = 1.39.7, string - scriptPath = /w, string - server = https://archive.guildofarchivists.org, string - stylePath = /w/skins,
    • Table .mw.site functions : interwikiMap,
    • Table .mw.site tables list : stats, talkNamespaces, namespaces, subjectNamespaces, contentNamespaces,
      • Table .mw.site.stats vars : number - articles = 2496, number - admins = 2, number - edits = 20938, number - users = 417, number - files = 901, number - pages = 6060, number - activeUsers = 5,
      • Table .mw.site.stats functions : usersInGroup, pagesInCategory, pagesInNamespace,
      • Table .mw.site.stats no sub-tables.
    • Table .mw.ustring vars : number - maxPatternLength = 10000, number - maxStringLength = 2097152,
    • Table .mw.ustring functions : byteoffset, isutf8, match, gmatch, toNFKD, upper, gsub, format, lower, sub, toNFKC, codepoint, rep, char, byte, len, find, gcodepoint, toNFC, toNFD,
    • Table .mw.ustring no sub-tables.
    • Table .mw.html no vars.
    • Table .mw.html functions : create,
    • Table .mw.html no sub-tables.
  • Content of the mw table, end.



--[[
-- Module:Unitest_Scribunto start
-- http://www.mediawiki.org/wiki/Module:Unitest_Scribunto

    Example of result :
* Init of unit tests. test_list = table
* Begin of unit tests :
* 1 Site tested : mw.uri.fullUrl = //fr.wikisource.org/wiki/Module:Auteur.
* 2 Environment : Mediawiki 1.21wmf10 (d6c6dba) version UTC 2013-03-01 21:53:59.
* 3 Runtime and spaces : server UTC 2013-03-04 01:09:22, namespace=Modèle module_space=Module, template_space=Modèle viewErrors=true.
* 4 frame.args Error : frame.args.lastname = "error". The OK value was "OK" = frame:getParent().args.lastname.
* 5 sub table write Test OK : sub table write OK. / / / result OKOK  ; exec_ok=true ; value_ok=true ; var_tested=td.C.X ; value_before=xx ; value_after=okvalue ; A=aa ; Y=yy .
* 6 Is Scribunto complete ? Error : Scribunto is not complete. / / /  ; math=table ; os=table ; package=table ; string=table ; table=table ; mw=table ; frame=table ; mw.language=table ; mw.message=nil ; mw.site=table ; mw.title=nil ; mw.uri=table ; mw.ustring=table .
* End of unit tests. Statistic count : Nerr=2 / Ntests=6

Guide and template

Unitest_Scribunto like/from Scribunto_LuaEngineTestBase
http://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Test_cases
class ClassNameTest extends Scribunto_LuaEngineTestBase {
    protected static $moduleName = 'ClassNameTest';
	
	function getTestModules() {
		return parent::getTestModules() + array(
			'ClassNameTest' => __DIR__ . 'ClassNameTests.lua';
		);
	}
}
local testframework = require 'Module:TestFramework'
return testframework.getTestProvider( {
	-- Tests go here
} )
Each test is itself a table, with the following properties:
    name: The name of the test.
    func: The function to execute.
    args: Optional table of arguments to pass to the function.
    expect: Results to expect.
    type: Optional "type" of the test, default is "Normal".

local function AuteurTestCurrentNamespace(space) {
	-- This repair a Lua bug : {{NAMESPACE}} give nothing in fr.wikisource.org
	current_space = frame:preprocess("{{NAMESPACE}}") -- present namespace from system
--	This repair a Lua bug : mw.site.namespaces.name is unknown
	module_space = frame:preprocess("{{ns:Module}}") -- Module namespace from system
	template_space = frame:preprocess("{{ns:Template}}") -- Module namespace from system
	author_space = frame:preprocess("{{ns:Author}}") -- Author namespace from system
	category_space = frame:preprocess("{{ns:Category}}") -- Category namespace from system
	if current_space == space then
	end
	return space .. "OK"
}

class ClassAuteurTest extends Scribunto_LuaEngineTestBase {
	protected static $moduleName = 'ClassAuteurTest';
	function getTestModules() {
		return parent::getTestModules() + array(
			'ClassAuteurTest' => __DIR__ . 'ClassAuteurTest.lua';
		);
	}
}

-- local unitestScribunto = require "Module:Unitest_Scribunto"

--]]

local formatdate = function(date_time)
	-- formatime(date_time)
	-- formate "20130302153557" en "2013-03-02 15:35:57"
	date_time = tostring(date_time.year) .. "-" .. tostring(date_time.month) .. "-" .. tostring(date_time.day) .. " " .. tostring(date_time.hour) .. ":" .. tostring(date_time.min) .. ":" .. tostring(date_time.sec)
	return date_time
end -- local formatdate = function(date_time)

local formatime = function(date_time)
	-- formatime(date_time)
	-- formate "20130302153557" en "2013-03-02 15:35:57"
	date_time = tostring(date_time)
	date_time = string.sub(date_time, 1, 4) .. "-" .. string.sub(date_time, 5, 6) .. "-" .. string.sub(date_time, 7, 8) .. " " .. string.sub(date_time, 9, 10) .. ":" .. string.sub( date_time, 11, 12) .. ":" .. string.sub(date_time, 13, 14)
	return date_time
end -- local formatime = function(date_time)

local function if_exist(ok, wt, mw_uri, mw_uri_name)
	-- ok, wt = if_exist(ok, wt, mw.uri, 'mw.uri')
	local isnil = tostring(mw_uri)
	local t = mw_uri_name .. '=' .. isnil
	if isnil == "nil" then -- Y a-t-il erreur de valeur ?
		ok = false -- Il y a une erreur de valeur
		wt = wt .. ' ; <span style="color:red;" >' .. t .. '</span>'
	else
		ok = ok -- Il n'y a pas d'erreur de valeur
		wt = wt .. ' ; ' .. t
	end
	return ok, wt
end -- function if_exist(ok, wt, mw_uri, mw_uri_name)

local sub_table_write_data = {
	A = "aa",
	B = "bb",
	C = {
		X = "xx",
		Y = "yy"
	},
	lastname = "Voltaire",
} -- sub_table_write_data

--	local arglingual = tostring(args_trad.lastname) -- multilingual name of the arg
--	local arglingual = sub_table_write_data.lastname

-- Series of unit tests. Série de tests unitaires.
-- require 'Module:TestFramework'
-- similar to : class ClassNameTest extends Scribunto_LuaEngineTestBase {
local tst = { 
	
	test_frame = nil, -- frame
	
	test_result = "",

	test_list = {
		-- Tests go here
	},

} -- tst object like Scribunto_LuaEngineTestBase

local Unitest_Scribunto_tested_site = { name = "Tested site",
	func_test = function(parm, frame)
	-- Get environment
		local ok, out_OK, out_HS = true, parm.expect, "error"
		local t = ""
		local mw_uri_fullUrl = mw.uri.fullUrl('Module:Auteur') --  "Module:Auteur" )
		t = t .. ": mw.uri.fullUrl = " .. tostring(mw_uri_fullUrl)
--		local mw_uri = mw.uri.new('Module:Auteur') --  "Module:Auteur" )
		-- mw.uri.new( s )
	--	local mw_title = mw.title.new('Module:Auteur') --  "Module:Auteur" )
--		t = t .. ": mw.uri.prefixedText = " .. tostring(mw_uri.prefixedText)
	-- futur : mw.title.new( 'Module:Foo', 'Template' ) will create an object for the page Module:Foo
	-- mw.title.makeTitle( 'Template', 'Module:Foo' ) will create an object for the page Template:Module:Foo.
	-- mw.title   object :
	-- nsText: The text of the namespace for the page.
	-- prefixedText: The title of the page, with the namespace and interwiki prefixes.
	-- fullText: The title of the page, with the namespace and interwiki prefixes and the fragment.
	
	--	local preprocess_url = frame:preprocess("{{fullurl:Module:Auteur}}")
	--	t = t .. "<br/> preprocess_url = " .. tostring(preprocess_url)
		out_OK = t
		out_HS = t
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = "_N_TEST_ <b>_PRM_NM_</b> _OUT_OK_.",
	iferr = '_N_TEST_ <b>_PRM_NM_</b> _OUT_HS_.</span>',
} -- Unitest_Scribunto_tested_site

local Unitest_Scribunto_environment = { name = "Environment",
	func_test = function(parm, frame)
	-- Get environment
		local ok, out_OK, out_HS = true, parm.expect, "error"
		local t = ""
		local date_time = frame:preprocess("{{#dateformat:{{CURRENTTIMESTAMP}}|ymd}}")
		local now_date = os.date("%Y-%m-%d %H:%M:%S")
		local mw_version = frame:preprocess("{{CURRENTVERSION}}")
		local revision_time = frame:preprocess("{{REVISIONTIMESTAMP}}")
		local dtnow, dtvers = now_date, revision_time
		-- dtnow = formatime(now_date)
		dtvers = formatime(dtvers)
		t = t .. ": Mediawiki " .. tostring(mw_version) .. " version UTC " .. tostring(dtvers)
	--	t = t .. " read by { {CURRENTVERSION} }"
		out_OK = t
		out_HS = t
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = "_N_TEST_ <b>_PRM_NM_</b> _OUT_OK_.",
	iferr = '_N_TEST_ <b>_PRM_NM_</b> _OUT_HS_.</span>',
} -- Unitest_Scribunto_environment

local Unitest_Scribunto_runtime_spaces = { name = "Runtime and spaces",
	func_test = function(parm, frame)
		local ok, out_OK, out_HS = true, parm.expect, "error"
		local now_date = os.date("%Y-%m-%d %H:%M:%S")
		local namespace = frame:preprocess("{{NAMESPACE}}") -- present namespace from system
		local module_space = frame:preprocess("{{ns:Module}}") -- Module namespace from system
		local template_space = frame:preprocess("{{ns:Template}}") -- Module namespace from system
		local viewErrors = (namespace == module_space) or (namespace == template_space)
		local t = ""
		t = t .. ": server UTC " .. tostring(now_date) .. ", namespace=" .. tostring(namespace)
		t = t .. " module_space=" .. tostring(module_space) .. ", template_space=" .. tostring(template_space)
		t = t .. " viewErrors=" .. tostring(viewErrors)
--[[
		current_space = frame:preprocess("{{NAMESPACE}}") -- present namespace from system
		-- mw.site.namespaces.name is unknown
		module_space = frame:preprocess("{{ns:Module}}") -- Module namespace from system
		template_space = frame:preprocess("{{ns:Template}}") -- Module namespace from system
		author_space = frame:preprocess("{{ns:Author}}") -- Author namespace from system
		category_space = frame:preprocess("{{ns:Category}}") -- Category namespace from system
		local current_space = frame:preprocess("{{NAMESPACE}}") -- present namespace from system
		local REVISIONUSER = frame:preprocess("{{REVISIONUSER}}") -- current user when previewing an edit
		local ns_Module = frame:preprocess("{{ns:Module}}") -- {{ns:User}} namespace translated
		local ns_Template = frame:preprocess("{{ns:Template}}") -- {{ns:User}} namespace translated
		local int_edit = frame:preprocess("{{int:edit}}") -- {{int:edit}} → Edit, translate "edit"
		local language_ar = frame:preprocess("{{#language:ar}}") -- full name of the language
		-- {{FULLPAGENAMEE}} {{PAGENAMEE}} {{BASEPAGENAMEE}} {{SUBPAGENAMEE}}
		-- {{SUBJECTPAGENAMEE}} {{ARTICLEPAGENAMEE}} {{TALKPAGENAMEE}}
		local ARTICLEPAGENAMEE = frame:preprocess("{{ARTICLEPAGENAMEE}}")
		local date_time = frame:preprocess("{{#dateformat:{{CURRENTTIMESTAMP}}|ymd}}")
		local mw_version = frame:preprocess("{{CURRENTVERSION}}")
		local revision_time = frame:preprocess("{{REVISIONTIMESTAMP}}")
		local now_date = os.date("%Y-%m-%d %H:%M:%S")
	--	local date_time = frame:preprocess("{{#dateformat:{{CURRENTTIMESTAMP}}|ymd}}")
	--	local mw_version = frame:preprocess("{{CURRENTVERSION}}")
	--	local revision_time = frame:preprocess("{{REVISIONTIMESTAMP}}")
	--	local dtnow, dtvers = now_date, revision_time
	--	dtnow = formatime(dtnow)
	--	dtvers = formatime(dtvers)
	--	t = t .. " read by { {CURRENTTIMESTAMP} }"
--]]
		out_OK = t
		out_HS = t
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = "_N_TEST_ <b>_PRM_NM_</b> _OUT_OK_.",
	iferr = '_N_TEST_ <b>_PRM_NM_</b> _OUT_HS_.</span>',
} -- Unitest_Scribunto_runtime_spaces

local Unitest_Scribunto_frame_args = { name = "frame.args",
	func_test = function(parm, frame)
		-- * frame:getParent().args.lastname = Rimbaud
		-- * frame.args.lastname = nil
		local ok, out_OK, out_HS = true, parm.expect, "error"
		local frame_args = frame.args
		local getParent_args = frame:getParent().args
		local arglingual = tostring("lastname") -- multilingual name of the arg
		local frame_val = tostring(frame_args[arglingual])
		local getParent_val = tostring(getParent_args[arglingual])
		local t = "* <b>" .. tostring(parm.name) .. "</b> "
		local ok, out_REF, out_TEST = true, "", ""
		out_REF = tostring(getParent_val)
		out_TEST = tostring(frame_val)
		if out_TEST == out_REF then -- OK
			ok = true
		else -- HS bug
			ok = false
		end
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = '_N_TEST_ <b>_PRM_NM_</b> Test OK : frame.args.lastname = "_OUT_HS_".',
	iferr = '_N_TEST_ <b>_PRM_NM_</b> <span style="color:red;" >Error : frame.args.lastname = "_OUT_HS_". The OK value was "_OUT_OK_" = frame:getParent().args.lastname.</span>',
} -- Unitest_Scribunto_frame_args

local function sub_table_write_function_OK(td)
	-- test d'ecriture dans une sous table
	local A = td.A
	local Y = td.C.Y
	local var_tested = "td.C.X"
	local value_before = td.C.X
	local value_after = value_before
	td.C.X = "okvalue" -- INSTRUCTION UNDER TEST
	value_after = td.C.X -- verifier aussi l'ecriture puis lecture
	local value_ok = ( value_after == "okvalue" )
	return value_ok, var_tested, value_before, value_after, A, Y
end -- function sub_table_write_function_OK(td)

local Unitest_Scribunto_sub_table_write_OK = { name = "sub table write OK",
	func_test = function(parm, frame)
		-- * Test sub table read OK : Test cx : td.C.X = cx, expected = cx, test OK.
		-- * Test sub table write HS : Test x : tst.test_data.C.X = new val -> error
		-- pcall( func, args ) -- return { true, func() } or return { false, error message }
		-- how to read cousins in table, like cd ..other branch
		local ok, out_OK, out_HS = true, parm.expect, "error"
	--	local td_C_X_read = td.C.X
		-- Run a protected call to detect the execution error
		local exec_ok, value_ok, var_tested, value_before, value_after, A, Y = pcall( sub_table_write_function_OK, sub_table_write_data )
		local outest = " ; exec_ok=" .. tostring(exec_ok) .. " ; value_ok=" .. tostring(value_ok) .. " ; var_tested=" .. tostring(var_tested) .. " ; value_before=" .. tostring(value_before) .. " ; value_after=" .. tostring(value_after) .. " ; A=" .. tostring(A) .. " ; Y=" .. tostring(Y)
		if exec_ok then -- OK
			ok = true -- Il n'y a pas d'erreur d'execution
			out_OK = " result OK-- " .. outest
			if value_ok then -- Y a-t-il erreur de valeur ?
				ok = true -- Il n'y a pas d'erreur de valeur
				out_OK = " result OKOK " .. outest
			else -- HS bug
				ok = false -- Il y a une erreur de valeur
				out_HS = " result OKHS " .. outest
			end
		else -- HS bug
			ok = false -- ll y a une erreur d'execution
			out_HS = " result HS-- " .. outest
		end
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = '_N_TEST_ <b>_PRM_NM_</b> Test OK : sub table write OK. / / / _OUT_OK_ .',
	iferr = '_N_TEST_ <b>_PRM_NM_</b> <span style="color:red;" >Error : sub table write HS. / / /  _OUT_HS_ .</span>',
} -- Unitest_Scribunto_sub_table_write_OK

local function sub_table_write_function_XX(td)
	-- test d'ecriture dans une sous table
	local A = td.A
	local Y = td.C.Y
	local var_tested = "td.C.X"
	local value_before = td.C.X
	local value_after = value_before
	-- local args_known = Argts.args_known -- PREVIEW LOCAL ORIGINAL INSTRUCTION
	-- local argname_ = Argts.args_known[argname] -- ORIGINAL INSTRUCTION WITH ERROR
	td.C.X = "okvalue" -- INSTRUCTION UNDER TEST
	--
	-- function Argts.arg_input(args_src, argname, tr) -- ORIGINAL FUNCTION WITH ERROR
	-- local args_known = Argts.args_known -- PREVIEW LOCAL ORIGINAL INSTRUCTION
	-- local argname_ = Argts.args_known[argname] -- ORIGINAL INSTRUCTION WITH ERROR
	-- Lua error in Module:Multilingual_Arguments at line 847: attempt to index field 'args_known' (a nil value).
	--
	value_after = td.C.X -- verifier aussi l'ecriture puis lecture
	local value_ok = ( value_after == "okvalue" )
	return value_ok, var_tested, value_before, value_after, A, Y
end -- function sub_table_write_function_XX(td)

local Unitest_Scribunto_sub_table_write_XX = { name = "sub table write XX",
	func_test = function(parm, frame)
		-- * Test sub table read OK : Test cx : td.C.X = cx, expected = cx, test OK.
		-- * Test sub table write HS : Test x : tst.test_data.C.X = new val -> error
		-- pcall( func, args ) -- return { true, func() } or return { false, error message }
		-- how to read cousins in table, like cd ..other branch
		local ok, out_OK, out_HS = true, parm.expect, "error"
	--	local td_C_X_read = td.C.X
		-- Run a protected call to detect the execution error
		local exec_ok, value_ok, var_tested, value_before, value_after, A, Y = pcall( sub_table_write_function_XX, sub_table_write_data )
		local outest = " ; exec_ok=" .. tostring(exec_ok) .. " ; value_ok=" .. tostring(value_ok) .. " ; var_tested=" .. tostring(var_tested) .. " ; value_before=" .. tostring(value_before) .. " ; value_after=" .. tostring(value_after) .. " ; A=" .. tostring(A) .. " ; Y=" .. tostring(Y)
		if exec_ok then -- OK
			ok = true -- Il n'y a pas d'erreur d'execution
			out_OK = " result OK-- " .. outest
			if value_ok then -- Y a-t-il erreur de valeur ?
				ok = true -- Il n'y a pas d'erreur de valeur
				out_OK = " result OKOK " .. outest
			else -- HS bug
				ok = false -- Il y a une erreur de valeur
				out_HS = " result OKHS " .. outest
			end
		else -- HS bug
			ok = false -- ll y a une erreur d'execution
			out_HS = " result HS-- " .. outest
		end
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = '_N_TEST_ <b>_PRM_NM_</b> Test OK : sub table write OK. / / / _OUT_OK_ .',
	iferr = '_N_TEST_ <b>_PRM_NM_</b> <span style="color:red;" >Error : sub table write HS. / / /  _OUT_HS_ .</span>',
} -- Unitest_Scribunto_sub_table_write_XX

local Unitest_Scribunto_complete = { name = "Is Scribunto complete ?",
	func_test = function(parm, frame)
		-- * Test sub table read OK : Test cx : td.C.X = cx, expected = cx, test OK.
		-- * Test sub table write HS : Test x : tst.test_data.C.X = new val -> error
		-- pcall( func, args ) -- return { true, func() } or return { false, error message }
		-- how to read cousins in table, like cd ..other branch
		local ok, out_OK, out_HS = true, parm.expect, "error"
	--	local td_C_X_read = td.C.X
		-- Run a protected call to detect the execution error
		local exec_ok, value_ok, var_tested, value_before, value_after, A, Y = pcall( sub_table_write_function_OK, sub_table_write_data )
		local outest = " ; exec_ok=" .. tostring(exec_ok) .. " ; value_ok=" .. tostring(value_ok) .. " ; var_tested=" .. tostring(var_tested) .. " ; value_before=" .. tostring(value_before) .. " ; value_after=" .. tostring(value_after) .. " ; A=" .. tostring(A) .. " ; Y=" .. tostring(Y)
		local ok, wt = true, ''
		ok, wt = if_exist(ok, wt, math, 		'math')
		ok, wt = if_exist(ok, wt, os, 			'os')
		ok, wt = if_exist(ok, wt, package, 		'package')
		ok, wt = if_exist(ok, wt, string, 		'string')
		ok, wt = if_exist(ok, wt, table, 		'table')
		ok, wt = if_exist(ok, wt, mw, 			'mw')
		ok, wt = if_exist(ok, wt, frame, 		'frame')
		ok, wt = if_exist(ok, wt, mw.language, 	'mw.language')
		ok, wt = if_exist(ok, wt, mw.message, 	'mw.message')
		ok, wt = if_exist(ok, wt, mw.site, 		'mw.site')
		ok, wt = if_exist(ok, wt, mw.title, 	'mw.title')
		ok, wt = if_exist(ok, wt, mw.uri, 		'mw.uri')
		ok, wt = if_exist(ok, wt, mw.ustring, 	'mw.ustring')
		out_OK = wt
		out_HS = wt
		return ok, tostring(out_OK), tostring(out_HS)
	end,
	args = {"x"},
	expect = "OK",
	type = "ToString",
	ifok = '_N_TEST_ <b>_PRM_NM_</b> Test OK : sub table write OK. / / / _OUT_OK_ .',
	iferr = '_N_TEST_ <b>_PRM_NM_</b> <span style="color:red;" >Error : Scribunto is not complete.</span> / / /  _OUT_HS_ .',
} -- Unitest_Scribunto_complete

-- empty: can be removed
-- test: the title librairy

-- try  argname to skip if unknown

-- mw content list
-- dump a table content, with formating
-- local p = {}
function tst.doctable_recursive(tbl, uppername, name, level_i, levelmaxi, exclude1, exclude2, exclude3)
	local res, newname, part, shift = "", "", "", ""
	local sep = ","
	local isempty = true
	local levelname = uppername .. "." .. name
	-- limit the number of the level of sub-tables
	level_i = tonumber(level_i)
	if type(level_i) ~= "number" then level_i = 1 end
	levelmaxi = tonumber(levelmaxi)
	if type(levelmaxi) ~= "number" then levelmaxi = 99 end
	if level_i > levelmaxi then
		return ""
	end
	--
	-- exit if exclude1 exclude2 exclude3 are in name
	if type(exclude1) == "string" then
		if string.find(name, exclude1) ~= nil then
			return ""
		end
	end
	if type(exclude2) == "string" then
		if string.find(name, exclude2) ~= nil then
			return ""
		end
	end
	if type(exclude3) == "string" then
		if string.find(name, exclude3) ~= nil then
			return ""
		end
	end
	-- display table error
	if type(tbl) ~= "table" then
		return 'Table "' .. tostring(name) .. '" is invalid.<br/>'
	end
	--
	-- Add all contents, first vars, then functions, then sub-tables list, then sub-tables contents
	-- Add vars
	isempty = true
	shift = string.rep("*", level_i)
	part = "\n" .. shift .. " Table '''" .. levelname .. "''' vars : "
	sep = ", " --  .. "\n"
	for k, v in pairs(tbl) do -- var, not functions and tables
		if (type(v) ~= "function" and type(v) ~= "table") then
			part = part .. type(v) .. " - " .. tostring(k) .. " = " .. (tostring(v or "<nil>")) .. sep
			isempty = false
		end
	end
	if not isempty then res = res .. part
	else res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''' no vars." end
	--
	-- Add functions
	isempty = true
	part = "\n" .. shift .. " Table '''" .. levelname .. "''' functions : "
	sep = ", " --  .. "\n"
	for k, v in pairs(tbl) do -- functions
		if (type(v) == "function") then
			part = part .. k .. sep
			isempty = false
		end
	end
	if not isempty then res = res .. part
	else res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''' no functions." end
	--
	-- Add sub-tables list
	isempty = true
	part = "\n" .. shift .. " Table '''" .. levelname .. "''' tables list : " 
	sep = ", " --  .. "\n"
	for k, v in pairs(tbl) do -- functions
		if (type(v) == "table") then
			part = part .. k .. sep
			isempty = false
		end
	end
	if isempty then
		res = res .. "\n" .. shift .. " Table '''" .. levelname .. "''' no sub-tables."
	else
		res = res .. part
		-- Add sub-tables contents
		for k, v in pairs(tbl) do -- only tables
			newname = ""
			if (type(v) == "table") then
				newname = k
				-- a table, we recursively treat what's inside it
				if level_i < levelmaxi then
					res = res .. tst.doctable_recursive(v, levelname, newname, level_i+1, levelmaxi, exclude1, exclude2, exclude3)
				else
					-- res = res .. newname .. ""
				end
				local sep = ""
			end
		end
	end
--	res = res .. "<br/>"
	return res
end -- function tst.doctable_recursive(tbl, uppername, name, level_i, levelmaxi, exclude1, exclude2, exclude3)

function tst.doctable_args(args, table, tablename)
	local res = "\n* Content of the '''" .. tostring(tablename) .. "''' table, begin :"
	-- test : check mw content
	local levelmaxi = args.levelmaxi
	local exclude1 = args.exclude1
	local exclude2 = args.exclude2
	local exclude3 = args.exclude3
	-- limit the number of the level of sub-tables
	level_i = tonumber(level_i)
	if type(level_i) ~= "number" then level_i = 1 end
	levelmaxi = tonumber(levelmaxi)
	if type(levelmaxi) ~= "number" then levelmaxi = 99 end
	if level_i > levelmaxi then
		return ""
	end
	res = res .. " levelmaxi=" .. tostring(levelmaxi)
	res = res .. " exclude1=" .. tostring(exclude1)
	res = res .. " exclude2=" .. tostring(exclude2)
	res = res .. " exclude3=" .. tostring(exclude3)
	res = res .. tst.doctable_recursive(table, "", tablename, 1, levelmaxi, exclude1, exclude2, exclude3)
	-- function tst.doctable_recursive(tbl, uppername, name, level_i, levelmaxi, exclude1, exclude2, exclude3)
	res = res .. "\n* Content of the '''" .. tostring(tablename) .. "''' table, end.\n"
	return res
end -- function tst.doctable_args(args, table, tablename)

function tst.init( frame, test_list, test_data )
	-- tst.init(frame, tst.test_list)
	--	tst.test_list is the default value
	if type(frame) == "table" then tst.test_frame = frame end
	if type(test_list) == "table" then tst.test_list = test_list end
--	table.insert( tst.test_list, Unitest_Scribunto_tested_site )
--	table.insert( tst.test_list, Unitest_Scribunto_environment )
--	table.insert( tst.test_list, Unitest_Scribunto_runtime_spaces )
	table.insert( tst.test_list, Unitest_Scribunto_frame_args )
	table.insert( tst.test_list, Unitest_Scribunto_sub_table_write_OK )
	table.insert( tst.test_list, Unitest_Scribunto_sub_table_write_XX )
--	table.insert( tst.test_list, Unitest_Scribunto_complete )
	if type(test_data) == "table" then tst.test_data = test_data end
	local t = "<br/>* Init of unit tests. test_list = " .. tostring( tst.test_list ) .. "<br/>"
	tst.test_result = tst.test_result .. t
    return t
end -- function tst.init( frame, test_list, test_data )

function tst.run_all_tests( frame )
    local input_args = frame.args.input or frame.args
	local result = ""
	local n, nerr = 0, 0
	local ok, out_OK, out_HS = true, "", ""
	local wt, t = "", "* Begin of unit tests :\n"
	t = t .. tst.WhereWhenWhatWeWork(frame) .. "<br/>"
	for key, parm in pairs(tst.test_list) do -- Pour tous les parametres connus
		n = n + 1
		wt = "\n"
		if parm.type == "ToString" then -- args en ordre numerique et renvoi vers un argument nommé
			ok, out_OK, out_HS = parm.func_test(parm, frame)
			-- pcall( func, args ) -- return { true, func() } or return { false, error message }
		--	t = t .. '* ' .. tostring(n) .. ' <b>' .. tostring(parm.name) .. '</b> result =' .. tostring(result) .. ' '
			if ok then -- si le test est OK
				wt = parm.ifok
				--	t = t .. ' ' .. parm.ifok .. '</span>' .. out_OK .. '<br/>'
			else -- si le test est HS
				wt = parm.iferr
				--	t = t .. '<span style="color:red;" >' .. parm.iferr .. '</span>' .. out_HS .. '<br/>'
				nerr = nerr + 1
			end
			wt = string.gsub(wt, "_PRM_NM_", tostring(parm.name) )
			wt = string.gsub(wt, "_N_TEST_", tostring(n) )
			wt = string.gsub(wt, "_N_ERR_", tostring(nerr) )
			wt = string.gsub(wt, "_OUT_OK_", tostring(out_OK) )
			wt = string.gsub(wt, "_OUT_HS_", tostring(out_HS) )
		--	t = t .. '* ' .. tostring(n) .. ' <b>' .. tostring(parm.name) .. '</b> ' .. wt
			t = t .. ''.. wt .. "<br/>"
		end
	end
	t = t .. "\n*End of unit tests. Statistic count : Nerr=" .. tostring( nerr ) .. " / Ntests=" .. tostring( n ) .. "<br/>"
	tst.test_result = tst.test_result .. t
    return tst.test_result
end -- function tst.run_all_tests( frame )

function tst.WhereWhenWhatWeWork(frame)
	local t = ""
	-- Get where wiki URL
	local ok, out_OK, out_HS = true, "parm.expect", "error"
	local title = mw.title.getCurrentTitle()
	local mw_uri_fullUrl = mw.uri.fullUrl('Module:Unitest Scribunto') --  "Module:Auteur" )
	t = t .. ": Page tested = " .. tostring(title.fullUrl( title.subjectPageTitle )) .. "\n" 
	-- Get what version
	local revision_time = frame:preprocess("{{REVISIONTIMESTAMP}}")
	dtvers = formatime(revision_time)
	t = t .. ": Mediawiki " .. mw.site.currentVersion .. " - UTC revision time " .. tostring(dtvers) .. " - " .. _VERSION .. "\n"
	-- Get when
	local date_time = frame:preprocess("{{#dateformat:{{CURRENTTIMESTAMP}}|ymd}}")
	local now_date = os.date("%Y-%m-%d %H:%M:%S")
	-- Runtime and spaces
	local namespace = frame:preprocess("{{NAMESPACE}}") -- present namespace from system
	local module_space = frame:preprocess("{{ns:Module}}") -- Module namespace from system
	local template_space = frame:preprocess("{{ns:Template}}") -- Module namespace from system
	local viewErrors = (namespace == module_space) or (namespace == template_space)
	t = t .. ": Server runtime UTC " .. tostring(now_date) .. ", namespace=" .. tostring(namespace) -- .. "\n"
--	t = t .. ": module_space=" .. tostring(module_space) .. ", template_space=" .. tostring(template_space)
--	t = t .. " viewErrors=" .. tostring(viewErrors) .. "\n"
	return t
end -- local fonction WhereWhenWhatWeWork()

function tst.wwwww(frame)
	return tst.WhereWhenWhatWeWork(frame)
end

-- Interface to document Where When What We Work
function tst.unitest_wwwww(frame)
	local args = frame.args
	local out = tst.WhereWhenWhatWeWork(frame)
    return out
end

-- Interface to document frame.args.
function tst.doctable_frame_args(frame)
	local frame_args = frame.args
	local args = frame.args
	local out = tst.doctable_args(args, frame_args, "frame_args")
    return out
end

-- Interface to document a list of frame.
function tst.doctable_frame(frame)
	local args = frame.args
	local out = tst.doctable_args(args, frame, "frame")
    return out
end

-- Interface to document a list of Module:mw.
function tst.doctable_mw(frame)
	local out = tst.doctable_args(frame.args, mw, "mw")
    return out
end

-- Interface to document some unit tests.
function tst.unitest(frame)
	local args = frame.args
	tst.init(frame) -- tst.init( frame, test_list, test_data )
	local out = tst.run_all_tests( frame )
    return out
end -- function tst.unitest(frame)

return tst

-- local unitestScribunto = require "Module:Unitest_Scribunto"