drd: Add a consistency check
[valgrind.git] / tests / vg_regtest.in
blob795369f0cdc6a5f4193e67828a953c9d46b4d639
1 #! @PERL@
2 ##--------------------------------------------------------------------##
3 ##--- Valgrind regression testing script                vg_regtest ---##
4 ##--------------------------------------------------------------------##
6 #  This file is part of Valgrind, a dynamic binary instrumentation
7 #  framework.
9 #  Copyright (C) 2003-2013 Nicholas Nethercote
10 #     njn@valgrind.org
12 #  This program is free software; you can redistribute it and/or
13 #  modify it under the terms of the GNU General Public License as
14 #  published by the Free Software Foundation; either version 2 of the
15 #  License, or (at your option) any later version.
17 #  This program is distributed in the hope that it will be useful, but
18 #  WITHOUT ANY WARRANTY; without even the implied warranty of
19 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 #  General Public License for more details.
22 #  You should have received a copy of the GNU General Public License
23 #  along with this program; if not, write to the Free Software
24 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 #  02111-1307, USA.
27 #  The GNU General Public License is contained in the file COPYING.
29 #----------------------------------------------------------------------------
30 # usage: vg_regtest [options] <dirs | files>
32 # Options:
33 #   --all:      run tests in all subdirs
34 #   --valgrind: valgrind launcher to use.  Default is ./coregrind/valgrind.
35 #               (This option should probably only be used in conjunction with
36 #               --valgrind-lib.)
37 #   --valgrind-lib: valgrind libraries to use.  Default is $tests_dir/.in_place.
38 #               (This option should probably only be used in conjunction with
39 #               --valgrind.)
40 #   --keep-unfiltered: keep a copy of the unfiltered output/error output
41 #     of each test by adding an extension .unfiltered.out
43 #   --outer-valgrind: run this valgrind under the given outer valgrind.
44 #     This valgrind must be configured with --enable-inner.
45 #   --outer-tool: tool to use by the outer valgrind (default memcheck).
46 #   --outer-args: use this as outer tool args.
48 # The easiest way is to run all tests in valgrind/ with (assuming you installed
49 # in $PREFIX):
51 #   $PREFIX/bin/vg_regtest --all
53 # You can specify individual files to test, or whole directories, or both.
54 # Directories are traversed recursively, except for ones named, for example, 
55 # CVS/ or docs/.
57 # Each test is defined in a file <test>.vgtest, containing one or more of the
58 # following lines, in any order:
59 #   - prog:   <prog to run>                         (compulsory)
60 #   - args:   <args for prog>                       (default: none)
61 #   - vgopts: <Valgrind options>                    (default: none;
62 #                                                    multiple are allowed)
63 #   - stdout_filter: <filter to run stdout through> (default: none)
64 #   - stderr_filter: <filter to run stderr through> (default: ./filter_stderr)
65 #   - stdout_filter_args: <args for stdout_filter>  (default: basename of .vgtest file)
66 #   - stderr_filter_args: <args for stderr_filter>  (default: basename of .vgtest file)
68 #   - progB:  <prog to run in parallel with prog>   (default: none)
69 #   - argsB:  <args for progB>                      (default: none)
70 #   - stdinB: <input file for progB>                (default: none)
71 #   - stdoutB_filter: <filter progB stdout through> (default: none)
72 #   - stderrB_filter: <filter progB stderr through> (default: ./filter_stderr)
73 #   - stdoutB_filter_args: <args for stdout_filterB> (default: basename of .vgtest file)
74 #   - stderrB_filter_args: <args for stderr_filterB>  (default: basename of .vgtest file)
76 #   - prereq: <prerequisite command>                (default: none)
77 #   - post: <post-test check command>               (default: none)
78 #   - cleanup: <post-test cleanup cmd>              (default: none)
80 # If prog or probB is a relative path, it will be prefix with the test directory.
81 # Note that filters are necessary for stderr results to filter out things that
82 # always change, eg. process id numbers.
83 # Note that if a progB is specified, it is started in background (before prog).
85 # Expected stdout (filtered) is kept in <test>.stdout.exp* (can be more
86 # than one expected output).  It can be missing if it would be empty.  Expected
87 # stderr (filtered) is kept in <test>.stderr.exp*.   There must be at least
88 # one stderr.exp* file.  Any .exp* file that ends in '~' or '#' is ignored;
89 # this is because Emacs creates temporary files of these names.
91 # Expected output for progB is handled similarly, except that
92 # expected stdout and stderr for progB are in  <test>.stdoutB.exp*
93 # and <test>.stderrB.exp*.
95 # If results don't match, the output can be found in <test>.std<strm>.out,
96 # and the diff between expected and actual in <test>.std<strm>.diff*.
97 # (for progB, in <test>.std<strm>2.out and <test>.std<strm>2.diff*).
99 # The prerequisite command, if present, works like this:
100 # - if it returns 0 the test is run
101 # - if it returns 1 the test is skipped
102 # - if it returns anything else the script aborts.
103 # The idea here is results other than 0 or 1 are likely to be due to
104 # problems with the commands, and you don't want to conflate them with the 1
105 # case, which would happen if you just tested for zero or non-zero.
107 # The post-test command, if present, must return 0 and its stdout must match
108 # the expected stdout which is kept in <test>.post.exp*.
110 # Sometimes it is useful to run all the tests at a high sanity check
111 # level or with arbitrary other flags.  To make this simple, extra 
112 # options, applied to all tests run, are read from $EXTRA_REGTEST_OPTS,
113 # and handed to valgrind prior to any other flags specified by the 
114 # .vgtest file.
116 # Some more notes on adding regression tests for a new tool are in
117 # docs/xml/manual-writing-tools.xml.
118 #----------------------------------------------------------------------------
120 use warnings;
121 use strict;
123 #----------------------------------------------------------------------------
124 # Global vars
125 #----------------------------------------------------------------------------
126 my $usage="\n"
127      . "Usage:\n"
128      . "   vg_regtest [--all, --valgrind, --valgrind-lib, --keep-unfiltered\n"
129      . "                 --outer-valgrind, --outer-tool, --outer-args]\n"
130      . "   Use EXTRA_REGTEST_OPTS to supply extra args for all tests\n"
131      . "\n";
133 my $tmp="vg_regtest.tmp.$$";
135 # Test variables
136 my $vgopts;             # valgrind options
137 my $prog;               # test prog
138 my $args;               # test prog args
139 my $stdout_filter;      # filter program to run stdout results file through
140 my $stderr_filter;      # filter program to run stderr results file through
141 my $stdout_filter_args; # arguments passed to stdout_filter
142 my $stderr_filter_args; # arguments passed to stderr_filter
143 my $progB;              # Same but for progB
144 my $argsB;              # 
145 my $stdoutB_filter;     # 
146 my $stderrB_filter;     # 
147 my $stdoutB_filter_args;# arguments passed to stdout_filterB
148 my $stderrB_filter_args;# arguments passed to stderr_filterB
149 my $stdinB;             # Input file for progB
150 my $prereq;             # prerequisite test to satisfy before running test
151 my $post;               # check command after running test
152 my $cleanup;            # cleanup command to run
154 my @failures;           # List of failed tests
156 my $num_tests_done      = 0;
157 my %num_failures        = (stderr => 0, stdout => 0, 
158                            stderrB => 0, stdoutB => 0,
159                            post => 0);
161 # Default valgrind to use is this build tree's (uninstalled) one
162 my $valgrind = "./coregrind/valgrind";
164 chomp(my $tests_dir = `pwd`);
166 # Outer valgrind to use, and args to use for it.
167 my $outer_valgrind;
168 my $outer_tool = "memcheck";
169 my $outer_args;
171 my $valgrind_lib = "$tests_dir/.in_place";
172 my $keepunfiltered = 0;
174 # default filter is the one named "filter_stderr" in the test's directory
175 my $default_stderr_filter = "filter_stderr";
178 #----------------------------------------------------------------------------
179 # Process command line, setup
180 #----------------------------------------------------------------------------
182 # If $prog is a relative path, it prepends $dir to it.  Useful for two reasons:
184 # 1. Can prepend "." onto programs to avoid trouble with users who don't have
185 #    "." in their path (by making $dir = ".")
186 # 2. Can prepend the current dir to make the command absolute to avoid
187 #    subsequent trouble when we change directories.
189 # Also checks the program exists and is executable.
190 sub validate_program ($$$$) 
192     my ($dir, $prog, $must_exist, $must_be_executable) = @_;
194     # If absolute path, leave it alone.  If relative, make it
195     # absolute -- by prepending current dir -- so we can change
196     # dirs and still use it.
197     $prog = "$dir/$prog" if ($prog !~ /^\//);
198     if ($must_exist) {
199         (-f $prog) or die "vg_regtest: `$prog' not found or not a file ($dir)\n";
200     }
201     if ($must_be_executable) { 
202         (-x $prog) or die "vg_regtest: `$prog' not executable ($dir)\n";
203     }
205     return $prog;
208 sub process_command_line() 
210     my $alldirs = 0;
211     my @fs;
212     
213     for my $arg (@ARGV) {
214         if ($arg =~ /^-/) {
215             if      ($arg =~ /^--all$/) {
216                 $alldirs = 1;
217             } elsif ($arg =~ /^--valgrind=(.*)$/) {
218                 $valgrind = $1;
219             } elsif ($arg =~ /^--outer-valgrind=(.*)$/) {
220                 $outer_valgrind = $1;
221             } elsif ($arg =~ /^--outer-tool=(.*)$/) {
222                 $outer_tool = $1;
223             } elsif ($arg =~ /^--outer-args=(.*)$/) {
224                 $outer_args = $1;
225             } elsif ($arg =~ /^--valgrind-lib=(.*)$/) {
226                 $valgrind_lib = $1;
227             } elsif ($arg =~ /^--keep-unfiltered$/) {
228                 $keepunfiltered = 1;
229             } else {
230                 die $usage;
231             }
232         } else {
233             push(@fs, $arg);
234         }
235     }
236     $valgrind = validate_program($tests_dir, $valgrind, 1, 0);
237     
238     if (defined $outer_valgrind) {
239       $outer_valgrind = validate_program($tests_dir, $outer_valgrind, 1, 1);
240       if (not defined $outer_args) {
241           $outer_args = 
242                 " --command-line-only=yes"
243               . " --run-libc-freeres=no --sim-hints=enable-outer"
244               . " --smc-check=all-non-file"
245               . " --vgdb=no --trace-children=yes --read-var-info=no"
246               . " --read-inline-info=yes"
247               . " --suppressions=" 
248               . validate_program($tests_dir,"./tests/outer_inner.supp",1,0)
249               . " --memcheck:leak-check=full --memcheck:show-reachable=no"
250               . " ";
251       }
252     }
254     if ($alldirs) {
255         @fs = ();
256         foreach my $f (glob "*") {
257             push(@fs, $f) if (-d $f);
258         }
259     }
261     (0 != @fs) or die "No test files or directories specified\n";
263     return @fs;
266 #----------------------------------------------------------------------------
267 # Read a .vgtest file
268 #----------------------------------------------------------------------------
269 sub read_vgtest_file($)
271     my ($f) = @_;
273     # Defaults.
274     ($vgopts, $prog, $args)            = ("", undef, "");
275     ($stdout_filter, $stderr_filter)   = (undef, undef);
276     ($progB, $argsB, $stdinB)          = (undef, "", undef);
277     ($stdoutB_filter, $stderrB_filter) = (undef, undef);
278     ($prereq, $post, $cleanup)         = (undef, undef, undef);
279     ($stdout_filter_args, $stderr_filter_args)   = (undef, undef);
280     ($stdoutB_filter_args, $stderrB_filter_args) = (undef, undef);
282     # Every test directory must have a "filter_stderr"
283     $stderr_filter = validate_program(".", $default_stderr_filter, 1, 1);
284     $stderrB_filter = validate_program(".", $default_stderr_filter, 1, 1);
285     
287     open(INPUTFILE, "< $f") || die "File $f not openable\n";
289     while (my $line = <INPUTFILE>) {
290         if      ($line =~ /^\s*#/ || $line =~ /^\s*$/) {
291             next;
292         } elsif ($line =~ /^\s*vgopts:\s*(.*)$/) {
293             my $addvgopts = $1;
294             $addvgopts =~ s/\${PWD}/$ENV{PWD}/g;
295             $vgopts = $vgopts . " " . $addvgopts;   # Nb: Make sure there's a space!
296         } elsif ($line =~ /^\s*prog:\s*(.*)$/) {
297             $prog = validate_program(".", $1, 0, 0);
298         } elsif ($line =~ /^\s*args:\s*(.*)$/) {
299             $args = $1;
300         } elsif ($line =~ /^\s*stdout_filter:\s*(.*)$/) {
301             $stdout_filter = validate_program(".", $1, 1, 1);
302         } elsif ($line =~ /^\s*stderr_filter:\s*(.*)$/) {
303             $stderr_filter = validate_program(".", $1, 1, 1);
304         } elsif ($line =~ /^\s*stdout_filter_args:\s*(.*)$/) {
305             $stdout_filter_args = $1;
306         } elsif ($line =~ /^\s*stderr_filter_args:\s*(.*)$/) {
307             $stderr_filter_args = $1;
308         } elsif ($line =~ /^\s*progB:\s*(.*)$/) {
309             $progB = validate_program(".", $1, 0, 0);
310         } elsif ($line =~ /^\s*argsB:\s*(.*)$/) {
311             $argsB = $1;
312         } elsif ($line =~ /^\s*stdinB:\s*(.*)$/) {
313             $stdinB = $1;
314         } elsif ($line =~ /^\s*stdoutB_filter:\s*(.*)$/) {
315             $stdoutB_filter = validate_program(".", $1, 1, 1);
316         } elsif ($line =~ /^\s*stderrB_filter:\s*(.*)$/) {
317             $stderrB_filter = validate_program(".", $1, 1, 1);
318         } elsif ($line =~ /^\s*stdoutB_filter_args:\s*(.*)$/) {
319             $stdoutB_filter_args = $1;
320         } elsif ($line =~ /^\s*stderrB_filter_args:\s*(.*)$/) {
321             $stderrB_filter_args = $1;
322         } elsif ($line =~ /^\s*prereq:\s*(.*)$/) {
323             $prereq = $1;
324         } elsif ($line =~ /^\s*post:\s*(.*)$/) {
325             $post = $1;
326         } elsif ($line =~ /^\s*cleanup:\s*(.*)$/) {
327             $cleanup = $1;
328         } else {
329             die "Bad line in $f: $line\n";
330         }
331     }
332     close(INPUTFILE);
334     if (!defined $prog) {
335         $prog = "";     # allow no prog for testing error and --help cases
336     }
339 #----------------------------------------------------------------------------
340 # Do one test
341 #----------------------------------------------------------------------------
342 # Since most of the program time is spent in system() calls, need this to
343 # propagate a Ctrl-C enabling us to quit.
344 sub mysystem($) 
346     my $exit_code = system($_[0]);
347     ($exit_code == 2) and exit 1;      # 2 is SIGINT
348     return $exit_code;
351 # if $keepunfiltered, copies $1 to $1.unfiltered.out
352 # renames $0 tp $1
353 sub filtered_rename($$) 
355     if ($keepunfiltered == 1) {
356         mysystem("cp  $_[1] $_[1].unfiltered.out");
357     }
358     rename ($_[0], $_[1]);
362 # from a directory name like "/foo/cachesim/tests/" determine the tool name
363 sub determine_tool()
365     my $dir = `pwd`;
366     $dir =~ /.*\/([^\/]+)\/tests.*/;   # foo/tool_name/tests/foo
367     return $1;
370 # Compare output against expected output;  it should match at least one of
371 # them.
372 sub do_diffs($$$$)
374     my ($fullname, $name, $mid, $f_exps) = @_;
375     
376     for my $f_exp (@$f_exps) {
377         (-r $f_exp) or die "Could not read `$f_exp'\n";
379         # Emacs produces temporary files that end in '~' and '#'.  We ignore
380         # these.
381         if ($f_exp !~ /[~#]$/) {
382             # $n is the (optional) suffix after the ".exp";  we tack it onto
383             # the ".diff" file.
384             my $n = "";
385             if ($f_exp =~ /.*\.exp(.*)$/) {
386                 $n = $1;
387             } else {
388                 $n = "";
389                 ($f_exp eq "/dev/null") or die "Unexpected .exp file: $f_exp\n";
390             }
392             mysystem("@DIFF@ $f_exp $name.$mid.out > $name.$mid.diff$n");
394             if (not -s "$name.$mid.diff$n") {
395                 # A match;  remove .out and any previously created .diff files.
396                 unlink("$name.$mid.out");
397                 unlink(<$name.$mid.diff*>);
398                 return;
399             }
400         }
401     }
402     # If we reach here, none of the .exp files matched.
403     print "*** $name failed ($mid) ***\n";
404     push(@failures, sprintf("%-40s ($mid)", "$fullname"));
405     $num_failures{$mid}++;
408 sub do_one_test($$) 
410     my ($dir, $vgtest) = @_;
411     $vgtest =~ /^(.*)\.vgtest/;
412     my $name = $1;
413     my $fullname = "$dir/$name"; 
415     # Pull any extra options (for example, --sanity-level=4)
416     # from $EXTRA_REGTEST_OPTS.
417     my $maybe_extraopts = $ENV{"EXTRA_REGTEST_OPTS"};
418     my $extraopts = $maybe_extraopts ?  $maybe_extraopts  : "";
420     read_vgtest_file($vgtest);
422     if (defined $prereq) {
423         my $prereq_res = system("$prereq");
424         if (0 == $prereq_res) {
425             # Do nothing (ie. continue with the test)
426         } elsif (256 == $prereq_res) {
427             # Nb: weird Perl-ism -- exit code of '1' is seen by Perl as 256...
428             # Prereq failed, skip.
429             printf("%-16s (skipping, prereq failed: $prereq)\n", "$name:");
430             return;
431         } else {
432             # Bad prereq; abort.
433             $prereq_res /= 256;
434             die "prereq returned $prereq_res: $prereq\n";
435         }
436     }
439     if (defined $progB) {
440         # If there is a progB, let's start it in background:
441         printf("%-16s valgrind $extraopts $vgopts $prog $args (progB: $progB $argsB)\n",
442                "$name:");
443         # progB.done used to detect child has finished. See below.
444         # Note: redirection of stdout and stderr is before $progB to allow argsB
445         # to e.g. redirect stdoutB to stderrB
446         if (defined $stdinB) {
447             mysystem("(rm -f progB.done;"
448                      . " < $stdinB > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;"
449                      . "touch progB.done) &");
450         } else {
451             mysystem("(rm -f progB.done;"
452                      . " > $name.stdoutB.out 2> $name.stderrB.out $progB $argsB;"
453                      . "touch progB.done)  &");
454         }
455     } else {
456         printf("%-16s valgrind $extraopts $vgopts $prog $args\n", "$name:");
457     }
459     # Pass the appropriate --tool option for the directory (can be overridden
460     # by an "args:" line, though).
461     my $tool=determine_tool();
462     if (defined $outer_valgrind ) {
463         # in an outer-inner setup, only set VALGRIND_LIB_INNER
464         mysystem(   "VALGRIND_LIB_INNER=$valgrind_lib "
465                   . "$outer_valgrind "
466                   . "--tool=" . $outer_tool . " "
467                   . "$outer_args "
468                   . "--log-file=" . "$name.outer.log "
469                   . "$valgrind --command-line-only=yes --memcheck:leak-check=no "
470                   . "--sim-hints=no-inner-prefix "
471                   . "--tool=$tool $extraopts $vgopts "
472                   . "$prog $args > $name.stdout.out 2> $name.stderr.out");
473     } else {
474         # Set both VALGRIND_LIB and VALGRIND_LIB_INNER in case this Valgrind
475         # was configured with --enable-inner.
476         mysystem(   "VALGRIND_LIB=$valgrind_lib VALGRIND_LIB_INNER=$valgrind_lib "
477                   . "$valgrind --command-line-only=yes --memcheck:leak-check=no "
478                   . "--tool=$tool $extraopts $vgopts "
479                   . "$prog $args > $name.stdout.out 2> $name.stderr.out");
480     }
482     # Filter stdout
483     if (defined $stdout_filter) {
484         $stdout_filter_args = $name if (! defined $stdout_filter_args);
485         mysystem("$stdout_filter $stdout_filter_args < $name.stdout.out > $tmp");
486         filtered_rename($tmp, "$name.stdout.out");
487     }
488     # Find all the .stdout.exp files.  If none, use /dev/null.
489     my @stdout_exps = <$name.stdout.exp*>;
490     @stdout_exps = ( "/dev/null" ) if (0 == scalar @stdout_exps);
491     do_diffs($fullname, $name, "stdout", \@stdout_exps); 
493     # Filter stderr
494     $stderr_filter_args = $name if (! defined $stderr_filter_args);
495     mysystem("$stderr_filter $stderr_filter_args < $name.stderr.out > $tmp");
496     filtered_rename($tmp, "$name.stderr.out");
497     # Find all the .stderr.exp files.  At least one must exist.
498     my @stderr_exps = <$name.stderr.exp*>;
499     (0 != scalar @stderr_exps) or die "Could not find `$name.stderr.exp*'\n";
500     do_diffs($fullname, $name, "stderr", \@stderr_exps); 
502     if (defined $progB) {
503         # wait for the child to be finished
504         # tried things such as:
505         #   wait;
506         #   $SIG{CHLD} = sub { wait };
507         # but nothing worked:
508         # e.g. running mssnapshot.vgtest in a loop failed from time to time
509         # due to some missing output (not yet written?).
510         # So, we search progB.done during max 100 times 100 millisecond.
511         my $count;
512         for ($count = 1; $count <= 100; $count++) {
513             (-f "progB.done") or select(undef, undef, undef, 0.100);
514         }
515         # Filter stdout
516         if (defined $stdoutB_filter) {
517             $stdoutB_filter_args = $name if (! defined $stdoutB_filter_args);
518             mysystem("$stdoutB_filter $stdoutB_filter_args < $name.stdoutB.out > $tmp");
519             filtered_rename($tmp, "$name.stdoutB.out");
520         }
521         # Find all the .stdoutB.exp files.  If none, use /dev/null.
522         my @stdoutB_exps = <$name.stdoutB.exp*>;
523         @stdoutB_exps = ( "/dev/null" ) if (0 == scalar @stdoutB_exps);
524         do_diffs($fullname, $name, "stdoutB", \@stdoutB_exps); 
525         
526         # Filter stderr
527         $stderrB_filter_args = $name if (! defined $stderrB_filter_args);
528         mysystem("$stderrB_filter $stderrB_filter_args < $name.stderrB.out > $tmp");
529         filtered_rename($tmp, "$name.stderrB.out");
530         # Find all the .stderrB.exp files.  At least one must exist.
531         my @stderrB_exps = <$name.stderrB.exp*>;
532         (0 != scalar @stderrB_exps) or die "Could not find `$name.stderrB.exp*'\n";
533         do_diffs($fullname, $name, "stderrB", \@stderrB_exps); 
534     }
536     # Maybe do post-test check
537     if (defined $post) {
538         if (mysystem("$post > $name.post.out") != 0) {
539             print("post check failed: $post\n");
540             $num_failures{"post"}++;
541         } else {
542             # Find all the .post.exp files.  If none, use /dev/null.
543             my @post_exps = <$name.post.exp*>;
544             @post_exps = ( "/dev/null" ) if (0 == scalar @post_exps);
545             do_diffs($fullname, $name, "post", \@post_exps);
546         }
547     }
549     if (defined $cleanup) {
550         (system("$cleanup") == 0) or 
551             print("(cleanup operation failed: $cleanup)\n");
552     }
554     $num_tests_done++;
557 #----------------------------------------------------------------------------
558 # Test one directory (and any subdirs)
559 #----------------------------------------------------------------------------
560 sub test_one_dir($$);    # forward declaration
562 sub test_one_dir($$) 
564     my ($dir, $prev_dirs) = @_;
565     $dir =~ s/\/$//;    # trim a trailing '/'
567     # Ignore dirs into which we should not recurse.
568     if ($dir =~ /^(BitKeeper|CVS|SCCS|docs|doc)$/) { return; }
570     (-x "$tests_dir/tests/arch_test") or die 
571         "vg_regtest: 'arch_test' is missing.  Did you forget to 'make check'?\n";
572     
573     # Ignore any dir whose name matches that of an architecture which is not
574     # the architecture we are running on.  Eg. when running on x86, ignore
575     # ppc/ directories ('arch_test' returns 1 for this case).  Likewise for
576     # the OS and platform.
577     # Nb: weird Perl-ism -- exit code of '1' is seen by Perl as 256...
578     if (256 == system("$tests_dir/tests/arch_test $dir"))  { return; }
579     if (256 == system("$tests_dir/tests/os_test   $dir"))  { return; }
580     if ($dir =~ /(\w+)-(\w+)/ &&
581         256 == system("sh $tests_dir/tests/platform_test $1 $2")) { return; }
582     
583     chdir($dir) or die "Could not change into $dir\n";
585     # Nb: Don't prepend a '/' to the base directory
586     my $full_dir = $prev_dirs . ($prev_dirs eq "" ? "" : "/") . $dir;
587     my $dashes = "-" x (50 - length $full_dir);
589     my @fs = glob "*";
590     my $found_tests = (0 != (grep { $_ =~ /\.vgtest$/ } @fs));
592     if ($found_tests) {
593         print "-- Running  tests in $full_dir $dashes\n";
594     }
595     foreach my $f (@fs) {
596         if (-d $f) {
597             test_one_dir($f, $full_dir);
598         } elsif ($f =~ /\.vgtest$/) {
599             do_one_test($full_dir, $f);
600         }
601     }
602     if ($found_tests) {
603         print "-- Finished tests in $full_dir $dashes\n";
604     }
606     chdir("..");
609 #----------------------------------------------------------------------------
610 # Summarise results
611 #----------------------------------------------------------------------------
612 sub plural($)
614    return ( $_[0] == 1 ? "" : "s" );
617 sub summarise_results 
619     my $x = ( $num_tests_done == 1 ? "test" : "tests" );
620     
621     printf("\n== %d test%s, %d stderr failure%s, %d stdout failure%s, "
622                          . "%d stderrB failure%s, %d stdoutB failure%s, "
623                          . "%d post failure%s ==\n", 
624            $num_tests_done, plural($num_tests_done),
625            $num_failures{"stderr"},   plural($num_failures{"stderr"}),
626            $num_failures{"stdout"},   plural($num_failures{"stdout"}),
627            $num_failures{"stderrB"},  plural($num_failures{"stderrB"}),
628            $num_failures{"stdoutB"},  plural($num_failures{"stdoutB"}),
629            $num_failures{"post"},     plural($num_failures{"post"}));
631     foreach my $failure (@failures) {
632         print "$failure\n";
633     }
634     print "\n";
637 #----------------------------------------------------------------------------
638 # main(), sort of
639 #----------------------------------------------------------------------------
640 sub warn_about_EXTRA_REGTEST_OPTS()
642     print "WARNING: \$EXTRA_REGTEST_OPTS is set.  You probably don't want\n";
643     print "to run the regression tests with it set, unless you are doing some\n";
644     print "strange experiment, and/or you really know what you are doing.\n";
645     print "\n";
648 # nuke VALGRIND_OPTS
649 $ENV{"VALGRIND_OPTS"} = "";
651 if ($ENV{"EXTRA_REGTEST_OPTS"}) {
652     print "\n";
653     warn_about_EXTRA_REGTEST_OPTS();
656 my @fs = process_command_line();
657 foreach my $f (@fs) {
658     if (-d $f) {
659         test_one_dir($f, "");
660     } else { 
661         # Allow the .vgtest suffix to be given or omitted
662         if ($f =~ /.vgtest$/ && -r $f) {
663             # do nothing
664         } elsif (-r "$f.vgtest") {
665             $f = "$f.vgtest";
666         } else {
667             die "`$f' neither a directory nor a readable test file/name\n"
668         }
669         my $dir  = `dirname  $f`;   chomp $dir;
670         my $file = `basename $f`;   chomp $file;
671         chdir($dir) or die "Could not change into $dir\n";
672         do_one_test($dir, $file);
673     }
674     chdir($tests_dir);
676 summarise_results();
678 if ($ENV{"EXTRA_REGTEST_OPTS"}) {
679     warn_about_EXTRA_REGTEST_OPTS();
682 if (0 == $num_failures{"stdout"} &&
683     0 == $num_failures{"stderr"} &&
684     0 == $num_failures{"stdoutB"} &&
685     0 == $num_failures{"stderrB"} &&
686     0 == $num_failures{"post"}) {
687     exit 0;
688 } else {
689     exit 1;
692 ##--------------------------------------------------------------------##
693 ##--- end                                               vg_regtest ---##
694 ##--------------------------------------------------------------------##