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.
7
# End of modification history
9
# Test driver routines used by a number of test suites, including
10
# those for SCS, make, roll_dir, and scan_deps (?).
12
# this routine controls the whole mess; each test suite sets up a few
13
# variables and then calls &toplevel, which does all the real work.
17
# Get a clean environment
21
# Pull in benign variables from the user's environment
23
foreach (# UNIX-specific things
24
'TZ', 'LANG', 'TMPDIR', 'HOME', 'USER', 'LOGNAME', 'PATH',
27
# Windows NT-specific stuff
29
# DJGPP-specific stuff
30
'DJDIR', 'DJGPP', 'SHELL', 'COMSPEC', 'HOSTNAME', 'LFN',
31
'FNCASE', '387', 'EMU387', 'GROUP'
33
$makeENV{$_} = $ENV{$_} if $ENV{$_};
36
# Replace the environment with the new one
40
# We used to say "%ENV = ();" but this doesn't work in Perl 5.000
41
# through Perl 5.004. It was fixed in Perl 5.004_01, but we don't
42
# want to require that here, so just delete each one individually.
44
foreach $v (keys %ENV) {
50
$| = 1; # unbuffered output
52
$debug = 0; # debug flag
53
$profile = 0; # profiling flag
54
$verbose = 0; # verbose mode flag
55
$detail = 0; # detailed verbosity
56
$keep = 0; # keep temp files around
57
$workdir = "work"; # The directory where the test will start running
58
$scriptdir = "scripts"; # The directory where we find the test scripts
59
$tmpfilesuffix = "t"; # the suffix used on tmpfiles
60
$default_output_stack_level = 0; # used by attach_default_output, etc.
61
$default_input_stack_level = 0; # used by attach_default_input, etc.
62
$cwd = "."; # don't we wish we knew
63
$cwdslash = ""; # $cwd . $pathsep, but "" rather than "./"
65
&get_osname; # sets $osname, $vos, $pathsep, and $fancy_file_names
67
&set_defaults; # suite-defined
69
&parse_command_line (@ARGV);
71
print "OS name = `$osname'\n" if $debug;
73
$workpath = "$cwdslash$workdir";
74
$scriptpath = "$cwdslash$scriptdir";
76
&set_more_defaults; # suite-defined
82
print "Clearing $workpath...\n";
83
&remove_directory_tree("$workpath/")
84
|| &error ("Couldn't wipe out $workpath\n");
88
mkdir ($workpath, 0777) || &error ("Couldn't mkdir $workpath: $!\n");
93
&error ("Failed to find $scriptpath containing perl test scripts.\n");
98
print "Making work dirs...\n";
99
foreach $test (@TESTS)
101
if ($test =~ /^([^\/]+)\//)
104
push (@rmdirs, $dir);
106
|| mkdir ("$workpath/$dir", 0777)
107
|| &error ("Couldn't mkdir $workpath/$dir: $!\n");
113
print "Finding tests...\n";
114
opendir (SCRIPTDIR, $scriptpath)
115
|| &error ("Couldn't opendir $scriptpath: $!\n");
116
@dirs = readdir (SCRIPTDIR);
117
closedir (SCRIPTDIR);
120
next if ! -d "$scriptpath/$dir" || $dir =~ /^\.\.?$/ || $dir eq 'CVS';
121
push (@rmdirs, $dir);
122
mkdir ("$workpath/$dir", 0777)
123
|| &error ("Couldn't mkdir $workpath/$dir: $!\n");
124
opendir (SCRIPTDIR, "$scriptpath/$dir")
125
|| &error ("Couldn't opendir $scriptpath/$dir: $!\n");
126
@files = readdir (SCRIPTDIR);
127
closedir (SCRIPTDIR);
128
foreach $test (@files)
130
next if $test =~ /^\.\.?$/ || $test =~ /~$/ || $test eq 'CVS';
131
push (@TESTS, "$dir/$test");
138
&error ("\nNo tests in $scriptpath, and none were specified.\n");
145
foreach $dir (@rmdirs)
147
rmdir ("$workpath/$dir");
154
print "\n$num_failed Test";
155
print "s" unless $num_failed == 1;
156
print " Failed (See .$diffext files in $workdir dir for details) :-(\n\n";
161
print "\n$counter Test";
162
print "s" unless $counter == 1;
163
print " Complete ... No Failures :-)\n\n";
170
# Set up an initial value. In perl5 we can do it the easy way.
172
$osname = defined($^O) ? $^O : '';
174
# See if the filesystem supports long file names with multiple
176
$fancy_file_names = 1;
177
(open (TOUCHFD, "> fancy.file.name") && close (TOUCHFD))
178
|| ($fancy_file_names = 0);
179
unlink ("fancy.file.name") || ($fancy_file_names = 0);
181
if ($fancy_file_names) {
182
# Thanks go to meyering@cs.utexas.edu (Jim Meyering) for suggesting a
183
# better way of doing this. (We used to test for existence of a /mnt
184
# dir, but that apparently fails on an SGI Indigo (whatever that is).)
185
# Because perl on VOS translates /'s to >'s, we need to test for
186
# VOSness rather than testing for Unixness (ie, try > instead of /).
188
mkdir (".ostest", 0777) || &error ("Couldn't create .ostest: $!\n", 1);
189
open (TOUCHFD, "> .ostest>ick") && close (TOUCHFD);
190
chdir (".ostest") || &error ("Couldn't chdir to .ostest: $!\n", 1);
193
if ($fancy_file_names && -f "ick")
201
# the following is regrettably knarly, but it seems to be the only way
202
# to not get ugly error messages if uname can't be found.
203
# Hmmm, BSD/OS 2.0's uname -a is excessively verbose. Let's try it
204
# with switches first.
205
eval "chop (\$osname = `sh -c 'uname -nmsr 2>&1'`)";
206
if ($osname =~ /not found/i)
208
$osname = "(something unixy with no uname)";
210
elsif ($@ ne "" || $?)
212
eval "chop (\$osname = `sh -c 'uname -a 2>&1'`)";
215
$osname = "(something unixy)";
222
if ($fancy_file_names) {
223
chdir ("..") || &error ("Couldn't chdir to ..: $!\n", 1);
224
unlink (".ostest>ick");
225
rmdir (".ostest") || &error ("Couldn't rmdir .ostest: $!\n", 1);
229
sub parse_command_line
233
# use @ARGV if no args were passed in
240
# look at each option; if we don't recognize it, maybe the suite-specific
241
# command line parsing code will...
245
$option = shift @argv;
246
if ($option =~ /^-debug$/i)
248
print "\nDEBUG ON\n";
251
elsif ($option =~ /^-usage$/i)
256
elsif ($option =~ /^-(h|help)$/i)
261
elsif ($option =~ /^-profile$/i)
265
elsif ($option =~ /^-verbose$/i)
269
elsif ($option =~ /^-detail$/i)
274
elsif ($option =~ /^-keep$/i)
278
elsif (&valid_option($option))
280
# The suite-defined subroutine takes care of the option
282
elsif ($option =~ /^-/)
284
print "Invalid option: $option\n";
288
else # must be the name of a test
290
$option =~ s/\.pl$//;
291
push(@TESTS,$option);
298
local($num) = shift @_;
315
local($width, $string) = @_;
318
if (length ($string))
320
$pad = " " x ( ($width - length ($string) + 1) / 2);
331
$info = "Running tests for $testee on $osname\n"; # $testee is suite-defined
332
$len = &max (length ($line), length ($testee_version),
333
length ($banner_info), 73) + 5;
334
$line = ("-" x $len) . "\n";
340
&print_centered ($len, $line);
341
&print_centered ($len, $info);
342
&print_centered ($len, $testee_version); # suite-defined
343
&print_centered ($len, $banner_info); # suite-defined
344
&print_centered ($len, $line);
352
foreach $testname (sort @TESTS)
355
$test_passed = 1; # reset by test on failure
356
$num_of_logfiles = 0;
357
$num_of_tmpfiles = 0;
360
$testname =~ s/^$scriptpath$pathsep//;
361
$perl_testname = "$scriptpath$pathsep$testname";
362
$testname =~ s/(\.pl|\.perl)$//;
363
$testpath = "$workpath$pathsep$testname";
364
# Leave enough space in the extensions to append a number, even
365
# though it needs to fit into 8+3 limits.
366
if ($port_host eq 'DOS') {
378
$log_filename = "$testpath.$logext";
379
$diff_filename = "$testpath.$diffext";
380
$base_filename = "$testpath.$baseext";
381
$tmp_filename = "$testpath.$tmpfilesuffix";
383
&setup_for_test; # suite-defined
385
$output = "........................................................ ";
387
substr($output,0,length($testname)) = "$testname ";
391
# Run the actual test!
393
$code = do $perl_testname;
399
warn "\n*** Test died ($testname): $@\n";
403
warn "\n*** Couldn't run $perl_testname\n";
406
elsif ($code == -1) {
409
elsif ($code != 1 && $code != -1) {
411
warn "\n*** Test returned $code\n";
416
for ($i = $num_of_tmpfiles; $i; $i--)
418
&delete ($tmp_filename . &num_suffix ($i) );
421
for ($i = $num_of_logfiles ? $num_of_logfiles : 1; $i; $i--)
423
&delete ($log_filename . &num_suffix ($i) );
424
&delete ($base_filename . &num_suffix ($i) );
436
# If the verbose option has been specified, then a short description
437
# of each test is printed before displaying the results of each test
438
# describing WHAT is being tested.
444
print "\nWHAT IS BEING TESTED\n";
445
print "--------------------";
447
print "\n\n$description\n\n";
450
# If the detail option has been specified, then the details of HOW
451
# the test is testing what it says it is testing in the verbose output
452
# will be displayed here before the results of the test are displayed.
456
print "\nHOW IT IS TESTED\n";
457
print "----------------";
458
print "\n\n$details\n\n";
465
# If the keep flag is not set, this subroutine deletes all filenames that
474
return (unlink @files);
480
sub print_standard_usage
482
local($plname,@moreusage) = @_;
485
print "Usage: perl $plname [testname] [-verbose] [-detail] [-keep]\n";
486
print " [-profile] [-usage] [-help] "
488
foreach $line (@moreusage)
494
sub print_standard_help
496
local(@morehelp) = @_;
501
$line = "Test Driver For $testee";
503
$line = "=" x length ($line);
509
. "${t}You may, if you wish, run only ONE test if you know the name\n"
510
. "${t}of that test and specify this name anywhere on the command\n"
511
. "${t}line. Otherwise ALL existing tests in the scripts directory\n"
512
. "${t}will be run.\n"
514
. "${t}If this option is given, a description of every test is\n"
515
. "${t}displayed before the test is run. (Not all tests may have\n"
516
. "${t}descriptions at this time)\n"
518
. "${t}If this option is given, a detailed description of every\n"
519
. "${t}test is displayed before the test is run. (Not all tests\n"
520
. "${t}have descriptions at this time)\n"
522
. "${t}If this option is given, then the profile file\n"
523
. "${t}is added to other profiles every time $testee is run.\n"
524
. "${t}This option only works on VOS at this time.\n"
526
. "${t}You may give this option if you DO NOT want ANY\n"
527
. "${t}of the files generated by the tests to be deleted. \n"
528
. "${t}Without this option, all files generated by the test will\n"
529
. "${t}be deleted IF THE TEST PASSES.\n"
531
. "${t}Use this option if you would like to see all of the system\n"
532
. "${t}calls issued and their return status while running the tests\n"
533
. "${t}This can be helpful if you're having a problem adding a test\n"
534
. "${t}to the suite, or if the test fails!\n";
536
foreach $line (@morehelp)
539
if (substr ($tline, 0, 1) eq "\t")
541
substr ($tline, 0, 1) = $t;
547
#######################################################################
548
########### Generic Test Driver Subroutines ###########
549
#######################################################################
558
$depth = defined ($_[0]) ? $_[0] : 1;
559
($package, $filename, $linenum) = caller ($depth + 1);
560
return "$filename: $linenum";
565
local($message) = $_[0];
566
local($caller) = &get_caller (1);
570
$caller = &get_caller ($_[1] + 1) . " -> $caller";
573
die "$caller: $message";
578
local($answer,$logfile) = @_;
583
print "Comparing Output ........ ";
586
$slurp = &read_file_into_string ($logfile);
588
# For make, get rid of any time skew error before comparing--too bad this
589
# has to go into the "generic" driver code :-/
590
$slurp =~ s/^.*modification time in the future.*\n//g;
591
$slurp =~ s/\n.*modification time in the future.*//g;
592
$slurp =~ s/^.*Clock skew detected.*\n//g;
593
$slurp =~ s/\n.*Clock skew detected.*//g;
595
if ($slurp eq $answer)
607
print "DIFFERENT OUTPUT\n";
610
&create_file (&get_basefile, $answer);
614
print "\nCreating Difference File ...\n";
616
# Create the difference file
617
local($command) = "diff -c " . &get_basefile . " " . $logfile;
618
&run_command_with_output(&get_difffile,$command);
624
sub read_file_into_string
626
local($filename) = @_;
627
local($oldslash) = $/;
631
open (RFISFILE, $filename) || return "";
632
local ($slurp) = <RFISFILE>;
640
sub attach_default_output
642
local ($filename) = @_;
647
$code = system "++attach_default_output_hack $filename";
648
$code == -2 || &error ("adoh death\n", 1);
652
open ("SAVEDOS" . $default_output_stack_level . "out", ">&STDOUT")
653
|| &error ("ado: $! duping STDOUT\n", 1);
654
open ("SAVEDOS" . $default_output_stack_level . "err", ">&STDERR")
655
|| &error ("ado: $! duping STDERR\n", 1);
657
open (STDOUT, "> " . $filename)
658
|| &error ("ado: $filename: $!\n", 1);
659
open (STDERR, ">&STDOUT")
660
|| &error ("ado: $filename: $!\n", 1);
662
$default_output_stack_level++;
665
# close the current stdout/stderr, and restore the previous ones from
668
sub detach_default_output
674
$code = system "++detach_default_output_hack";
675
$code == -2 || &error ("ddoh death\n", 1);
679
if (--$default_output_stack_level < 0)
681
&error ("default output stack has flown under!\n", 1);
687
open (STDOUT, ">&SAVEDOS" . $default_output_stack_level . "out")
688
|| &error ("ddo: $! duping STDOUT\n", 1);
689
open (STDERR, ">&SAVEDOS" . $default_output_stack_level . "err")
690
|| &error ("ddo: $! duping STDERR\n", 1);
692
close ("SAVEDOS" . $default_output_stack_level . "out")
693
|| &error ("ddo: $! closing SCSDOSout\n", 1);
694
close ("SAVEDOS" . $default_output_stack_level . "err")
695
|| &error ("ddo: $! closing SAVEDOSerr\n", 1);
698
# run one command (passed as a list of arg 0 - n), returning 0 on success
699
# and nonzero on failure.
707
print "\nrun_command: @_\n";
709
print "run_command: \"@_\" returned $code.\n";
716
# run one command (passed as a list of arg 0 - n, with arg 0 being the
717
# second arg to this routine), returning 0 on success and non-zero on failure.
718
# The first arg to this routine is a filename to connect to the stdout
719
# & stderr of the child process.
721
sub run_command_with_output
723
local ($filename) = shift;
726
&attach_default_output ($filename);
728
&detach_default_output;
731
print "run_command_with_output: \"@_\" returned $code.\n";
737
# performs the equivalent of an "rm -rf" on the first argument. Like
738
# rm, if the path ends in /, leaves the (now empty) directory; otherwise
741
sub remove_directory_tree
743
local ($targetdir) = @_;
744
local ($nuketop) = 1;
747
$ch = substr ($targetdir, length ($targetdir) - 1);
748
if ($ch eq "/" || $ch eq $pathsep)
750
$targetdir = substr ($targetdir, 0, length ($targetdir) - 1);
759
&remove_directory_tree_inner ("RDT00", $targetdir) || return 0;
762
rmdir $targetdir || return 0;
768
sub remove_directory_tree_inner
770
local ($dirhandle, $targetdir) = @_;
772
local ($subdirhandle);
774
opendir ($dirhandle, $targetdir) || return 0;
775
$subdirhandle = $dirhandle;
777
while ($object = readdir ($dirhandle))
779
if ($object eq "." || $object eq "..")
784
$object = "$targetdir$pathsep$object";
787
if (-d _ && &remove_directory_tree_inner ($subdirhandle, $object))
789
rmdir $object || return 0;
793
unlink $object || return 0;
796
closedir ($dirhandle);
800
# We used to use this behavior for this function:
804
# local (@filenames) = @_;
805
# local ($now) = time;
808
# foreach $file (@filenames)
810
# utime ($now, $now, $file)
811
# || (open (TOUCHFD, ">> $file") && close (TOUCHFD))
812
# || &error ("Couldn't touch $file: $!\n", 1);
817
# But this behaves badly on networked filesystems where the time is
818
# skewed, because it sets the time of the file based on the _local_
819
# host. Normally when you modify a file, it's the _remote_ host that
820
# determines the modtime, based on _its_ clock. So, instead, now we open
821
# the file and write something into it to force the remote host to set
822
# the modtime correctly according to its clock.
827
local (@filenames) = @_;
830
foreach $file (@filenames) {
831
(open(T, ">> $file") && print(T "\n") && close(T))
832
|| &error("Couldn't touch $file: $!\n", 1);
836
# open a file, write some stuff to it, and close it.
840
local ($filename, @lines) = @_;
842
open (CF, "> $filename") || &error ("Couldn't open $filename: $!\n", 1);
843
foreach $line (@lines)
850
# create a directory tree described by an associative array, wherein each
851
# key is a relative pathname (using slashes) and its associated value is
853
# DIR indicates a directory
854
# FILE:contents indicates a file, which should contain contents +\n
855
# LINK:target indicates a symlink, pointing to $basedir/target
856
# The first argument is the dir under which the structure will be created
857
# (the dir will be made and/or cleaned if necessary); the second argument
858
# is the associative array.
862
local ($basedir, %dirtree) = @_;
865
&remove_directory_tree ("$basedir");
866
mkdir ($basedir, 0777) || &error ("Couldn't mkdir $basedir: $!\n", 1);
868
foreach $path (sort keys (%dirtree))
870
if ($dirtree {$path} =~ /^DIR$/)
872
mkdir ("$basedir/$path", 0777)
873
|| &error ("Couldn't mkdir $basedir/$path: $!\n", 1);
875
elsif ($dirtree {$path} =~ /^FILE:(.*)$/)
877
&create_file ("$basedir/$path", $1 . "\n");
879
elsif ($dirtree {$path} =~ /^LINK:(.*)$/)
881
symlink ("$basedir/$1", "$basedir/$path")
882
|| &error ("Couldn't symlink $basedir/$path -> $basedir/$1: $!\n", 1);
886
&error ("Bogus dirtree type: \"$dirtree{$path}\"\n", 1);
889
if ($just_setup_tree)
891
die "Tree is setup...\n";
895
# compare a directory tree with an associative array in the format used
896
# by create_dir_tree, above.
897
# The first argument is the dir under which the structure should be found;
898
# the second argument is the associative array.
902
local ($basedir, %dirtree) = @_;
913
opendir (DIR, $basedir) || &error ("Couldn't open $basedir: $!\n", 1);
914
@allfiles = grep (!/^\.\.?$/, readdir (DIR) );
918
print "dirtree: (%dirtree)\n$basedir: (@allfiles)\n";
921
foreach $path (sort keys (%dirtree))
925
print "Checking $path ($dirtree{$path}).\n";
929
foreach $i (0 .. $#allfiles)
931
if ($allfiles[$i] eq $path)
933
splice (@allfiles, $i, 1); # delete it
936
print " Zapped $path; files now (@allfiles).\n";
938
lstat ("$basedir/$path");
946
print "compare_dir_tree: $path does not exist.\n";
951
if ($dirtree {$path} =~ /^DIR$/)
953
if (-d _ && opendir (DIR, "$basedir/$path") )
955
@files = readdir (DIR);
957
@files = grep (!/^\.\.?$/ && ($_ = "$path/$_"), @files);
958
push (@allfiles, @files);
961
print " Read in $path; new files (@files).\n";
966
print "compare_dir_tree: $path is not a dir.\n";
970
elsif ($dirtree {$path} =~ /^FILE:(.*)$/)
974
print "compare_dir_tree: $path is not a file.\n";
981
$contents = &read_file_into_string ("$basedir/$path");
982
if ($contents ne "$1\n")
984
print "compare_dir_tree: $path contains wrong stuff."
985
. " Is:\n$contentsShould be:\n$1\n";
990
elsif ($dirtree {$path} =~ /^LINK:(.*)$/)
995
print "compare_dir_tree: $path is not a link.\n";
1000
$contents = readlink ("$basedir/$path");
1001
$contents =~ tr/>/\//;
1002
$fulltarget = "$basedir/$target";
1003
$fulltarget =~ tr/>/\//;
1004
if (!($contents =~ /$fulltarget$/))
1008
$target = $fulltarget;
1010
print "compare_dir_tree: $path should be link to $target, "
1011
. "not $contents.\n";
1017
&error ("Bogus dirtree type: \"$dirtree{$path}\"\n", 1);
1023
print "leftovers: (@allfiles).\n";
1026
foreach $file (@allfiles)
1028
print "compare_dir_tree: $file should not exist.\n";
1035
# this subroutine generates the numeric suffix used to keep tmp filenames,
1036
# log filenames, etc., unique. If the number passed in is 1, then a null
1037
# string is returned; otherwise, we return ".n", where n + 1 is the number
1045
return "$extext$num";
1051
# This subroutine returns a log filename with a number appended to
1052
# the end corresponding to how many logfiles have been created in the
1053
# current running test. An optional parameter may be passed (0 or 1).
1054
# If a 1 is passed, then it does NOT increment the logfile counter
1055
# and returns the name of the latest logfile. If either no parameter
1056
# is passed at all or a 0 is passed, then the logfile counter is
1057
# incremented and the new name is returned.
1061
local($no_increment) = @_;
1063
$num_of_logfiles += !$no_increment;
1065
return ($log_filename . &num_suffix ($num_of_logfiles));
1068
# This subroutine returns a base (answer) filename with a number
1069
# appended to the end corresponding to how many logfiles (and thus
1070
# base files) have been created in the current running test.
1071
# NO PARAMETERS ARE PASSED TO THIS SUBROUTINE.
1075
return ($base_filename . &num_suffix ($num_of_logfiles));
1078
# This subroutine returns a difference filename with a number appended
1079
# to the end corresponding to how many logfiles (and thus diff files)
1080
# have been created in the current running test.
1084
return ($diff_filename . &num_suffix ($num_of_logfiles));
1087
# just like logfile, only a generic tmp filename for use by the test.
1088
# they are automatically cleaned up unless -keep was used, or the test fails.
1089
# Pass an argument of 1 to return the same filename as the previous call.
1093
local($no_increment) = @_;
1095
$num_of_tmpfiles += !$no_increment;
1097
return ($tmp_filename . &num_suffix ($num_of_tmpfiles));