Module:User:Mr. Stradivarius/gambiarra

-- -- MIT LICENSE -- --  Copyright (c) 2015 Serge Zaitsev -- --  Permission is hereby granted, free of charge, to any person obtaining a --  copy of this software and associated documentation files (the --  "Software"), to deal in the Software without restriction, including --  without limitation the rights to use, copy, modify, merge, publish, --  distribute, sublicense, and/or sell copies of the Software, and to --  permit persons to whom the Software is furnished to do so, subject to --  the following conditions: -- --  The above copyright notice and this permission notice shall be included --  in all copies or substantial portions of the Software. -- --  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS --  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF --  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. --  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY --  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, --  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE --  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --

local function TERMINAL_HANDLER(e, test, msg) if e == 'pass' then mw.log("�[32m✔�[0m "..test..': '..msg) elseif e == 'fail' then mw.log("�[31m✘�[0m "..test..': '..msg) elseif e == 'except' then mw.log("�[31m✘�[0m "..test..': '..msg) end end

local function deepeq(a, b)	-- Different types: false if type(a) ~= type(b) then return false end -- Functions if type(a) == 'function' then return string.dump(a) == string.dump(b) end -- Primitives and equal pointers if a == b then return true end -- Only equal tables could have passed previous tests if type(a) ~= 'table' then return false end -- Compare tables field by field for k,v in pairs(a) do		if b[k] == nil or not deepeq(v, b[k]) then return false end end for k,v in pairs(b) do		if a[k] == nil or not deepeq(v, a[k]) then return false end end return true end

-- Compatibility for Lua 5.1 and Lua 5.2 local function args(...) return {n=select('#', ...), ...} end

local function spy(f) local s = {} setmetatable(s, {__call = function(s, ...)		s.called = s.called or {}		local a = args(...)		table.insert(s.called, {...})		if f then			local r			r = args(pcall(f, (unpack or table.unpack)(a, 1, a.n)))			if not r[1] then				s.errors = s.errors or {}				s.errors[#s.called] = r[2]			else				return (unpack or table.unpack)(r, 2, r.n)			end		end	end}) return s end

return function(handler, env)

local pendingtests = {}

local function runpending if pendingtests[1] ~= nil then pendingtests[1](runpending) end end

local function test(name, f, async) local testfn = function(next)

local prev = { ok = env.ok, spy = env.spy, eq = env.eq			}

local restore = function env.ok = prev.ok				env.spy = prev.spy env.eq = prev.eq				env.gambiarrahandler('end', name) table.remove(pendingtests, 1) if next then next end end

local handler = env.gambiarrahandler

env.eq = deepeq env.spy = spy env.ok = function(cond, msg) if cond then handler('pass', name, msg) else handler('fail', name, msg) end end

handler('begin', name); local ok, err = pcall(f, restore) if not ok then handler('except', name, err) end

if not async then handler('end', name); env.ok = prev.ok; env.spy = prev.spy; env.eq = prev.eq; end end

if not async then testfn else table.insert(pendingtests, testfn) if #pendingtests == 1 then runpending end end end

env = env or _G env.gambiarrahandler = handler or TERMINAL_HANDLER

env.test = test end