]> mj.ucw.cz Git - libucw.git/blob - build/tester
Tester: Always print test numbers
[libucw.git] / build / tester
1 #!/usr/bin/perl
2 # A simple unit testing script
3 # (c) 2004--2013 Martin Mares <mj@ucw.cz>
4 # (c) 2007 Pavel Charvat <pchar@ucw.cz>
5
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:
9 #
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)
22 #
23 # A value of a field can be optionally given as a shell-style here-document:
24 #
25 #       In <<AMEN
26 #       multiple
27 #       lines
28 #       of input
29 #       AMEN
30
31 use Getopt::Long;
32
33 my $verbose = 0;
34 my $rundir = ".";
35 GetOptions("verbose!" => \$verbose,
36            "rundir=s" => \$rundir)
37         or die "Usage: tester [--verbose] [--rundir=<dir>] <tests>\n";
38
39 my @tests = ();
40 my $tt;
41 my $append_to;
42
43 while (<>) {
44         /^#/ && next;
45         if (/^\s*$/) {
46                 $tt = undef;
47                 $append_to = undef;
48         } elsif (defined($append_to) && /^\s+(.*)$/) {
49                 $$append_to .= "\n$1";
50         } elsif (my ($n,$v) = /^(\w+):\s+(.*)$/) {
51                 if (!$tt) {
52                         $tt = {};
53                         push @tests, $tt;
54                 }
55                 ($tt->{$n}) && die "$n already defined";
56                 $tt->{$n} = $v;
57                 $append_to = \($tt->{$n});
58         } elsif (my ($n,$sep) = /^(\w+)\s*<<(\w+)\s*$/) {
59                 if (!$tt) {
60                         $tt = {};
61                         push @tests, $tt;
62                 }
63                 ($tt->{$n}) && die "$n already defined";
64                 $tt->{$n} = "";
65                 $sep .= "\n";
66                 while (1) {
67                         my $line = <>;
68                         defined $line or die "Here-document not terminated";
69                         last if $line eq $sep;
70                         $tt->{$n} .= $line;
71                 }
72                 chomp $tt->{$n};
73         } else {
74                 die "Test script syntax error";
75         }
76 }
77
78 if (! -d "$rundir/tmp") {
79         mkdir "$rundir/tmp" or die "Unable to create $rundir/tmp: $!";
80 }
81
82 my $i = 0;
83 my $errors = 0;
84 my $prev_run = undef;
85 TEST: foreach $tt (@tests) {
86         $i++;
87         my $name = $tt->{'Name'};
88         printf "Test %03d", $i;
89         print " [$name]" if defined $name;
90         print ": ";
91         $run = ($tt->{'Run'} || $prev_run) or die "Don't know what to run";
92         $prev_run = $run;
93
94         my @out_files = ();
95         my @out_checks = ();
96         my $redirs = "";
97
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";
102                 close X;
103                 $redirs .= " <$ifi";
104         } else {
105                 $redirs .= " </dev/null";
106         }
107         if (defined $tt->{'Out'}) {
108                 my $ofi = "tmp/test$i.out";
109                 unlink "$rundir/$ofi";
110                 $redirs .= " >$ofi";
111                 push @out_files, $ofi;
112                 push @out_checks, $tt->{'Out'};
113         } else {
114                 $redirs .= " >/dev/null";
115         }
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'};
122         }
123         foreach my $arg (0..9) {
124                 my $f = "tmp/test$i.$arg";
125                 if (defined $tt->{"Out$arg"}) {
126                         unlink "$rundir/$f";
127                         push @out_files, $f;
128                         push @out_checks, $tt->{"Out$arg"};
129                 }
130                 if (defined $tt->{"In$arg"}) {
131                         open X, ">$rundir/$f" or die "Unable to create $f";
132                         print X $tt->{"In$arg"}, "\n";
133                         close X;
134                 }
135         }
136         $run =~ s/\$(\d)/tmp\/test$i.$1/g;
137         print "(running $run) " if $verbose;
138         system "cd $rundir && ( $run ) $redirs";
139         if ($? % 256) {
140                 print "FAILED with status code $?\n";
141                 $errors++;
142                 next;
143         }
144         my $ec = $? / 256;
145         my $expect_ec = $tt->{'Exit'} || 0;
146         if ($ec != $expect_ec) {
147                 print "FAILED: unexpected exit code $ec\n";
148                 $errors++;
149                 next;
150         }
151
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";
155                 my $out;
156                 {
157                         local $/ = undef;
158                         $out = <X>;
159                 }
160                 close X;
161                 $out =~ /\n$/s or $out .= "\n";
162                 if ($out ne $out_checks[$i] . "\n") {
163                         print "FAILED (see $ofi)\n";
164                         $errors++;
165                         next TEST;
166                 }
167         }
168
169         system "rm -f $rundir/tmp/test$i.*";
170         print "OK\n";
171 }
172
173 exit !!$errors;