3b99431477f34a9a4b448ad6f024d90d71bc46e2
[prosody.git] / tests / test.lua
1 -- Prosody IM v0.3
2 -- Copyright (C) 2008-2009 Matthew Wild
3 -- Copyright (C) 2008-2009 Waqas Hussain
4 -- 
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9
10
11 function run_all_tests()
12         dotest "util.jid"
13         dotest "util.multitable"
14         dotest "core.stanza_router"
15         dotest "core.s2smanager"
16         dotest "core.configmanager"
17         dotest "util.stanza"
18                 
19         dosingletest("test_sasl.lua", "latin1toutf8");
20 end
21
22 local verbosity = tonumber(arg[1]) or 2;
23
24 package.path = package.path..";../?.lua";
25 package.cpath = package.cpath..";../?.so";
26
27 require "util.import"
28
29 local env_mt = { __index = function (t,k) return rawget(_G, k) or print("WARNING: Attempt to access nil global '"..tostring(k).."'"); end };
30 function testlib_new_env(t)
31         return setmetatable(t or {}, env_mt);
32 end
33
34 function assert_equal(a, b, message, level)
35         if not (a == b) then
36                 error("\n   assert_equal failed: "..tostring(a).." ~= "..tostring(b)..(message and ("\n   Message: "..message) or ""), (level or 1) + 1);
37         elseif verbosity >= 4 then
38                 print("assert_equal succeeded: "..tostring(a).." == "..tostring(b));
39         end
40 end
41
42 function assert_table(a, message, level)
43         assert_equal(type(a), "table", message, (level or 1) + 1);
44 end
45 function assert_function(a, message, level)
46         assert_equal(type(a), "function", message, (level or 1) + 1);
47 end
48 function assert_string(a, message, level)
49         assert_equal(type(a), "string", message, (level or 1) + 1);
50 end
51 function assert_boolean(a, message)
52         assert_equal(type(a), "boolean", message);
53 end
54 function assert_is(a, message)
55         assert_equal(not not a, true, message);
56 end
57 function assert_is_not(a, message)
58         assert_equal(not not a, false, message);
59 end
60
61
62 function dosingletest(testname, fname)
63         local tests = setmetatable({}, { __index = _G });
64         tests.__unit = testname;
65         tests.__test = fname;
66         local chunk, err = loadfile(testname);
67         if not chunk then
68                 print("WARNING: ", "Failed to load tests for "..testname, err);
69                 return;
70         end
71
72         setfenv(chunk, tests);
73         local success, err = pcall(chunk);
74         if not success then
75                 print("WARNING: ", "Failed to initialise tests for "..testname, err);
76                 return;
77         end
78         
79         if type(tests[fname]) ~= "function" then
80                 error(testname.." has no test '"..fname.."'", 0);
81         end
82         
83         
84         local line_hook, line_info = new_line_coverage_monitor(testname);
85         debug.sethook(line_hook, "l")
86         local success, ret = pcall(tests[fname]);
87         debug.sethook();
88         if not success then
89                 print("TEST FAILED! Unit: ["..testname.."] Function: ["..fname.."]");
90                 print("   Location: "..ret:gsub(":%s*\n", "\n"));
91                 line_info(fname, false, report_file);
92         elseif verbosity >= 2 then
93                 print("TEST SUCCEEDED: ", testname, fname);
94                 print(string.format("TEST COVERED %d/%d lines", line_info(fname, true, report_file)));
95         else
96                 line_info(name, success, report_file);
97         end
98 end
99
100 function dotest(unitname)
101         local tests = setmetatable({}, { __index = _G });
102         tests.__unit = unitname;
103         local chunk, err = loadfile("test_"..unitname:gsub("%.", "_")..".lua");
104         if not chunk then
105                 print("WARNING: ", "Failed to load tests for "..unitname, err);
106                 return;
107         end
108
109         setfenv(chunk, tests);
110         local success, err = pcall(chunk);
111         if not success then
112                 print("WARNING: ", "Failed to initialise tests for "..unitname, err);
113                 return;
114         end
115         
116         local unit = setmetatable({}, { __index = setmetatable({ module = function () end }, { __index = _G }) });
117
118         local fn = "../"..unitname:gsub("%.", "/")..".lua";
119         local chunk, err = loadfile(fn);
120         if not chunk then
121                 print("WARNING: ", "Failed to load module: "..unitname, err);
122                 return;
123         end
124
125         setfenv(chunk, unit);
126         local success, err = pcall(chunk);
127         if not success then
128                 print("WARNING: ", "Failed to initialise module: "..unitname, err);
129                 return;
130         end
131         
132         for name, f in pairs(unit) do
133                 local test = rawget(tests, name);
134                 if type(f) ~= "function" then
135                         if verbosity >= 3 then
136                                 print("INFO: ", "Skipping "..unitname.."."..name.." because it is not a function");
137                         end
138                 elseif type(test) ~= "function" then
139                         if verbosity >= 1 then
140                                 print("WARNING: ", unitname.."."..name.." has no test!");
141                         end
142                 else
143                         local line_hook, line_info = new_line_coverage_monitor(fn);
144                         debug.sethook(line_hook, "l")
145                         local success, ret = pcall(test, f, unit);
146                         debug.sethook();
147                         if not success then
148                                 print("TEST FAILED! Unit: ["..unitname.."] Function: ["..name.."]");
149                                 print("   Location: "..ret:gsub(":%s*\n", "\n"));
150                                 line_info(name, false, report_file);
151                         elseif verbosity >= 2 then
152                                 print("TEST SUCCEEDED: ", unitname, name);
153                                 print(string.format("TEST COVERED %d/%d lines", line_info(name, true, report_file)));
154                         else
155                                 line_info(name, success, report_file);
156                         end
157                 end
158         end
159 end
160
161 function runtest(f, msg)
162         if not f then print("SUBTEST NOT FOUND: "..(msg or "(no description)")); return; end
163         local success, ret = pcall(f);
164         if success and verbosity >= 2 then
165                 print("SUBTEST PASSED: "..(msg or "(no description)"));
166         elseif (not success) and verbosity >= 0 then
167                 print("SUBTEST FAILED: "..(msg or "(no description)"));
168                 error(ret, 0);
169         end
170 end
171
172 function new_line_coverage_monitor(file)
173         local lines_hit, funcs_hit = {}, {};
174         local total_lines, covered_lines = 0, 0;
175         
176         for line in io.lines(file) do
177                 total_lines = total_lines + 1;
178         end
179         
180         return function (event, line) -- Line hook
181                         if not lines_hit[line] then
182                                 local info = debug.getinfo(2, "fSL")
183                                 if not info.source:find(file) then return; end
184                                 if not funcs_hit[info.func] and info.activelines then
185                                         funcs_hit[info.func] = true;
186                                         for line in pairs(info.activelines) do
187                                                 lines_hit[line] = false; -- Marks it as hittable, but not hit yet
188                                         end
189                                 end
190                                 if lines_hit[line] == false then
191                                         --print("New line hit: "..line.." in "..debug.getinfo(2, "S").source);
192                                         lines_hit[line] = true;
193                                         covered_lines = covered_lines + 1;
194                                 end
195                         end
196                 end,
197                 function (test_name, success) -- Get info
198                         local fn = file:gsub("^%W*", "");
199                         local total_active_lines = 0;
200                         local coverage_file = io.open("reports/coverage_"..fn:gsub("%W+", "_")..".report", "a+");
201                         for line, active in pairs(lines_hit) do
202                                 if active ~= nil then total_active_lines = total_active_lines + 1; end
203                                 if coverage_file then
204                                         if active == false then coverage_file:write(fn, "|", line, "|", name or "", "|miss\n"); 
205                                         else coverage_file:write(fn, "|", line, "|", name or "", "|", tostring(success), "\n"); end
206                                 end
207                         end
208                         if coverage_file then coverage_file:close(); end
209                         return covered_lines, total_active_lines, lines_hit;
210                 end
211 end
212
213 run_all_tests()