-- Helper variables and functions local get_info = debug.getinfo local pcall = pcall local slice = string.sub local sprintf = string.format local str_find = string.find local tonumber = tonumber -- Lua 5.3 moved unpack to table.unpack local unpack = unpack or table.unpack local write = io.write local rawget = rawget local getmetatable = getmetatable local exit = os.exit ---- Helper methods --- C-like printf method local printf = function(fmt, ...) write(sprintf(fmt, ...)) end --- Compare potentially complex tables or objects -- -- Ideas here are taken from [Penlight][p], [Underscore][u], [cwtest][cw], and -- [luassert][l]. -- [p]: https://github.com/stevedonovan/Penlight -- [u]: https://github.com/mirven/underscore.lua -- [cw]: https://github.com/catwell/cwtest -- [l]: https://github.com/Olivine-Labs/luassert -- -- Predeclare both function names local keyvaluesame local deepsame -- --- keyvaluesame(table, table) => true or false -- Helper method to compare all the keys and values of a table keyvaluesame = function (t1, t2) for k1, v1 in pairs(t1) do local v2 = t2[k1] if v2 == nil or not deepsame(v1, v2) then return false end end -- Check for any keys present in t2 but not t1 for k2, _ in pairs(t2) do if t1[k2] == nil then return false end end return true end -- --- deepsame(item, item) => true or false -- Compare two items of any type for identity deepsame = function (t1, t2) local ty1, ty2 = type(t1), type(t2) if ty1 ~= ty2 then return false end if ty1 ~= 'table' then return t1 == t2 end -- If ._eq is found, use == and end quickly. -- As of Lua 5.3 == only cares if **one** of the two items has a __eq -- metamethod. Penlight, underscore and cwtest take the same approach, -- so I will as well. local eq = rawget(getmetatable(t1) or {}, '__eq') if (type(eq) == 'function') then return not not eq(t1, t2) else return keyvaluesame(t1, t2) end end ---- tapered test suite local exit_status = 0 local test_count = 0 local debug_level = 3 -- All other tests are defined in terms of this primitive, which is -- kept private. local _test = function (exp, msg) test_count = test_count + 1 if msg then msg = sprintf(" - %s", msg) else msg = '' end if exp then printf("ok %s%s\n", test_count, msg) else exit_status = 1 + exit_status printf("not ok %s%s\n", test_count, msg) local info = get_info(debug_level) printf("# Trouble in %s around line %s\n", slice(info.source, 2), info.currentline) end end local ok = function (expression, msg) _test(expression, msg) end local nok = function (expression, msg) _test(not expression, msg) end local is = function (actual, expected, msg) _test(actual == expected, msg) end local isnt = function (actual, expected, msg) _test(actual ~= expected, msg) end local same = function (actual, expected, msg) _test(deepsame(actual, expected), msg) end local like = function (str, pattern, msg) _test(str_find(str, pattern), msg) end local unlike = function (str, pattern, msg) _test(not str_find(str, pattern), msg) end local pass = function (msg) _test(true, msg) end local fail = function (msg) _test(false, msg) end local boom = function (func, args, msg) local err, errmsg err, errmsg = pcall(func, unpack(args)) _test(not err, msg) if not err and type(errmsg) == 'string' then printf('# Got this error: "%s"\n', errmsg) end end local done = function (n) local expected = tonumber(n) if not expected or test_count == expected then printf('1..%d\n', test_count) elseif expected ~= test_count then exit_status = 1 + exit_status local s if expected == 1 then s = '' else s = 's' end printf("# Bad plan. You planned %d test%s but ran %d\n", expected, s, test_count) end exit(exit_status) end local version = function () return "2.3.0" end local author = function () return "Peter Aronoff" end local url = function () return "https://github.com/telemachus/tapered" end local license = function () return "BSD 3-Clause" end return { ok = ok, nok = nok, is = is, isnt = isnt, same = same, like = like, unlike = unlike, pass = pass, fail = fail, boom = boom, done = done, version = version, author = author, url = url, license = license }