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 execute(path, ...)
273 local pid
274 local ret = -1
275 pid = spawn(path, unpack(arg))
276 if (pid ~= -1) then ret, pid = wait(pid) end
277 return ret
278end
279
280function cmd_as_str(cmd_table)
281 local str = ""
282 for i,x in ipairs(cmd_table) do
283 if str ~= "" then str = str .. " " end
284 if type(x) == "function" then
285 str = str .. "<function>"
286 else
287 local s = tostring(x)
288 if string.find(s, " ") then
289 str = str .. '"'..s..'"'
290 else
291 str = str .. s
292 end
293 end
294 end
295 return str
296end
297
298function runcmd(cmd, prefix, bgnd)
299 if prefix == nil then prefix = "ts-" end
300 if type(cmd) ~= "table" then err("runcmd called with bad argument") end
301 local local_redir = cmd.local_redirect
302 if cmd.local_redirect == nil then
303 if type(cmd[1]) == "function" then
304 local_redir = true
305 else
306 local_redir = false
307 end
308 end
309 if bgnd == true and type(cmd[1]) == "string" then local_redir = false end
310 L("\nruncmd: ", tostring(cmd[1]), ", local_redir = ", tostring(local_redir), ", requested = ", tostring(cmd.local_redirect))
311 local redir
312 if local_redir then
313 files.stdin = open_or_err(prefix.."stdin", nil, 2)
314 files.stdout = open_or_err(prefix.."stdout", "w", 2)
315 files.stderr = open_or_err(prefix.."stderr", "w", 2)
316 else
317 redir = set_redirect(prefix.."stdin", prefix.."stdout", prefix.."stderr")
318 end
319
320 local result
321 if cmd.logline ~= nil then
322 L(locheader(), cmd.logline, "\n")
323 else
324 L(locheader(), cmd_as_str(cmd), "\n")
325 end
326 if type(cmd[1]) == "function" then
327 result = {pcall(unpack(cmd))}
328 elseif type(cmd[1]) == "string" then
329 if bgnd then
330 result = {pcall(spawn, unpack(cmd))}
331 else
332 result = {pcall(execute, unpack(cmd))}
333 end
334 else
335 err("runcmd called with bad command table")
336 end
337
338 if local_redir then
339 files.stdin:close()
340 files.stdout:close()
341 files.stderr:close()
342 else
343 redir:restore()
344 end
345 return unpack(result)
346end
347
348function samefile(left, right)
349 local ldat = nil
350 local rdat = nil
351 if left == "-" then
352 ldat = io.input:read("*a")
353 rdat = readfile(right)
354 elseif right == "-" then
355 rdat = io.input:read("*a")
356 ldat = readfile(left)
357 else
358 if fsize(left) ~= fsize(right) then
359 return false
360 else
361 ldat = readfile(left)
362 rdat = readfile(right)
363 end
364 end
365 return ldat == rdat
366end
367
368function samelines(f, t)
369 local fl = {}
370 for l in io.lines(f) do table.insert(fl, l) end
371 if not table.getn(fl) == table.getn(t) then
372 L(locheader(), string.format("file has %s lines; table has %s\n",
373 table.getn(fl), table.getn(t)))
374 return false
375 end
376 for i=1,table.getn(t) do
377 if fl[i] ~= t[i] then
378 L(locheader(), string.format("file[i] = '%s'; table[i] = '%s'\n",
379 fl[i], t[i]))
380 return false
381 end
382 end
383 return true
384end
385
386function greplines(f, t)
387 local fl = {}
388 for l in io.lines(f) do table.insert(fl, l) end
389 if not table.getn(fl) == table.getn(t) then
390 L(locheader(), string.format("file has %s lines; table has %s\n",
391 table.getn(fl), table.getn(t)))
392 return false
393 end
394 for i=1,table.getn(t) do
395 if not regex.search(t[i], fl[i]) then
396 L(locheader(), string.format("file[i] = '%s'; table[i] = '%s'\n",
397 fl[i], t[i]))
398 return false
399 end
400 end
401 return true
402end
403
404function grep(...)
405 local flags, what, where = unpack(arg)
406 local dogrep = function ()
407 if where == nil and string.sub(flags, 1, 1) ~= "-" then
408 where = what
409 what = flags
410 flags = ""
411 end
412 local quiet = string.find(flags, "q") ~= nil
413 local reverse = string.find(flags, "v") ~= nil
414 if not quiet and files.stdout == nil then err("non-quiet grep not redirected") end
415 local out = 1
416 local infile = files.stdin
417 if where ~= nil then infile = open_or_err(where) end
418 for line in infile:lines() do
419 local matched = regex.search(what, line)
420 if reverse then matched = not matched end
421 if matched then
422 if not quiet then files.stdout:write(line, "\n") end
423 out = 0
424 end
425 end
426 if where ~= nil then infile:close() end
427 return out
428 end
429 return {dogrep, logline = "grep "..cmd_as_str(arg)}
430end
431
432function cat(...)
433 local arguments = arg
434 local function docat()
435 local bsize = 8*1024
436 for _,x in ipairs(arguments) do
437 local infile
438 if x == "-" then
439 infile = files.stdin
440 else
441 infile = open_or_err(x, "rb", 3)
442 end
443 local block = infile:read(bsize)
444 while block do
445 files.stdout:write(block)
446 block = infile:read(bsize)
447 end
448 if x ~= "-" then
449 infile:close()
450 end
451 end
452 return 0
453 end
454 return {docat, logline = "cat "..cmd_as_str(arg)}
455end
456
457function tail(...)
458 local file, num = unpack(arg)
459 local function dotail()
460 if num == nil then num = 10 end
461 local mylines = {}
462 for l in io.lines(file) do
463 table.insert(mylines, l)
464 if table.getn(mylines) > num then
465 table.remove(mylines, 1)
466 end
467 end
468 for _,x in ipairs(mylines) do
469 files.stdout:write(x, "\n")
470 end
471 return 0
472 end
473 return {dotail, logline = "tail "..cmd_as_str(arg)}
474end
475
476function sort(file)
477 local function dosort(file)
478 local infile
479 if file == nil then
480 infile = files.stdin
481 else
482 infile = open_or_err(file)
483 end
484 local lines = {}
485 for l in infile:lines() do
486 table.insert(lines, l)
487 end
488 if file ~= nil then infile:close() end
489 table.sort(lines)
490 for _,l in ipairs(lines) do
491 files.stdout:write(l, "\n")
492 end
493 return 0
494 end
495 return {dosort, file, logline = "sort "..file}
496end
497
498function log_file_contents(filename)
499 L(readfile_q(filename), "\n")
500end
501
502function pre_cmd(stdin, ident)
503 if ident == nil then ident = "ts-" end
504 if stdin == true then
505 unlogged_copy("stdin", ident .. "stdin")
506 elseif type(stdin) == "table" then
507 unlogged_copy(stdin[1], ident .. "stdin")
508 else
509 local infile = open_or_err(ident .. "stdin", "w", 3)
510 if stdin ~= nil and stdin ~= false then
511 infile:write(stdin)
512 end
513 infile:close()
514 end
515 L("stdin:\n")
516 log_file_contents(ident .. "stdin")
517end
518
519function post_cmd(result, ret, stdout, stderr, ident)
520 if ret == nil then ret = 0 end
521 if ident == nil then ident = "ts-" end
522 L("stdout:\n")
523 log_file_contents(ident .. "stdout")
524 L("stderr:\n")
525 log_file_contents(ident .. "stderr")
526 if result ~= ret and ret ~= false then
527 err("Check failed (return value): wanted " .. ret .. " got " .. result, 3)
528 end
529
530 if stdout == nil then
531 if fsize(ident .. "stdout") ~= 0 then
532 err("Check failed (stdout): not empty", 3)
533 end
534 elseif type(stdout) == "string" then
535 local realout = open_or_err(ident .. "stdout", nil, 3)
536 local contents = realout:read("*a")
537 realout:close()
538 if contents ~= stdout then
539 err("Check failed (stdout): doesn't match", 3)
540 end
541 elseif type(stdout) == "table" then
542 if not samefile(ident .. "stdout", stdout[1]) then
543 err("Check failed (stdout): doesn't match", 3)
544 end
545 elseif stdout == true then
546 unlogged_remove("stdout")
547 unlogged_rename(ident .. "stdout", "stdout")
548 end
549
550 if stderr == nil then
551 if fsize(ident .. "stderr") ~= 0 then
552 err("Check failed (stderr): not empty", 3)
553 end
554 elseif type(stderr) == "string" then
555 local realerr = open_or_err(ident .. "stderr", nil, 3)
556 local contents = realerr:read("*a")
557 realerr:close()
558 if contents ~= stderr then
559 err("Check failed (stderr): doesn't match", 3)
560 end
561 elseif type(stderr) == "table" then
562 if not samefile(ident .. "stderr", stderr[1]) then
563 err("Check failed (stderr): doesn't match", 3)
564 end
565 elseif stderr == true then
566 unlogged_remove("stderr")
567 unlogged_rename(ident .. "stderr", "stderr")
568 end
569end
570
571-- std{out,err} can be:
572-- * false: ignore
573-- * true: ignore, copy to stdout
574-- * string: check that it matches the contents
575-- * nil: must be empty
576-- * {string}: check that it matches the named file
577-- stdin can be:
578-- * true: use existing "stdin" file
579-- * nil, false: empty input
580-- * string: contents of string
581-- * {string}: contents of the named file
582
583function bg(torun, ret, stdout, stderr, stdin)
584 test.bgid = test.bgid + 1
585 local out = {}
586 out.prefix = "ts-" .. test.bgid .. "-"
587 pre_cmd(stdin, out.prefix)
588 L("Starting background command...")
589 local ok,pid = runcmd(torun, out.prefix, true)
590 if not ok then err(pid, 2) end
591 if pid == -1 then err("Failed to start background process\n", 2) end
592 out.pid = pid
593 test.bglist[test.bgid] = out
594 out.id = test.bgid
595 out.retval = nil
596 out.locstr = locheader()
597 out.cmd = torun
598 out.expret = ret
599 out.expout = stdout
600 out.experr = stderr
601 local mt = {}
602 mt.__index = mt
603 mt.finish = function(obj, timeout)
604 if obj.retval ~= nil then return end
605
606 if timeout == nil then timeout = 0 end
607 if type(timeout) ~= "number" then
608 err("Bad timeout of type "..type(timeout))
609 end
610 local res
611 obj.retval, res = timed_wait(obj.pid, timeout)
612 if (res == -1) then
613 kill(obj.pid, 15) -- TERM
614 obj.retval, res = timed_wait(obj.pid, 2)
615 if (res == -1) then
616 kill(obj.pid, 9) -- KILL
617 obj.retval, res = timed_wait(obj.pid, 2)
618 end
619 end
620
621 test.bglist[obj.id] = nil
622 L(locheader(), "checking background command from ", out.locstr,
623 table.concat(out.cmd, " "), "\n")
624 post_cmd(obj.retval, out.expret, out.expout, out.experr, obj.prefix)
625 return true
626 end
627 mt.wait = function(obj, timeout)
628 if obj.retval ~= nil then return end
629 if timeout == nil then
630 obj.retval = wait(obj.pid)
631 else
632 local res
633 obj.retval, res = timed_wait(obj.pid, timeout)
634 if res == -1 then
635 obj.retval = nil
636 return false
637 end
638 end
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 return setmetatable(out, mt)
646end
647
648function runcheck(cmd, ret, stdout, stderr, stdin)
649 if ret == nil then ret = 0 end
650 pre_cmd(stdin)
651 local ok, result = runcmd(cmd)
652 if ok == false then
653 err(result, 2)
654 end
655 post_cmd(result, ret, stdout, stderr)
656 return result
657end
658
659function indir(dir, what)
660 if type(what) ~= "table" then
661 err("bad argument of type "..type(what).." to indir()")
662 end
663 local function do_indir()
664 local savedir = chdir(dir)
665 if savedir == nil then
666 err("Cannot chdir to "..dir)
667 end
668 local ok, res
669 if type(what[1]) == "function" then
670 ok, res = pcall(unpack(what))
671 elseif type(what[1]) == "string" then
672 ok, res = pcall(execute, unpack(what))
673 else
674 err("bad argument to indir(): cannot execute a "..type(what[1]))
675 end
676 chdir(savedir)
677 if not ok then err(res) end
678 return res
679 end
680 local want_local
681 if type(what[1]) == "function" then
682 if type(what.local_redirect) == "nil" then
683 want_local = true
684 else
685 want_local = what.local_redirect
686 end
687 else
688 want_local = false
689 end
690 local ll = "In directory "..dir..": "
691 if what.logline ~= nil then ll = ll .. tostring(what.logline)
692 else
693 ll = ll .. cmd_as_str(what)
694 end
695 return {do_indir, local_redirect = want_local, logline = ll}
696end
697
698function check(first, ...)
699 if type(first) == "table" then
700 return runcheck(first, unpack(arg))
701 elseif type(first) == "boolean" then
702 if not first then err("Check failed: false", 2) end
703 elseif type(first) == "number" then
704 if first ~= 0 then
705 err("Check failed: " .. first .. " ~= 0", 2)
706 end
707 else
708 err("Bad argument to check() (" .. type(first) .. ")", 2)
709 end
710 return first
711end
712
713function skip_if(chk)
714 if chk then
715 err(true, 2)
716 end
717end
718
719function xfail_if(chk, ...)
720 local ok,res = pcall(check, unpack(arg))
721 if ok == false then
722 if chk then err(false, 2) else err(err, 2) end
723 else
724 if chk then
725 test.wanted_fail = true
726 L("UNEXPECTED SUCCESS\n")
727 end
728 end
729end
730
731function xfail(...)
732 xfail_if(true, unpack(arg))
733end
734
735function log_error(e)
736 if type(e) == "table" then
737 L("\n", tostring(e.e), "\n")
738 for i,bt in ipairs(e.bt) do
739 if i ~= 1 then L("Rethrown from:") end
740 L(bt)
741 end
742 else
743 L("\n", tostring(e), "\n")
744 end
745end
746
747function run_tests(args)
748 local torun = {}
749 local run_all = true
750 local list_only = false
751 for i,a in pairs(args) do
752 local _1,_2,l,r = string.find(a, "^(-?%d+)%.%.(-?%d+)$")
753 if _1 then
754 l = l + 0
755 r = r + 0
756 if l < 1 then l = table.getn(tests) + l + 1 end
757 if r < 1 then r = table.getn(tests) + r + 1 end
758 if l > r then l,r = r,l end
759 for j = l,r do
760 torun[j]=j
761 end
762 run_all = false
763 elseif string.find(a, "^-?%d+$") then
764 r = a + 0
765 if r < 1 then r = table.getn(tests) + r + 1 end
766 torun[r] = r
767 run_all = false
768 elseif a == "-d" then
769 debugging = true
770 elseif a == "-l" then
771 list_only = true
772 else
773 -- pattern
774 local matched = false
775 for i,t in pairs(tests) do
776 if regex.search(a, t) then
777 torun[i] = i
778 matched = true
779 end
780 end
781 if matched then
782 run_all = false
783 else
784 print(string.format("Warning: pattern '%s' does not match any tests.", a))
785 end
786 end
787 end
788 if not list_only then
789 logfile:write("Running on ", get_ostype(), "\n\n")
790 P("Running tests...\n")
791 end
792 local counts = {}
793 counts.success = 0
794 counts.skip = 0
795 counts.xfail = 0
796 counts.noxfail = 0
797 counts.fail = 0
798 counts.total = 0
799 counts.of_interest = 0
800 local of_interest = {}
801
802 local function runtest(i, tname)
803 local env = {}
804 local genv = getfenv(0)
805 for x,y in pairs(genv) do
806 env[x] = y
807 -- we want changes to globals in a test to be visible to
808 -- already-defined functions
809 if type(y) == "function" then
810 pcall(setfenv, y, env)
811 end
812 end
813 env.tests = nil -- don't let them mess with this
814
815 test.bgid = 0
816 test.name = tname
817 test.wanted_fail = false
818 test.partial_skip = false
819 local shortname = nil
820 test.root, shortname = go_to_test_dir(tname)
821 test.errfile = ""
822 test.errline = -1
823 test.bglist = {}
824
825 local test_header = ""
826 if i < 100 then test_header = test_header .. " " end
827 if i < 10 then test_header = test_header .. " " end
828 test_header = test_header .. i .. " " .. shortname .. " "
829 local spacelen = 45 - string.len(shortname)
830 local spaces = string.rep(" ", 50)
831 if spacelen > 0 then
832 test_header = test_header .. string.sub(spaces, 1, spacelen)
833 end
834 P(test_header)
835
836 local tlog = test.root .. "/tester.log"
837 test.log = io.open(tlog, "w")
838 L("Test number ", i, ", ", shortname, "\n")
839
840 local driverfile = testdir .. "/" .. test.name .. "/__driver__.lua"
841 local driver, e = loadfile(driverfile)
842 local r
843 if driver == nil then
844 r = false
845 e = "Could not load driver file " .. driverfile .. " .\n" .. e
846 else
847 setfenv(driver, env)
848 local oldmask = posix_umask(0)
849 posix_umask(oldmask)
850 r,e = xpcall(driver, debug.traceback)
851 local errline = test.errline
852 for i,b in pairs(test.bglist) do
853 local a,x = pcall(function () b:finish(0) end)
854 if r and not a then
855 r = a
856 e = x
857 elseif not a then
858 L("Error cleaning up background processes: ", tostring(b.locstr), "\n")
859 end
860 end
861 if type(env.cleanup) == "function" then
862 local a,b = pcall(env.cleanup)
863 if r and not a then
864 r = a
865 e = b
866 end
867 end
868 test.errline = errline
869 restore_env()
870 posix_umask(oldmask)
871 end
872
873 -- set our functions back to the proper environment
874 local genv = getfenv(0)
875 for x,y in pairs(genv) do
876 if type(y) == "function" then
877 pcall(setfenv, y, genv)
878 end
879 end
880
881 if r then
882 if test.wanted_fail then
883 P("unexpected success\n")
884 test.log:close()
885 leave_test_dir()
886 counts.noxfail = counts.noxfail + 1
887 counts.of_interest = counts.of_interest + 1
888 table.insert(of_interest, test_header .. "unexpected success")
889 else
890 if test.partial_skip then
891 P("partial skip\n")
892 else
893 P("ok\n")
894 end
895 test.log:close()
896 if not debugging then clean_test_dir(tname) end
897 counts.success = counts.success + 1
898 end
899 else
900 if test.errline == nil then test.errline = -1 end
901 if type(e) ~= "table" then
902 local tbl = {e = e, bt = {"no backtrace; type(err) = "..type(e)}}
903 e = tbl
904 end
905 if e.e == true then
906 P(string.format("skipped (line %i)\n", test.errline))
907 test.log:close()
908 if not debugging then clean_test_dir(tname) end
909 counts.skip = counts.skip + 1
910 elseif e.e == false then
911 P(string.format("expected failure (line %i)\n", test.errline))
912 test.log:close()
913 leave_test_dir()
914 counts.xfail = counts.xfail + 1
915 else
916 result = string.format("FAIL (line %i)", test.errline)
917 P(result, "\n")
918 log_error(e)
919 table.insert(failed_testlogs, tlog)
920 test.log:close()
921 leave_test_dir()
922 counts.fail = counts.fail + 1
923 counts.of_interest = counts.of_interest + 1
924 table.insert(of_interest, test_header .. result)
925 end
926 end
927 counts.total = counts.total + 1
928 end
929
930 save_env()
931 if run_all then
932 for i,t in pairs(tests) do
933 if list_only then
934 if i < 10 then P(" ") end
935 if i < 100 then P(" ") end
936 P(i .. " " .. t .. "\n")
937 else
938 runtest(i, t)
939 end
940 end
941 else
942 for i,t in pairs(tests) do
943 if torun[i] == i then
944 if list_only then
945 if i < 10 then P(" ") end
946 if i < 100 then P(" ") end
947 P(i .. " " .. t .. "\n")
948 else
949 runtest(i, t)
950 end
951 end
952 end
953 end
954
955 if list_only then
956 logfile:close()
957 return 0
958 end
959
960 if counts.of_interest ~= 0 and (counts.total / counts.of_interest) > 4 then
961 P("\nInteresting tests:\n")
962 for i,x in ipairs(of_interest) do
963 P(x, "\n")
964 end
965 end
966 P("\n")
967 P(string.format("Of %i tests run:\n", counts.total))
968 P(string.format("\t%i succeeded\n", counts.success))
969 P(string.format("\t%i failed\n", counts.fail))
970 P(string.format("\t%i had expected failures\n", counts.xfail))
971 P(string.format("\t%i succeeded unexpectedly\n", counts.noxfail))
972 P(string.format("\t%i were skipped\n", counts.skip))
973
974 for i,log in pairs(failed_testlogs) do
975 local tlog = io.open(log, "r")
976 if tlog ~= nil then
977 local dat = tlog:read("*a")
978 tlog:close()
979 logfile:write("\n", string.rep("*", 50), "\n")
980 logfile:write(dat)
981 end
982 end
983 logfile:close()
984
985 if counts.success + counts.skip + counts.xfail == counts.total then
986 return 0
987 else
988 return 1
989 end
990end

Archive Download this file

Branches

Tags

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