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