diff --git a/clean.do b/clean.do index 01bf423..97f6955 100644 --- a/clean.do +++ b/clean.do @@ -1 +1 @@ -rm -f hello *.o *~ .*~ *.pyc CC LD +rm -f t/hello t/*.o *~ .*~ t/*~ t/.*~ *.pyc t/CC t/LD diff --git a/it.do b/it.do index 22e2266..5ebcc71 100644 --- a/it.do +++ b/it.do @@ -1 +1,2 @@ -redo --ifchange hello +echo "Nothing to do. Try 'redo test'" >&2 + diff --git a/redo.py b/redo.py index 6c314c6..abf9a30 100755 --- a/redo.py +++ b/redo.py @@ -132,11 +132,19 @@ def build(t): raise Exception('non-zero return code building %r' % t) +assert(not (opt.ifchange and opt.ifcreate)) + mkdirp('.redo') REDO_TARGET = os.getenv('REDO_TARGET', '') REDO_DEPTH = os.getenv('REDO_DEPTH', '') -assert(not (opt.ifchange and opt.ifcreate)) +if not REDO_DEPTH: + exenames = [os.path.abspath(sys.argv[0]), os.path.realpath(sys.argv[0])] + if exenames[0] == exenames[1]: + exenames = [exenames[0]] + dirnames = [os.path.dirname(p) for p in exenames] + os.putenv('PATH', ':'.join(dirnames) + ':' + os.getenv('PATH')) + if opt.debug: REDO_DEBUG = 1 os.putenv('REDO_DEBUG', '1') diff --git a/runtests.do b/runtests.do new file mode 100644 index 0000000..494f26b --- /dev/null +++ b/runtests.do @@ -0,0 +1,5 @@ +. wvtest.sh + +WVSTART "t/runtests" +cd t +WVPASS redo runtests diff --git a/CC.do b/t/CC.do similarity index 100% rename from CC.do rename to t/CC.do diff --git a/LD.do b/t/LD.do similarity index 100% rename from LD.do rename to t/LD.do diff --git a/hello.c b/t/hello.c similarity index 100% rename from hello.c rename to t/hello.c diff --git a/hello.do b/t/hello.do similarity index 100% rename from hello.do rename to t/hello.do diff --git a/hello.o.do b/t/hello.o.do similarity index 100% rename from hello.o.do rename to t/hello.o.do diff --git a/t/it.do b/t/it.do new file mode 100644 index 0000000..22e2266 --- /dev/null +++ b/t/it.do @@ -0,0 +1 @@ +redo --ifchange hello diff --git a/t/runtests.do b/t/runtests.do new file mode 100644 index 0000000..3ef758a --- /dev/null +++ b/t/runtests.do @@ -0,0 +1,2 @@ +redo --ifchange it +./hello >&2 diff --git a/test.do b/test.do index 53195c6..33376b4 100644 --- a/test.do +++ b/test.do @@ -1,3 +1,3 @@ -redo --ifchange it -./hello >&2 +wvtestrun redo runtests >&2 + diff --git a/wvtest.sh b/wvtest.sh new file mode 100644 index 0000000..0cfc6b2 --- /dev/null +++ b/wvtest.sh @@ -0,0 +1,134 @@ +# +# 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. +_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=$(_wvtextclean "$2") + OK=ok + if [ "$CODE" -ne 0 ]; then + OK=FAILED + fi + echo "! $WVCALLER_FILE:$WVCALLER_LINE $TEXT $OK" >&2 + if [ "$CODE" -ne 0 ]; then + exit $CODE + else + return 0 + fi +} + + +WVPASS() +{ + TEXT="$*" + + _wvfind_caller + if "$@"; then + _wvcheck 0 "$TEXT" + return 0 + else + _wvcheck 1 "$TEXT" + # NOTREACHED + return 1 + fi +} + + +WVFAIL() +{ + TEXT="$*" + + _wvfind_caller + if "$@"; then + _wvcheck 1 "NOT($TEXT)" + # NOTREACHED + return 1 + else + _wvcheck 0 "NOT($TEXT)" + return 0 + fi +} + + +_wvgetrv() +{ + ( "$@" >&2 ) + echo -n $? +} + + +WVPASSEQ() +{ + _wvfind_caller + _wvcheck $(_wvgetrv [ "$#" -eq 2 ]) "exactly 2 arguments" + echo "Comparing:" >&2 + echo "$1" >&2 + echo "--" >&2 + echo "$2" >&2 + _wvcheck $(_wvgetrv [ "$1" = "$2" ]) "'$1' = '$2'" +} + + +WVPASSNE() +{ + _wvfind_caller + _wvcheck $(_wvgetrv [ "$#" -eq 2 ]) "exactly 2 arguments" + echo "Comparing:" >&2 + echo "$1" >&2 + echo "--" >&2 + echo "$2" >&2 + _wvcheck $(_wvgetrv [ "$1" != "$2" ]) "'$1' != '$2'" +} + + +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 + _wvfind_caller + echo "Testing \"$*\" in $WVCALLER_FILE:" >&2 +} diff --git a/wvtestrun b/wvtestrun new file mode 100755 index 0000000..248a1c5 --- /dev/null +++ b/wvtestrun @@ -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 \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) );