#!/usr/bin/perl
# A simple unit testing script
-# (c) 2004 Martin Mares <mj@ucw.cz>
+# (c) 2004--2007 Martin Mares <mj@ucw.cz>
# (c) 2007 Pavel Charvat <pchar@ucw.cz>
+# Tests in the test file have a syntax similar to mail headers,
+# individual test case are separated by blank lines and they can contain
+# the following fields:
+#
+# Name: name of the case (default: sequence number since start of file)
+# Run: command to run (default: command from the previous test case)
+# This can be an arbitrary shell pipeline, sequences $0 to $9 are
+# replaced by file names of In<N> or Out<N> files (see below).
+# In: lines to pass to the program as standard input
+# Out: lines to expect at the program's standard output
+# In<N>: lines to pass to the program as input file <N>
+# Out<N>: lines to expect from the program in output file <N>
+# Both In<N> and Out<N> can be specified simultaneously if we
+# are testing a program which modifies some of its input files.
+# Exit: expected exit code of the program (default: 0)
+
+use Getopt::Long;
+
+my $verbose = 0;
+my $rundir = ".";
+GetOptions("verbose!" => \$verbose,
+ "rundir=s" => \$rundir)
+ or die "Usage: tester [--verbose] [--rundir=<dir>] <tests>\n";
+
my @tests = ();
my $tt;
my $append_to;
my $i = 0;
my $errors = 0;
my $prev_run = undef;
-foreach $tt (@tests) {
+TEST: foreach $tt (@tests) {
$i++;
- print "Test $i: ";
+ my $name = $tt->{'Name'} || $i;
+ print "Test $name: ";
$run = ($tt->{'Run'} || $prev_run) or die "Don't know what to run";
$prev_run = $run;
- my ($ifi, $ofi);
+
+ my @out_files = ();
+ my @out_checks = ();
+ my @temps = ();
+ my $redirs = "";
+
if (defined $tt->{'In'}) {
- $ifi = "tmp/test$i.in";
- open X, ">run/$ifi" or die "Unable to create $ifi";
+ my $ifi = "tmp/test$i.in";
+ open X, ">$rundir/$ifi" or die "Unable to create $ifi";
print X $tt->{'In'}, "\n";
close X;
- $run .= " <$ifi";
+ $redirs .= " <$ifi";
+ push @temps, $ifi;
} else {
- $run .= " </dev/null";
+ $redirs .= " </dev/null";
}
if (defined $tt->{'Out'}) {
- $ofi = "tmp/test$i.out";
- unlink "run/$ofi";
- $run .= " >$ofi";
+ my $ofi = "tmp/test$i.out";
+ unlink "$rundir/$ofi";
+ $redirs .= " >$ofi";
+ push @temps, $ofi;
+ push @out_files, $ofi;
+ push @out_checks, $tt->{'Out'};
} else {
- $run .= " >/dev/null";
+ $redirs .= " >/dev/null";
+ }
+ foreach my $arg (0..9) {
+ my $f = "tmp/test$i.$arg";
+ if (defined $tt->{"Out$arg"}) {
+ unlink "$rundir/$f";
+ push @temps, $f;
+ push @out_files, $f;
+ push @out_checks, $tt->{"Out$arg"};
+ }
+ if (defined $tt->{"In$arg"}) {
+ open X, ">$rundir/$f" or die "Unable to create $f";
+ print X $tt->{"In$arg"}, "\n";
+ close X;
+ push @temps, $f;
+ }
}
- system "cd run && ( $run )";
- if ($?) {
- print "FAILED with exit code $?\n";
+ $run =~ s/\$(\d)/tmp\/test$i.$1/g;
+ print "(running $run) " if $verbose;
+ system "cd $rundir && ( $run ) $redirs";
+ if ($? % 256) {
+ print "FAILED with status code $?\n";
$errors++;
next;
}
- if (defined $tt->{'Out'}) {
- open X, "<run/$ofi" or die "Unable to read $ofi";
+ my $ec = $? / 256;
+ my $expect_ec = $tt->{'Exit'} || 0;
+ if ($ec != $expect_ec) {
+ print "FAILED: unexpected exit code $ec\n";
+ $errors++;
+ next;
+ }
+
+ for (my $i=0; $i<=$#out_files; $i++) {
+ my $ofi = $out_files[$i];
+ open X, "<$rundir/$ofi" or die "Unable to read $ofi";
my $out;
{
local $/ = undef;
$out = <X>;
}
close X;
- if ($out ne $tt->{'Out'} . "\n") {
+ if ($out ne $out_checks[$i] . "\n") {
print "FAILED (see $ofi)\n";
$errors++;
- next;
+ next TEST;
}
}
- unlink $ifi if $ifi;
- unlink $ofi if $ofi;
+
+ foreach my $f (@temps) {
+ unlink "$rundir/$f";
+ }
print "OK\n";
}