]> mj.ucw.cz Git - moe.git/blob - judge/tester
ae3165a44ba8336d4db485c82e303fabffb55124
[moe.git] / judge / tester
1 #!/usr/bin/perl
2 # A simple unit testing script
3 # (c) 2004--2007 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 #       In<N>:  lines to pass to the program as input file <N>
17 #       Out<N>: lines to expect from the program in output file <N>
18 #               Both In<N> and Out<N> can be specified simultaneously if we
19 #               are testing a program which modifies some of its input files.
20 #       Exit:   expected exit code of the program (default: 0)
21
22 use Getopt::Long;
23
24 my $verbose = 0;
25 my $rundir = ".";
26 GetOptions("verbose!" => \$verbose,
27            "rundir=s" => \$rundir)
28         or die "Usage: tester [--verbose] [--rundir=<dir>] <tests>\n";
29
30 my @tests = ();
31 my $tt;
32 my $append_to;
33
34 while (<>) {
35         /^#/ && next;
36         if (/^\s*$/) {
37                 $tt = undef;
38                 $append_to = undef;
39         } elsif (defined($append_to) && /^\s+(.*)$/) {
40                 $$append_to .= "\n$1";
41         } elsif (my ($n,$v) = /^(\w+):\s+(.*)$/) {
42                 if (!$tt) {
43                         $tt = {};
44                         push @tests, $tt;
45                 }
46                 ($tt->{$n}) && die "$n already defined";
47                 $tt->{$n} = $v;
48                 $append_to = \($tt->{$n});
49         } else {
50                 die "Test script syntax error";
51         }
52 }
53
54 if (! -d "$rundir/tmp") {
55         mkdir "$rundir/tmp" or die "Unable to create $rundir/tmp: $!";
56 }
57
58 my $i = 0;
59 my $errors = 0;
60 my $prev_run = undef;
61 TEST: foreach $tt (@tests) {
62         $i++;
63         my $name = $tt->{'Name'} || $i;
64         print "Test $name: ";
65         $run = ($tt->{'Run'} || $prev_run) or die "Don't know what to run";
66         $prev_run = $run;
67
68         my @out_files = ();
69         my @out_checks = ();
70         my @temps = ();
71         my $redirs = "";
72
73         if (defined $tt->{'In'}) {
74                 my $ifi = "tmp/test$i.in";
75                 open X, ">$rundir/$ifi" or die "Unable to create $ifi";
76                 print X $tt->{'In'}, "\n";
77                 close X;
78                 $redirs .= " <$ifi";
79                 push @temps, $ifi;
80         } else {
81                 $redirs .= " </dev/null";
82         }
83         if (defined $tt->{'Out'}) {
84                 my $ofi = "tmp/test$i.out";
85                 unlink "$rundir/$ofi";
86                 $redirs .= " >$ofi";
87                 push @temps, $ofi;
88                 push @out_files, $ofi;
89                 push @out_checks, $tt->{'Out'};
90         } else {
91                 $redirs .= " >/dev/null";
92         }
93         foreach my $arg (0..9) {
94                 my $f = "tmp/test$i.$arg";
95                 if (defined $tt->{"Out$arg"}) {
96                         unlink "$rundir/$f";
97                         push @temps, $f;
98                         push @out_files, $f;
99                         push @out_checks, $tt->{"Out$arg"};
100                 }
101                 if (defined $tt->{"In$arg"}) {
102                         open X, ">$rundir/$f" or die "Unable to create $f";
103                         print X $tt->{"In$arg"}, "\n";
104                         close X;
105                         push @temps, $f;
106                 }
107         }
108         $run =~ s/\$(\d)/tmp\/test$i.$1/g;
109         print "(running $run) " if $verbose;
110         system "cd $rundir && ( $run ) $redirs";
111         if ($? % 256) {
112                 print "FAILED with status code $?\n";
113                 $errors++;
114                 next;
115         }
116         my $ec = $? / 256;
117         my $expect_ec = $tt->{'Exit'} || 0;
118         if ($ec != $expect_ec) {
119                 print "FAILED: unexpected exit code $ec\n";
120                 $errors++;
121                 next;
122         }
123
124         for (my $i=0; $i<=$#out_files; $i++) {
125                 my $ofi = $out_files[$i];
126                 open X, "<$rundir/$ofi" or die "Unable to read $ofi";
127                 my $out;
128                 {
129                         local $/ = undef;
130                         $out = <X>;
131                 }
132                 close X;
133                 if ($out ne $out_checks[$i] . "\n") {
134                         print "FAILED (see $ofi)\n";
135                         $errors++;
136                         next TEST;
137                 }
138         }
139
140         foreach my $f (@temps) {
141                 unlink "$rundir/$f";
142         }
143         print "OK\n";
144 }
145
146 exit !!$errors;