4 # Modification history:
5 # Written 91-12-02 through 92-01-01 by Stephen McGee.
6 # Modified 92-02-11 through 92-02-22 by Chris Arthur to further generalize.
8 # Copyright (C) 1991-2022 Free Software Foundation, Inc.
9 # This file is part of GNU Make.
11 # GNU Make is free software; you can redistribute it and/or modify it under
12 # the terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 3 of the License, or (at your option) any later
16 # GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 # You should have received a copy of the GNU General Public License along with
22 # this program. If not, see <https://www.gnu.org/licenses/>.
25 # Test driver routines used by a number of test suites, including
26 # those for SCS, make, roll_dir, and scan_deps (?).
28 # this routine controls the whole mess; each test suite sets up a few
29 # variables and then calls &toplevel, which does all the real work.
38 # The number of test categories we've run
40 # The number of test categroies that have passed
41 $categories_passed = 0;
42 # The total number of individual tests that have been run
44 # The total number of individual tests that have passed
45 $total_tests_passed = 0;
46 # The number of tests in this category that have been run
48 # The number of tests in this category that have passed
56 # Yeesh. This whole test environment is such a hack!
59 # Timeout in seconds. If the test takes longer than this we'll fail it.
60 # This is to prevent hung tests.
68 $perl_name .= $Config{_exe} unless $perl_name =~ m/$Config{_exe}$/i;
74 # Poor man's File::Which
75 my ($v,$d,$f) = File::Spec->splitpath($cmd);
77 # The command has a pathname so don't look for it in PATH.
78 # Use forward-slashes even on Windows, else it fails in recipes.
79 (-f $cmd and -x _) or return undef;
85 if ($port_type eq 'UNIX' || $port_type eq 'VMS-DCL') {
88 @ext = index($f, '.') == -1 ? () : ('');
89 push @ext, split /;/, $ENV{PATHEXT};
92 foreach my $dir (File::Spec->path()) {
93 foreach my $e (@ext) {
94 my $p = File::Spec->catfile($dir, "$cmd$e");
95 (-f $p and -x _) or next;
96 # Use forward-slashes even on Windows, else it fails in recipes.
104 # %makeENV is the cleaned-out environment. Tests must not modify it.
107 sub vms_get_process_logicals {
108 # Sorry for the long note here, but to keep this test running on
109 # VMS, it is needed to be understood.
111 # Perl on VMS by default maps the %ENV array to the system wide logical
114 # This is a very large dynamically changing table.
115 # On Linux, this would be the equivalent of a table that contained
116 # every mount point, temporary pipe, and symbolic link on every
117 # file system. You normally do not have permission to clear or replace it,
118 # and if you did, the results would be catastrophic.
120 # On VMS, added/changed %ENV items show up in the process logical
121 # name table. So to track changes, a copy of it needs to be captured.
123 my $raw_output = `show log/process/access_mode=supervisor`;
124 my @raw_output_lines = split('\n',$raw_output);
126 foreach my $line (@raw_output_lines) {
127 if ($line =~ /^\s+"([A-Za-z\$_]+)"\s+=\s+"(.+)"$/) {
134 # %origENV is the caller's original environment
138 my $proc_env = vms_get_process_logicals;
139 %origENV = %{$proc_env};
144 # We used to say "%ENV = ();" but this doesn't work in Perl 5.000
145 # through Perl 5.004. It was fixed in Perl 5.004_01, but we don't
146 # want to require that here, so just delete each one individually.
149 foreach $v (keys %ENV) {
155 my $proc_env = vms_get_process_logicals();
156 my %delta = %{$proc_env};
157 foreach my $v (keys %delta) {
158 if (exists $origENV{$v}) {
159 if ($origENV{$v} ne $delta{$v}) {
160 $ENV{$v} = $origENV{$v};
169 # Returns a string-ified version of cmd which is a value provided to exec()
170 # so it can either be a ref of a list or a string.
180 if (/[][#;"*?&|<>(){}\$`^~!]/) {
187 return join(' ', @c);
192 %origENV = %ENV unless $^O eq 'VMS';
194 # Pull in benign variables from the user's environment
196 foreach (# POSIX-specific things
197 'TZ', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH',
200 'ASAN_OPTIONS', 'UBSAN_OPTIONS', 'LSAN_OPTIONS',
203 # Windows-specific things
204 'Path', 'SystemRoot', 'TEMP', 'TMP', 'USERPROFILE', 'PATHEXT',
205 # DJGPP-specific things
206 'DJDIR', 'DJGPP', 'SHELL', 'COMSPEC', 'HOSTNAME', 'LFN',
207 'FNCASE', '387', 'EMU387', 'GROUP'
209 $makeENV{$_} = $ENV{$_} if $ENV{$_};
212 # Make sure our compares are not foiled by locale differences
214 $makeENV{LC_ALL} = 'C';
215 $makeENV{LANG} = 'C';
216 $makeENV{LANGUAGE} = 'C';
218 $| = 1; # unbuffered output
220 $debug = 0; # debug flag
221 $profile = 0; # profiling flag
222 $verbose = 0; # verbose mode flag
223 $detail = 0; # detailed verbosity
224 $keep = 0; # keep temp files around
225 $workdir = "work"; # The directory where the test will start running
226 $tempdir = "_tmp"; # A temporary directory
227 $scriptdir = "scripts"; # The directory where we find the test scripts
228 $tmpfilesuffix = "t"; # the suffix used on tmpfiles
229 $default_output_stack_level = 0; # used by attach_default_output, etc.
230 $default_input_stack_level = 0; # used by attach_default_input, etc.
231 $cwd = "."; # don't we wish we knew
232 $cwdslash = ""; # $cwd . $pathsep, but "" rather than "./"
234 &get_osname; # sets $osname, $vos, $pathsep, and $short_filenames
236 $perl_name = which($perl_name);
238 # See if we have a diff
239 $diff_name = which('diff');
241 print "No diff found; differences will not be shown\n";
244 &set_defaults; # suite-defined
246 &parse_command_line (@ARGV);
248 print "OS name = '$osname'\n" if $debug;
250 $temppath = File::Spec->rel2abs($tempdir);
253 print "Clearing $temppath...\n";
254 &remove_directory_tree("$temppath/")
255 or &error ("Couldn't wipe out $temppath: $!\n");
257 mkdir ($temppath, 0777) or error ("Cannot mkdir $temppath: $!\n");
260 # This is used by POSIX systems
261 $makeENV{TMPDIR} = $temppath;
263 # These are used on Windows
264 $makeENV{TMP} = $temppath;
265 $makeENV{TEMP} = $temppath;
267 # Replace the environment with the new one
270 $workpath = "$cwdslash$workdir";
271 $scriptpath = "$cwdslash$scriptdir";
273 &set_more_defaults; # suite-defined
277 if ($osname eq 'VMS' && $cwdslash eq "") {
278 # Porting this script to VMS revealed a small bug in opendir() not
279 # handling search lists correctly when the directory only exists in
280 # one of the logical_devices. Need to find the first directory in
281 # the search list, as that is where things will be written to.
282 my @dirs = split('/', $cwdpath);
284 my $logical_device = $ENV{$dirs[1]};
285 if ($logical_device =~ /([A-Za-z0-9_]+):(:?.+:)+/) {
286 # A search list was found. Grab the first logical device
287 # and use it instead of the search list.
289 my $lcl_pwd = join('/', @dirs);
290 $workpath = $lcl_pwd . '/' . $workdir
295 print "Clearing $workpath...\n";
296 &remove_directory_tree("$workpath/")
297 or &error ("Couldn't wipe out $workpath: $!\n");
299 mkdir ($workpath, 0777) or &error ("Cannot mkdir $workpath: $!\n");
302 if (!-d $scriptpath) {
303 &error ("Failed to find $scriptpath containing perl test scripts.\n");
307 print "Making work dirs...\n";
308 foreach $test (@TESTS) {
309 if ($test =~ /^([^\/]+)\//) {
311 push (@rmdirs, $dir);
313 or mkdir ("$workpath/$dir", 0777)
314 or &error ("Couldn't mkdir $workpath/$dir: $!\n");
318 print "Finding tests...\n";
319 opendir (SCRIPTDIR, $scriptpath)
320 or &error ("Couldn't opendir $scriptpath: $!\n");
321 @dirs = grep (!/^(\..*|CVS|RCS)$/, readdir (SCRIPTDIR) );
322 closedir (SCRIPTDIR);
323 foreach my $dir (@dirs) {
324 next if ($dir =~ /^(\..*|CVS|RCS)$/ || ! -d "$scriptpath/$dir");
325 push (@rmdirs, $dir);
326 # VMS can have overlaid file systems, so directories may repeat.
327 next if -d "$workpath/$dir";
328 mkdir ("$workpath/$dir", 0777)
329 or &error ("Couldn't mkdir $workpath/$dir: $!\n");
330 opendir (SCRIPTDIR, "$scriptpath/$dir")
331 or &error ("Couldn't opendir $scriptpath/$dir: $!\n");
332 @files = grep (!/^(\..*|CVS|RCS|.*~)$/, readdir (SCRIPTDIR) );
333 closedir (SCRIPTDIR);
334 foreach my $test (@files) {
336 push (@TESTS, "$dir/$test");
342 &error ("\nNo tests in $scriptpath, and none were specified.\n");
349 foreach my $dir (@rmdirs) {
350 rmdir ("$workpath/$dir");
357 $categories_failed = $categories_run - $categories_passed;
358 $total_tests_failed = $total_tests_run - $total_tests_passed;
360 if ($total_tests_failed) {
361 print "\n$total_tests_failed Test";
362 print "s" unless $total_tests_failed == 1;
363 print " in $categories_failed Categor";
364 print ($categories_failed == 1 ? "y" : "ies");
365 print " Failed (See .$diffext* files in $workdir dir for details) :-(\n\n";
369 print "\n$total_tests_passed Test";
370 print "s" unless $total_tests_passed == 1;
371 print " in $categories_passed Categor";
372 print ($categories_passed == 1 ? "y" : "ies");
373 print " Complete ... No Failures :-)\n\n";
379 # Set up an initial value. In perl5 we can do it the easy way.
380 $osname = defined($^O) ? $^O : '';
384 # find the type of the port. We do this up front to have a single
385 # point of change if it needs to be tweaked.
387 # This is probably not specific enough.
389 if ($osname =~ /MSWin32/i || $osname =~ /Windows/i
390 || $osname =~ /MINGW32/i || $osname =~ /CYGWIN_NT/i) {
393 # Bleah, the osname is so variable on DOS. This kind of bites.
394 # Well, as far as I can tell if we check for some text at the
395 # beginning of the line with either no spaces or a single space, then
396 # a D, then either "OS", "os", or "ev" and a space. That should
397 # match and be pretty specific.
398 elsif ($osname =~ /^([^ ]*|[^ ]* [^ ]*)D(OS|os|ev) /) {
402 elsif ($osname =~ m%OS/2%) {
406 # VMS has a GNV Unix mode or a DCL mode.
407 # The SHELL environment variable should not be defined in VMS-DCL mode.
408 elsif ($osname eq 'VMS' && !defined $ENV{"SHELL"}) {
409 $port_type = 'VMS-DCL';
411 # Everything else, right now, is UNIX. Note that we should integrate
412 # the VOS support into this as well and get rid of $vos; we'll do
418 if ($osname eq 'VMS') {
422 # Find a path to Perl
424 # See if the filesystem supports long file names with multiple
426 $short_filenames = 0;
427 (open (TOUCHFD, "> fancy.file.name") and close (TOUCHFD))
428 or $short_filenames = 1;
429 unlink ("fancy.file.name") or $short_filenames = 1;
431 if (! $short_filenames) {
432 # Thanks go to meyering@cs.utexas.edu (Jim Meyering) for suggesting a
433 # better way of doing this. (We used to test for existence of a /mnt
434 # dir, but that apparently fails on an SGI Indigo (whatever that is).)
435 # Because perl on VOS translates /'s to >'s, we need to test for
436 # VOSness rather than testing for Unixness (ie, try > instead of /).
438 mkdir (".ostest", 0777) or &error ("Couldn't create .ostest: $!\n", 1);
439 open (TOUCHFD, "> .ostest>ick") and close (TOUCHFD);
440 chdir (".ostest") or &error ("Couldn't chdir to .ostest: $!\n", 1);
443 if (! $short_filenames && -f "ick") {
448 } elsif ($osname eq '') {
449 # the following is regrettably gnarly, but it seems to be the only way
450 # to not get ugly error messages if uname can't be found.
451 # Hmmm, BSD/OS 2.0's uname -a is excessively verbose. Let's try it
452 # with switches first.
453 eval "chop (\$osname = `sh -c 'uname -nmsr 2>&1'`)";
454 if ($osname =~ /not found/i) {
455 $osname = "(something posixy with no uname)";
457 } elsif ($@ ne "" || $?) {
458 eval "chop (\$osname = `sh -c 'uname -a 2>&1'`)";
459 if ($@ ne "" || $?) {
460 $osname = "(something posixy)";
465 if (! $short_filenames) {
466 chdir ("..") or &error ("Couldn't chdir to ..: $!\n", 1);
467 unlink (".ostest>ick");
468 rmdir (".ostest") or &error ("Couldn't rmdir .ostest: $!\n", 1);
472 sub parse_command_line
476 # use @ARGV if no args were passed in
482 # look at each option; if we don't recognize it, maybe the suite-specific
483 # command line parsing code will...
486 $option = shift @argv;
487 if ($option =~ /^-usage$/i) {
491 if ($option =~ /^-(h|help)$/i) {
496 if ($option =~ /^-debug$/i) {
497 print "\nDEBUG ON\n";
500 } elsif ($option =~ /^-profile$/i) {
503 } elsif ($option =~ /^-verbose$/i) {
506 } elsif ($option =~ /^-detail$/i) {
510 } elsif ($option =~ /^-keep$/i) {
513 } elsif (&valid_option($option)) {
514 # The suite-defined subroutine takes care of the option
516 } elsif ($option =~ /^-/) {
517 print "Invalid option: $option\n";
521 } else { # must be the name of a test
522 $option =~ s/\.pl$//;
523 push(@TESTS,$option);
535 if ($newnum > $num) {
545 my ($width, $string) = @_;
547 if (length ($string)) {
548 my $pad = " " x ( ($width - length ($string) + 1) / 2);
555 # $testee is suite-defined
556 my $info = "Running tests for $testee on $osname";
557 my $len = &max (length($info), length($testee_version), 77) + 2;
558 my $line = ("-" x $len) . "\n";
560 &print_centered ($len, $line);
561 &print_centered ($len, $info."\n");
562 &print_centered ($len, $testee_version);
563 &print_centered ($len, $line);
569 # Make sure we always run the tests from the current directory
570 unshift(@INC, cwd());
574 # Make a copy of STDIN so we can reset it
575 open(INCOPY, "<&STDIN");
577 # Leave enough space in the extensions to append a number, even
578 # though it needs to fit into 8+3 limits.
579 if ($short_filenames) {
594 # $testname is published
595 foreach $testname (sort @TESTS) {
596 # Skip duplicates on VMS caused by logical name search lists.
597 next if $testname eq $lasttest;
599 $lasttest = $testname;
600 $suite_passed = 1; # reset by test on failure
601 $num_of_logfiles = 0;
602 $num_of_tmpfiles = 0;
605 $old_makefile = undef;
606 $testname =~ s/^$scriptpath$pathsep//;
607 $perl_testname = "$scriptpath$pathsep$testname";
608 $testname =~ s/(\.pl|\.perl)$//;
609 $testpath = "$workpath$pathsep$testname";
610 $extext = '_' if $^O eq 'VMS';
611 $log_filename = "$testpath.$logext";
612 $diff_filename = "$testpath.$diffext";
613 $base_filename = "$testpath.$baseext";
614 $run_filename = "$testpath.$runext";
615 $tmp_filename = "$testpath.$tmpfilesuffix";
617 -f $perl_testname or die "Invalid test: $testname\n\n";
621 $output = "........................................................ ";
623 substr($output, 0, length($testname)) = "$testname ";
631 $code = do $perl_testname;
633 # Reset STDIN from the copy in case it was changed
634 open(STDIN, "<&INCOPY");
637 $total_tests_run += $tests_run;
638 $total_tests_passed += $tests_passed;
641 if (!defined($code)) {
642 # Failed to parse or called die
644 warn "\n*** Test died ($testname): $@\n";
646 warn "\n*** Couldn't parse $perl_testname\n";
648 $status = "FAILED ($tests_passed/$tests_run passed)";
650 } elsif ($code == -1) {
651 # Skipped... not supported
655 } elsif ($code != 1) {
656 # Bad result... this shouldn't really happen. Usually means that
657 # the suite forgot to end with "1;".
658 warn "\n*** Test returned $code\n";
659 $status = "FAILED ($tests_passed/$tests_run passed)";
661 } elsif ($tests_run == 0) {
663 $status = "FAILED (no tests found!)";
665 } elsif ($tests_run > $tests_passed) {
667 $status = "FAILED ($tests_passed/$tests_run passed)";
671 ++$categories_passed;
672 $status = "ok ($tests_passed passed)";
675 for ($i = $num_of_tmpfiles; $i; $i--) {
676 rmfiles($tmp_filename . num_suffix($i));
678 for ($i = $num_of_logfiles ? $num_of_logfiles : 1; $i; $i--) {
679 rmfiles($log_filename . num_suffix($i));
680 rmfiles($base_filename . num_suffix($i));
684 # If the verbose option has been specified, then a short description
685 # of each test is printed before displaying the results of each test
686 # describing WHAT is being tested.
690 print "\nWHAT IS BEING TESTED\n";
691 print "--------------------";
693 print "\n\n$description\n\n";
696 # If the detail option has been specified, then the details of HOW
697 # the test is testing what it says it is testing in the verbose output
698 # will be displayed here before the results of the test are displayed.
701 print "\nHOW IT IS TESTED\n";
702 print "----------------";
703 print "\n\n$details\n\n";
712 # If the keep flag is not set, this subroutine deletes all filenames that
720 return (unlink @files);
726 sub print_standard_usage
728 my ($plname, @moreusage) = @_;
730 print "usage:\t$plname [testname] [-verbose] [-detail] [-keep]\n";
731 print "\t\t\t[-profile] [-usage] [-help] [-debug]\n";
732 foreach (@moreusage) {
737 sub print_standard_help
742 my $line = "Test Driver For $testee";
744 $line = "=" x length ($line);
750 . "${t}You may, if you wish, run only ONE test if you know the name\n"
751 . "${t}of that test and specify this name anywhere on the command\n"
752 . "${t}line. Otherwise ALL existing tests in the scripts directory\n"
753 . "${t}will be run.\n"
755 . "${t}If this option is given, a description of every test is\n"
756 . "${t}displayed before the test is run. (Not all tests may have\n"
757 . "${t}descriptions at this time)\n"
759 . "${t}If this option is given, a detailed description of every\n"
760 . "${t}test is displayed before the test is run. (Not all tests\n"
761 . "${t}have descriptions at this time)\n"
763 . "${t}If this option is given, then the profile file\n"
764 . "${t}is added to other profiles every time $testee is run.\n"
765 . "${t}This option only works on VOS at this time.\n"
767 . "${t}You may give this option if you DO NOT want ANY\n"
768 . "${t}of the files generated by the tests to be deleted. \n"
769 . "${t}Without this option, all files generated by the test will\n"
770 . "${t}be deleted IF THE TEST PASSES.\n"
772 . "${t}Use this option if you would like to see all of the system\n"
773 . "${t}calls issued and their return status while running the tests\n"
774 . "${t}This can be helpful if you're having a problem adding a test\n"
775 . "${t}to the suite, or if the test fails!\n";
777 foreach $line (@morehelp) {
779 if (substr ($tline, 0, 1) eq "\t") {
780 substr ($tline, 0, 1) = $t;
786 #######################################################################
787 ########### Generic Test Driver Subroutines ###########
788 #######################################################################
792 my $depth = defined ($_[0]) ? $_[0] : 1;
793 my ($pkg, $filename, $linenum) = caller ($depth + 1);
794 return "$filename: $linenum";
800 my $caller = &get_caller (1);
802 if (defined ($_[1])) {
803 $caller = &get_caller ($_[1] + 1) . " -> $caller";
806 die "$caller: $message";
809 my %old_tempfiles = ();
813 my ($answer, $logfile) = @_;
814 my ($slurp, $answer_matched, $extra) = ('', 0, 0);
819 foreach my $file (glob(File::Spec->catfile($temppath, "*"))) {
820 if (!exists $old_tempfiles{$file}) {
822 $old_tempfiles{$file} = 1;
826 open (LOGFILE, '>>', $logfile) or die "Cannot open log file $logfile: $!\n";
827 print LOGFILE "Leftover temporary files: @tf\n";
832 if (! defined $answer) {
833 print "Ignoring output ........ " if $debug;
836 print "Comparing output ........ " if $debug;
838 $slurp = &read_file_into_string ($logfile);
840 # For make, get rid of any time skew error before comparing--too bad this
841 # has to go into the "generic" driver code :-/
842 $slurp =~ s/^.*modification time .*in the future.*\n//gm;
843 $slurp =~ s/^.*Clock skew detected.*\n//gm;
845 if ($slurp eq $answer) {
848 # See if it is a slash or CRLF problem
849 my ($answer_mod, $slurp_mod) = ($answer, $slurp);
851 $answer_mod =~ tr,\\,/,;
852 $answer_mod =~ s,\r\n,\n,gs;
854 $slurp_mod =~ tr,\\,/,;
855 $slurp_mod =~ s,\r\n,\n,gs;
857 $answer_matched = ($slurp_mod eq $answer_mod);
859 if (!$answer_matched && $^O eq 'VMS') {
861 # VMS has extra blank lines in output sometimes.
863 if (!$answer_matched) {
864 $slurp_mod =~ s/\n\n+/\n/gm;
865 $slurp_mod =~ s/\A\n+//g;
866 $answer_matched = ($slurp_mod eq $answer_mod);
869 # VMS adding a "Waiting for unfinished jobs..."
870 # Remove it for now to see what else is going on.
871 if (!$answer_matched) {
872 $slurp_mod =~ s/^.+\*\*\* Waiting for unfinished jobs.+$//m;
873 $slurp_mod =~ s/\n\n/\n/gm;
874 $slurp_mod =~ s/^\n+//gm;
875 $answer_matched = ($slurp_mod eq $answer_mod);
878 # VMS wants target device to exist or generates an error,
879 # Some test tagets look like VMS devices and trip this.
880 if (!$answer_matched) {
881 $slurp_mod =~ s/^.+\: no such device or address.*$//gim;
882 $slurp_mod =~ s/\n\n/\n/gm;
883 $slurp_mod =~ s/^\n+//gm;
884 $answer_matched = ($slurp_mod eq $answer_mod);
887 # VMS error message has a different case
888 if (!$answer_matched) {
889 $slurp_mod =~ s/no such file /No such file /gm;
890 $answer_matched = ($slurp_mod eq $answer_mod);
893 # VMS is putting comas instead of spaces in output
894 if (!$answer_matched) {
895 $slurp_mod =~ s/,/ /gm;
896 $answer_matched = ($slurp_mod eq $answer_mod);
899 # VMS Is sometimes adding extra leading spaces to output?
900 if (!$answer_matched) {
901 my $slurp_mod = $slurp_mod;
902 $slurp_mod =~ s/^ +//gm;
903 $answer_matched = ($slurp_mod eq $answer_mod);
906 # VMS port not handling POSIX encoded child status
907 # Translate error case it for now.
908 if (!$answer_matched) {
909 $slurp_mod =~ s/0x1035a00a/1/gim;
910 $answer_matched = 1 if $slurp_mod =~ /\Q$answer_mod\E/i;
913 if (!$answer_matched) {
914 $slurp_mod =~ s/0x1035a012/2/gim;
915 $answer_matched = ($slurp_mod eq $answer_mod);
918 # Tests are using a UNIX null command, temp hack
919 # until this can be handled by the VMS port.
921 if (!$answer_matched) {
922 $slurp_mod =~ s/^.+DCL-W-NOCOMD.*$//gim;
923 $slurp_mod =~ s/\n\n+/\n/gm;
924 $slurp_mod =~ s/^\n+//gm;
925 $answer_matched = ($slurp_mod eq $answer_mod);
927 # Tests are using exit 0;
928 # this generates a warning that should stop the make, but does not
929 if (!$answer_matched) {
930 $slurp_mod =~ s/^.+NONAME-W-NOMSG.*$//gim;
931 $slurp_mod =~ s/\n\n+/\n/gm;
932 $slurp_mod =~ s/^\n+//gm;
933 $answer_matched = ($slurp_mod eq $answer_mod);
936 # VMS is sometimes adding single quotes to output?
937 if (!$answer_matched) {
938 my $noq_slurp_mod = $slurp_mod;
939 $noq_slurp_mod =~ s/\'//gm;
940 $answer_matched = ($noq_slurp_mod eq $answer_mod);
942 # And missing an extra space in output
943 if (!$answer_matched) {
944 $noq_answer_mod = $answer_mod;
945 $noq_answer_mod =~ s/\h\h+/ /gm;
946 $answer_matched = ($noq_slurp_mod eq $noq_answer_mod);
949 # VMS adding ; to end of some lines.
950 if (!$answer_matched) {
951 $noq_slurp_mod =~ s/;\n/\n/gm;
952 $answer_matched = ($noq_slurp_mod eq $noq_answer_mod);
955 # VMS adding trailing space to end of some quoted lines.
956 if (!$answer_matched) {
957 $noq_slurp_mod =~ s/\h+\n/\n/gm;
958 $answer_matched = ($noq_slurp_mod eq $noq_answer_mod);
961 # And VMS missing leading blank line
962 if (!$answer_matched) {
963 $noq_answer_mod =~ s/\A\n//g;
964 $answer_matched = ($noq_slurp_mod eq $noq_answer_mod);
967 # Unix double quotes showing up as single quotes on VMS.
968 if (!$answer_matched) {
969 $noq_answer_mod =~ s/\"//g;
970 $answer_matched = ($noq_slurp_mod eq $noq_answer_mod);
975 # If it still doesn't match, see if the answer might be a regex.
976 if (!$answer_matched && $answer =~ m,^/(.+)/$,) {
977 $answer_matched = ($slurp =~ /$1/);
978 if (!$answer_matched && $answer_mod =~ m,^/(.+)/$,) {
979 $answer_matched = ($slurp_mod =~ /$1/);
985 if ($keep || ! $answer_matched) {
986 &create_file(&get_basefile, $answer);
987 &create_file(&get_runfile, $command_string);
990 if ($answer_matched && $test_passed && !$extra) {
991 print "ok\n" if $debug;
996 if (! $answer_matched) {
997 print "DIFFERENT OUTPUT\n" if $debug;
999 print "\nCreating Difference File ...\n" if $debug;
1001 # Create the difference file
1002 my $base = get_basefile();
1004 my $command = "$diff_name -c $base $logfile";
1005 &run_command_with_output(get_difffile(), $command);
1007 create_file(get_difffile(), "Log file $logfile differs from base file $base\n");
1014 sub read_file_into_string
1016 my ($filename) = @_;
1020 open (RFISFILE, '<', $filename) or return "";
1021 my $slurp = <RFISFILE>;
1032 sub attach_default_output
1034 my ($filename) = @_;
1038 my $code = system "++attach_default_output_hack $filename";
1039 $code == -2 or &error ("ado death\n", 1);
1044 open($dup, '>&', STDOUT) or error("ado: $! duping STDOUT\n", 1);
1045 push @OUTSTACK, $dup;
1048 open($dup, '>&', STDERR) or error("ado: $! duping STDERR\n", 1);
1049 push @ERRSTACK, $dup;
1051 open(STDOUT, '>', $filename) or error("ado: $filename: $!\n", 1);
1052 open(STDERR, ">&STDOUT") or error("ado: $filename: $!\n", 1);
1055 # close the current stdout/stderr, and restore the previous ones from
1058 sub detach_default_output
1062 my $code = system "++detach_default_output_hack";
1063 $code == -2 or &error ("ddoh death\n", 1);
1067 @OUTSTACK or error("default output stack has flown under!\n", 1);
1070 close(STDERR) unless $^O eq 'VMS';
1073 open (STDOUT, '>&', pop @OUTSTACK) or error("ddo: $! duping STDOUT\n", 1);
1074 open (STDERR, '>&', pop @ERRSTACK) or error("ddo: $! duping STDERR\n", 1);
1077 sub _run_with_timeout
1081 #local $SIG{ALRM} = sub {
1082 # my $e = $ERRSTACK[0];
1083 # print $e "\nTest timed out after $test_timeout seconds\n";
1086 #alarm $test_timeout;
1089 my $severity = ${^CHILD_ERROR_NATIVE} & 7;
1091 if (($severity & 1) == 0) {
1095 # Get the vms status.
1096 my $vms_code = ${^CHILD_ERROR_NATIVE};
1098 # Remove the print status bit
1099 $vms_code &= ~0x10000000;
1101 # Posix code translation.
1102 if (($vms_code & 0xFFFFF000) == 0x35a000) {
1103 $code = (($vms_code & 0xFFF) >> 3) * 256;
1106 } elsif ($port_type eq 'W32') {
1107 my $pid = system(1, @_);
1108 $pid > 0 or die "Cannot execute $_[0]\n";
1109 local $SIG{ALRM} = sub {
1110 my $e = $ERRSTACK[0];
1111 print $e "\nTest timed out after $test_timeout seconds\n";
1115 alarm $test_timeout;
1116 my $r = waitpid($pid, 0);
1118 $r == -1 and die "No such pid: $pid\n";
1119 # This shouldn't happen since we wait forever or timeout via SIGALRM
1120 $r == 0 and die "No process exited.\n";
1126 exec(@_) or die "exec: Cannot execute $_[0]: $!\n";
1128 local $SIG{ALRM} = sub {
1129 my $e = $ERRSTACK[0];
1130 print $e "\nTest timed out after $test_timeout seconds\n";
1131 # Resend the alarm to our process group to kill the children.
1132 $SIG{ALRM} = 'IGNORE';
1136 alarm $test_timeout;
1137 my $r = waitpid($pid, 0);
1139 $r == -1 and die "No such pid: $pid\n";
1140 # This shouldn't happen since we wait forever or timeout via SIGALRM
1141 $r == 0 and die "No process exited.\n";
1148 # This runs a command without any debugging info.
1151 my $orig = $SIG{ALRM};
1152 my $code = eval { _run_with_timeout(@_); };
1155 # Reset then environment so that it's clean for the next test.
1159 # The eval failed. If it wasn't SIGALRM then die.
1160 $@ eq "timeout\n" or die "Command failed: $@";
1167 # run one command (passed as a list of arg 0 - n), returning 0 on success
1168 # and nonzero on failure.
1172 print "\nrun_command: @_\n" if $debug;
1173 my $code = _run_command(@_);
1174 print "run_command returned $code.\n" if $debug;
1175 print "vms status = ${^CHILD_ERROR_NATIVE}\n" if $debug and $^O eq 'VMS';
1179 # run one command (passed as a list of arg 0 - n, with arg 0 being the
1180 # second arg to this routine), returning 0 on success and non-zero on failure.
1181 # The first arg to this routine is a filename to connect to the stdout
1182 # & stderr of the child process.
1184 sub run_command_with_output
1186 my $filename = shift;
1188 print "\nrun_command_with_output($filename,$runname): @_\n" if $debug;
1189 &attach_default_output ($filename);
1190 my $code = eval { _run_command(@_) };
1192 &detach_default_output;
1196 print "run_command_with_output returned $code.\n" if $debug;
1197 print "vms status = ${^CHILD_ERROR_NATIVE}\n" if $debug and $^O eq 'VMS';
1201 # performs the equivalent of an "rm -rf" on the first argument. Like
1202 # rm, if the path ends in /, leaves the (now empty) directory; otherwise
1205 sub remove_directory_tree
1207 my ($targetdir) = @_;
1210 my $ch = substr ($targetdir, length ($targetdir) - 1);
1211 if ($ch eq "/" || $ch eq $pathsep) {
1212 $targetdir = substr ($targetdir, 0, length ($targetdir) - 1);
1216 -e $targetdir or return 1;
1218 &remove_directory_tree_inner ("RDT00", $targetdir) or return 0;
1219 if ($nuketop && !rmdir ($targetdir)) {
1220 print "Cannot remove $targetdir: $!\n";
1227 sub remove_directory_tree_inner
1229 my ($dirhandle, $targetdir) = @_;
1231 opendir ($dirhandle, $targetdir) or return 0;
1232 my $subdirhandle = $dirhandle;
1234 while (my $object = readdir ($dirhandle)) {
1235 $object =~ /^(\.\.?|CVS|RCS)$/ and next;
1236 $object = "$targetdir$pathsep$object";
1239 if (-d _ && &remove_directory_tree_inner ($subdirhandle, $object)) {
1240 if (!rmdir($object)) {
1241 print "Cannot remove $object: $!\n";
1246 if (!unlink $object) {
1247 print "Cannot unlink $object: $!\n";
1251 # VMS can have multiple versions of a file.
1252 1 while unlink $object;
1256 closedir ($dirhandle);
1260 # We used to use this behavior for this function:
1264 # my (@filenames) = @_;
1267 # foreach my $file (@filenames) {
1268 # utime ($now, $now, $file)
1269 # or (open (TOUCHFD, ">> $file") and close (TOUCHFD))
1270 # or &error ("Couldn't touch $file: $!\n", 1);
1275 # But this behaves badly on networked filesystems where the time is
1276 # skewed, because it sets the time of the file based on the _local_
1277 # host. Normally when you modify a file, it's the _remote_ host that
1278 # determines the modtime, based on _its_ clock. So, instead, now we open
1279 # the file and write something into it to force the remote host to set
1280 # the modtime correctly according to its clock.
1285 foreach my $file (@_) {
1286 (open(T, '>>', $file) and print(T "\n") and close(T))
1287 or &error("Couldn't touch $file: $!\n", 1);
1293 # Touch with a time offset. To DTRT, call touch() then use stat() to get the
1294 # access/mod time for each file and apply the offset.
1302 foreach my $f (@_) {
1304 utime($s[8]+$off, $s[9]+$off, $f);
1310 # open a file, write some stuff to it, and close it.
1314 my ($filename, @lines) = @_;
1316 open (CF, "> $filename") or &error ("Couldn't open $filename: $!\n", 1);
1317 foreach $line (@lines) {
1323 # create a directory tree described by an associative array, wherein each
1324 # key is a relative pathname (using slashes) and its associated value is
1326 # DIR indicates a directory
1327 # FILE:contents indicates a file, which should contain contents +\n
1328 # LINK:target indicates a symlink, pointing to $basedir/target
1329 # The first argument is the dir under which the structure will be created
1330 # (the dir will be made and/or cleaned if necessary); the second argument
1331 # is the associative array.
1335 my ($basedir, %dirtree) = @_;
1337 &remove_directory_tree ("$basedir");
1338 mkdir ($basedir, 0777) or &error ("Couldn't mkdir $basedir: $!\n", 1);
1340 foreach my $path (sort keys (%dirtree)) {
1341 if ($dirtree {$path} =~ /^DIR$/) {
1342 mkdir ("$basedir/$path", 0777)
1343 or &error ("Couldn't mkdir $basedir/$path: $!\n", 1);
1345 } elsif ($dirtree {$path} =~ /^FILE:(.*)$/) {
1346 &create_file ("$basedir/$path", $1 . "\n");
1348 } elsif ($dirtree {$path} =~ /^LINK:(.*)$/) {
1349 symlink ("$basedir/$1", "$basedir/$path")
1350 or &error ("Couldn't symlink $basedir/$path -> $basedir/$1: $!\n", 1);
1353 &error ("Bogus dirtree type: \"$dirtree{$path}\"\n", 1);
1356 if ($just_setup_tree) {
1357 die "Tree is setup...\n";
1361 # compare a directory tree with an associative array in the format used
1362 # by create_dir_tree, above.
1363 # The first argument is the dir under which the structure should be found;
1364 # the second argument is the associative array.
1366 sub compare_dir_tree
1368 my ($basedir, %dirtree) = @_;
1371 opendir (DIR, $basedir) or &error ("Couldn't open $basedir: $!\n", 1);
1372 my @allfiles = grep (!/^(\.\.?|CVS|RCS)$/, readdir (DIR) );
1375 print "dirtree: (%dirtree)\n$basedir: (@allfiles)\n";
1378 foreach my $path (sort keys (%dirtree))
1381 print "Checking $path ($dirtree{$path}).\n";
1385 foreach my $i (0 .. $#allfiles) {
1386 if ($allfiles[$i] eq $path) {
1387 splice (@allfiles, $i, 1); # delete it
1389 print " Zapped $path; files now (@allfiles).\n";
1391 lstat ("$basedir/$path");
1398 print "compare_dir_tree: $path does not exist.\n";
1403 if ($dirtree {$path} =~ /^DIR$/) {
1404 if (-d _ && opendir (DIR, "$basedir/$path") ) {
1405 my @files = readdir (DIR);
1407 @files = grep (!/^(\.\.?|CVS|RCS)$/ && ($_ = "$path/$_"), @files);
1408 push (@allfiles, @files);
1411 print " Read in $path; new files (@files).\n";
1415 print "compare_dir_tree: $path is not a dir.\n";
1419 } elsif ($dirtree {$path} =~ /^FILE:(.*)$/) {
1420 if (-l _ || !-f _) {
1421 print "compare_dir_tree: $path is not a file.\n";
1427 my $contents = &read_file_into_string ("$basedir/$path");
1428 if ($contents ne "$1\n") {
1429 print "compare_dir_tree: $path contains wrong stuff."
1430 . " Is:\n$contentsShould be:\n$1\n";
1435 } elsif ($dirtree {$path} =~ /^LINK:(.*)$/) {
1438 print "compare_dir_tree: $path is not a link.\n";
1443 my $contents = readlink ("$basedir/$path");
1444 $contents =~ tr/>/\//;
1445 my $fulltarget = "$basedir/$target";
1446 $fulltarget =~ tr/>/\//;
1447 if (!($contents =~ /$fulltarget$/)) {
1449 $target = $fulltarget;
1451 print "compare_dir_tree: $path should be link to $target, "
1452 . "not $contents.\n";
1457 &error ("Bogus dirtree type: \"$dirtree{$path}\"\n", 1);
1462 print "leftovers: (@allfiles).\n";
1465 foreach my $file (@allfiles) {
1466 print "compare_dir_tree: $file should not exist.\n";
1473 # this subroutine generates the numeric suffix used to keep tmp filenames,
1474 # log filenames, etc., unique. If the number passed in is 1, then a null
1475 # string is returned; otherwise, we return ".n", where n + 1 is the number
1482 return "$extext$num";
1488 # This subroutine returns a log filename with a number appended to
1489 # the end corresponding to how many logfiles have been created in the
1490 # current running test. An optional parameter may be passed (0 or 1).
1491 # If a 1 is passed, then it does NOT increment the logfile counter
1492 # and returns the name of the latest logfile. If either no parameter
1493 # is passed at all or a 0 is passed, then the logfile counter is
1494 # incremented and the new name is returned.
1498 my ($no_increment) = @_;
1500 $num_of_logfiles += !$no_increment;
1502 return ($log_filename . &num_suffix ($num_of_logfiles));
1505 # This subroutine returns a base (answer) filename with a number
1506 # appended to the end corresponding to how many logfiles (and thus
1507 # base files) have been created in the current running test.
1508 # NO PARAMETERS ARE PASSED TO THIS SUBROUTINE.
1512 return ($base_filename . &num_suffix ($num_of_logfiles));
1515 # This subroutine returns a difference filename with a number appended
1516 # to the end corresponding to how many logfiles (and thus diff files)
1517 # have been created in the current running test.
1521 return ($diff_filename . &num_suffix ($num_of_logfiles));
1524 # This subroutine returns a command filename with a number appended
1525 # to the end corresponding to how many logfiles (and thus command files)
1526 # have been created in the current running test.
1530 return ($run_filename . &num_suffix ($num_of_logfiles));
1533 # just like logfile, only a generic tmp filename for use by the test.
1534 # they are automatically cleaned up unless -keep was used, or the test fails.
1535 # Pass an argument of 1 to return the same filename as the previous call.
1539 my ($no_increment) = @_;
1541 $num_of_tmpfiles += !$no_increment;
1543 return ($tmp_filename . &num_suffix ($num_of_tmpfiles));