CHANGES: Update for release
[prosody.git] / tests / test.lua
blobbc33bb76284b7177dff59162b1b92c03aef43949
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
9 local tests_passed = true;
11 function run_all_tests()
12 package.loaded["net.connlisteners"] = { get = function () return {} end };
13 dotest "util.jid"
14 dotest "util.multitable"
15 dotest "util.rfc6724"
16 dotest "util.http"
17 dotest "core.stanza_router"
18 dotest "core.s2smanager"
19 dotest "core.configmanager"
20 dotest "util.ip"
21 dotest "util.json"
22 dotest "util.stanza"
23 dotest "util.sasl.scram"
24 dotest "util.cache"
25 dotest "util.throttle"
26 dotest "util.uuid"
27 dotest "util.random"
28 dotest "util.xml"
29 dotest "util.xmppstream"
30 dotest "util.queue"
31 dotest "net.http.parser"
33 dosingletest("test_sasl.lua", "latin1toutf8");
34 dosingletest("test_utf8.lua", "valid");
35 end
37 local verbosity = tonumber(arg[1]) or 2;
39 if os.getenv("WINDIR") then
40 package.path = package.path..";..\\?.lua";
41 package.cpath = package.cpath..";..\\?.dll";
42 else
43 package.path = package.path..";../?.lua";
44 package.cpath = package.cpath..";../?.so";
45 end
47 local _realG = _G;
49 require "util.import"
51 local envloadfile = require "util.envload".envloadfile;
53 local env_mt = { __index = function (t,k) return rawget(_realG, k) or print("WARNING: Attempt to access nil global '"..tostring(k).."'"); end };
54 function testlib_new_env(t)
55 return setmetatable(t or {}, env_mt);
56 end
58 function assert_equal(a, b, message, level)
59 if not (a == b) then
60 error("\n assert_equal failed: "..tostring(a).." ~= "..tostring(b)..(message and ("\n Message: "..message) or ""), (level or 1) + 1);
61 elseif verbosity >= 4 then
62 print("assert_equal succeeded: "..tostring(a).." == "..tostring(b));
63 end
64 end
66 function assert_table(a, message, level)
67 assert_equal(type(a), "table", message, (level or 1) + 1);
68 end
69 function assert_function(a, message, level)
70 assert_equal(type(a), "function", message, (level or 1) + 1);
71 end
72 function assert_string(a, message, level)
73 assert_equal(type(a), "string", message, (level or 1) + 1);
74 end
75 function assert_boolean(a, message)
76 assert_equal(type(a), "boolean", message);
77 end
78 function assert_is(a, message)
79 assert_equal(not not a, true, message);
80 end
81 function assert_is_not(a, message)
82 assert_equal(not not a, false, message);
83 end
86 function dosingletest(testname, fname)
87 local tests = setmetatable({}, { __index = _realG });
88 tests.__unit = testname;
89 tests.__test = fname;
90 local chunk, err = envloadfile(testname, tests);
91 if not chunk then
92 print("WARNING: ", "Failed to load tests for "..testname, err);
93 return;
94 end
96 local success, err = pcall(chunk);
97 if not success then
98 print("WARNING: ", "Failed to initialise tests for "..testname, err);
99 return;
102 if type(tests[fname]) ~= "function" then
103 error(testname.." has no test '"..fname.."'", 0);
107 local line_hook, line_info = new_line_coverage_monitor(testname);
108 debug.sethook(line_hook, "l")
109 local success, ret = pcall(tests[fname]);
110 debug.sethook();
111 if not success then
112 tests_passed = false;
113 print("TEST FAILED! Unit: ["..testname.."] Function: ["..fname.."]");
114 print(" Location: "..ret:gsub(":%s*\n", "\n"));
115 line_info(fname, false, report_file);
116 elseif verbosity >= 2 then
117 print("TEST SUCCEEDED: ", testname, fname);
118 print(string.format("TEST COVERED %d/%d lines", line_info(fname, true, report_file)));
119 else
120 line_info(name, success, report_file);
124 function dotest(unitname)
125 local _fakeG = setmetatable({}, {__index = _realG});
126 _fakeG._G = _fakeG;
127 local tests = setmetatable({}, { __index = _fakeG });
128 tests.__unit = unitname;
129 local chunk, err = envloadfile("test_"..unitname:gsub("%.", "_")..".lua", tests);
130 if not chunk then
131 print("WARNING: ", "Failed to load tests for "..unitname, err);
132 return;
135 local success, err = pcall(chunk);
136 if not success then
137 print("WARNING: ", "Failed to initialise tests for "..unitname, err);
138 return;
140 if tests.env then setmetatable(tests.env, { __index = _realG }); end
141 local unit = setmetatable({}, { __index = setmetatable({ _G = tests.env or _fakeG }, { __index = tests.env or _fakeG }) });
142 local fn = "../"..unitname:gsub("%.", "/")..".lua";
143 local chunk, err = envloadfile(fn, unit);
144 if not chunk then
145 print("WARNING: ", "Failed to load module: "..unitname, err);
146 return;
149 local oldmodule, old_M = _fakeG.module, _fakeG._M;
150 _fakeG.module = function ()
151 setmetatable(unit, nil);
152 unit._M = unit;
154 local success, ret = pcall(chunk);
155 _fakeG.module, _fakeG._M = oldmodule, old_M;
156 if not success then
157 print("WARNING: ", "Failed to initialise module: "..unitname, ret);
158 return;
161 if type(ret) == "table" then
162 for k,v in pairs(ret) do
163 unit[k] = v;
167 for name, f in pairs(unit) do
168 local test = rawget(tests, name);
169 if type(f) ~= "function" then
170 if verbosity >= 3 then
171 print("INFO: ", "Skipping "..unitname.."."..name.." because it is not a function");
173 elseif type(test) ~= "function" then
174 if verbosity >= 1 then
175 print("WARNING: ", unitname.."."..name.." has no test!");
177 else
178 if verbosity >= 4 then
179 print("INFO: ", "Testing "..unitname.."."..name);
181 local line_hook, line_info = new_line_coverage_monitor(fn);
182 debug.sethook(line_hook, "l")
183 local success, ret = pcall(test, f, unit);
184 debug.sethook();
185 if not success then
186 tests_passed = false;
187 print("TEST FAILED! Unit: ["..unitname.."] Function: ["..name.."]");
188 print(" Location: "..ret:gsub(":%s*\n", "\n"));
189 line_info(name, false, report_file);
190 elseif verbosity >= 2 then
191 print("TEST SUCCEEDED: ", unitname, name);
192 print(string.format("TEST COVERED %d/%d lines", line_info(name, true, report_file)));
193 else
194 line_info(name, success, report_file);
200 function runtest(f, msg)
201 if not f then print("SUBTEST NOT FOUND: "..(msg or "(no description)")); return; end
202 local success, ret = pcall(f);
203 if success and verbosity >= 2 then
204 print("SUBTEST PASSED: "..(msg or "(no description)"));
205 elseif (not success) and verbosity >= 0 then
206 tests_passed = false;
207 print("SUBTEST FAILED: "..(msg or "(no description)"));
208 error(ret, 0);
212 function new_line_coverage_monitor(file)
213 local lines_hit, funcs_hit = {}, {};
214 local total_lines, covered_lines = 0, 0;
216 for line in io.lines(file) do
217 total_lines = total_lines + 1;
220 return function (event, line) -- Line hook
221 if not lines_hit[line] then
222 local info = debug.getinfo(2, "fSL")
223 if not info.source:find(file) then return; end
224 if not funcs_hit[info.func] and info.activelines then
225 funcs_hit[info.func] = true;
226 for line in pairs(info.activelines) do
227 lines_hit[line] = false; -- Marks it as hittable, but not hit yet
230 if lines_hit[line] == false then
231 --print("New line hit: "..line.." in "..debug.getinfo(2, "S").source);
232 lines_hit[line] = true;
233 covered_lines = covered_lines + 1;
236 end,
237 function (test_name, success) -- Get info
238 local fn = file:gsub("^%W*", "");
239 local total_active_lines = 0;
240 local coverage_file = io.open("reports/coverage_"..fn:gsub("%W+", "_")..".report", "a+");
241 for line, active in pairs(lines_hit) do
242 if active ~= nil then total_active_lines = total_active_lines + 1; end
243 if coverage_file then
244 if active == false then coverage_file:write(fn, "|", line, "|", name or "", "|miss\n");
245 else coverage_file:write(fn, "|", line, "|", name or "", "|", tostring(success), "\n"); end
248 if coverage_file then coverage_file:close(); end
249 return covered_lines, total_active_lines, lines_hit;
253 run_all_tests()
255 os.exit(tests_passed and 0 or 1);