make Test::Harness use wait.h/WCOREDUMP if available
[p5sagit/p5-mst-13.2.git] / lib / Test / Harness.pm
CommitLineData
a0d0e21e 1package Test::Harness;
2
17f410f9 3use 5.005_64;
a0d0e21e 4use Exporter;
5use Benchmark;
4633a7c4 6use Config;
6c31b336 7use FileHandle;
760ac839 8use strict;
9
17f410f9 10our($VERSION, $verbose, $switches, $have_devel_corestack, $curtest,
11 @ISA, @EXPORT, @EXPORT_OK);
c0ee6f5c 12$have_devel_corestack = 0;
4633a7c4 13
0d0c0d42 14$VERSION = "1.1604";
9b0ceca9 15
f19ae7a7 16$ENV{HARNESS_ACTIVE} = 1;
17
9b0ceca9 18# Some experimental versions of OS/2 build have broken $?
19my $ignore_exitcode = $ENV{HARNESS_IGNORE_EXITCODE};
20
17a79f5b 21my $files_in_dir = $ENV{HARNESS_FILELEAK_IN_DIR};
22
9b0ceca9 23my $tests_skipped = 0;
24my $subtests_skipped = 0;
4633a7c4 25
c07a80fd 26@ISA=('Exporter');
cb1a09d0 27@EXPORT= qw(&runtests);
a0d0e21e 28@EXPORT_OK= qw($verbose $switches);
29
760ac839 30format STDOUT_TOP =
31Failed Test Status Wstat Total Fail Failed List of failed
fb73857a 32-------------------------------------------------------------------------------
760ac839 33.
34
35format STDOUT =
fb73857a 36@<<<<<<<<<<<<<< @>> @>>>> @>>>> @>>> ^##.##% ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
760ac839 37{ $curtest->{name},
38 $curtest->{estat},
39 $curtest->{wstat},
40 $curtest->{max},
41 $curtest->{failed},
42 $curtest->{percent},
43 $curtest->{canon}
44}
fb73857a 45~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
46 $curtest->{canon}
760ac839 47.
48
c07a80fd 49
6c31b336 50$verbose = 0;
51$switches = "-w";
a0d0e21e 52
17a79f5b 53sub globdir { opendir DIRH, shift; my @f = readdir DIRH; closedir DIRH; @f }
54
a0d0e21e 55sub runtests {
56 my(@tests) = @_;
57 local($|) = 1;
7b13a3f5 58 my($test,$te,$ok,$next,$max,$pct,$totok,$totbonus,@failed,%failedtests);
6c31b336 59 my $totmax = 0;
60 my $files = 0;
a0d0e21e 61 my $bad = 0;
62 my $good = 0;
63 my $total = @tests;
774d564b 64
65 # pass -I flags to children
81ff29e3 66 my $old5lib = $ENV{PERL5LIB};
774d564b 67
1250aba5 68 # VMS has a 255-byte limit on the length of %ENV entries, so
69 # toss the ones that involve perl_root, the install location
70 # for VMS
71 my $new5lib;
72 if ($^O eq 'VMS') {
73 $new5lib = join($Config{path_sep}, grep {!/perl_root/i;} @INC);
74 $switches =~ s/-(\S*[A-Z]\S*)/"-$1"/g;
75 }
76 else {
77 $new5lib = join($Config{path_sep}, @INC);
78 }
79
80 local($ENV{'PERL5LIB'}) = $new5lib;
a0d0e21e 81
17a79f5b 82 my @dir_files = globdir $files_in_dir if defined $files_in_dir;
a0d0e21e 83 my $t_start = new Benchmark;
84 while ($test = shift(@tests)) {
c07a80fd 85 $te = $test;
86 chop($te);
fe6f1558 87 if ($^O eq 'VMS') { $te =~ s/^.*\.t\./[.t./s; }
45c0de28 88 my $blank = (' ' x 77);
0d0c0d42 89 my $leader = "$te" . '.' x (20 - length($te));
90 my $ml = "";
23c4718a 91 $ml = "\r$blank\r$leader"
92 if -t STDOUT and not $ENV{HARNESS_NOTTY} and not $verbose;
0d0c0d42 93 print $leader;
6c31b336 94 my $fh = new FileHandle;
aa689395 95 $fh->open($test) or print "can't open $test. $!\n";
96 my $first = <$fh>;
97 my $s = $switches;
2b32313b 98 $s .= " $ENV{'HARNESS_PERL_SWITCHES'}"
99 if exists $ENV{'HARNESS_PERL_SWITCHES'};
da4582cd 100 $s .= join " ", q[ "-T"], map {qq["-I$_"]} @INC
101 if $first =~ /^#!.*\bperl.*-\w*T/;
aa689395 102 $fh->close or print "can't close $test. $!\n";
9636a016 103 my $cmd = ($ENV{'HARNESS_COMPILE_TEST'})
104 ? "./perl -I../lib ../utils/perlcc $test "
105 . "-run 2>> ./compilelog |"
106 : "$^X $s $test|";
a5077310 107 $cmd = "MCR $cmd" if $^O eq 'VMS';
aa689395 108 $fh->open($cmd) or print "can't run $test. $!\n";
c07a80fd 109 $ok = $next = $max = 0;
110 @failed = ();
7b13a3f5 111 my %todo = ();
112 my $bonus = 0;
fac76ed7 113 my $skipped = 0;
c854dc73 114 my $skip_reason;
c07a80fd 115 while (<$fh>) {
6c31b336 116 if( $verbose ){
c07a80fd 117 print $_;
118 }
7b13a3f5 119 if (/^1\.\.([0-9]+) todo([\d\s]+)\;/) {
120 $max = $1;
121 for (split(/\s+/, $2)) { $todo{$_} = 1; }
122 $totmax += $max;
123 $files++;
124 $next = 1;
45c0de28 125 } elsif (/^1\.\.([0-9]+)(\s*\#\s*[Ss]kip\S*(?>\s+)(.+))?/) {
c0ee6f5c 126 $max = $1;
127 $totmax += $max;
128 $files++;
129 $next = 1;
45c0de28 130 $skip_reason = $3 if not $max and defined $3;
c0ee6f5c 131 } elsif ($max && /^(not\s+)?ok\b/) {
132 my $this = $next;
133 if (/^not ok\s*(\d*)/){
134 $this = $1 if $1 > 0;
23c4718a 135 print "${ml}NOK $this" if $ml;
7b13a3f5 136 if (!$todo{$this}) {
137 push @failed, $this;
138 } else {
139 $ok++;
140 $totok++;
141 }
c854dc73 142 } elsif (/^ok\s*(\d*)(\s*\#\s*[Ss]kip\S*(?:(?>\s+)(.+))?)?/) {
c0ee6f5c 143 $this = $1 if $1 > 0;
45c0de28 144 print "${ml}ok $this/$max" if $ml;
c0ee6f5c 145 $ok++;
146 $totok++;
fac76ed7 147 $skipped++ if defined $2;
c854dc73 148 my $reason;
149 $reason = 'unknown reason' if defined $2;
150 $reason = $3 if defined $3;
151 if (defined $reason and defined $skip_reason) {
152 # print "was: '$skip_reason' new '$reason'\n";
153 $skip_reason = 'various reasons'
154 if $skip_reason ne $reason;
155 } elsif (defined $reason) {
156 $skip_reason = $reason;
157 }
7b13a3f5 158 $bonus++, $totbonus++ if $todo{$this};
c07a80fd 159 }
c0ee6f5c 160 if ($this > $next) {
161 # warn "Test output counter mismatch [test $this]\n";
162 # no need to warn probably
163 push @failed, $next..$this-1;
164 } elsif ($this < $next) {
165 #we have seen more "ok" lines than the number suggests
166 warn "Confused test output: test $this answered after test ", $next-1, "\n";
167 $next = $this;
168 }
169 $next = $this + 1;
c07a80fd 170 }
171 }
6c31b336 172 $fh->close; # must close to reap child resource values
9b0ceca9 173 my $wstatus = $ignore_exitcode ? 0 : $?; # Can trust $? ?
395b061e 174 my $estatus;
175 $estatus = ($^O eq 'VMS'
68dc0745 176 ? eval 'use vmsish "status"; $estatus = $?'
177 : $wstatus >> 8);
178 if ($wstatus) {
aa689395 179 my ($failed, $canon, $percent) = ('??', '??');
0d0c0d42 180 printf "${ml}dubious\n\tTest returned status $estatus (wstat %d, 0x%x)\n",
fb73857a 181 $wstatus,$wstatus;
68dc0745 182 print "\t\t(VMS status is $estatus)\n" if $^O eq 'VMS';
c0ee6f5c 183 if (corestatus($wstatus)) { # until we have a wait module
184 if ($have_devel_corestack) {
185 Devel::CoreStack::stack($^X);
186 } else {
187 print "\ttest program seems to have generated a core\n";
188 }
189 }
190 $bad++;
aa689395 191 if ($max) {
192 if ($next == $max + 1 and not @failed) {
193 print "\tafter all the subtests completed successfully\n";
194 $percent = 0;
195 $failed = 0; # But we do not set $canon!
196 } else {
197 push @failed, $next..$max;
198 $failed = @failed;
89d3b7e2 199 (my $txt, $canon) = canonfailed($max,$skipped,@failed);
aa689395 200 $percent = 100*(scalar @failed)/$max;
201 print "DIED. ",$txt;
202 }
203 }
204 $failedtests{$test} = { canon => $canon, max => $max || '??',
205 failed => $failed,
206 name => $test, percent => $percent,
760ac839 207 estat => $estatus, wstat => $wstatus,
208 };
c0ee6f5c 209 } elsif ($ok == $max && $next == $max+1) {
7b13a3f5 210 if ($max and $skipped + $bonus) {
211 my @msg;
45c0de28 212 push(@msg, "$skipped/$max skipped: $skip_reason")
7b13a3f5 213 if $skipped;
45c0de28 214 push(@msg, "$bonus/$max unexpectedly succeeded")
7b13a3f5 215 if $bonus;
45c0de28 216 print "${ml}ok, ".join(', ', @msg)."\n";
fac76ed7 217 } elsif ($max) {
45c0de28 218 print "${ml}ok\n";
219 } elsif (defined $skip_reason) {
220 print "skipped: $skip_reason\n";
221 $tests_skipped++;
c0ee6f5c 222 } else {
45c0de28 223 print "skipped test on this platform\n";
9b0ceca9 224 $tests_skipped++;
c0ee6f5c 225 }
c07a80fd 226 $good++;
6c31b336 227 } elsif ($max) {
228 if ($next <= $max) {
229 push @failed, $next..$max;
230 }
c07a80fd 231 if (@failed) {
89d3b7e2 232 my ($txt, $canon) = canonfailed($max,$skipped,@failed);
760ac839 233 print $txt;
234 $failedtests{$test} = { canon => $canon, max => $max,
235 failed => scalar @failed,
236 name => $test, percent => 100*(scalar @failed)/$max,
237 estat => '', wstat => '',
238 };
c07a80fd 239 } else {
c0ee6f5c 240 print "Don't know which tests failed: got $ok ok, expected $max\n";
760ac839 241 $failedtests{$test} = { canon => '??', max => $max,
242 failed => '??',
243 name => $test, percent => undef,
244 estat => '', wstat => '',
245 };
c07a80fd 246 }
247 $bad++;
6c31b336 248 } elsif ($next == 0) {
249 print "FAILED before any test output arrived\n";
250 $bad++;
760ac839 251 $failedtests{$test} = { canon => '??', max => '??',
252 failed => '??',
253 name => $test, percent => undef,
254 estat => '', wstat => '',
255 };
6c31b336 256 }
9b0ceca9 257 $subtests_skipped += $skipped;
17a79f5b 258 if (defined $files_in_dir) {
259 my @new_dir_files = globdir $files_in_dir;
260 if (@new_dir_files != @dir_files) {
261 my %f;
262 @f{@new_dir_files} = (1) x @new_dir_files;
263 delete @f{@dir_files};
264 my @f = sort keys %f;
265 print "LEAKED FILES: @f\n";
266 @dir_files = @new_dir_files;
267 }
268 }
a0d0e21e 269 }
270 my $t_total = timediff(new Benchmark, $t_start);
c07a80fd 271
774d564b 272 if ($^O eq 'VMS') {
273 if (defined $old5lib) {
274 $ENV{PERL5LIB} = $old5lib;
b876d4a6 275 } else {
774d564b 276 delete $ENV{PERL5LIB};
277 }
278 }
7b13a3f5 279 my $bonusmsg = '';
280 $bonusmsg = (" ($totbonus subtest".($totbonus>1?'s':'').
281 " UNEXPECTEDLY SUCCEEDED)")
282 if $totbonus;
9b0ceca9 283 if ($tests_skipped) {
c4f6c246 284 $bonusmsg .= ", $tests_skipped test" . ($tests_skipped != 1 ? 's' : '');
285 if ($subtests_skipped) {
286 $bonusmsg .= " and $subtests_skipped subtest"
287 . ($subtests_skipped != 1 ? 's' : '');
288 }
289 $bonusmsg .= ' skipped';
9b0ceca9 290 }
c4f6c246 291 elsif ($subtests_skipped) {
292 $bonusmsg .= ", $subtests_skipped subtest"
293 . ($subtests_skipped != 1 ? 's' : '')
294 . " skipped";
9b0ceca9 295 }
6c31b336 296 if ($bad == 0 && $totmax) {
7b13a3f5 297 print "All tests successful$bonusmsg.\n";
6c31b336 298 } elsif ($total==0){
299 die "FAILED--no tests were run for some reason.\n";
300 } elsif ($totmax==0) {
301 my $blurb = $total==1 ? "script" : "scripts";
c0ee6f5c 302 die "FAILED--$total test $blurb could be run, alas--no output ever seen\n";
c07a80fd 303 } else {
304 $pct = sprintf("%.2f", $good / $total * 100);
6c31b336 305 my $subpct = sprintf " %d/%d subtests failed, %.2f%% okay.",
306 $totmax - $totok, $totmax, 100*$totok/$totmax;
760ac839 307 my $script;
308 for $script (sort keys %failedtests) {
309 $curtest = $failedtests{$script};
310 write;
311 }
b876d4a6 312 if ($bad) {
9b0ceca9 313 $bonusmsg =~ s/^,\s*//;
314 print "$bonusmsg.\n" if $bonusmsg;
6c31b336 315 die "Failed $bad/$total test scripts, $pct% okay.$subpct\n";
c07a80fd 316 }
317 }
5603f27d 318 printf("Files=%d, Tests=%d, %s\n", $files, $totmax, timestr($t_total, 'nop'));
f0a9308e 319
320 return ($bad == 0 && $totmax) ;
c07a80fd 321}
322
aa689395 323my $tried_devel_corestack;
c0ee6f5c 324sub corestatus {
325 my($st) = @_;
c0ee6f5c 326
327 eval {require 'wait.ph'};
0dfaec25 328 my $ret = defined &WCOREDUMP ? WCOREDUMP($st) : $st & 0200;
c0ee6f5c 329
aa689395 330 eval { require Devel::CoreStack; $have_devel_corestack++ }
331 unless $tried_devel_corestack++;
c0ee6f5c 332
333 $ret;
334}
335
c07a80fd 336sub canonfailed ($@) {
89d3b7e2 337 my($max,$skipped,@failed) = @_;
6c31b336 338 my %seen;
339 @failed = sort {$a <=> $b} grep !$seen{$_}++, @failed;
c07a80fd 340 my $failed = @failed;
341 my @result = ();
342 my @canon = ();
343 my $min;
344 my $last = $min = shift @failed;
760ac839 345 my $canon;
c07a80fd 346 if (@failed) {
347 for (@failed, $failed[-1]) { # don't forget the last one
348 if ($_ > $last+1 || $_ == $last) {
349 if ($min == $last) {
350 push @canon, $last;
351 } else {
352 push @canon, "$min-$last";
353 }
354 $min = $_;
355 }
356 $last = $_;
357 }
358 local $" = ", ";
359 push @result, "FAILED tests @canon\n";
760ac839 360 $canon = "@canon";
a0d0e21e 361 } else {
c07a80fd 362 push @result, "FAILED test $last\n";
760ac839 363 $canon = $last;
a0d0e21e 364 }
c07a80fd 365
366 push @result, "\tFailed $failed/$max tests, ";
89d3b7e2 367 push @result, sprintf("%.2f",100*(1-$failed/$max)), "% okay";
368 my $ender = 's' x ($skipped > 1);
369 my $good = $max - $failed - $skipped;
370 my $goodper = sprintf("%.2f",100*($good/$max));
371 push @result, " (-$skipped skipped test$ender: $good okay, $goodper%)" if $skipped;
372 push @result, "\n";
760ac839 373 my $txt = join "", @result;
374 ($txt, $canon);
a0d0e21e 375}
376
3771;
cb1a09d0 378__END__
379
380=head1 NAME
381
382Test::Harness - run perl standard test scripts with statistics
383
384=head1 SYNOPSIS
385
386use Test::Harness;
387
388runtests(@tests);
389
390=head1 DESCRIPTION
391
7b13a3f5 392(By using the L<Test> module, you can write test scripts without
393knowing the exact output this module expects. However, if you need to
394know the specifics, read on!)
395
cb1a09d0 396Perl test scripts print to standard output C<"ok N"> for each single
397test, where C<N> is an increasing sequence of integers. The first line
c0ee6f5c 398output by a standard test script is C<"1..M"> with C<M> being the
cb1a09d0 399number of tests that should be run within the test
c0ee6f5c 400script. Test::Harness::runtests(@tests) runs all the testscripts
cb1a09d0 401named as arguments and checks standard output for the expected
402C<"ok N"> strings.
403
c0ee6f5c 404After all tests have been performed, runtests() prints some
cb1a09d0 405performance statistics that are computed by the Benchmark module.
406
6c31b336 407=head2 The test script output
408
409Any output from the testscript to standard error is ignored and
410bypassed, thus will be seen by the user. Lines written to standard
c0ee6f5c 411output containing C</^(not\s+)?ok\b/> are interpreted as feedback for
412runtests(). All other lines are discarded.
6c31b336 413
414It is tolerated if the test numbers after C<ok> are omitted. In this
415case Test::Harness maintains temporarily its own counter until the
416script supplies test numbers again. So the following test script
417
418 print <<END;
419 1..6
420 not ok
421 ok
422 not ok
423 ok
424 ok
425 END
426
427will generate
428
429 FAILED tests 1, 3, 6
430 Failed 3/6 tests, 50.00% okay
431
432The global variable $Test::Harness::verbose is exportable and can be
c0ee6f5c 433used to let runtests() display the standard output of the script
6c31b336 434without altering the behavior otherwise.
435
fb73857a 436The global variable $Test::Harness::switches is exportable and can be
437used to set perl command line options used for running the test
438script(s). The default value is C<-w>.
439
fac76ed7 440If the standard output line contains substring C< # Skip> (with
441variations in spacing and case) after C<ok> or C<ok NUMBER>, it is
442counted as a skipped test. If the whole testscript succeeds, the
443count of skipped tests is included in the generated output.
444
45c0de28 445C<Test::Harness> reports the text after C< # Skip(whatever)> as a
446reason for skipping. Similarly, one can include a similar explanation
447in a C<1..0> line emitted if the test is skipped completely:
448
449 1..0 # Skipped: no leverage found
450
cb1a09d0 451=head1 EXPORT
452
c0ee6f5c 453C<&runtests> is exported by Test::Harness per default.
cb1a09d0 454
455=head1 DIAGNOSTICS
456
457=over 4
458
459=item C<All tests successful.\nFiles=%d, Tests=%d, %s>
460
461If all tests are successful some statistics about the performance are
462printed.
463
6c31b336 464=item C<FAILED tests %s\n\tFailed %d/%d tests, %.2f%% okay.>
465
466For any single script that has failing subtests statistics like the
467above are printed.
468
469=item C<Test returned status %d (wstat %d)>
470
81ff29e3 471Scripts that return a non-zero exit status, both C<$? E<gt>E<gt> 8> and C<$?> are
6c31b336 472printed in a message similar to the above.
473
474=item C<Failed 1 test, %.2f%% okay. %s>
cb1a09d0 475
6c31b336 476=item C<Failed %d/%d tests, %.2f%% okay. %s>
cb1a09d0 477
478If not all tests were successful, the script dies with one of the
479above messages.
480
481=back
482
9b0ceca9 483=head1 ENVIRONMENT
484
17a79f5b 485Setting C<HARNESS_IGNORE_EXITCODE> makes harness ignore the exit status
9b0ceca9 486of child processes.
487
0d0c0d42 488Setting C<HARNESS_NOTTY> to a true value forces it to behave as though
489STDOUT were not a console. You may need to set this if you don't want
490harness to output more frequent progress messages using carriage returns.
491Some consoles may not handle carriage returns properly (which results
492in a somewhat messy output).
493
9636a016 494Setting C<HARNESS_COMPILE_TEST> to a true value will make harness attempt
495to compile the test using C<perlcc> before running it.
496
17a79f5b 497If C<HARNESS_FILELEAK_IN_DIR> is set to the name of a directory, harness
498will check after each test whether new files appeared in that directory,
499and report them as
500
501 LEAKED FILES: scr.tmp 0 my.db
502
503If relative, directory name is with respect to the current directory at
504the moment runtests() was called. Putting absolute path into
505C<HARNESS_FILELEAK_IN_DIR> may give more predicatable results.
506
2b32313b 507The value of C<HARNESS_PERL_SWITCHES> will be prepended to the
508switches used to invoke perl on each test. For example, setting
509C<HARNESS_PERL_SWITCHES> to "-W" will run all tests with all
510warnings enabled.
511
f19ae7a7 512Harness sets C<HARNESS_ACTIVE> before executing the individual tests.
513This allows the tests to determine if they are being executed through the
514harness or by any other means.
515
cb1a09d0 516=head1 SEE ALSO
517
7b13a3f5 518L<Test> for writing test scripts and also L<Benchmark> for the
519underlying timing routines.
c07a80fd 520
521=head1 AUTHORS
522
523Either Tim Bunce or Andreas Koenig, we don't know. What we know for
524sure is, that it was inspired by Larry Wall's TEST script that came
b876d4a6 525with perl distributions for ages. Numerous anonymous contributors
526exist. Current maintainer is Andreas Koenig.
cb1a09d0 527
528=head1 BUGS
529
530Test::Harness uses $^X to determine the perl binary to run the tests
6c31b336 531with. Test scripts running via the shebang (C<#!>) line may not be
532portable because $^X is not consistent for shebang scripts across
cb1a09d0 533platforms. This is no problem when Test::Harness is run with an
6c31b336 534absolute path to the perl binary or when $^X can be found in the path.
cb1a09d0 535
536=cut