xref: /openbsd/gnu/usr.bin/perl/t/TEST (revision 3bef86f7)
1#!./perl
2
3# This is written in a peculiar style, since we're trying to avoid
4# most of the constructs we'll be testing for.  (This comment is
5# probably obsolete on the avoidance side, though still current
6# on the peculiarity side.)
7
8# t/TEST and t/harness need to share code. The logical way to do this would be
9# to have the common code in a file both require or use. However, t/TEST needs
10# to still work, to generate test results, even if require isn't working, so
11# we cannot do that. t/harness has no such restriction, so it is quite
12# acceptable to have it require t/TEST.
13
14# In which case, we need to stop t/TEST actually running tests, as all
15# t/harness needs are its subroutines.
16
17# Measure the elapsed wallclock time.
18my $t0 = time();
19
20# If we're doing deparse tests, ignore failures for these
21my $deparse_failures;
22
23# And skip even running these
24my $deparse_skips;
25
26my $deparse_skip_file = '../Porting/deparse-skips.txt';
27
28# directories with special sets of test switches
29my %dir_to_switch =
30    (base => '',
31     comp => '',
32     run => '',
33     '../ext/File-Glob/t' => '-I.. -MTestInit', # FIXME - tests assume t/
34     );
35
36# "not absolute" is the default, as it saves some fakery within TestInit
37# which can perturb tests, and takes CPU. Working with the upstream author of
38# any of these, to figure out how to remove them from this list, considered
39# "a good thing".
40my %abs = (
41	   '../cpan/Archive-Tar' => 1,
42	   '../cpan/AutoLoader' => 1,
43	   '../cpan/CPAN' => 1,
44	   '../cpan/Encode' => 1,
45	   '../cpan/ExtUtils-Constant' => 1,
46	   '../cpan/ExtUtils-Install' => 1,
47	   '../cpan/ExtUtils-MakeMaker' => 1,
48	   '../cpan/ExtUtils-Manifest' => 1,
49	   '../cpan/File-Fetch' => 1,
50	   '../cpan/IPC-Cmd' => 1,
51	   '../cpan/IPC-SysV' => 1,
52	   '../cpan/Module-Load' => 1,
53	   '../cpan/Module-Load-Conditional' => 1,
54	   '../cpan/Pod-Simple' => 1,
55	   '../cpan/Test-Simple' => 1,
56	   '../cpan/podlators' => 1,
57	   '../dist/Cwd' => 1,
58	   '../dist/Devel-PPPort' => 1,
59	   '../dist/ExtUtils-ParseXS' => 1,
60	   '../dist/Tie-File' => 1,
61	  );
62
63my %temp_no_core = (
64     '../cpan/Compress-Raw-Bzip2' => 1,
65     '../cpan/Compress-Raw-Zlib' => 1,
66     '../cpan/Devel-PPPort' => 1,
67     '../cpan/Getopt-Long' => 1,
68     '../cpan/IO-Compress' => 1,
69     '../cpan/MIME-Base64' => 1,
70     '../cpan/parent' => 1,
71     '../cpan/Pod-Simple' => 1,
72     '../cpan/podlators' => 1,
73     '../cpan/Test-Simple' => 1,
74     '../cpan/Tie-RefHash' => 1,
75     '../cpan/Unicode-Collate' => 1,
76     '../dist/Unicode-Normalize' => 1,
77    );
78
79# delete env vars that may influence the results
80# but allow override via *_TEST env var if wanted
81# (e.g. PERL5OPT_TEST=-d:NYTProf)
82my @bad_env_vars = qw(
83    PERL5LIB PERLLIB PERL5OPT PERL_UNICODE
84    PERL_YAML_BACKEND PERL_JSON_BACKEND
85);
86
87for my $envname (@bad_env_vars) {
88    my $override = $ENV{"${envname}_TEST"};
89    if (defined $override) {
90	warn "$0: $envname=$override\n";
91	$ENV{$envname} = $override;
92    }
93    else {
94	delete $ENV{$envname};
95    }
96}
97
98# Location to put the Valgrind log.
99our $Valgrind_Log;
100
101my %skip = (
102	    '.' => 1,
103	    '..' => 1,
104	    'CVS' => 1,
105	    'RCS' => 1,
106	    'SCCS' => 1,
107	    '.svn' => 1,
108	   );
109
110
111if ($::do_nothing) {
112    return 1;
113}
114
115$| = 1;
116
117# for testing TEST only
118#BEGIN { require '../lib/strict.pm'; "strict"->import() };
119#BEGIN { require '../lib/warnings.pm'; "warnings"->import() };
120
121# remove empty elements due to insertion of empty symbols via "''p1'" syntax
122@ARGV = grep($_,@ARGV) if $^O eq 'VMS';
123
124# String eval to avoid loading File::Glob on non-miniperl.
125# (Windows only uses this script for miniperl.)
126@ARGV = eval 'map glob, @ARGV' if $^O eq 'MSWin32';
127
128our $show_elapsed_time = $ENV{HARNESS_TIMER} || 0;
129
130# Cheesy version of Getopt::Std.  We can't replace it with that, because we
131# can't rely on require working.
132{
133    my @argv = ();
134    foreach my $idx (0..$#ARGV) {
135	push( @argv, $ARGV[$idx] ), next unless $ARGV[$idx] =~ /^-(\S+)$/;
136	$::benchmark = 1 if $1 eq 'benchmark';
137	$::core    = 1 if $1 eq 'core';
138	$::verbose = 1 if $1 eq 'v';
139	$::torture = 1 if $1 eq 'torture';
140	$::with_utf8 = 1 if $1 eq 'utf8';
141	$::with_utf16 = 1 if $1 eq 'utf16';
142	$::taintwarn = 1 if $1 eq 'taintwarn';
143	if ($1 =~ /^deparse(,.+)?$/) {
144	    $::deparse = 1;
145	    $::deparse_opts = $1;
146            _process_deparse_config();
147	}
148    }
149    @ARGV = @argv;
150}
151
152chdir 't' if -f 't/TEST';
153if (-f 'TEST' && -f 'harness' && -d '../lib') {
154    @INC = '../lib';
155}
156
157die "You need to run \"make test_prep\" first to set things up.\n"
158  unless -e 'perl' or -e 'perl.exe' or -e 'perl.pm';
159
160# check leakage for embedders
161$ENV{PERL_DESTRUCT_LEVEL} = 2 unless exists $ENV{PERL_DESTRUCT_LEVEL};
162# check existence of all symbols
163$ENV{PERL_DL_NONLAZY} = 1 unless exists $ENV{PERL_DL_NONLAZY};
164
165$ENV{EMXSHELL} = 'sh';        # For OS/2
166
167if ($show_elapsed_time) { require Time::HiRes }
168my %timings = (); # testname => [@et] pairs if $show_elapsed_time.
169
170# Roll your own File::Find!
171our @found;
172sub _find_tests { @found=(); push @ARGV, _find_files('\.t$', $_[0]) }
173sub _find_files {
174    my($patt, @dirs) = @_;
175    for my $dir (@dirs) {
176	opendir DIR, $dir or die "Trouble opening $dir: $!";
177	foreach my $f (sort { $a cmp $b } readdir DIR) {
178	    next if $skip{$f};
179
180	    my $fullpath = "$dir/$f";
181	    if (-d $fullpath) {
182		_find_files($patt, $fullpath);
183	    } elsif ($f =~ /$patt/) {
184		push @found, $fullpath;
185	    }
186	}
187    }
188    @found;
189}
190
191
192# Scan the text of the test program to find switches and special options
193# we might need to apply.
194sub _scan_test {
195    my($test, $type) = @_;
196
197    open(my $script, "<", $test) or die "Can't read $test.\n";
198    my $first_line = <$script>;
199
200    $first_line =~ tr/\0//d if $::with_utf16;
201
202    my $switch = "";
203    if ($first_line =~ /#!.*\bperl.*\s-\w*([tT])/) {
204        $switch = "-$1";
205    } else {
206        if ($::taintwarn) {
207            # not all tests are expected to pass with this option
208            $switch = '-t';
209        } else {
210            $switch = '';
211        }
212    }
213
214    my $file_opts = "";
215    if ($type eq 'deparse') {
216        # Look for #line directives which change the filename
217        while (<$script>) {
218            $file_opts = $file_opts . ",-f$3$4"
219              if /^#\s*line\s+(\d+)\s+((\w+)|"([^"]+)")/;
220        }
221    }
222
223    close $script;
224
225    my $perl = $^O eq 'MSWin32' ? '.\perl' : './perl';
226    my $lib  = '../lib';
227    my $run_dir;
228    my $return_dir;
229
230    $test =~ /^(.+)\/[^\/]+/;
231    my $dir = $1;
232    my $testswitch = $dir_to_switch{$dir};
233    if (!defined $testswitch) {
234	if ($test =~ s!^(\.\./(cpan|dist|ext)/[^/]+)/t!t!) {
235	    $run_dir = $1;
236	    $return_dir = '../../t';
237	    $lib = '../../lib';
238	    $perl = '../../t/perl';
239	    $testswitch = "-I../.. -MTestInit=U2T";
240	    if ($2 eq 'cpan' || $2 eq 'dist') {
241		if($abs{$run_dir}) {
242		    $testswitch = $testswitch . ',A';
243		}
244		if ($temp_no_core{$run_dir}) {
245		    $testswitch = $testswitch . ',NC';
246		}
247	    }
248	} elsif ($test =~ m!^\.\./lib!) {
249	    $testswitch = '-I.. -MTestInit=U1'; # -T will remove . from @INC
250	} else {
251	    $testswitch = '-I.. -MTestInit';  # -T will remove . from @INC
252	}
253    }
254
255    my $utf8 = ($::with_utf8 || $::with_utf16) ? "-I$lib -Mutf8" : '';
256
257    my %options = (
258	perl => $perl,
259	lib => $lib,
260	test => $test,
261	run_dir => $run_dir,
262	return_dir => $return_dir,
263	testswitch => $testswitch,
264	utf8 => $utf8,
265	file => $file_opts,
266	switch => $switch,
267    );
268
269    return \%options;
270}
271
272sub _cmd {
273    my($options, $type) = @_;
274
275    my $test = $options->{test};
276
277    my $cmd;
278    if ($type eq 'deparse') {
279        my $perl = "$options->{perl} $options->{testswitch}";
280        my $lib = $options->{lib};
281
282        $cmd = (
283          "$perl $options->{switch} -I$lib -MO=-qq,Deparse,-sv1.,".
284          "-l$::deparse_opts$options->{file} ".
285          "$test > $test.dp ".
286          "&& $perl $options->{switch} -I$lib $test.dp"
287        );
288    }
289    elsif ($type eq 'perl') {
290        my $perl = $options->{perl};
291        my $redir = $^O eq 'VMS' ? '2>&1' : '';
292
293        if ($ENV{PERL_VALGRIND}) {
294            my $perl_supp = $options->{return_dir} ? "$options->{return_dir}/perl.supp" : "perl.supp";
295            my $valgrind_exe = $ENV{VALGRIND} // 'valgrind';
296            if ($options->{run_dir}) {
297                require Cwd;
298                $Valgrind_Log = Cwd::abs_path("$options->{run_dir}/$Valgrind_Log");
299            }
300            my $vg_opts = $ENV{VG_OPTS}
301	       //   "--log-file=$Valgrind_Log "
302		  . "--suppressions=$perl_supp --leak-check=yes "
303		  . "--leak-resolution=high --show-reachable=yes "
304		  . "--num-callers=50 --track-origins=yes";
305	    # Force logging if not asked for (so cachegrind reporting works below)
306	    if ($vg_opts !~ /--log-file/) {
307		$vg_opts = "--log-file=$Valgrind_Log $vg_opts";
308	    }
309            $perl = "$valgrind_exe $vg_opts $perl";
310        }
311
312        my $args = "$options->{testswitch} $options->{switch} $options->{utf8}";
313        $cmd = $perl . _quote_args($args) . " $test $redir";
314    }
315    return $cmd;
316}
317
318sub _before_fork {
319    my ($options) = @_;
320
321    if ($options->{run_dir}) {
322	my $run_dir = $options->{run_dir};
323	chdir $run_dir or die "Can't chdir to '$run_dir': $!";
324    }
325
326    # Remove previous valgrind output otherwise it will interfere
327    my $test = $options->{test};
328
329    (local $Valgrind_Log = "$test.valgrind-current") =~ s/^.*\///;
330
331    if ($ENV{PERL_VALGRIND} && -e $Valgrind_Log) {
332        unlink $Valgrind_Log
333            or warn "$0: Failed to unlink '$Valgrind_Log': $!\n";
334    }
335
336    return;
337}
338
339sub _after_fork {
340    my ($options) = @_;
341
342    if ($options->{return_dir}) {
343	my $return_dir = $options->{return_dir};
344	chdir $return_dir
345	   or die "Can't chdir from '$options->{run_dir}' to '$return_dir': $!";
346    }
347
348    return;
349}
350
351sub _run_test {
352    my ($test, $type) = @_;
353
354    my $options = _scan_test($test, $type);
355    # $test might have changed if we're in ext/Foo, so don't use it anymore
356    # from now on. Use $options->{test} instead.
357
358    _before_fork($options);
359
360    my $cmd = _cmd($options, $type);
361
362    open(my $results, "$cmd |") or print "can't run '$cmd': $!.\n";
363
364    _after_fork($options);
365
366    # Our environment may force us to use UTF-8, but we can't be sure that
367    # anything we're reading from will be generating (well formed) UTF-8
368    # This may not be the best way - possibly we should unset ${^OPEN} up
369    # top?
370    binmode $results;
371
372    return $results;
373}
374
375sub _quote_args {
376    my ($args) = @_;
377    my $argstring = '';
378
379    foreach (split(/\s+/,$args)) {
380       # In VMS protect with doublequotes because otherwise
381       # DCL will lowercase -- unless already doublequoted.
382       $_ = q(").$_.q(") if ($^O eq 'VMS') && !/^\"/ && length($_) > 0;
383       $argstring = $argstring . ' ' . $_;
384    }
385    return $argstring;
386}
387
388sub _populate_hash {
389    return unless defined $_[0];
390    return map {$_, 1} split /\s+/, $_[0];
391}
392
393sub _tests_from_manifest {
394    my ($extensions, $known_extensions) = @_;
395    my %skip;
396    my %extensions = _populate_hash($extensions);
397    my %known_extensions = _populate_hash($known_extensions);
398    my %printed_skip_warning;
399
400    foreach (keys %known_extensions) {
401	$skip{$_} = 1 unless $extensions{$_};
402    }
403
404    my @results;
405    my $mani = '../MANIFEST';
406    if (open(MANI, $mani)) {
407	while (<MANI>) {
408	    if (m!^((?:cpan|dist|ext)/(\S+)/+(?:[^/\s]+\.t|test\.pl)|lib/\S+?(?:\.t|test\.pl))\s!) {
409		my $t = $1;
410		my $extension = $2;
411
412		if (    ord "A" != 65
413		     && defined $extension
414		     && $extension =~ m! \b (?:
415						Archive-Tar/
416					      | Config-Perl-V/
417				              | CPAN-Meta/
418					      | CPAN-Meta-YAML/
419					      | Digest-SHA/
420					      | ExtUtils-MakeMaker/
421					      | HTTP-Tiny/
422					      | IO-Compress/
423					      | JSON-PP/
424					      | libnet/
425					      | MIME-Base64/
426					      | podlators/
427					      | Pod-Simple/
428					      | Pod-Checker/
429					      | Digest-MD5/
430					      | Test-Harness/
431					      | IPC-Cmd/
432					      | Encode/
433					      | Socket/
434					      | ExtUtils-Manifest/
435					      | Module-Metadata/
436					      | PerlIO-via-QuotedPrint/
437					    )
438				       !x)
439		{
440		    print STDERR "Skipping testing of $extension on EBCDIC\n"
441				     unless $printed_skip_warning{$extension}++;
442		    next;
443		}
444
445		if (!$::core || $t =~ m!^lib/[a-z]!) {
446		    if (defined $extension) {
447			$extension =~ s!/t(:?/\S+)*$!!;
448			# XXX Do I want to warn that I'm skipping these?
449			next if $skip{$extension};
450			my $flat_extension = $extension;
451			$flat_extension =~ s!-!/!g;
452			next if $skip{$flat_extension}; # Foo/Bar may live in Foo-Bar
453		    }
454		    my $path = "../$t";
455		    push @results, $path;
456		    $::path_to_name{$path} = $t;
457		}
458	    }
459	}
460	close MANI;
461    } else {
462	warn "$0: cannot open $mani: $!\n";
463    }
464    return @results;
465}
466
467unless (@ARGV) {
468    # base first, as TEST bails out if that can't run
469    # then comp, to validate that require works
470    # then run, to validate that -M works
471    # then we know we can -MTestInit for everything else, making life simpler
472    foreach my $dir (qw(base comp run cmd io re opbasic op uni mro perf)) {
473	_find_tests($dir);
474    }
475    unless ($::core) {
476	_find_tests('porting');
477        _find_tests("lib");
478    }
479    # Config.pm may be broken for make minitest. And this is only a refinement
480    # for skipping tests on non-default builds, so it is allowed to fail.
481    # What we want to do is make a list of extensions which we did not build.
482    my $configsh = '../config.sh';
483    my ($extensions, $known_extensions);
484    if (-f $configsh) {
485	open FH, $configsh or die "Can't open $configsh: $!";
486	while (<FH>) {
487	    if (/^extensions=['"](.*)['"]$/) {
488		$extensions = $1;
489	    }
490	    elsif (/^known_extensions=['"](.*)['"]$/) {
491		$known_extensions = $1;
492	    }
493	}
494	if (!defined $known_extensions) {
495	    warn "No known_extensions line found in $configsh";
496	}
497	if (!defined $extensions) {
498	    warn "No extensions line found in $configsh";
499	}
500    }
501    # The "complex" constructions of list return from a subroutine, and push of
502    # a list, might fail if perl is really hosed, but they aren't needed for
503    # make minitest, and the building of extensions will likely also fail if
504    # something is that badly wrong.
505    push @ARGV, _tests_from_manifest($extensions, $known_extensions);
506    unless ($::core) {
507	_find_tests('japh') if $::torture;
508	_find_tests('benchmark') if $::benchmark or $ENV{PERL_BENCHMARK};
509	_find_tests('bigmem') if $ENV{PERL_TEST_MEMORY};
510    }
511}
512@ARGV= do {
513    my @order= (
514	"base",
515	"comp",
516	"run",
517	"cmd",
518	"io",
519	"re",
520	"opbasic",
521	"op",
522	"uni",
523	"mro",
524	"lib",
525	"ext",
526	"dist",
527	"cpan",
528	"perf",
529	"porting",
530    );
531    my %order= map { $order[$_] => 1+$_ } 0..$#order;
532    my $idx= 0;
533    map {
534	$_->[0]
535    } sort {
536	    $a->[3] <=> $b->[3] ||
537	    $a->[1] <=> $b->[1]
538    } map {
539	my $root= /(\w+)/ ? $1 : "";
540	[ $_, $idx++, $root, $order{$root}||=0 ]
541    } @ARGV;
542};
543
544if ($::deparse) {
545    _testprogs('deparse', '',   @ARGV);
546}
547elsif ($::with_utf16) {
548    for my $e (0, 1) {
549	for my $b (0, 1) {
550	    print STDERR "# ENDIAN $e BOM $b\n";
551	    my @UARGV;
552	    for my $a (@ARGV) {
553		my $u = $a . "." . ($e ? "l" : "b") . "e" . ($b ? "b" : "");
554		my $f = $e ? "v" : "n";
555		push @UARGV, $u;
556		unlink($u);
557		if (open(A, $a)) {
558		    if (open(U, ">$u")) {
559			print U pack("$f", 0xFEFF) if $b;
560			while (<A>) {
561			    print U pack("$f*", unpack("C*", $_));
562			}
563			close(U);
564		    }
565		    close(A);
566		}
567	    }
568	    _testprogs('perl', '', @UARGV);
569	    unlink(@UARGV);
570	}
571    }
572}
573else {
574    _testprogs('perl',    '',   @ARGV);
575}
576
577sub _testprogs {
578    my ($type, $args, @tests) = @_;
579
580    print <<'EOT' if ($type eq 'deparse');
581------------------------------------------------------------------------------
582TESTING DEPARSER
583------------------------------------------------------------------------------
584EOT
585
586    $::bad_files = 0;
587
588    foreach my $t (@tests) {
589      unless (exists $::path_to_name{$t}) {
590	my $tname = "t/$t";
591	$::path_to_name{$t} = $tname;
592      }
593    }
594    my $maxlen = 0;
595    foreach (@::path_to_name{@tests}) {
596	s/\.\w+\z/ /; # space gives easy doubleclick to select fname
597	my $len = length ;
598	$maxlen = $len if $len > $maxlen;
599    }
600    # + 3 : we want three dots between the test name and the "ok"
601    my $dotdotdot = $maxlen + 3 ;
602    my $grind_ct = 0;		# count of non-empty valgrind reports
603    my $total_files = @tests;
604    my $good_files = 0;
605    my $tested_files  = 0;
606    my $totmax = 0;
607    my %failed_tests;
608    my @unexpected_pass; # files where deparse-skips.txt says fail but passed
609    my $toolnm;		# valgrind, cachegrind, perf
610
611    while (my $test = shift @tests) {
612        my ($test_start_time, @starttimes) = 0;
613	if ($show_elapsed_time) {
614	    $test_start_time = Time::HiRes::time();
615	    # times() reports usage by TEST, but we want usage of each
616	    # testprog it calls, so record accumulated times now,
617	    # subtract them out afterwards.  Ideally, we'd take times
618	    # in BEGIN/END blocks (giving better visibility of self vs
619	    # children of each testprog), but that would require some
620	    # IPC to send results back here, or a completely different
621	    # collection scheme (Storable isn't tuned for incremental use)
622	    @starttimes = times;
623	}
624	if ($test =~ /^$/) {
625	    next;
626	}
627	if ($type eq 'deparse' && $test =~ $deparse_skips) {
628	    next;
629	}
630	my $te = $::path_to_name{$test} . '.'
631		    x ($dotdotdot - length($::path_to_name{$test})) .' ';
632
633	if ($^O ne 'VMS') {  # defer printing on VMS due to piping bug
634	    print $te;
635	    $te = '';
636	}
637
638	(local $Valgrind_Log = "$test.valgrind-current") =~ s/^.*\///;
639
640	my $results = _run_test($test, $type);
641
642	my $failure;
643	my $next = 0;
644	my $seen_leader = 0;
645	my $seen_ok = 0;
646	my $trailing_leader = 0;
647	my $max;
648	my %todo;
649	while (<$results>) {
650	    next if /^\s*$/; # skip blank lines
651	    if (/^1..$/ && ($^O eq 'VMS')) {
652		# VMS pipe bug inserts blank lines.
653		my $l2 = <$results>;
654		if ($l2 =~ /^\s*$/) {
655		    $l2 = <$results>;
656		}
657		$_ = '1..' . $l2;
658	    }
659	    if ($::verbose) {
660		print $_;
661	    }
662	    unless (/^\#/) {
663		if ($trailing_leader) {
664		    # shouldn't be anything following a postfix 1..n
665		    $failure = 'FAILED--extra output after trailing 1..n';
666		    last;
667		}
668		if (/^1\.\.([0-9]+)( todo ([\d ]+))?/) {
669		    if ($seen_leader) {
670			$failure = 'FAILED--seen duplicate leader';
671			last;
672		    }
673		    $max = $1;
674		    %todo = map { $_ => 1 } split / /, $3 if $3;
675		    $totmax = $totmax + $max;
676		    $tested_files = $tested_files + 1;
677		    if ($seen_ok) {
678			# 1..n appears at end of file
679			$trailing_leader = 1;
680			if ($next != $max) {
681			    $failure = "FAILED--expected $max tests, saw $next";
682			    last;
683			}
684		    }
685		    else {
686			$next = 0;
687		    }
688		    $seen_leader = 1;
689		}
690		else {
691		    if (/^(not )?ok(?: (\d+))?[^\#]*(\s*\#.*)?/) {
692			unless ($seen_leader) {
693			    unless ($seen_ok) {
694				$next = 0;
695			    }
696			}
697			$seen_ok = 1;
698			$next = $next + 1;
699			my($not, $num, $extra, $istodo) = ($1, $2, $3, 0);
700			$num = $next unless $num;
701
702			if ($num == $next) {
703
704			    # SKIP is essentially the same as TODO for t/TEST
705			    # this still conforms to TAP:
706			    # http://testanything.org/wiki/index.php/TAP_specification
707			    $extra and $istodo = $extra =~ /#\s*(?:TODO|SKIP)\b/;
708			    $istodo = 1 if $todo{$num};
709
710			    if( $not && !$istodo ) {
711				$failure = "FAILED at test $num";
712				last;
713			    }
714			}
715			else {
716			    $failure ="FAILED--expected test $next, saw test $num";
717			    last;
718			}
719		    }
720		    elsif (/^Bail out!\s*(.*)/i) { # magic words
721			die "FAILED--Further testing stopped" . ($1 ? ": $1\n" : ".\n");
722		    }
723		    else {
724			# module tests are allowed extra output,
725			# because Test::Harness allows it
726			next if $test =~ /^\W*(cpan|dist|ext|lib)\b/;
727			$failure = "FAILED--unexpected output at test $next";
728			last;
729		    }
730		}
731	    }
732	}
733	my  @junk = <$results>;  # dump remaining output to prevent SIGPIPE
734				 # (so far happens only on os390)
735	close $results;
736	undef @junk;
737
738	if (not defined $failure) {
739	    $failure = 'FAILED--no leader found' unless $seen_leader;
740	}
741
742	_check_valgrind(\$toolnm, \$grind_ct, \$test);
743
744	if ($type eq 'deparse' && !$ENV{KEEP_DEPARSE_FILES}) {
745	    unlink "./$test.dp";
746	}
747	if (not defined $failure and $next != $max) {
748	    $failure="FAILED--expected $max tests, saw $next";
749	}
750
751	if( !defined $failure  # don't mask a test failure
752	    and $? )
753	{
754	    $failure = "FAILED--non-zero wait status: $?";
755	}
756
757	# Deparse? Should it have passed or failed?
758	if ($type eq 'deparse' && $test =~ $deparse_failures) {
759	    if (!$failure) {
760		# Wait, it didn't fail? Great news!
761		push @unexpected_pass, $test;
762	    } else {
763		# Bah, still failing. Mask it.
764		print "${te}skipped\n";
765		$tested_files = $tested_files - 1;
766		next;
767	    }
768	}
769
770	if (defined $failure) {
771	    print "${te}$failure\n";
772	    $::bad_files = $::bad_files + 1;
773	    if ($test =~ /^base/ && ! defined &DynaLoader::boot_DynaLoader) {
774		# Die if running under minitest (no DynaLoader).  Otherwise
775		# keep going, as  we know that Perl basically works, or we
776		# would not have been able to actually compile it all the way.
777		die "Failed a basic test ($test) under minitest -- cannot continue.\n";
778	    }
779	    $failed_tests{$test} = 1;
780	}
781	else {
782	    if ($max) {
783		my ($elapsed, $etms) = ("", 0);
784		if ( $show_elapsed_time ) {
785		    $etms = (Time::HiRes::time() - $test_start_time) * 1000;
786		    $elapsed = sprintf(" %8.0f ms", $etms);
787
788		    my (@endtimes) = times;
789		    $endtimes[$_] -= $starttimes[$_] for 0..$#endtimes;
790		    splice @endtimes, 0, 2;    # drop self/harness times
791		    $_ *= 1000 for @endtimes;  # and scale to ms
792		    $timings{$test} = [$etms,@endtimes];
793		    $elapsed .= sprintf(" %5.0f ms", $_) for @endtimes;
794		}
795		print "${te}ok$elapsed\n";
796		$good_files = $good_files + 1;
797	    }
798	    else {
799		print "${te}skipped\n";
800		$tested_files = $tested_files - 1;
801	    }
802	}
803    } # while tests
804
805    if ($::bad_files == 0) {
806	if ($good_files) {
807	    print "All tests successful.\n";
808	    # XXX add mention of 'perlbug -ok' ?
809	}
810	else {
811	    die "FAILED--no tests were run for some reason.\n";
812	}
813    }
814    else {
815	my $pct = $tested_files ? sprintf("%.2f", ($tested_files - $::bad_files) / $tested_files * 100) : "0.00";
816	my $s = $::bad_files == 1 ? "" : "s";
817	warn "Failed $::bad_files test$s out of $tested_files, $pct% okay.\n";
818	for my $test ( sort keys %failed_tests ) {
819	    print "\t$test\n";
820	}
821
822	if (@unexpected_pass) {
823	    print <<EOF;
824
825The following scripts were expected to fail under -deparse (at least
826according to $deparse_skip_file), but unexpectedly succeeded:
827EOF
828	    print "\t$_\n" for sort @unexpected_pass;
829	    print "\n";
830	}
831
832	warn <<'SHRDLU_1';
833### Since not all tests were successful, you may want to run some of
834### them individually and examine any diagnostic messages they produce.
835### See the INSTALL document's section on "make test".
836SHRDLU_1
837	warn <<'SHRDLU_2' if $good_files / $total_files > 0.8;
838### You have a good chance to get more information by running
839###   ./perl harness
840### in the 't' directory since most (>=80%) of the tests succeeded.
841SHRDLU_2
842	if (eval {require Config; import Config; 1}) {
843	    if ($::Config{usedl} && (my $p = $::Config{ldlibpthname})) {
844		warn <<SHRDLU_3;
845### You may have to set your dynamic library search path,
846### $p, to point to the build directory:
847SHRDLU_3
848		if (exists $ENV{$p} && $ENV{$p} ne '') {
849		    warn <<SHRDLU_4a;
850###   setenv $p `pwd`:\$$p; cd t; ./perl harness
851###   $p=`pwd`:\$$p; export $p; cd t; ./perl harness
852###   export $p=`pwd`:\$$p; cd t; ./perl harness
853SHRDLU_4a
854		} else {
855		    warn <<SHRDLU_4b;
856###   setenv $p `pwd`; cd t; ./perl harness
857###   $p=`pwd`; export $p; cd t; ./perl harness
858###   export $p=`pwd`; cd t; ./perl harness
859SHRDLU_4b
860		}
861		warn <<SHRDLU_5;
862### for csh-style shells, like tcsh; or for traditional/modern
863### Bourne-style shells, like bash, ksh, and zsh, respectively.
864SHRDLU_5
865	    }
866	}
867    }
868    printf "Elapsed: %d sec\n", time() - $t0;
869    my ($user,$sys,$cuser,$csys) = times;
870    my $tot = sprintf("u=%.2f  s=%.2f  cu=%.2f  cs=%.2f  scripts=%d  tests=%d",
871		      $user,$sys,$cuser,$csys,$tested_files,$totmax);
872    print "$tot\n";
873    if ($good_files) {
874	if (-d $show_elapsed_time) {
875	    # HARNESS_TIMER = <a-directory>.  Save timings etc to
876	    # storable file there.  NB: the test cds to ./t/, so
877	    # relative path must account for that, ie ../../perf
878	    # points to dir next to source tree.
879	    require Storable;
880	    my @dt = localtime;
881	    $dt[5] += 1900; $dt[4] += 1; # fix year, month
882	    my $fn = "$show_elapsed_time/".join('-', @dt[5,4,3,2,1]).".ttimes";
883	    Storable::store({ perf => \%timings,
884			      gather_conf_platform_info(),
885			      total => $tot,
886			    }, $fn);
887	    print "wrote storable file: $fn\n";
888	}
889    }
890
891    _cleanup_valgrind(\$toolnm, \$grind_ct);
892}
893exit ($::bad_files != 0);
894
895# Collect platform, config data that should allow comparing
896# performance data between different machines.  With enough data,
897# and/or clever statistical analysis, it should be possible to
898# determine the effect of config choices, more memory, etc
899
900sub gather_conf_platform_info {
901    # currently rather quick & dirty, and subject to change
902    # for both content and format.
903    require Config;
904    my (%conf, @platform) = ();
905    $conf{$_} = $Config::Config{$_} for
906	grep /cc|git|config_arg\d+/, keys %Config::Config;
907    if (-f '/proc/cpuinfo') {
908	open my $fh, '/proc/cpuinfo' or warn "$!: /proc/cpuinfo\n";
909	@platform = grep /name|cpu/, <$fh>;
910	chomp $_ for @platform;
911    }
912    unshift @platform, $^O;
913
914    return (
915	conf => \%conf,
916	platform => {cpu => \@platform,
917		     mem => [ grep s/\s+/ /,
918			      grep chomp, `free` ],
919		     load => [ grep chomp, `uptime` ],
920	},
921	host => (grep chomp, `hostname -f`),
922	version => '0.03', # bump for conf, platform, or data collection changes
923	);
924}
925
926sub _check_valgrind {
927    return unless $ENV{PERL_VALGRIND};
928
929    my ($toolnm, $grind_ct, $test) = @_;
930
931    $$toolnm = $ENV{VALGRIND};
932    $$toolnm =~ s|.*/||;  # keep basename
933    my @valgrind;	# gets content of file
934    if (-e $Valgrind_Log) {
935	if (open(V, $Valgrind_Log)) {
936	    @valgrind = <V>;
937	    close V;
938	} else {
939	    warn "$0: Failed to open '$Valgrind_Log': $!\n";
940	}
941    }
942    if ($ENV{VG_OPTS} =~ /(cachegrind)/ or $$toolnm =~ /(perf)/) {
943	$$toolnm = $1;
944	if ($$toolnm eq 'perf') {
945	    # append perfs subcommand, not just stat
946	    my ($sub) = split /\s/, $ENV{VG_OPTS};
947	    $$toolnm .= "-$sub";
948	}
949	if (rename $Valgrind_Log, "$$test.$$toolnm") {
950	    $$grind_ct++;
951	} else {
952	    warn "$0: Failed to create '$$test.$$toolnm': $!\n";
953	}
954    }
955    elsif (@valgrind) {
956	my $leaks = 0;
957	my $errors = 0;
958	for my $i (0..$#valgrind) {
959	    local $_ = $valgrind[$i];
960	    if (/^==\d+== ERROR SUMMARY: (\d+) errors? /) {
961		$errors = $errors + $1;   # there may be multiple error summaries
962	    } elsif (/^==\d+== LEAK SUMMARY:/) {
963		for my $off (1 .. 4) {
964		    if ($valgrind[$i+$off] =~
965			/(?:lost|reachable):\s+\d+ bytes in (\d+) blocks/) {
966			    $leaks = $leaks + $1;
967		    }
968		}
969	    }
970	}
971	if ($errors or $leaks) {
972	    if (rename $Valgrind_Log, "$$test.valgrind") {
973		$$grind_ct = $$grind_ct + 1;
974	    } else {
975		warn "$0: Failed to create '$$test.valgrind': $!\n";
976	    }
977	}
978    } else {
979        # Quiet wasn't asked for? Something may be amiss
980	if ($ENV{VG_OPTS} && $ENV{VG_OPTS} !~ /(^|\s)(-q|--quiet)(\s|$)/) {
981	    warn "No valgrind output?\n";
982	}
983    }
984    if (-e $Valgrind_Log) {
985	unlink $Valgrind_Log
986	    or warn "$0: Failed to unlink '$Valgrind_Log': $!\n";
987    }
988}
989
990sub _cleanup_valgrind {
991    return unless $ENV{PERL_VALGRIND};
992
993    my ($toolnm, $grind_ct) = @_;
994    my $s = $$grind_ct == 1 ? '' : 's';
995    print "$$grind_ct valgrind report$s created.\n", ;
996    if ($$toolnm eq 'cachegrind') {
997	# cachegrind leaves a lot of cachegrind.out.$pid litter
998	# around the tree, find and delete them
999	unlink _find_files('cachegrind.out.\d+$',
1000		     qw ( ../t ../cpan ../ext ../dist/ ));
1001    }
1002    elsif ($$toolnm eq 'valgrind') {
1003	# Remove empty, hence non-error, output files
1004	unlink grep { -z } _find_files('valgrind-current',
1005		     qw ( ../t ../cpan ../ext ../dist/ ));
1006    }
1007}
1008
1009# Generate regexps of known bad filenames / skips from Porting/deparse-skips.txt
1010
1011sub _process_deparse_config {
1012    my @deparse_failures;
1013    my @deparse_skips;
1014
1015    my $f = $deparse_skip_file;
1016
1017    my $skips;
1018    if (!open($skips, '<', $f)) {
1019        warn "Failed to find $f: $!\n";
1020        return;
1021    }
1022
1023    my $in;
1024    while(<$skips>) {
1025        if (/__DEPARSE_FAILURES__/) {
1026            $in = \@deparse_failures; next;
1027        } elsif (/__DEPARSE_SKIPS__/) {
1028            $in = \@deparse_skips; next;
1029        } elsif (!$in) {
1030            next;
1031	}
1032
1033        s/#.*$//; # Kill comments
1034        s/\s+$//; # And trailing whitespace
1035
1036        next unless $_;
1037
1038        push @$in, $_;
1039	warn "WARNING: $f:$.: excluded file doesn't exist: $_\n" unless -f $_;
1040    }
1041
1042    for my $f (@deparse_failures, @deparse_skips) {
1043        if ($f =~ m|/$|) { # Dir? Skip everything below it
1044            $f = qr/\Q$f\E.*/;
1045        } else {
1046            $f = qr/\Q$f\E/;
1047        }
1048    }
1049
1050    $deparse_failures = join('|', @deparse_failures);
1051    $deparse_failures = qr/^(?:$deparse_failures)$/;
1052
1053    $deparse_skips = join('|', @deparse_skips);
1054    $deparse_skips = qr/^(?:$deparse_skips)$/;
1055}
1056
1057# ex: set ts=8 sts=4 sw=4 noet:
1058