monotone

monotone Mtn Source Tree

Root/tester.lua

1tests = {} -- list of all tests, not visible when running tests
2test = {} -- table of per-test values
3
4-- misc global values
5
6-- where the main testsuite file is
7srcdir = get_source_dir()
8-- where the individual test dirs are
9-- most paths will be testdir.."/something"
10testdir = srcdir
11-- was the -d switch given?
12debugging = false
13
14-- combined logfile
15logfile = io.open("tester.log", "w")
16-- logfiles of failed tests; append these to the main logfile
17failed_testlogs = {}
18
19-- This is for redirected output from local implementations
20-- of shellutils type stuff (ie, grep).
21-- Reason: {set,clear}_redirect don't seem to (always?) work
22-- for this (at least on Windows).
23files = {stdout = nil, stdin = nil, stderr = nil}
24
25
26-- misc per-test values
27test.root = nil
28test.name = nil
29test.wanted_fail = false
30test.partial_skip = false -- set this to true if you skip part of the test
31
32--probably should put these in the error that gets thrown...
33test.errfile = ""
34test.errline = -1
35
36-- for tracking background processes
37test.bgid = 0
38test.bglist = {}
39
40test.log = nil -- logfile for this test
41
42
43function P(...)
44 io.write(unpack(arg))
45 io.flush()
46 logfile:write(unpack(arg))
47end
48
49function L(...)
50 test.log:write(unpack(arg))
51 test.log:flush()
52end
53
54function getsrcline()
55 local info
56 local depth = 1
57 repeat
58 depth = depth + 1
59 info = debug.getinfo(depth)
60 until info == nil
61 while src == nil and depth > 1 do
62 depth = depth - 1
63 info = debug.getinfo(depth)
64 if string.find(info.source, "^@.*__driver__%.lua") then
65 -- return info.source, info.currentline
66 return test.name, info.currentline
67 end
68 end
69end
70
71function locheader()
72 local _,line = getsrcline()
73 if line == nil then line = -1 end
74 if test.name == nil then
75 return "\n<unknown>:" .. line .. ": "
76 else
77 return "\n" .. test.name .. ":" .. line .. ": "
78 end
79end
80
81function err(what, level)
82 if level == nil then level = 2 end
83 test.errfile, test.errline = getsrcline()
84 local e
85 if type(what) == "table" then
86 e = what
87 if e.bt == nil then e.bt = {} end
88 table.insert(e.bt, debug.traceback())
89 else
90 e = {e = what, bt = {debug.traceback()}}
91 end
92 error(e, level)
93end
94
95do -- replace some builtings with logged versions
96 old_mtime = mtime
97 mtime = function(name)
98 local x = old_mtime(name)
99 L(locheader(), "mtime(", name, ") = ", tostring(x), "\n")
100 return x
101 end
102
103 old_mkdir = mkdir
104 mkdir = function(name)
105 L(locheader(), "mkdir ", name, "\n")
106 old_mkdir(name)
107 end
108
109 old_existsonpath = existsonpath
110 existsonpath = function(name)
111 local r = (old_existsonpath(name) == 0)
112 local what
113 if r then
114 what = "exists"
115 else
116 what = "does not exist"
117 end
118 L(locheader(), name, " ", what, " on the path\n")
119 return r
120 end
121end
122
123function numlines(filename)
124 local n = 0
125 for _ in io.lines(filename) do n = n + 1 end
126 L(locheader(), "numlines(", filename, ") = ", n, "\n")
127 return n
128end
129
130function open_or_err(filename, mode, depth)
131 local file, e = io.open(filename, mode)
132 if file == nil then
133 err("Cannot open file " .. filename .. ": " .. e, depth)
134 end
135 return file
136end
137
138function fsize(filename)
139 local file = open_or_err(filename, "r", 3)
140 local size = file:seek("end")
141 file:close()
142 return size
143end
144
145function readfile_q(filename)
146 local file = open_or_err(filename, "rb", 3)
147 local dat = file:read("*a")
148 file:close()
149 return dat
150end
151
152function readfile(filename)
153 L(locheader(), "readfile ", filename, "\n")
154 return readfile_q(filename)
155end
156
157function readstdfile(filename)
158 return readfile(testdir.."/"..filename)
159end
160
161function writefile_q(filename, dat)
162 local file,e
163 if dat == nil then
164 file,e = open_or_err(filename, "a+b", 3)
165 else
166 file,e = open_or_err(filename, "wb", 3)
167 end
168 if dat ~= nil then
169 file:write(dat)
170 end
171 file:close()
172 return true
173end
174
175function writefile(filename, dat)
176 L(locheader(), "writefile ", filename, "\n")
177 return writefile_q(filename, dat)
178end
179
180function append(filename, dat)
181 L(locheader(), "append to file ", filename, "\n")
182 local file,e = open_or_err(filename, "a+", 3)
183 file:write(dat)
184 file:close()
185 return true
186end
187
188do
189 unlogged_copy = copy_recursive
190 copy_recursive = nil
191 function copy(from, to)
192 L(locheader(), "copy ", from, " -> ", to, "\n")
193 local ok, res = unlogged_copy(from, to)
194 if not ok then
195 L(res, "\n")
196 return false
197 else
198 return true
199 end
200 end
201end
202
203do
204 local os_rename = os.rename
205 os.rename = nil
206 os.remove = nil
207 function rename(from, to)
208 L(locheader(), "rename ", from, " ", to, "\n")
209 if exists(to) and not isdir(to) then
210 L("Destination ", to, " exists; removing...\n")
211 local ok, res = unlogged_remove(to)
212 if not ok then
213 L("Could not remove ", to, ": ", res, "\n")
214 return false
215 end
216 end
217 local ok,res = os_rename(from, to)
218 if not ok then
219 L(res, "\n")
220 return false
221 else
222 return true
223 end
224 end
225 function unlogged_rename(from, to)
226 if exists(to) and not isdir(to) then
227 unlogged_remove(to)
228 end
229 os_rename(from, to)
230 end
231 unlogged_remove = remove_recursive
232 remove_recursive = nil
233 function remove(file)
234 L(locheader(), "remove ", file, "\n")
235 local ok,res = unlogged_remove(file)
236 if not ok then
237 L(res, "\n")
238 return false
239 else
240 return true
241 end
242 end
243end
244
245
246function getstd(name, as)
247 if as == nil then as = name end
248 local ret = copy(testdir .. "/" .. name, as)
249 make_tree_accessible(as)
250 return ret
251end
252
253function get(name, as)
254 if as == nil then as = name end
255 return getstd(test.name .. "/" .. name, as)
256end
257
258-- include from the main tests directory; there's no reason
259-- to want to include from the dir for the current test,
260-- since in that case it could just go in the driver file.
261function include(name)
262 local func, e = loadfile(testdir.."/"..name)
263 if func == nil then err(e, 2) end
264 setfenv(func, getfenv(2))
265 func()
266end
267
268function trim(str)
269 return string.gsub(str, "^%s*(.-)%s*$", "%1")
270end
271
272function prepare_redirect(fin, fout, ferr)
273 local cwd = chdir(".").."/"
274 redir = {fin = cwd..fin, fout = cwd..fout, ferr = cwd..ferr}
275end
276do
277 oldspawn = spawn
278 function spawn(...)
279 if redir == nil then
280 return oldspawn(unpack(arg))
281 else
282 return spawn_redirected(redir.fin, redir.fout, redir.ferr, unpack(arg))
283 end
284 end
285end
286function execute(path, ...)
287 local pid
288 local ret = -1
289 pid = spawn(path, unpack(arg))
290 redir = nil
291 if (pid ~= -1) then ret, pid = wait(pid) end
292 return ret
293end
294
295function cmd_as_str(cmd_table)
296 local str = ""
297 for i,x in ipairs(cmd_table) do
298 if str ~= "" then str = str .. " " end
299 if type(x) == "function" then
300 str = str .. "<function>"
301 else
302 local s = tostring(x)
303 if string.find(s, " ") then
304 str = str .. '"'..s..'"'
305 else
306 str = str .. s
307 end
308 end
309 end
310 return str
311end
312
313function runcmd(cmd, prefix, bgnd)
314 if prefix == nil then prefix = "ts-" end
315 if type(cmd) ~= "table" then err("runcmd called with bad argument") end
316 local local_redir = cmd.local_redirect
317 if cmd.local_redirect == nil then
318 if type(cmd[1]) == "function" then
319 local_redir = true
320 else
321 local_redir = false
322 end
323 end
324 if bgnd == true and type(cmd[1]) == "string" then local_redir = false end
325 L("\nruncmd: ", tostring(cmd[1]), ", local_redir = ", tostring(local_redir), ", requested = ", tostring(cmd.local_redirect))
326 local redir
327 if local_redir then
328 files.stdin = open_or_err(prefix.."stdin", nil, 2)
329 files.stdout = open_or_err(prefix.."stdout", "w", 2)
330 files.stderr = open_or_err(prefix.."stderr", "w", 2)
331 else
332 prepare_redirect(prefix.."stdin", prefix.."stdout", prefix.."stderr")
333 end
334
335 local result
336 if cmd.logline ~= nil then
337 L(locheader(), cmd.logline, "\n")
338 else
339 L(locheader(), cmd_as_str(cmd), "\n")
340 end
341 if type(cmd[1]) == "function" then
342 result = {pcall(unpack(cmd))}
343 elseif type(cmd[1]) == "string" then
344 if bgnd then
345 result = {pcall(spawn, unpack(cmd))}
346 else
347 result = {pcall(execute, unpack(cmd))}
348 end
349 else
350 err("runcmd called with bad command table")
351 end
352
353 if local_redir then
354 files.stdin:close()
355 files.stdout:close()
356 files.stderr:close()
357 end
358 return unpack(result)
359end
360
361function samefile(left, right)
362 local ldat = nil
363 local rdat = nil
364 if left == "-" then
365 ldat = io.input:read("*a")
366 rdat = readfile(right)
367 elseif right == "-" then
368 rdat = io.input:read("*a")
369 ldat = readfile(left)
370 else
371 if fsize(left) ~= fsize(right) then
372 return false
373 else
374 ldat = readfile(left)
375 rdat = readfile(right)
376 end
377 end
378 return ldat == rdat
379end
380
381function samelines(f, t)
382 local fl = {}
383 for l in io.lines(f) do table.insert(fl, l) end
384 if not table.getn(fl) == table.getn(t) then
385 L(locheader(), string.format("file has %s lines; table has %s\n",
386 table.getn(fl), table.getn(t)))
387 return false
388 end
389 for i=1,table.getn(t) do
390 if fl[i] ~= t[i] then
391 if fl[i] then
392 L(locheader(), string.format("file[i] = '%s'; table[i] = '%s'\n",
393 fl[i], t[i]))
394 else
395 L(locheader(), string.format("file[i] = ''; table[i] = '%s'\n",
396 t[i]))
397 end
398 return false
399 end
400 end
401 return true
402end
403
404function greplines(f, t)
405 local fl = {}
406 for l in io.lines(f) do table.insert(fl, l) end
407 if not table.getn(fl) == table.getn(t) then
408 L(locheader(), string.format("file has %s lines; table has %s\n",
409 table.getn(fl), table.getn(t)))
410 return false
411 end
412 for i=1,table.getn(t) do
413 if not regex.search(t[i], fl[i]) then
414 L(locheader(), string.format("file[i] = '%s'; table[i] = '%s'\n",
415 fl[i], t[i]))
416 return false
417 end
418 end
419 return true
420end
421
422function grep(...)
423 local flags, what, where = unpack(arg)
424 local dogrep = function ()
425 if where == nil and string.sub(flags, 1, 1) ~= "-" then
426 where = what
427 what = flags
428 flags = ""
429 end
430 local quiet = string.find(flags, "q") ~= nil
431 local reverse = string.find(flags, "v") ~= nil
432 if not quiet and files.stdout == nil then err("non-quiet grep not redirected") end
433 local out = 1
434 local infile = files.stdin
435 if where ~= nil then infile = open_or_err(where) end
436 for line in infile:lines() do
437 local matched = regex.search(what, line)
438 if reverse then matched = not matched end
439 if matched then
440 if not quiet then files.stdout:write(line, "\n") end
441 out = 0
442 end
443 end
444 if where ~= nil then infile:close() end
445 return out
446 end
447 return {dogrep, logline = "grep "..cmd_as_str(arg)}
448end
449
450function cat(...)
451 local arguments = arg
452 local function docat()
453 local bsize = 8*1024
454 for _,x in ipairs(arguments) do
455 local infile
456 if x == "-" then
457 infile = files.stdin
458 else
459 infile = open_or_err(x, "rb", 3)
460 end
461 local block = infile:read(bsize)
462 while block do
463 files.stdout:write(block)
464 block = infile:read(bsize)
465 end
466 if x ~= "-" then
467 infile:close()
468 end
469 end
470 return 0
471 end
472 return {docat, logline = "cat "..cmd_as_str(arg)}
473end
474
475function tail(...)
476 local file, num = unpack(arg)
477 local function dotail()
478 if num == nil then num = 10 end
479 local mylines = {}
480 for l in io.lines(file) do
481 table.insert(mylines, l)
482 if table.getn(mylines) > num then
483 table.remove(mylines, 1)
484 end
485 end
486 for _,x in ipairs(mylines) do
487 files.stdout:write(x, "\n")
488 end
489 return 0
490 end
491 return {dotail, logline = "tail "..cmd_as_str(arg)}
492end
493
494function sort(file)
495 local function dosort(file)
496 local infile
497 if file == nil then
498 infile = files.stdin
499 else
500 infile = open_or_err(file)
501 end
502 local lines = {}
503 for l in infile:lines() do
504 table.insert(lines, l)
505 end
506 if file ~= nil then infile:close() end
507 table.sort(lines)
508 for _,l in ipairs(lines) do
509 files.stdout:write(l, "\n")
510 end
511 return 0
512 end
513 return {dosort, file, logline = "sort "..file}
514end
515
516function log_file_contents(filename)
517 L(readfile_q(filename), "\n")
518end
519
520function pre_cmd(stdin, ident)
521 if ident == nil then ident = "ts-" end
522 if stdin == true then
523 unlogged_copy("stdin", ident .. "stdin")
524 elseif type(stdin) == "table" then
525 unlogged_copy(stdin[1], ident .. "stdin")
526 else
527 local infile = open_or_err(ident .. "stdin", "w", 3)
528 if stdin ~= nil and stdin ~= false then
529 infile:write(stdin)
530 end
531 infile:close()
532 end
533 L("stdin:\n")
534 log_file_contents(ident .. "stdin")
535end
536
537function post_cmd(result, ret, stdout, stderr, ident)
538 if ret == nil then ret = 0 end
539 if ident == nil then ident = "ts-" end
540 L("stdout:\n")
541 log_file_contents(ident .. "stdout")
542 L("stderr:\n")
543 log_file_contents(ident .. "stderr")
544 if result ~= ret and ret ~= false then
545 err("Check failed (return value): wanted " .. ret .. " got " .. result, 3)
546 end
547
548 if stdout == nil then
549 if fsize(ident .. "stdout") ~= 0 then
550 err("Check failed (stdout): not empty", 3)
551 end
552 elseif type(stdout) == "string" then
553 local realout = open_or_err(ident .. "stdout", nil, 3)
554 local contents = realout:read("*a")
555 realout:close()
556 if contents ~= stdout then
557 err("Check failed (stdout): doesn't match", 3)
558 end
559 elseif type(stdout) == "table" then
560 if not samefile(ident .. "stdout", stdout[1]) then
561 err("Check failed (stdout): doesn't match", 3)
562 end
563 elseif stdout == true then
564 unlogged_remove("stdout")
565 unlogged_rename(ident .. "stdout", "stdout")
566 end
567
568 if stderr == nil then
569 if fsize(ident .. "stderr") ~= 0 then
570 err("Check failed (stderr): not empty", 3)
571 end
572 elseif type(stderr) == "string" then
573 local realerr = open_or_err(ident .. "stderr", nil, 3)
574 local contents = realerr:read("*a")
575 realerr:close()
576 if contents ~= stderr then
577 err("Check failed (stderr): doesn't match", 3)
578 end
579 elseif type(stderr) == "table" then
580 if not samefile(ident .. "stderr", stderr[1]) then
581 err("Check failed (stderr): doesn't match", 3)
582 end
583 elseif stderr == true then
584 unlogged_remove("stderr")
585 unlogged_rename(ident .. "stderr", "stderr")
586 end
587end
588
589-- std{out,err} can be:
590-- * false: ignore
591-- * true: ignore, copy to stdout
592-- * string: check that it matches the contents
593-- * nil: must be empty
594-- * {string}: check that it matches the named file
595-- stdin can be:
596-- * true: use existing "stdin" file
597-- * nil, false: empty input
598-- * string: contents of string
599-- * {string}: contents of the named file
600
601function bg(torun, ret, stdout, stderr, stdin)
602 test.bgid = test.bgid + 1
603 local out = {}
604 out.prefix = "ts-" .. test.bgid .. "-"
605 pre_cmd(stdin, out.prefix)
606 L("Starting background command...")
607 local ok,pid = runcmd(torun, out.prefix, true)
608 if not ok then err(pid, 2) end
609 if pid == -1 then err("Failed to start background process\n", 2) end
610 out.pid = pid
611 test.bglist[test.bgid] = out
612 out.id = test.bgid
613 out.retval = nil
614 out.locstr = locheader()
615 out.cmd = torun
616 out.expret = ret
617 out.expout = stdout
618 out.experr = stderr
619 local mt = {}
620 mt.__index = mt
621 mt.finish = function(obj, timeout)
622 if obj.retval ~= nil then return end
623
624 if timeout == nil then timeout = 0 end
625 if type(timeout) ~= "number" then
626 err("Bad timeout of type "..type(timeout))
627 end
628 local res
629 obj.retval, res = timed_wait(obj.pid, timeout)
630 if (res == -1) then
631 kill(obj.pid, 15) -- TERM
632 obj.retval, res = timed_wait(obj.pid, 2)
633 if (res == -1) then
634 kill(obj.pid, 9) -- KILL
635 obj.retval, res = timed_wait(obj.pid, 2)
636 end
637 end
638
639 test.bglist[obj.id] = nil
640 L(locheader(), "checking background command from ", out.locstr,
641 table.concat(out.cmd, " "), "\n")
642 post_cmd(obj.retval, out.expret, out.expout, out.experr, obj.prefix)
643 return true
644 end
645 mt.wait = function(obj, timeout)
646 if obj.retval ~= nil then return end
647 if timeout == nil then
648 obj.retval = wait(obj.pid)
649 else
650 local res
651 obj.retval, res = timed_wait(obj.pid, timeout)
652 if res == -1 then
653 obj.retval = nil
654 return false
655 end
656 end
657 test.bglist[obj.id] = nil
658 L(locheader(), "checking background command from ", out.locstr,
659 table.concat(out.cmd, " "), "\n")
660 post_cmd(obj.retval, out.expret, out.expout, out.experr, obj.prefix)
661 return true
662 end
663 return setmetatable(out, mt)
664end
665
666function runcheck(cmd, ret, stdout, stderr, stdin)
667 if ret == nil then ret = 0 end
668 pre_cmd(stdin)
669 local ok, result = runcmd(cmd)
670 if ok == false then
671 err(result, 2)
672 end
673 post_cmd(result, ret, stdout, stderr)
674 return result
675end
676
677function indir(dir, what)
678 if type(what) ~= "table" then
679 err("bad argument of type "..type(what).." to indir()")
680 end
681 local function do_indir()
682 local savedir = chdir(dir)
683 if savedir == nil then
684 err("Cannot chdir to "..dir)
685 end
686 local ok, res
687 if type(what[1]) == "function" then
688 ok, res = pcall(unpack(what))
689 elseif type(what[1]) == "string" then
690 ok, res = pcall(execute, unpack(what))
691 else
692 err("bad argument to indir(): cannot execute a "..type(what[1]))
693 end
694 chdir(savedir)
695 if not ok then err(res) end
696 return res
697 end
698 local want_local
699 if type(what[1]) == "function" then
700 if type(what.local_redirect) == "nil" then
701 want_local = true
702 else
703 want_local = what.local_redirect
704 end
705 else
706 want_local = false
707 end
708 local ll = "In directory "..dir..": "
709 if what.logline ~= nil then ll = ll .. tostring(what.logline)
710 else
711 ll = ll .. cmd_as_str(what)
712 end
713 return {do_indir, local_redirect = want_local, logline = ll}
714end
715
716function check(first, ...)
717 if type(first) == "table" then
718 return runcheck(first, unpack(arg))
719 elseif type(first) == "boolean" then
720 if not first then err("Check failed: false", 2) end
721 elseif type(first) == "number" then
722 if first ~= 0 then
723 err("Check failed: " .. first .. " ~= 0", 2)
724 end
725 else
726 err("Bad argument to check() (" .. type(first) .. ")", 2)
727 end
728 return first
729end
730
731function skip_if(chk)
732 if chk then
733 err(true, 2)
734 end
735end
736
737function xfail_if(chk, ...)
738 local ok,res = pcall(check, unpack(arg))
739 if ok == false then
740 if chk then err(false, 2) else err(err, 2) end
741 else
742 if chk then
743 test.wanted_fail = true
744 L("UNEXPECTED SUCCESS\n")
745 end
746 end
747end
748
749function xfail(...)
750 xfail_if(true, unpack(arg))
751end
752
753function log_error(e)
754 if type(e) == "table" then
755 L("\n", tostring(e.e), "\n")
756 for i,bt in ipairs(e.bt) do
757 if i ~= 1 then L("Rethrown from:") end
758 L(bt)
759 end
760 else
761 L("\n", tostring(e), "\n")
762 end
763end
764
765function run_tests(args)
766 local torun = {}
767 local run_all = true
768 local list_only = false
769 for i,a in pairs(args) do
770 local _1,_2,l,r = string.find(a, "^(-?%d+)%.%.(-?%d+)$")
771 if _1 then
772 l = l + 0
773 r = r + 0
774 if l < 1 then l = table.getn(tests) + l + 1 end
775 if r < 1 then r = table.getn(tests) + r + 1 end
776 if l > r then l,r = r,l end
777 for j = l,r do
778 torun[j]=j
779 end
780 run_all = false
781 elseif string.find(a, "^-?%d+$") then
782 r = a + 0
783 if r < 1 then r = table.getn(tests) + r + 1 end
784 torun[r] = r
785 run_all = false
786 elseif a == "-d" then
787 debugging = true
788 elseif a == "-l" then
789 list_only = true
790 else
791 -- pattern
792 local matched = false
793 for i,t in pairs(tests) do
794 if regex.search(a, t) then
795 torun[i] = i
796 matched = true
797 end
798 end
799 if matched then
800 run_all = false
801 else
802 print(string.format("Warning: pattern '%s' does not match any tests.", a))
803 end
804 end
805 end
806 if not list_only then
807 logfile:write("Running on ", get_ostype(), "\n\n")
808 P("Running tests...\n")
809 end
810 local counts = {}
811 counts.success = 0
812 counts.skip = 0
813 counts.xfail = 0
814 counts.noxfail = 0
815 counts.fail = 0
816 counts.total = 0
817 counts.of_interest = 0
818 local of_interest = {}
819
820 local function runtest(i, tname)
821 save_env()
822 local env = {}
823 local genv = getfenv(0)
824 for x,y in pairs(genv) do
825 env[x] = y
826 -- we want changes to globals in a test to be visible to
827 -- already-defined functions
828 if type(y) == "function" then
829 pcall(setfenv, y, env)
830 end
831 end
832 env.tests = nil -- don't let them mess with this
833
834 test.bgid = 0
835 test.name = tname
836 test.wanted_fail = false
837 test.partial_skip = false
838 local shortname = nil
839 test.root, shortname = go_to_test_dir(tname)
840 if shortname == nil or test.root == nil then
841 P("ERROR: could not enter scratch dir for test '", tname, "'\n")
842 error("")
843 end
844 test.errfile = ""
845 test.errline = -1
846 test.bglist = {}
847
848 local test_header = ""
849 if i < 100 then test_header = test_header .. " " end
850 if i < 10 then test_header = test_header .. " " end
851 test_header = test_header .. i .. " " .. shortname .. " "
852 local spacelen = 45 - string.len(shortname)
853 local spaces = string.rep(" ", 50)
854 if spacelen > 0 then
855 test_header = test_header .. string.sub(spaces, 1, spacelen)
856 end
857 P(test_header)
858
859 local tlog = test.root .. "/tester.log"
860 test.log = io.open(tlog, "w")
861 L("Test number ", i, ", ", shortname, "\n")
862
863 local driverfile = testdir .. "/" .. test.name .. "/__driver__.lua"
864 local driver, e = loadfile(driverfile)
865 local r
866 if driver == nil then
867 r = false
868 e = "Could not load driver file " .. driverfile .. " .\n" .. e
869 else
870 setfenv(driver, env)
871 local oldmask = posix_umask(0)
872 posix_umask(oldmask)
873 r,e = xpcall(driver, debug.traceback)
874 local errline = test.errline
875 for i,b in pairs(test.bglist) do
876 local a,x = pcall(function () b:finish(0) end)
877 if r and not a then
878 r = a
879 e = x
880 elseif not a then
881 L("Error cleaning up background processes: ", tostring(b.locstr), "\n")
882 end
883 end
884 if type(env.cleanup) == "function" then
885 local a,b = pcall(env.cleanup)
886 if r and not a then
887 r = a
888 e = b
889 end
890 end
891 test.errline = errline
892 posix_umask(oldmask)
893 end
894
895 -- set our functions back to the proper environment
896 local genv = getfenv(0)
897 for x,y in pairs(genv) do
898 if type(y) == "function" then
899 pcall(setfenv, y, genv)
900 end
901 end
902
903 if r then
904 if test.wanted_fail then
905 P("unexpected success\n")
906 test.log:close()
907 leave_test_dir()
908 counts.noxfail = counts.noxfail + 1
909 counts.of_interest = counts.of_interest + 1
910 table.insert(of_interest, test_header .. "unexpected success")
911 else
912 if test.partial_skip then
913 P("partial skip\n")
914 else
915 P("ok\n")
916 end
917 test.log:close()
918 if not debugging then clean_test_dir(tname) end
919 counts.success = counts.success + 1
920 end
921 else
922 if test.errline == nil then test.errline = -1 end
923 if type(e) ~= "table" then
924 local tbl = {e = e, bt = {"no backtrace; type(err) = "..type(e)}}
925 e = tbl
926 end
927 if e.e == true then
928 P(string.format("skipped (line %i)\n", test.errline))
929 test.log:close()
930 if not debugging then clean_test_dir(tname) end
931 counts.skip = counts.skip + 1
932 elseif e.e == false then
933 P(string.format("expected failure (line %i)\n", test.errline))
934 test.log:close()
935 leave_test_dir()
936 counts.xfail = counts.xfail + 1
937 else
938 result = string.format("FAIL (line %i)", test.errline)
939 P(result, "\n")
940 log_error(e)
941 table.insert(failed_testlogs, tlog)
942 test.log:close()
943 leave_test_dir()
944 counts.fail = counts.fail + 1
945 counts.of_interest = counts.of_interest + 1
946 table.insert(of_interest, test_header .. result)
947 end
948 end
949 counts.total = counts.total + 1
950 restore_env()
951 end
952
953 if run_all then
954 for i,t in pairs(tests) do
955 if list_only then
956 if i < 10 then P(" ") end
957 if i < 100 then P(" ") end
958 P(i .. " " .. t .. "\n")
959 else
960 runtest(i, t)
961 end
962 end
963 else
964 for i,t in pairs(tests) do
965 if torun[i] == i then
966 if list_only then
967 if i < 10 then P(" ") end
968 if i < 100 then P(" ") end
969 P(i .. " " .. t .. "\n")
970 else
971 runtest(i, t)
972 end
973 end
974 end
975 end
976
977 if list_only then
978 logfile:close()
979 return 0
980 end
981
982 if counts.of_interest ~= 0 and (counts.total / counts.of_interest) > 4 then
983 P("\nInteresting tests:\n")
984 for i,x in ipairs(of_interest) do
985 P(x, "\n")
986 end
987 end
988 P("\n")
989 P(string.format("Of %i tests run:\n", counts.total))
990 P(string.format("\t%i succeeded\n", counts.success))
991 P(string.format("\t%i failed\n", counts.fail))
992 P(string.format("\t%i had expected failures\n", counts.xfail))
993 P(string.format("\t%i succeeded unexpectedly\n", counts.noxfail))
994 P(string.format("\t%i were skipped\n", counts.skip))
995
996 for i,log in pairs(failed_testlogs) do
997 local tlog = io.open(log, "r")
998 if tlog ~= nil then
999 local dat = tlog:read("*a")
1000 tlog:close()
1001 logfile:write("\n", string.rep("*", 50), "\n")
1002 logfile:write(dat)
1003 end
1004 end
1005 logfile:close()
1006
1007 if counts.success + counts.skip + counts.xfail == counts.total then
1008 return 0
1009 else
1010 return 1
1011 end
1012end

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status