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