tester.lua (3741B)
1 2 3 local tester = { 4 test = {}, 5 linecache = {}, 6 globals = {}, 7 passcount = 0, 8 failcount = 0 9 } 10 11 12 local function isequal(a, b) 13 if type(a) ~= type(b) then return nil end 14 local t = {} 15 function t.table(a, b) 16 for k, v in pairs(a) do if not isequal(b[k], v) then return nil end end 17 for k, v in pairs(b) do if not isequal(a[k], v) then return nil end end 18 return true 19 end 20 function t.number(a, b) return math.abs(a - b) < 10e-9 end 21 return t[type(a)] and t[type(a)](a, b) or (a == b) 22 end 23 24 25 local function stringify(x) 26 if type(x) == "number" then return string.format("%.2f", x) end 27 return string.format("%q", tostring(x)) 28 end 29 30 31 local function getline(file, line) 32 if not tester.linecache[file] then 33 local t = {} 34 for line in io.lines(file) do 35 t[#t + 1] = line 36 end 37 tester.linecache[file] = t 38 end 39 return tester.linecache[file][line] 40 end 41 42 43 local function truncate(str, max) 44 max = max or 72 45 if #str > max then 46 return str:sub(1, max - 3) .. "..." 47 end 48 return str 49 end 50 51 52 local function has(t, value) 53 for k, v in pairs(t) do 54 if v == value then return true end 55 end 56 return false 57 end 58 59 60 local function makelogstr(passed, file, line) 61 local t = {} 62 t[#t + 1] = passed and "[\27[32mPASS\27[0m]" or "[\27[31mFAIL\27[0m]" 63 t[#t + 1] = file .. ":" .. line .. ":" 64 t[#t + 1] = getline(file, line) :gsub(" %s+", " ") :gsub("^ *", "") 65 return truncate(table.concat(t, " ")) 66 end 67 68 69 local function dopass(file, line) 70 print(makelogstr(true, file, line)) 71 tester.passcount = tester.passcount + 1 72 end 73 74 75 local function dofail(file, line) 76 print(makelogstr(false, file, line)) 77 tester.failcount = tester.failcount + 1 78 end 79 80 81 local function printfailmsg(str) 82 print(string.rep(" ", 7) .. str) 83 end 84 85 86 87 88 function tester.init() 89 for k, v in pairs(_G) do 90 tester.globals[k] = v 91 end 92 return tester 93 end 94 95 96 function tester.test.global(expectedglobals) 97 expectedglobals = expectedglobals or {} 98 local info = debug.getinfo(2) 99 local unexpected = {} 100 for k in pairs(_G) do 101 if not tester.globals[k] and not has(expectedglobals, k) then 102 table.insert(unexpected, "Unexpected global '" .. k .. "'") 103 end 104 end 105 if #unexpected == 0 then 106 dopass(info.short_src, info.currentline) 107 else 108 dofail(info.short_src, info.currentline) 109 for _, v in pairs(unexpected) do printfailmsg(v) end 110 end 111 end 112 113 114 function tester.test.equal(result, expected) 115 local passed = isequal(result, expected) 116 local info = debug.getinfo(2) 117 if passed then 118 dopass(info.short_src, info.currentline) 119 else 120 dofail(info.short_src, info.currentline) 121 if type(expected) == "table" and type(result) == "table" then 122 printfailmsg("Tables do not match") 123 else 124 printfailmsg(string.format("Expected %s got %s", 125 stringify(expected), stringify(result) )) 126 end 127 end 128 end 129 130 131 function tester.test.error(fn, ...) 132 local passed = not pcall(fn, ...) 133 local info = debug.getinfo(2) 134 if passed then 135 dopass(info.short_src, info.currentline) 136 else 137 dofail(info.short_src, info.currentline) 138 printfailmsg("Expected an error to be raised") 139 end 140 end 141 142 143 function tester.dotests(t) 144 local keys = {} 145 for k in pairs(t) do table.insert(keys, k) end 146 table.sort(keys) 147 for _, k in pairs(keys) do 148 print("\27[33m-- " .. k .. "\27[0m") 149 t[k]() 150 end 151 end 152 153 154 function tester.printresults() 155 local str = table.concat{ 156 "-- ", 157 string.format("Results: %d Total", tester.passcount + tester.failcount), 158 " ", string.format("%d Passed", tester.passcount), 159 " ", string.format("%d Failed", tester.failcount), 160 " --", } 161 local b = string.rep("-", #str) 162 print(table.concat{b, "\n", str, "\n", b}) 163 end 164 165 166 return tester.init()