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