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