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