-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import wvtestrun and wvtest.py from the wvtest.git project.
Corresponding wvtest commit is db65ff5907571a5004bb3c500efd19421cb06d1a.
- Loading branch information
Showing
2 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
#!/usr/bin/env python | ||
import traceback | ||
import os | ||
import re | ||
import sys | ||
|
||
if __name__ != "__main__": # we're imported as a module | ||
_registered = [] | ||
_tests = 0 | ||
_fails = 0 | ||
|
||
def wvtest(func): | ||
""" Use this decorator (@wvtest) in front of any function you want to run | ||
as part of the unit test suite. Then run: | ||
python wvtest.py path/to/yourtest.py | ||
to run all the @wvtest functions in that file. | ||
""" | ||
_registered.append(func) | ||
return func | ||
|
||
|
||
def _result(msg, tb, code): | ||
global _tests, _fails | ||
_tests += 1 | ||
if code != 'ok': | ||
_fails += 1 | ||
(filename, line, func, text) = tb | ||
filename = os.path.basename(filename) | ||
msg = re.sub(r'\s+', ' ', str(msg)) | ||
sys.stderr.flush() | ||
print '! %-70s %s' % ('%s:%-4d %s' % (filename, line, msg), | ||
code) | ||
sys.stdout.flush() | ||
|
||
|
||
def _check(cond, msg = 'unknown', tb = None): | ||
if tb == None: tb = traceback.extract_stack()[-3] | ||
if cond: | ||
_result(msg, tb, 'ok') | ||
else: | ||
_result(msg, tb, 'FAILED') | ||
return cond | ||
|
||
|
||
def _code(): | ||
(filename, line, func, text) = traceback.extract_stack()[-3] | ||
text = re.sub(r'^\w+\((.*)\)$', r'\1', text); | ||
return text | ||
|
||
|
||
def WVPASS(cond = True): | ||
''' Throws an exception unless cond is true. ''' | ||
return _check(cond, _code()) | ||
|
||
def WVFAIL(cond = True): | ||
''' Throws an exception unless cond is false. ''' | ||
return _check(not cond, 'NOT(%s)' % _code()) | ||
|
||
def WVPASSEQ(a, b): | ||
''' Throws an exception unless a == b. ''' | ||
return _check(a == b, '%s == %s' % (repr(a), repr(b))) | ||
|
||
def WVPASSNE(a, b): | ||
''' Throws an exception unless a != b. ''' | ||
return _check(a != b, '%s != %s' % (repr(a), repr(b))) | ||
|
||
def WVPASSLT(a, b): | ||
''' Throws an exception unless a < b. ''' | ||
return _check(a < b, '%s < %s' % (repr(a), repr(b))) | ||
|
||
def WVPASSLE(a, b): | ||
''' Throws an exception unless a <= b. ''' | ||
return _check(a <= b, '%s <= %s' % (repr(a), repr(b))) | ||
|
||
def WVPASSGT(a, b): | ||
''' Throws an exception unless a > b. ''' | ||
return _check(a > b, '%s > %s' % (repr(a), repr(b))) | ||
|
||
def WVPASSGE(a, b): | ||
''' Throws an exception unless a >= b. ''' | ||
return _check(a >= b, '%s >= %s' % (repr(a), repr(b))) | ||
|
||
else: # we're the main program | ||
# NOTE | ||
# Why do we do this in such convoluted way? Because if you run | ||
# wvtest.py as a main program and it imports your test files, then | ||
# those test files will try to import the wvtest module recursively. | ||
# That actually *works* fine, because we don't run this main program | ||
# when we're imported as a module. But you end up with two separate | ||
# wvtest modules, the one that gets imported, and the one that's the | ||
# main program. Each of them would have duplicated global variables | ||
# (most importantly, wvtest._registered), and so screwy things could | ||
# happen. Thus, we make the main program module *totally* different | ||
# from the imported module. Then we import wvtest (the module) into | ||
# wvtest (the main program) here and make sure to refer to the right | ||
# versions of global variables. | ||
# | ||
# All this is done just so that wvtest.py can be a single file that's | ||
# easy to import into your own applications. | ||
import wvtest | ||
|
||
def _runtest(modname, fname, f): | ||
print 'Testing "%s" in %s.py:' % (fname, modname) | ||
sys.stdout.flush() | ||
try: | ||
f() | ||
except Exception, e: | ||
print traceback.format_exc() | ||
tb = sys.exc_info()[2] | ||
wvtest._result(e, traceback.extract_tb(tb)[-1], | ||
'EXCEPTION') | ||
|
||
# main code | ||
for modname in sys.argv[1:]: | ||
if not os.path.exists(modname): | ||
print 'Skipping: %s' % modname | ||
continue | ||
if modname.endswith('.py'): | ||
modname = modname[:-3] | ||
print 'Importing: %s' % modname | ||
wvtest._registered = [] | ||
mod = __import__(modname.replace('/', '.'), None, None, []) | ||
|
||
for t in wvtest._registered: | ||
_runtest(modname, t.func_name, t) | ||
|
||
print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
#!/usr/bin/perl -w | ||
# | ||
# WvTest: | ||
# Copyright (C)2007-2009 Versabanq Innovations Inc. and contributors. | ||
# Licensed under the GNU Library General Public License, version 2. | ||
# See the included file named LICENSE for license information. | ||
# | ||
use strict; | ||
use Time::HiRes qw(time); | ||
|
||
# always flush | ||
$| = 1; | ||
|
||
if (@ARGV < 1) { | ||
print STDERR "Usage: $0 <command line...>\n"; | ||
exit 127; | ||
} | ||
|
||
print STDERR "Testing \"all\" in @ARGV:\n"; | ||
|
||
my $pid = open(my $fh, "-|"); | ||
if (!$pid) { | ||
# child | ||
setpgrp(); | ||
open STDERR, '>&STDOUT' or die("Can't dup stdout: $!\n"); | ||
exec(@ARGV); | ||
exit 126; # just in case | ||
} | ||
|
||
my $istty = -t STDOUT; | ||
my @log = (); | ||
my ($gpasses, $gfails) = (0,0); | ||
|
||
sub bigkill($) | ||
{ | ||
my $pid = shift; | ||
|
||
if (@log) { | ||
print "\n" . join("\n", @log) . "\n"; | ||
} | ||
|
||
print STDERR "\n! Killed by signal FAILED\n"; | ||
|
||
($pid > 0) || die("pid is '$pid'?!\n"); | ||
|
||
local $SIG{CHLD} = sub { }; # this will wake us from sleep() faster | ||
kill 15, $pid; | ||
sleep(2); | ||
|
||
if ($pid > 1) { | ||
kill 9, -$pid; | ||
} | ||
kill 9, $pid; | ||
|
||
exit(125); | ||
} | ||
|
||
# parent | ||
local $SIG{INT} = sub { bigkill($pid); }; | ||
local $SIG{TERM} = sub { bigkill($pid); }; | ||
local $SIG{ALRM} = sub { | ||
print STDERR "Alarm timed out! No test results for too long.\n"; | ||
bigkill($pid); | ||
}; | ||
|
||
sub colourize($) | ||
{ | ||
my $result = shift; | ||
my $pass = ($result eq "ok"); | ||
|
||
if ($istty) { | ||
my $colour = $pass ? "\e[32;1m" : "\e[31;1m"; | ||
return "$colour$result\e[0m"; | ||
} else { | ||
return $result; | ||
} | ||
} | ||
|
||
sub mstime($$$) | ||
{ | ||
my ($floatsec, $warntime, $badtime) = @_; | ||
my $ms = int($floatsec * 1000); | ||
my $str = sprintf("%d.%03ds", $ms/1000, $ms % 1000); | ||
|
||
if ($istty && $ms > $badtime) { | ||
return "\e[31;1m$str\e[0m"; | ||
} elsif ($istty && $ms > $warntime) { | ||
return "\e[33;1m$str\e[0m"; | ||
} else { | ||
return "$str"; | ||
} | ||
} | ||
|
||
sub resultline($$) | ||
{ | ||
my ($name, $result) = @_; | ||
return sprintf("! %-65s %s", $name, colourize($result)); | ||
} | ||
|
||
my $allstart = time(); | ||
my ($start, $stop); | ||
|
||
sub endsect() | ||
{ | ||
$stop = time(); | ||
if ($start) { | ||
printf " %s %s\n", mstime($stop - $start, 500, 1000), colourize("ok"); | ||
} | ||
} | ||
|
||
while (<$fh>) | ||
{ | ||
chomp; | ||
s/\r//g; | ||
|
||
if (/^\s*Testing "(.*)" in (.*):\s*$/) | ||
{ | ||
alarm(120); | ||
my ($sect, $file) = ($1, $2); | ||
|
||
endsect(); | ||
|
||
printf("! %s %s: ", $file, $sect); | ||
@log = (); | ||
$start = $stop; | ||
} | ||
elsif (/^!\s*(.*?)\s+(\S+)\s*$/) | ||
{ | ||
alarm(120); | ||
|
||
my ($name, $result) = ($1, $2); | ||
my $pass = ($result eq "ok"); | ||
|
||
if (!$start) { | ||
printf("\n! Startup: "); | ||
$start = time(); | ||
} | ||
|
||
push @log, resultline($name, $result); | ||
|
||
if (!$pass) { | ||
$gfails++; | ||
if (@log) { | ||
print "\n" . join("\n", @log) . "\n"; | ||
@log = (); | ||
} | ||
} else { | ||
$gpasses++; | ||
print "."; | ||
} | ||
} | ||
else | ||
{ | ||
push @log, $_; | ||
} | ||
} | ||
|
||
endsect(); | ||
|
||
my $newpid = waitpid($pid, 0); | ||
if ($newpid != $pid) { | ||
die("waitpid returned '$newpid', expected '$pid'\n"); | ||
} | ||
|
||
my $code = $?; | ||
my $ret = ($code >> 8); | ||
|
||
# return death-from-signal exits as >128. This is what bash does if you ran | ||
# the program directly. | ||
if ($code && !$ret) { $ret = $code | 128; } | ||
|
||
if ($ret && @log) { | ||
print "\n" . join("\n", @log) . "\n"; | ||
} | ||
|
||
if ($code != 0) { | ||
print resultline("Program returned non-zero exit code ($ret)", "FAILED"); | ||
} | ||
|
||
my $gtotal = $gpasses+$gfails; | ||
printf("\nWvTest: %d test%s, %d failure%s, total time %s.\n", | ||
$gtotal, $gtotal==1 ? "" : "s", | ||
$gfails, $gfails==1 ? "" : "s", | ||
mstime(time() - $allstart, 2000, 5000)); | ||
print STDERR "\nWvTest result code: $ret\n"; | ||
exit( $ret ? $ret : ($gfails ? 125 : 0) ); |