perlcall.pod SAVETMPS/FREETMPS bracket
[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
9b0ceca9 14$VERSION = "1.1602";
15
16# Some experimental versions of OS/2 build have broken $?
17my $ignore_exitcode = $ENV{HARNESS_IGNORE_EXITCODE};
18
19my $tests_skipped = 0;
20my $subtests_skipped = 0;
4633a7c4 21
c07a80fd 22@ISA=('Exporter');
cb1a09d0 23@EXPORT= qw(&runtests);
a0d0e21e 24@EXPORT_OK= qw($verbose $switches);
25
760ac839 26format STDOUT_TOP =
27Failed Test Status Wstat Total Fail Failed List of failed
fb73857a 28-------------------------------------------------------------------------------
760ac839 29.
30
31format STDOUT =
fb73857a 32@<<<<<<<<<<<<<< @>> @>>>> @>>>> @>>> ^##.##% ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
760ac839 33{ $curtest->{name},
34 $curtest->{estat},
35 $curtest->{wstat},
36 $curtest->{max},
37 $curtest->{failed},
38 $curtest->{percent},
39 $curtest->{canon}
40}
fb73857a 41~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
42 $curtest->{canon}
760ac839 43.
44
c07a80fd 45
6c31b336 46$verbose = 0;
47$switches = "-w";
a0d0e21e 48
49sub runtests {
50 my(@tests) = @_;
51 local($|) = 1;
7b13a3f5 52 my($test,$te,$ok,$next,$max,$pct,$totok,$totbonus,@failed,%failedtests);
6c31b336 53 my $totmax = 0;
54 my $files = 0;
a0d0e21e 55 my $bad = 0;
56 my $good = 0;
57 my $total = @tests;
774d564b 58
59 # pass -I flags to children
81ff29e3 60 my $old5lib = $ENV{PERL5LIB};
774d564b 61 local($ENV{'PERL5LIB'}) = join($Config{path_sep}, @INC);
62
a5077310 63 if ($^O eq 'VMS') { $switches =~ s/-(\S*[A-Z]\S*)/"-$1"/g }
a0d0e21e 64
65 my $t_start = new Benchmark;
66 while ($test = shift(@tests)) {
c07a80fd 67 $te = $test;
68 chop($te);
68dc0745 69 if ($^O eq 'VMS') { $te =~ s/^.*\.t\./[.t./; }
c07a80fd 70 print "$te" . '.' x (20 - length($te));
6c31b336 71 my $fh = new FileHandle;
aa689395 72 $fh->open($test) or print "can't open $test. $!\n";
73 my $first = <$fh>;
74 my $s = $switches;
68dc0745 75 $s .= q[ "-T"] if $first =~ /^#!.*\bperl.*-\w*T/;
aa689395 76 $fh->close or print "can't close $test. $!\n";
52cebf5e 77 my $cmd = ($ENV{'COMPILE_TEST'})?
78"./perl -I../lib ../utils/perlcc $test -run -verbose dcf -log ./compilelog |"
79 : "$^X $s $test|";
a5077310 80 $cmd = "MCR $cmd" if $^O eq 'VMS';
aa689395 81 $fh->open($cmd) or print "can't run $test. $!\n";
c07a80fd 82 $ok = $next = $max = 0;
83 @failed = ();
7b13a3f5 84 my %todo = ();
85 my $bonus = 0;
fac76ed7 86 my $skipped = 0;
c07a80fd 87 while (<$fh>) {
6c31b336 88 if( $verbose ){
c07a80fd 89 print $_;
90 }
7b13a3f5 91 if (/^1\.\.([0-9]+) todo([\d\s]+)\;/) {
92 $max = $1;
93 for (split(/\s+/, $2)) { $todo{$_} = 1; }
94 $totmax += $max;
95 $files++;
96 $next = 1;
97 } elsif (/^1\.\.([0-9]+)/) {
c0ee6f5c 98 $max = $1;
99 $totmax += $max;
100 $files++;
101 $next = 1;
102 } elsif ($max && /^(not\s+)?ok\b/) {
103 my $this = $next;
104 if (/^not ok\s*(\d*)/){
105 $this = $1 if $1 > 0;
7b13a3f5 106 if (!$todo{$this}) {
107 push @failed, $this;
108 } else {
109 $ok++;
110 $totok++;
111 }
fac76ed7 112 } elsif (/^ok\s*(\d*)(\s*\#\s*[Ss]kip)?/) {
c0ee6f5c 113 $this = $1 if $1 > 0;
114 $ok++;
115 $totok++;
fac76ed7 116 $skipped++ if defined $2;
7b13a3f5 117 $bonus++, $totbonus++ if $todo{$this};
c07a80fd 118 }
c0ee6f5c 119 if ($this > $next) {
120 # warn "Test output counter mismatch [test $this]\n";
121 # no need to warn probably
122 push @failed, $next..$this-1;
123 } elsif ($this < $next) {
124 #we have seen more "ok" lines than the number suggests
125 warn "Confused test output: test $this answered after test ", $next-1, "\n";
126 $next = $this;
127 }
128 $next = $this + 1;
c07a80fd 129 }
130 }
6c31b336 131 $fh->close; # must close to reap child resource values
9b0ceca9 132 my $wstatus = $ignore_exitcode ? 0 : $?; # Can trust $? ?
395b061e 133 my $estatus;
134 $estatus = ($^O eq 'VMS'
68dc0745 135 ? eval 'use vmsish "status"; $estatus = $?'
136 : $wstatus >> 8);
137 if ($wstatus) {
aa689395 138 my ($failed, $canon, $percent) = ('??', '??');
fb73857a 139 printf "dubious\n\tTest returned status $estatus (wstat %d, 0x%x)\n",
140 $wstatus,$wstatus;
68dc0745 141 print "\t\t(VMS status is $estatus)\n" if $^O eq 'VMS';
c0ee6f5c 142 if (corestatus($wstatus)) { # until we have a wait module
143 if ($have_devel_corestack) {
144 Devel::CoreStack::stack($^X);
145 } else {
146 print "\ttest program seems to have generated a core\n";
147 }
148 }
149 $bad++;
aa689395 150 if ($max) {
151 if ($next == $max + 1 and not @failed) {
152 print "\tafter all the subtests completed successfully\n";
153 $percent = 0;
154 $failed = 0; # But we do not set $canon!
155 } else {
156 push @failed, $next..$max;
157 $failed = @failed;
158 (my $txt, $canon) = canonfailed($max,@failed);
159 $percent = 100*(scalar @failed)/$max;
160 print "DIED. ",$txt;
161 }
162 }
163 $failedtests{$test} = { canon => $canon, max => $max || '??',
164 failed => $failed,
165 name => $test, percent => $percent,
760ac839 166 estat => $estatus, wstat => $wstatus,
167 };
c0ee6f5c 168 } elsif ($ok == $max && $next == $max+1) {
7b13a3f5 169 if ($max and $skipped + $bonus) {
170 my @msg;
171 push(@msg, "$skipped subtest".($skipped>1?'s':'')." skipped")
172 if $skipped;
173 push(@msg, "$bonus subtest".($bonus>1?'s':'').
174 " unexpectedly succeeded")
175 if $bonus;
176 print "ok, ".join(', ', @msg)."\n";
fac76ed7 177 } elsif ($max) {
c0ee6f5c 178 print "ok\n";
179 } else {
180 print "skipping test on this platform\n";
9b0ceca9 181 $tests_skipped++;
c0ee6f5c 182 }
c07a80fd 183 $good++;
6c31b336 184 } elsif ($max) {
185 if ($next <= $max) {
186 push @failed, $next..$max;
187 }
c07a80fd 188 if (@failed) {
760ac839 189 my ($txt, $canon) = canonfailed($max,@failed);
190 print $txt;
191 $failedtests{$test} = { canon => $canon, max => $max,
192 failed => scalar @failed,
193 name => $test, percent => 100*(scalar @failed)/$max,
194 estat => '', wstat => '',
195 };
c07a80fd 196 } else {
c0ee6f5c 197 print "Don't know which tests failed: got $ok ok, expected $max\n";
760ac839 198 $failedtests{$test} = { canon => '??', max => $max,
199 failed => '??',
200 name => $test, percent => undef,
201 estat => '', wstat => '',
202 };
c07a80fd 203 }
204 $bad++;
6c31b336 205 } elsif ($next == 0) {
206 print "FAILED before any test output arrived\n";
207 $bad++;
760ac839 208 $failedtests{$test} = { canon => '??', max => '??',
209 failed => '??',
210 name => $test, percent => undef,
211 estat => '', wstat => '',
212 };
6c31b336 213 }
9b0ceca9 214 $subtests_skipped += $skipped;
a0d0e21e 215 }
216 my $t_total = timediff(new Benchmark, $t_start);
c07a80fd 217
774d564b 218 if ($^O eq 'VMS') {
219 if (defined $old5lib) {
220 $ENV{PERL5LIB} = $old5lib;
b876d4a6 221 } else {
774d564b 222 delete $ENV{PERL5LIB};
223 }
224 }
7b13a3f5 225 my $bonusmsg = '';
226 $bonusmsg = (" ($totbonus subtest".($totbonus>1?'s':'').
227 " UNEXPECTEDLY SUCCEEDED)")
228 if $totbonus;
9b0ceca9 229 if ($tests_skipped) {
230 $bonusmsg .= ", $tests_skipped test" . ($tests_skipped != 1 ? 's' : '') .
231 ' skipped';
232 }
233 if ($subtests_skipped) {
234 $bonusmsg .= ($tests_skipped ? ', plus ' : ', ').
235 "$subtests_skipped subtest"
236 . ($subtests_skipped != 1 ? 's' : '') .
237 " skipped";
238 }
6c31b336 239 if ($bad == 0 && $totmax) {
7b13a3f5 240 print "All tests successful$bonusmsg.\n";
6c31b336 241 } elsif ($total==0){
242 die "FAILED--no tests were run for some reason.\n";
243 } elsif ($totmax==0) {
244 my $blurb = $total==1 ? "script" : "scripts";
c0ee6f5c 245 die "FAILED--$total test $blurb could be run, alas--no output ever seen\n";
c07a80fd 246 } else {
247 $pct = sprintf("%.2f", $good / $total * 100);
6c31b336 248 my $subpct = sprintf " %d/%d subtests failed, %.2f%% okay.",
249 $totmax - $totok, $totmax, 100*$totok/$totmax;
760ac839 250 my $script;
251 for $script (sort keys %failedtests) {
252 $curtest = $failedtests{$script};
253 write;
254 }
b876d4a6 255 if ($bad) {
9b0ceca9 256 $bonusmsg =~ s/^,\s*//;
257 print "$bonusmsg.\n" if $bonusmsg;
6c31b336 258 die "Failed $bad/$total test scripts, $pct% okay.$subpct\n";
c07a80fd 259 }
260 }
261 printf("Files=%d, Tests=%d, %s\n", $files, $totmax, timestr($t_total, 'nop'));
f0a9308e 262
263 return ($bad == 0 && $totmax) ;
c07a80fd 264}
265
aa689395 266my $tried_devel_corestack;
c0ee6f5c 267sub corestatus {
268 my($st) = @_;
269 my($ret);
270
271 eval {require 'wait.ph'};
272 if ($@) {
273 SWITCH: {
274 $ret = ($st & 0200); # Tim says, this is for 90%
275 }
276 } else {
277 $ret = WCOREDUMP($st);
278 }
279
aa689395 280 eval { require Devel::CoreStack; $have_devel_corestack++ }
281 unless $tried_devel_corestack++;
c0ee6f5c 282
283 $ret;
284}
285
c07a80fd 286sub canonfailed ($@) {
287 my($max,@failed) = @_;
6c31b336 288 my %seen;
289 @failed = sort {$a <=> $b} grep !$seen{$_}++, @failed;
c07a80fd 290 my $failed = @failed;
291 my @result = ();
292 my @canon = ();
293 my $min;
294 my $last = $min = shift @failed;
760ac839 295 my $canon;
c07a80fd 296 if (@failed) {
297 for (@failed, $failed[-1]) { # don't forget the last one
298 if ($_ > $last+1 || $_ == $last) {
299 if ($min == $last) {
300 push @canon, $last;
301 } else {
302 push @canon, "$min-$last";
303 }
304 $min = $_;
305 }
306 $last = $_;
307 }
308 local $" = ", ";
309 push @result, "FAILED tests @canon\n";
760ac839 310 $canon = "@canon";
a0d0e21e 311 } else {
c07a80fd 312 push @result, "FAILED test $last\n";
760ac839 313 $canon = $last;
a0d0e21e 314 }
c07a80fd 315
316 push @result, "\tFailed $failed/$max tests, ";
317 push @result, sprintf("%.2f",100*(1-$failed/$max)), "% okay\n";
760ac839 318 my $txt = join "", @result;
319 ($txt, $canon);
a0d0e21e 320}
321
3221;
cb1a09d0 323__END__
324
325=head1 NAME
326
327Test::Harness - run perl standard test scripts with statistics
328
329=head1 SYNOPSIS
330
331use Test::Harness;
332
333runtests(@tests);
334
335=head1 DESCRIPTION
336
7b13a3f5 337(By using the L<Test> module, you can write test scripts without
338knowing the exact output this module expects. However, if you need to
339know the specifics, read on!)
340
cb1a09d0 341Perl test scripts print to standard output C<"ok N"> for each single
342test, where C<N> is an increasing sequence of integers. The first line
c0ee6f5c 343output by a standard test script is C<"1..M"> with C<M> being the
cb1a09d0 344number of tests that should be run within the test
c0ee6f5c 345script. Test::Harness::runtests(@tests) runs all the testscripts
cb1a09d0 346named as arguments and checks standard output for the expected
347C<"ok N"> strings.
348
c0ee6f5c 349After all tests have been performed, runtests() prints some
cb1a09d0 350performance statistics that are computed by the Benchmark module.
351
6c31b336 352=head2 The test script output
353
354Any output from the testscript to standard error is ignored and
355bypassed, thus will be seen by the user. Lines written to standard
c0ee6f5c 356output containing C</^(not\s+)?ok\b/> are interpreted as feedback for
357runtests(). All other lines are discarded.
6c31b336 358
359It is tolerated if the test numbers after C<ok> are omitted. In this
360case Test::Harness maintains temporarily its own counter until the
361script supplies test numbers again. So the following test script
362
363 print <<END;
364 1..6
365 not ok
366 ok
367 not ok
368 ok
369 ok
370 END
371
372will generate
373
374 FAILED tests 1, 3, 6
375 Failed 3/6 tests, 50.00% okay
376
377The global variable $Test::Harness::verbose is exportable and can be
c0ee6f5c 378used to let runtests() display the standard output of the script
6c31b336 379without altering the behavior otherwise.
380
fb73857a 381The global variable $Test::Harness::switches is exportable and can be
382used to set perl command line options used for running the test
383script(s). The default value is C<-w>.
384
fac76ed7 385If the standard output line contains substring C< # Skip> (with
386variations in spacing and case) after C<ok> or C<ok NUMBER>, it is
387counted as a skipped test. If the whole testscript succeeds, the
388count of skipped tests is included in the generated output.
389
cb1a09d0 390=head1 EXPORT
391
c0ee6f5c 392C<&runtests> is exported by Test::Harness per default.
cb1a09d0 393
394=head1 DIAGNOSTICS
395
396=over 4
397
398=item C<All tests successful.\nFiles=%d, Tests=%d, %s>
399
400If all tests are successful some statistics about the performance are
401printed.
402
6c31b336 403=item C<FAILED tests %s\n\tFailed %d/%d tests, %.2f%% okay.>
404
405For any single script that has failing subtests statistics like the
406above are printed.
407
408=item C<Test returned status %d (wstat %d)>
409
81ff29e3 410Scripts that return a non-zero exit status, both C<$? E<gt>E<gt> 8> and C<$?> are
6c31b336 411printed in a message similar to the above.
412
413=item C<Failed 1 test, %.2f%% okay. %s>
cb1a09d0 414
6c31b336 415=item C<Failed %d/%d tests, %.2f%% okay. %s>
cb1a09d0 416
417If not all tests were successful, the script dies with one of the
418above messages.
419
420=back
421
9b0ceca9 422=head1 ENVIRONMENT
423
424Setting C<HARNESS_IGNORE_EXITCODE> makes it ignore the exit status
425of child processes.
426
cb1a09d0 427=head1 SEE ALSO
428
7b13a3f5 429L<Test> for writing test scripts and also L<Benchmark> for the
430underlying timing routines.
c07a80fd 431
432=head1 AUTHORS
433
434Either Tim Bunce or Andreas Koenig, we don't know. What we know for
435sure is, that it was inspired by Larry Wall's TEST script that came
b876d4a6 436with perl distributions for ages. Numerous anonymous contributors
437exist. Current maintainer is Andreas Koenig.
cb1a09d0 438
439=head1 BUGS
440
441Test::Harness uses $^X to determine the perl binary to run the tests
6c31b336 442with. Test scripts running via the shebang (C<#!>) line may not be
443portable because $^X is not consistent for shebang scripts across
cb1a09d0 444platforms. This is no problem when Test::Harness is run with an
6c31b336 445absolute path to the perl binary or when $^X can be found in the path.
cb1a09d0 446
447=cut