From dd87ea1e9fd87dc67d5d01a9c277590742857b37 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Wed, 14 Jul 2010 02:28:22 -0400 Subject: [PATCH] Update to latest wvtest.py, wvtest.sh, and wvtestrun from wvtest project. Imported from wvtest commit a975b39ddcca5c894e2e2b656b8e28c11af36f47. Because of changes to wvtest.py's chdir() handling, had to make some slight changes to filenames used by the bup tests themselves - all changes for the better. Signed-off-by: Avery Pennarun --- Makefile | 2 +- t/tclient.py | 7 ++-- t/tindex.py | 14 ++++---- wvtest.py | 93 +++++++++++++++++++++++++++++++++------------------- wvtest.sh | 61 +++++++++++++++++++++++++++++----- wvtestrun | 28 ++++++++-------- 6 files changed, 138 insertions(+), 67 deletions(-) diff --git a/Makefile b/Makefile index 629eaa5..2628730 100644 --- a/Makefile +++ b/Makefile @@ -110,4 +110,4 @@ clean: Documentation/clean *.pyc */*.pyc */*/*.pyc \ bup bup-* cmd/bup-* lib/bup/_version.py randomgen memtest \ out[12] out2[tc] tags[12] tags2[tc] - rm -rf *.tmp build lib/bup/build + rm -rf *.tmp t/*.tmp build lib/bup/build diff --git a/t/tclient.py b/t/tclient.py index 895e052..d6f236a 100644 --- a/t/tclient.py +++ b/t/tclient.py @@ -1,4 +1,5 @@ -import os, time, random, subprocess +import sys, os, time, random, subprocess +sys.path.append('../lib') from bup import client, git, hashsplit from wvtest import * @@ -13,7 +14,7 @@ s2 = randbytes(10000) @wvtest def test_server_split_with_indexes(): - os.environ['BUP_MAIN_EXE'] = './bup' + os.environ['BUP_MAIN_EXE'] = '../bup' os.environ['BUP_DIR'] = bupdir = 'buptest_tclient.tmp' subprocess.call(['rm', '-rf', bupdir]) git.init_repo(bupdir) @@ -31,7 +32,7 @@ def test_server_split_with_indexes(): @wvtest def test_midx_refreshing(): - os.environ['BUP_MAIN_EXE'] = bupmain = './bup' + os.environ['BUP_MAIN_EXE'] = bupmain = '../bup' os.environ['BUP_DIR'] = bupdir = 'buptest_tmidx.tmp' subprocess.call(['rm', '-rf', bupdir]) git.init_repo(bupdir) diff --git a/t/tindex.py b/t/tindex.py index 330b444..d237757 100644 --- a/t/tindex.py +++ b/t/tindex.py @@ -7,18 +7,18 @@ from wvtest import * def index_basic(): cd = os.path.realpath('') WVPASS(cd) - sd = os.path.realpath('t/sampledata') - WVPASSEQ(index.realpath('t/sampledata'), cd + '/t/sampledata') - WVPASSEQ(os.path.realpath('t/sampledata/x'), sd + '/x') - WVPASSEQ(os.path.realpath('t/sampledata/etc'), os.path.realpath('/etc')) - WVPASSEQ(index.realpath('t/sampledata/etc'), sd + '/etc') + sd = os.path.realpath('sampledata') + WVPASSEQ(index.realpath('sampledata'), cd + '/sampledata') + WVPASSEQ(os.path.realpath('sampledata/x'), sd + '/x') + WVPASSEQ(os.path.realpath('sampledata/etc'), os.path.realpath('/etc')) + WVPASSEQ(index.realpath('sampledata/etc'), sd + '/etc') @wvtest def index_writer(): unlink('index.tmp') ds = os.stat('.') - fs = os.stat('t/tindex.py') + fs = os.stat('tindex.py') w = index.Writer('index.tmp') w.add('/var/tmp/sporky', fs) w.add('/etc/passwd', fs) @@ -50,7 +50,7 @@ def index_dirty(): unlink('index.tmp') unlink('index2.tmp') ds = os.stat('.') - fs = os.stat('t/tindex.py') + fs = os.stat('tindex.py') w1 = index.Writer('index.tmp') w1.add('/a/b/x', fs) diff --git a/wvtest.py b/wvtest.py index 7edd91a..fd6994e 100755 --- a/wvtest.py +++ b/wvtest.py @@ -8,7 +8,7 @@ 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: @@ -17,8 +17,8 @@ if __name__ != "__main__": # we're imported as a module """ _registered.append(func) return func - - + + def _result(msg, tb, code): global _tests, _fails _tests += 1 @@ -31,8 +31,8 @@ if __name__ != "__main__": # we're imported as a module 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: @@ -40,49 +40,65 @@ if __name__ != "__main__": # we're imported as a module else: _result(msg, tb, 'FAILED') return cond - - + + def _code(): (filename, line, func, text) = traceback.extract_stack()[-3] - text = re.sub(r'^\w+\((.*)\)$', r'\1', unicode(text)); + text = re.sub(r'^\w+\((.*)\)(\s*#.*)?$', r'\1', text); return text - - + + def WVPASS(cond = True): - ''' Throws an exception unless cond is true. ''' + ''' Counts a test failure unless cond is true. ''' return _check(cond, _code()) - + def WVFAIL(cond = True): - ''' Throws an exception unless cond is false. ''' + ''' Counts a test failure unless cond is false. ''' return _check(not cond, 'NOT(%s)' % _code()) - + def WVPASSEQ(a, b): - ''' Throws an exception unless a == b. ''' + ''' Counts a test failure unless a == b. ''' return _check(a == b, '%s == %s' % (repr(a), repr(b))) - + def WVPASSNE(a, b): - ''' Throws an exception unless a != b. ''' + ''' Counts a test failure unless a != b. ''' return _check(a != b, '%s != %s' % (repr(a), repr(b))) - + def WVPASSLT(a, b): - ''' Throws an exception unless a < b. ''' + ''' Counts a test failure unless a < b. ''' return _check(a < b, '%s < %s' % (repr(a), repr(b))) - + def WVPASSLE(a, b): - ''' Throws an exception unless a <= b. ''' + ''' Counts a test failure unless a <= b. ''' return _check(a <= b, '%s <= %s' % (repr(a), repr(b))) - + def WVPASSGT(a, b): - ''' Throws an exception unless a > b. ''' + ''' Counts a test failure unless a > b. ''' return _check(a > b, '%s > %s' % (repr(a), repr(b))) - + def WVPASSGE(a, b): - ''' Throws an exception unless a >= b. ''' + ''' Counts a test failure unless a >= b. ''' return _check(a >= b, '%s >= %s' % (repr(a), repr(b))) + def WVEXCEPT(etype, func, *args, **kwargs): + ''' Counts a test failure unless func throws an 'etype' exception. + You have to spell out the function name and arguments, rather than + calling the function yourself, so that WVEXCEPT can run before + your test code throws an exception. + ''' + try: + func(*args, **kwargs) + except etype, e: + return _check(True, 'EXCEPT(%s)' % _code()) + except: + _check(False, 'EXCEPT(%s)' % _code()) + raise + else: + return _check(False, 'EXCEPT(%s)' % _code()) + else: # we're the main program # NOTE - # Why do we do this in such convoluted way? Because if you run + # Why do we do this in such a 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 @@ -98,7 +114,7 @@ else: # we're the main program # 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 print 'Testing "%s" in %s.py:' % (fname, modname) @@ -109,9 +125,9 @@ else: # we're the main program print print traceback.format_exc() tb = sys.exc_info()[2] - wvtest._result(e, traceback.extract_tb(tb)[-1], + wvtest._result(e, traceback.extract_tb(tb)[1], 'EXCEPTION') - + # main code for modname in sys.argv[1:]: if not os.path.exists(modname): @@ -121,11 +137,20 @@ else: # we're the main program 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 + oldwd = os.getcwd() + oldpath = sys.path + try: + modpath = os.path.abspath(modname).split('/')[:-1] + os.chdir('/'.join(modpath)) + sys.path += ['/'.join(modpath), + '/'.join(modpath[:-1])] + mod = __import__(modname.replace('/', '.'), None, None, []) + for t in wvtest._registered: + _runtest(modname, t.func_name, t) + print + finally: + os.chdir(oldwd) + sys.path = oldpath print print 'WvTest: %d tests, %d failures.' % (wvtest._tests, wvtest._fails) diff --git a/wvtest.sh b/wvtest.sh index 90bdc90..0cfc6b2 100644 --- a/wvtest.sh +++ b/wvtest.sh @@ -1,20 +1,44 @@ +# +# Include this file in your shell script by using: +# #!/bin/sh +# . ./wvtest.sh +# + # we don't quote $TEXT in case it contains newlines; newlines # aren't allowed in test output. However, we set -f so that # at least shell glob characters aren't processed. -_textclean() +_wvtextclean() { ( set -f; echo $* ) } + +if [ -n "$BASH_VERSION" ]; then + _wvfind_caller() + { + LVL=$1 + WVCALLER_FILE=${BASH_SOURCE[2]} + WVCALLER_LINE=${BASH_LINENO[1]} + } +else + _wvfind_caller() + { + LVL=$1 + WVCALLER_FILE="unknown" + WVCALLER_LINE=0 + } +fi + + _wvcheck() { CODE="$1" - TEXT=$(_textclean "$2") + TEXT=$(_wvtextclean "$2") OK=ok if [ "$CODE" -ne 0 ]; then OK=FAILED fi - echo "! ${BASH_SOURCE[2]}:${BASH_LINENO[1]} $TEXT $OK" >&2 + echo "! $WVCALLER_FILE:$WVCALLER_LINE $TEXT $OK" >&2 if [ "$CODE" -ne 0 ]; then exit $CODE else @@ -26,7 +50,8 @@ _wvcheck() WVPASS() { TEXT="$*" - + + _wvfind_caller if "$@"; then _wvcheck 0 "$TEXT" return 0 @@ -41,7 +66,8 @@ WVPASS() WVFAIL() { TEXT="$*" - + + _wvfind_caller if "$@"; then _wvcheck 1 "NOT($TEXT)" # NOTREACHED @@ -62,7 +88,8 @@ _wvgetrv() WVPASSEQ() { - WVPASS [ "$#" -eq 2 ] + _wvfind_caller + _wvcheck $(_wvgetrv [ "$#" -eq 2 ]) "exactly 2 arguments" echo "Comparing:" >&2 echo "$1" >&2 echo "--" >&2 @@ -73,7 +100,8 @@ WVPASSEQ() WVPASSNE() { - WVPASS [ "$#" -eq 2 ] + _wvfind_caller + _wvcheck $(_wvgetrv [ "$#" -eq 2 ]) "exactly 2 arguments" echo "Comparing:" >&2 echo "$1" >&2 echo "--" >&2 @@ -82,8 +110,25 @@ WVPASSNE() } +WVPASSRC() +{ + RC=$? + _wvfind_caller + _wvcheck $(_wvgetrv [ $RC -eq 0 ]) "return code($RC) == 0" +} + + +WVFAILRC() +{ + RC=$? + _wvfind_caller + _wvcheck $(_wvgetrv [ $RC -ne 0 ]) "return code($RC) != 0" +} + + WVSTART() { echo >&2 - echo "Testing \"$*\" in ${BASH_SOURCE[1]}:" >&2 + _wvfind_caller + echo "Testing \"$*\" in $WVCALLER_FILE:" >&2 } diff --git a/wvtestrun b/wvtestrun index 4be41e3..248a1c5 100755 --- a/wvtestrun +++ b/wvtestrun @@ -34,11 +34,11 @@ 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"); @@ -46,19 +46,19 @@ sub bigkill($) 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 { +local $SIG{ALRM} = sub { print STDERR "Alarm timed out! No test results for too long.\n"; bigkill($pid); }; @@ -67,7 +67,7 @@ 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"; @@ -81,7 +81,7 @@ 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) { @@ -112,14 +112,14 @@ 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; @@ -127,17 +127,17 @@ while (<$fh>) 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) { -- 2.39.2