tests: Add basic test for net.http.parser
[prosody.git] / tests / test.lua
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 --
8
9 local tests_passed = true;
10
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 "net.http.parser"
31
32         dosingletest("test_sasl.lua", "latin1toutf8");
33         dosingletest("test_utf8.lua", "valid");
34 end
35
36 local verbosity = tonumber(arg[1]) or 2;
37
38 if os.getenv("WINDIR") then
39         package.path = package.path..";..\\?.lua";
40         package.cpath = package.cpath..";..\\?.dll";
41 else
42         package.path = package.path..";../?.lua";
43         package.cpath = package.cpath..";../?.so";
44 end
45
46 local _realG = _G;
47
48 require "util.import"
49
50 local envloadfile = require "util.envload".envloadfile;
51
52 local env_mt = { __index = function (t,k) return rawget(_realG, k) or print("WARNING: Attempt to access nil global '"..tostring(k).."'"); end };
53 function testlib_new_env(t)
54         return setmetatable(t or {}, env_mt);
55 end
56
57 function assert_equal(a, b, message, level)
58         if not (a == b) then
59                 error("\n   assert_equal failed: "..tostring(a).." ~= "..tostring(b)..(message and ("\n   Message: "..message) or ""), (level or 1) + 1);
60         elseif verbosity >= 4 then
61                 print("assert_equal succeeded: "..tostring(a).." == "..tostring(b));
62         end
63 end
64
65 function assert_table(a, message, level)
66         assert_equal(type(a), "table", message, (level or 1) + 1);
67 end
68 function assert_function(a, message, level)
69         assert_equal(type(a), "function", message, (level or 1) + 1);
70 end
71 function assert_string(a, message, level)
72         assert_equal(type(a), "string", message, (level or 1) + 1);
73 end
74 function assert_boolean(a, message)
75         assert_equal(type(a), "boolean", message);
76 end
77 function assert_is(a, message)
78         assert_equal(not not a, true, message);
79 end
80 function assert_is_not(a, message)
81         assert_equal(not not a, false, message);
82 end
83
84
85 function dosingletest(testname, fname)
86         local tests = setmetatable({}, { __index = _realG });
87         tests.__unit = testname;
88         tests.__test = fname;
89         local chunk, err = envloadfile(testname, tests);
90         if not chunk then
91                 print("WARNING: ", "Failed to load tests for "..testname, err);
92                 return;
93         end
94
95         local success, err = pcall(chunk);
96         if not success then
97                 print("WARNING: ", "Failed to initialise tests for "..testname, err);
98                 return;
99         end
100
101         if type(tests[fname]) ~= "function" then
102                 error(testname.." has no test '"..fname.."'", 0);
103         end
104
105
106         local line_hook, line_info = new_line_coverage_monitor(testname);
107         debug.sethook(line_hook, "l")
108         local success, ret = pcall(tests[fname]);
109         debug.sethook();
110         if not success then
111                 tests_passed = false;
112                 print("TEST FAILED! Unit: ["..testname.."] Function: ["..fname.."]");
113                 print("   Location: "..ret:gsub(":%s*\n", "\n"));
114                 line_info(fname, false, report_file);
115         elseif verbosity >= 2 then
116                 print("TEST SUCCEEDED: ", testname, fname);
117                 print(string.format("TEST COVERED %d/%d lines", line_info(fname, true, report_file)));
118         else
119                 line_info(name, success, report_file);
120         end
121 end
122
123 function dotest(unitname)
124         local _fakeG = setmetatable({}, {__index = _realG});
125         _fakeG._G = _fakeG;
126         local tests = setmetatable({}, { __index = _fakeG });
127         tests.__unit = unitname;
128         local chunk, err = envloadfile("test_"..unitname:gsub("%.", "_")..".lua", tests);
129         if not chunk then
130                 print("WARNING: ", "Failed to load tests for "..unitname, err);
131                 return;
132         end
133
134         local success, err = pcall(chunk);
135         if not success then
136                 print("WARNING: ", "Failed to initialise tests for "..unitname, err);
137                 return;
138         end
139         if tests.env then setmetatable(tests.env, { __index = _realG }); end
140         local unit = setmetatable({}, { __index = setmetatable({ _G = tests.env or _fakeG }, { __index = tests.env or _fakeG }) });
141         local fn = "../"..unitname:gsub("%.", "/")..".lua";
142         local chunk, err = envloadfile(fn, unit);
143         if not chunk then
144                 print("WARNING: ", "Failed to load module: "..unitname, err);
145                 return;
146         end
147
148         local oldmodule, old_M = _fakeG.module, _fakeG._M;
149         _fakeG.module = function ()
150                 setmetatable(unit, nil);
151                 unit._M = unit;
152         end
153         local success, ret = pcall(chunk);
154         _fakeG.module, _fakeG._M = oldmodule, old_M;
155         if not success then
156                 print("WARNING: ", "Failed to initialise module: "..unitname, ret);
157                 return;
158         end
159
160         if type(ret) == "table" then
161                 for k,v in pairs(ret) do
162                         unit[k] = v;
163                 end
164         end
165
166         for name, f in pairs(unit) do
167                 local test = rawget(tests, name);
168                 if type(f) ~= "function" then
169                         if verbosity >= 3 then
170                                 print("INFO: ", "Skipping "..unitname.."."..name.." because it is not a function");
171                         end
172                 elseif type(test) ~= "function" then
173                         if verbosity >= 1 then
174                                 print("WARNING: ", unitname.."."..name.." has no test!");
175                         end
176                 else
177                         if verbosity >= 4 then
178                                 print("INFO: ", "Testing "..unitname.."."..name);
179                         end
180                         local line_hook, line_info = new_line_coverage_monitor(fn);
181                         debug.sethook(line_hook, "l")
182                         local success, ret = pcall(test, f, unit);
183                         debug.sethook();
184                         if not success then
185                                 tests_passed = false;
186                                 print("TEST FAILED! Unit: ["..unitname.."] Function: ["..name.."]");
187                                 print("   Location: "..ret:gsub(":%s*\n", "\n"));
188                                 line_info(name, false, report_file);
189                         elseif verbosity >= 2 then
190                                 print("TEST SUCCEEDED: ", unitname, name);
191                                 print(string.format("TEST COVERED %d/%d lines", line_info(name, true, report_file)));
192                         else
193                                 line_info(name, success, report_file);
194                         end
195                 end
196         end
197 end
198
199 function runtest(f, msg)
200         if not f then print("SUBTEST NOT FOUND: "..(msg or "(no description)")); return; end
201         local success, ret = pcall(f);
202         if success and verbosity >= 2 then
203                 print("SUBTEST PASSED: "..(msg or "(no description)"));
204         elseif (not success) and verbosity >= 0 then
205                 tests_passed = false;
206                 print("SUBTEST FAILED: "..(msg or "(no description)"));
207                 error(ret, 0);
208         end
209 end
210
211 function new_line_coverage_monitor(file)
212         local lines_hit, funcs_hit = {}, {};
213         local total_lines, covered_lines = 0, 0;
214
215         for line in io.lines(file) do
216                 total_lines = total_lines + 1;
217         end
218
219         return function (event, line) -- Line hook
220                         if not lines_hit[line] then
221                                 local info = debug.getinfo(2, "fSL")
222                                 if not info.source:find(file) then return; end
223                                 if not funcs_hit[info.func] and info.activelines then
224                                         funcs_hit[info.func] = true;
225                                         for line in pairs(info.activelines) do
226                                                 lines_hit[line] = false; -- Marks it as hittable, but not hit yet
227                                         end
228                                 end
229                                 if lines_hit[line] == false then
230                                         --print("New line hit: "..line.." in "..debug.getinfo(2, "S").source);
231                                         lines_hit[line] = true;
232                                         covered_lines = covered_lines + 1;
233                                 end
234                         end
235                 end,
236                 function (test_name, success) -- Get info
237                         local fn = file:gsub("^%W*", "");
238                         local total_active_lines = 0;
239                         local coverage_file = io.open("reports/coverage_"..fn:gsub("%W+", "_")..".report", "a+");
240                         for line, active in pairs(lines_hit) do
241                                 if active ~= nil then total_active_lines = total_active_lines + 1; end
242                                 if coverage_file then
243                                         if active == false then coverage_file:write(fn, "|", line, "|", name or "", "|miss\n");
244                                         else coverage_file:write(fn, "|", line, "|", name or "", "|", tostring(success), "\n"); end
245                                 end
246                         end
247                         if coverage_file then coverage_file:close(); end
248                         return covered_lines, total_active_lines, lines_hit;
249                 end
250 end
251
252 run_all_tests()
253
254 os.exit(tests_passed and 0 or 1);