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