change#5905 wasn't quite right--it's intent only applies when arguments
[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) = @_;
326 my($ret);
327
328 eval {require 'wait.ph'};
329 if ($@) {
330 SWITCH: {
331 $ret = ($st & 0200); # Tim says, this is for 90%
332 }
333 } else {
334 $ret = WCOREDUMP($st);
335 }
336
aa689395 337 eval { require Devel::CoreStack; $have_devel_corestack++ }
338 unless $tried_devel_corestack++;
c0ee6f5c 339
340 $ret;
341}
342
c07a80fd 343sub canonfailed ($@) {
89d3b7e2 344 my($max,$skipped,@failed) = @_;
6c31b336 345 my %seen;
346 @failed = sort {$a <=> $b} grep !$seen{$_}++, @failed;
c07a80fd 347 my $failed = @failed;
348 my @result = ();
349 my @canon = ();
350 my $min;
351 my $last = $min = shift @failed;
760ac839 352 my $canon;
c07a80fd 353 if (@failed) {
354 for (@failed, $failed[-1]) { # don't forget the last one
355 if ($_ > $last+1 || $_ == $last) {
356 if ($min == $last) {
357 push @canon, $last;
358 } else {
359 push @canon, "$min-$last";
360 }
361 $min = $_;
362 }
363 $last = $_;
364 }
365 local $" = ", ";
366 push @result, "FAILED tests @canon\n";
760ac839 367 $canon = "@canon";
a0d0e21e 368 } else {
c07a80fd 369 push @result, "FAILED test $last\n";
760ac839 370 $canon = $last;
a0d0e21e 371 }
c07a80fd 372
373 push @result, "\tFailed $failed/$max tests, ";
89d3b7e2 374 push @result, sprintf("%.2f",100*(1-$failed/$max)), "% okay";
375 my $ender = 's' x ($skipped > 1);
376 my $good = $max - $failed - $skipped;
377 my $goodper = sprintf("%.2f",100*($good/$max));
378 push @result, " (-$skipped skipped test$ender: $good okay, $goodper%)" if $skipped;
379 push @result, "\n";
760ac839 380 my $txt = join "", @result;
381 ($txt, $canon);
a0d0e21e 382}
383
3841;
cb1a09d0 385__END__
386
387=head1 NAME
388
389Test::Harness - run perl standard test scripts with statistics
390
391=head1 SYNOPSIS
392
393use Test::Harness;
394
395runtests(@tests);
396
397=head1 DESCRIPTION
398
7b13a3f5 399(By using the L<Test> module, you can write test scripts without
400knowing the exact output this module expects. However, if you need to
401know the specifics, read on!)
402
cb1a09d0 403Perl test scripts print to standard output C<"ok N"> for each single
404test, where C<N> is an increasing sequence of integers. The first line
c0ee6f5c 405output by a standard test script is C<"1..M"> with C<M> being the
cb1a09d0 406number of tests that should be run within the test
c0ee6f5c 407script. Test::Harness::runtests(@tests) runs all the testscripts
cb1a09d0 408named as arguments and checks standard output for the expected
409C<"ok N"> strings.
410
c0ee6f5c 411After all tests have been performed, runtests() prints some
cb1a09d0 412performance statistics that are computed by the Benchmark module.
413
6c31b336 414=head2 The test script output
415
416Any output from the testscript to standard error is ignored and
417bypassed, thus will be seen by the user. Lines written to standard
c0ee6f5c 418output containing C</^(not\s+)?ok\b/> are interpreted as feedback for
419runtests(). All other lines are discarded.
6c31b336 420
421It is tolerated if the test numbers after C<ok> are omitted. In this
422case Test::Harness maintains temporarily its own counter until the
423script supplies test numbers again. So the following test script
424
425 print <<END;
426 1..6
427 not ok
428 ok
429 not ok
430 ok
431 ok
432 END
433
434will generate
435
436 FAILED tests 1, 3, 6
437 Failed 3/6 tests, 50.00% okay
438
439The global variable $Test::Harness::verbose is exportable and can be
c0ee6f5c 440used to let runtests() display the standard output of the script
6c31b336 441without altering the behavior otherwise.
442
fb73857a 443The global variable $Test::Harness::switches is exportable and can be
444used to set perl command line options used for running the test
445script(s). The default value is C<-w>.
446
fac76ed7 447If the standard output line contains substring C< # Skip> (with
448variations in spacing and case) after C<ok> or C<ok NUMBER>, it is
449counted as a skipped test. If the whole testscript succeeds, the
450count of skipped tests is included in the generated output.
451
45c0de28 452C<Test::Harness> reports the text after C< # Skip(whatever)> as a
453reason for skipping. Similarly, one can include a similar explanation
454in a C<1..0> line emitted if the test is skipped completely:
455
456 1..0 # Skipped: no leverage found
457
cb1a09d0 458=head1 EXPORT
459
c0ee6f5c 460C<&runtests> is exported by Test::Harness per default.
cb1a09d0 461
462=head1 DIAGNOSTICS
463
464=over 4
465
466=item C<All tests successful.\nFiles=%d, Tests=%d, %s>
467
468If all tests are successful some statistics about the performance are
469printed.
470
6c31b336 471=item C<FAILED tests %s\n\tFailed %d/%d tests, %.2f%% okay.>
472
473For any single script that has failing subtests statistics like the
474above are printed.
475
476=item C<Test returned status %d (wstat %d)>
477
81ff29e3 478Scripts that return a non-zero exit status, both C<$? E<gt>E<gt> 8> and C<$?> are
6c31b336 479printed in a message similar to the above.
480
481=item C<Failed 1 test, %.2f%% okay. %s>
cb1a09d0 482
6c31b336 483=item C<Failed %d/%d tests, %.2f%% okay. %s>
cb1a09d0 484
485If not all tests were successful, the script dies with one of the
486above messages.
487
488=back
489
9b0ceca9 490=head1 ENVIRONMENT
491
17a79f5b 492Setting C<HARNESS_IGNORE_EXITCODE> makes harness ignore the exit status
9b0ceca9 493of child processes.
494
0d0c0d42 495Setting C<HARNESS_NOTTY> to a true value forces it to behave as though
496STDOUT were not a console. You may need to set this if you don't want
497harness to output more frequent progress messages using carriage returns.
498Some consoles may not handle carriage returns properly (which results
499in a somewhat messy output).
500
9636a016 501Setting C<HARNESS_COMPILE_TEST> to a true value will make harness attempt
502to compile the test using C<perlcc> before running it.
503
17a79f5b 504If C<HARNESS_FILELEAK_IN_DIR> is set to the name of a directory, harness
505will check after each test whether new files appeared in that directory,
506and report them as
507
508 LEAKED FILES: scr.tmp 0 my.db
509
510If relative, directory name is with respect to the current directory at
511the moment runtests() was called. Putting absolute path into
512C<HARNESS_FILELEAK_IN_DIR> may give more predicatable results.
513
2b32313b 514The value of C<HARNESS_PERL_SWITCHES> will be prepended to the
515switches used to invoke perl on each test. For example, setting
516C<HARNESS_PERL_SWITCHES> to "-W" will run all tests with all
517warnings enabled.
518
f19ae7a7 519Harness sets C<HARNESS_ACTIVE> before executing the individual tests.
520This allows the tests to determine if they are being executed through the
521harness or by any other means.
522
cb1a09d0 523=head1 SEE ALSO
524
7b13a3f5 525L<Test> for writing test scripts and also L<Benchmark> for the
526underlying timing routines.
c07a80fd 527
528=head1 AUTHORS
529
530Either Tim Bunce or Andreas Koenig, we don't know. What we know for
531sure is, that it was inspired by Larry Wall's TEST script that came
b876d4a6 532with perl distributions for ages. Numerous anonymous contributors
533exist. Current maintainer is Andreas Koenig.
cb1a09d0 534
535=head1 BUGS
536
537Test::Harness uses $^X to determine the perl binary to run the tests
6c31b336 538with. Test scripts running via the shebang (C<#!>) line may not be
539portable because $^X is not consistent for shebang scripts across
cb1a09d0 540platforms. This is no problem when Test::Harness is run with an
6c31b336 541absolute path to the perl binary or when $^X can be found in the path.
cb1a09d0 542
543=cut