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