Modulo:UnitTests

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca

Modulo Lua per la gestione di unit test sugli script.

UnitTests fornisce uno strumento che può essere usato da altri script per mezzo di require. Vedi Wikipedia:Lua#Unit_testing per i dettagli. Di seguito un esempio da Modulo:HelloWorld/test:

-- Unit tests for [[Module:HelloWorld]]. Cliccate sulla pagina di discussione per eseguire il test.
local p = require('Module:UnitTests')
 
function p:test_hello()
    self:preprocess_equals('{{#invoke:HelloWorld | hello}}', 'Hello, world!')
end
 
return p

La pagina di discussione Discussioni modulo:HelloWorld/test lo esegue con il codice {{#invoke: HelloWorld/testcases | run_tests}}. I metodi di test come test_hello sopra devono iniziare con "test".

Metodi

run_tests

  • run_tests(differs_at): Esegue tutti i test. Se si specifica "differs_at=1" sarà aggiunta una colonna che mostra la prima posizione dove un carattere del risultato atteso differisce dal risultato ottenuto. Normalmente usato sulla pagina di discussioni di un unit test.
    {{#invoke:HelloWorld/testcases|run_tests}}

preprocess_equals

  • preprocess_equals(text, expected): Fornisce una porzione di testo wiki da preprocessare e un risultato atteso. Script e template possono essere invocati nella stessa maniera in cui sarebbero invocati in una voce.
    self:preprocess_equals('{{#invoke:HelloWord | hello}}', 'Hello, world!')

-- UnitTester gestisce l'unit testing per altri script Lua.
local UnitTester = {}

local frame, tick, cross
local result_table_header = "{|class=\"wikitable\"\n! !! Testo !! Risultato atteso !! Risultato ottenuto"
local result_table = ''
local num_failures = 0

function first_difference(s1, s2)
    if s1 == s2 then return '' end
    local max = math.min(#s1, #s2)
    for i = 1, max do
        if s1:sub(i,i) ~= s2:sub(i,i) then return i end
    end
    return max + 1
end

function UnitTester:preprocess_equals(text, expected, options)
	local options = options or {}
    local actual = frame:preprocess(text)

	local compared_actual = actual;
    if options.ignoreTemplateStyles then
    	local pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-)(%x+)(%-QINU[^\127]*\127)'
    	compared_actual = actual:gsub(pattern, '')
    end
    
    if compared_actual == expected then
        result_table = result_table .. '| ' .. tick
    else
        result_table = result_table .. '| ' .. cross
        num_failures = num_failures + 1
    end
    local nowiki_open = options.nowiki and '<nowiki>' or ''
    local nowiki_close = options.nowiki and '</nowiki>' or ''
    local differs_at = self.differs_at and (' || ' .. first_difference(expected, compared_actual)) or ''
    result_table = result_table .. ' || <nowiki>' .. text:gsub('%|', '&#124;') .. '</nowiki> || ' .. nowiki_open .. expected .. nowiki_close .. ' || ' .. nowiki_open .. actual .. nowiki_close .. differs_at .. "\n|-\n"
end

function UnitTester:preprocess_equals_many(prefix, suffix, cases, options)
    for _, case in ipairs(cases) do
        self:preprocess_equals(prefix .. case[1] .. suffix, case[2], options)
    end
end

function UnitTester:preprocess_equals_preprocess(text1, text2, options)
    local actual = frame:preprocess(text1)
    local expected = frame:preprocess(text2)
    if actual == expected then
        result_table = result_table .. '| ' .. tick
    else
        result_table = result_table .. '| ' .. cross
        num_failures = num_failures + 1
    end
    local nowiki_open = (options and options.nowiki) and '<nowiki>' or ''
    local nowiki_close = (options and options.nowiki) and '</nowiki>' or ''
    local differs_at = self.differs_at and (' || ' .. first_difference(expected, actual)) or ''
    result_table = result_table .. ' || <nowiki>' .. text1:gsub('%|', '&#124;') .. '</nowiki> || ' .. nowiki_open .. expected .. nowiki_close .. ' || ' .. nowiki_open .. actual .. nowiki_close .. differs_at .. "\n|-\n"
end

function UnitTester:preprocess_equals_preprocess_many(prefix1, suffix1, prefix2, suffix2, cases, options)
    for _, case in ipairs(cases) do
        self:preprocess_equals_preprocess(prefix1 .. case[1] .. suffix1, prefix2 .. (case[2] and case[2] or case[1]) .. suffix2, options)
    end
end

function UnitTester:equals(name, actual, expected, options)
    if actual == expected then
        result_table = result_table .. '| ' .. tick
    else
        result_table = result_table .. '| ' .. cross
        num_failures = num_failures + 1
    end
    local nowiki_open = (options and options.nowiki) and '<nowiki>' or ''
    local nowiki_close = (options and options.nowiki) and '</nowiki>' or ''
    local differs_at = self.differs_at and (' || ' .. first_difference(expected, actual)) or ''
    result_table = result_table .. ' || ' .. name .. ' || ' .. nowiki_open .. expected .. nowiki_close .. ' || ' .. nowiki_open .. actual .. nowiki_close .. differs_at .. "\n|-\n"
end

local function deep_compare(t1, t2, ignore_mt)
    local ty1 = type(t1)
    local ty2 = type(t2)
    if ty1 ~= ty2 then return false end
    if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
    
    local mt = getmetatable(t1)
    if not ignore_mt and mt and mt.__eq then return t1 == t2 end
    
    for k1, v1 in pairs(t1) do
        local v2 = t2[k1]
        if v2 == nil or not deep_compare(v1, v2) then return false end
    end
    for k2, v2 in pairs(t2) do
        local v1 = t1[k2]
        if v1 == nil or not deep_compare(v1, v2) then return false end
    end

    return true
end

function val_to_str(v)
    if type(v) == 'string' then
        v = mw.ustring.gsub(v, '\n', '\\n')
        if mw.ustring.match(mw.ustring.gsub(v, '[^\'"]', ''), '^"+$') then
            return "'" .. v .. "'"
        end
        return '"' .. mw.ustring.gsub(v, '"', '\\"' ) .. '"'
    else
        return type(v) == 'table' and table_to_str(v) or tostring(v)
    end
end

function table_key_to_str(k)
    if type(k) == 'string' and mw.ustring.match(k, '^[_%a][_%a%d]*$') then
        return k
    else
        return '[' .. val_to_str(k) .. ']'
    end
end

function table_to_str(tbl)
    local result, done = {}, {}
    for k, v in ipairs(tbl) do
        table.insert(result, val_to_str(v))
        done[k] = true
    end
    for k, v in pairs(tbl) do
        if not done[k] then
            table.insert(result, table_key_to_str(k) .. '=' .. val_to_str(v))
        end
    end
    return '{' .. table.concat(result, ',') .. '}'
end

function UnitTester:equals_deep(name, actual, expected, options)
    if deep_compare(actual, expected) then
        result_table = result_table .. '| ' .. tick
    else
        result_table = result_table .. '| ' .. cross
        num_failures = num_failures + 1
    end
    local nowiki_open = (options and options.nowiki) and '<nowiki>' or ''
    local nowiki_close = (options and options.nowiki) and '</nowiki>' or ''
    local actual_str = val_to_str(actual)
    local expected_str = val_to_str(expected)
    local differs_at = self.differs_at and (' || ' .. first_difference(expected_str, actual_str)) or ''
    result_table = result_table .. ' || ' .. name .. ' || ' .. nowiki_open .. expected_str .. nowiki_close .. ' || ' .. nowiki_open .. actual_str .. nowiki_close .. differs_at .. "\n|-\n"
end

function UnitTester:run(frame_arg)
    frame = frame_arg
    self.frame = frame
    self.differs_at = frame.args['differs_at']
    tick = frame:preprocess('{{Fatto|}}')
    cross = frame:preprocess('{{Non fatto| }}')

    local table_header = result_table_header
    if self.differs_at then
        table_header = table_header .. ' !! Differiscono a'
    end

    for key,value in pairs(self) do
        if key:find('^test') then
            result_table = result_table .. "'''" .. key .. "''':\n" .. table_header .. "\n|-\n"
            value(self)
            result_table = result_table .. "|}\n\n"
        end
    end
    local failures = ""
    if num_failures >1 then
        failures = "falliti"
    elseif num_failures == 1 then
        failures = "fallito"
    end
    return '__NOTALK__\n' .. (num_failures == 0 and "<span style=\"color:#008000\">'''Passati tutti i test.'''</span>" or "<span style=\"color:#800000\">'''" .. num_failures .. " test " .. failures .. ".'''</span>") .. "\n\n" .. frame:preprocess(result_table)
end

function UnitTester:new()
    local o = {}
    setmetatable(o, self)
    self.__index = self
    return o
end

local p = UnitTester:new()
function p.run_tests(frame) return p:run(frame) end
return p

-- 30/3/2013 Importato dalla versione 546791841 di en:Module:UnitTests del 30/3/2013