2 # A simple unit testing script
3 # (c) 2004--2013 Martin Mares <mj@ucw.cz>
4 # (c) 2007 Pavel Charvat <pchar@ucw.cz>
6 # Tests in the test file have a syntax similar to mail headers,
7 # individual test case are separated by blank lines and they can contain
8 # the following fields:
10 # Name: name of the case (default: sequence number since start of file)
11 # Run: command to run (default: command from the previous test case)
12 # This can be an arbitrary shell pipeline, sequences $0 to $9 are
13 # replaced by file names of In<N> or Out<N> files (see below).
14 # In: lines to pass to the program as standard input
15 # Out: lines to expect at the program's standard output
16 # Err: lines to expect at the program's standard error output
17 # In<N>: lines to pass to the program as input file <N>
18 # Out<N>: lines to expect from the program in output file <N>
19 # Both In<N> and Out<N> can be specified simultaneously if we
20 # are testing a program which modifies some of its input files.
21 # Exit: expected exit code of the program (default: 0)
23 # A value of a field can be optionally given as a shell-style here-document:
35 GetOptions("verbose!" => \$verbose,
36 "rundir=s" => \$rundir)
37 or die "Usage: tester [--verbose] [--rundir=<dir>] <tests>\n";
48 } elsif (defined($append_to) && /^\s+(.*)$/) {
49 $$append_to .= "\n$1";
50 } elsif (my ($n,$v) = /^(\w+):\s+(.*)$/) {
55 ($tt->{$n}) && die "$n already defined";
57 $append_to = \($tt->{$n});
58 } elsif (my ($n,$sep) = /^(\w+)\s*<<(\w+)\s*$/) {
63 ($tt->{$n}) && die "$n already defined";
68 defined $line or die "Here-document not terminated";
69 last if $line eq $sep;
74 die "Test script syntax error";
78 if (! -d "$rundir/tmp") {
79 mkdir "$rundir/tmp" or die "Unable to create $rundir/tmp: $!";
85 TEST: foreach $tt (@tests) {
87 my $name = $tt->{'Name'};
88 printf "Test %03d", $i;
89 print " [$name]" if defined $name;
91 $run = ($tt->{'Run'} || $prev_run) or die "Don't know what to run";
98 if (defined $tt->{'In'}) {
99 my $ifi = "tmp/test$i.in";
100 open X, ">$rundir/$ifi" or die "Unable to create $ifi";
101 print X $tt->{'In'}, "\n";
105 $redirs .= " </dev/null";
107 if (defined $tt->{'Out'}) {
108 my $ofi = "tmp/test$i.out";
109 unlink "$rundir/$ofi";
111 push @out_files, $ofi;
112 push @out_checks, $tt->{'Out'};
114 $redirs .= " >/dev/null";
116 if (defined $tt->{'Err'}) {
117 my $efi = "tmp/test$i.err";
118 unlink "$rundir/$efi";
119 $redirs .= " 2>$efi";
120 push @out_files, $efi;
121 push @out_checks, $tt->{'Err'};
123 foreach my $arg (0..9) {
124 my $f = "tmp/test$i.$arg";
125 if (defined $tt->{"Out$arg"}) {
128 push @out_checks, $tt->{"Out$arg"};
130 if (defined $tt->{"In$arg"}) {
131 open X, ">$rundir/$f" or die "Unable to create $f";
132 print X $tt->{"In$arg"}, "\n";
136 $run =~ s/\$(\d)/tmp\/test$i.$1/g;
137 print "(running $run) " if $verbose;
138 system "cd $rundir && ( $run ) $redirs";
140 print "FAILED with status code $?\n";
145 my $expect_ec = $tt->{'Exit'} || 0;
146 if ($ec != $expect_ec) {
147 print "FAILED: unexpected exit code $ec\n";
152 for (my $i=0; $i<=$#out_files; $i++) {
153 my $ofi = $out_files[$i];
154 open X, "<$rundir/$ofi" or die "Unable to read $ofi";
161 $out =~ /\n$/s or $out .= "\n";
162 if ($out ne $out_checks[$i] . "\n") {
163 print "FAILED (see $ofi)\n";
169 system "rm -f $rundir/tmp/test$i.*";