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