perl 5.003_01: lib/Sys/Syslog.pm
[p5sagit/p5-mst-13.2.git] / lib / Test / Harness.pm
CommitLineData
a0d0e21e 1package Test::Harness;
2
3use Exporter;
4use Benchmark;
4633a7c4 5use Config;
6c31b336 6use FileHandle;
7use vars qw($VERSION $verbose $switches);
c07a80fd 8require 5.002;
4633a7c4 9
6c31b336 10$VERSION = "1.07";
4633a7c4 11
c07a80fd 12@ISA=('Exporter');
cb1a09d0 13@EXPORT= qw(&runtests);
a0d0e21e 14@EXPORT_OK= qw($verbose $switches);
15
c07a80fd 16
6c31b336 17$verbose = 0;
18$switches = "-w";
a0d0e21e 19
20sub runtests {
21 my(@tests) = @_;
22 local($|) = 1;
6c31b336 23 my($test,$te,$ok,$next,$max,$pct);
24 my $totmax = 0;
25 my $files = 0;
a0d0e21e 26 my $bad = 0;
27 my $good = 0;
28 my $total = @tests;
c07a80fd 29 local($ENV{'PERL5LIB'}) = join($Config{path_sep}, @INC); # pass -I flags to children
a0d0e21e 30
31 my $t_start = new Benchmark;
32 while ($test = shift(@tests)) {
c07a80fd 33 $te = $test;
34 chop($te);
35 print "$te" . '.' x (20 - length($te));
6c31b336 36 my $fh = new FileHandle;
37 $fh->open("$^X $switches $test|") || (print "can't run. $!\n");
c07a80fd 38 $ok = $next = $max = 0;
39 @failed = ();
40 while (<$fh>) {
6c31b336 41 if( $verbose ){
c07a80fd 42 print $_;
43 }
6c31b336 44 unless (/^\s*\#/) {
c07a80fd 45 if (/^1\.\.([0-9]+)/) {
46 $max = $1;
47 $totmax += $max;
48 $files++;
49 $next = 1;
6c31b336 50 } elsif ($max && /^(not\s+)?ok\b/) {
51 my $this = $next;
52 if (/^not ok\s*(\d*)/){
53 $this = $1 if $1 > 0;
54 push @failed, $this;
55 } elsif (/^ok\s*(\d*)/) {
56 $this = $1 if $1 > 0;
c07a80fd 57 $ok++;
6c31b336 58 $totok++;
c07a80fd 59 }
6c31b336 60 if ($this > $next) {
61 # warn "Test output counter mismatch [test $this]\n";
62 # no need to warn probably
63 push @failed, $next..$this-1;
64 } elsif ($this < $next) {
65 #we have seen more "ok" lines than the number suggests
66 warn "Aborting test: output counter mismatch [test $this answered when test $next expected]\n";
67 last;
68 }
69 $next = $this + 1;
c07a80fd 70 }
71 }
72 }
6c31b336 73 $fh->close; # must close to reap child resource values
c07a80fd 74 my $wstatus = $?;
75 my $estatus = $wstatus >> 8;
6c31b336 76 if ($ok == $max && $next == $max+1 && ! $estatus) {
c07a80fd 77 print "ok\n";
78 $good++;
6c31b336 79 } elsif ($max) {
80 if ($next <= $max) {
81 push @failed, $next..$max;
82 }
c07a80fd 83 if (@failed) {
84 print canonfailed($max,@failed);
85 } else {
6c31b336 86 print "Don't know which tests failed for some reason\n";
c07a80fd 87 }
88 $bad++;
6c31b336 89 } elsif ($next == 0) {
90 print "FAILED before any test output arrived\n";
91 $bad++;
92 }
93 if ($wstatus) {
94 print "\tTest returned status $estatus (wstat $wstatus)\n";
c07a80fd 95 }
a0d0e21e 96 }
97 my $t_total = timediff(new Benchmark, $t_start);
c07a80fd 98
6c31b336 99 if ($bad == 0 && $totmax) {
c07a80fd 100 print "All tests successful.\n";
6c31b336 101 } elsif ($total==0){
102 die "FAILED--no tests were run for some reason.\n";
103 } elsif ($totmax==0) {
104 my $blurb = $total==1 ? "script" : "scripts";
105 die "FAILED--$total test $blurb could be run, alas -- no output ever seen\n";
c07a80fd 106 } else {
107 $pct = sprintf("%.2f", $good / $total * 100);
6c31b336 108 my $subpct = sprintf " %d/%d subtests failed, %.2f%% okay.",
109 $totmax - $totok, $totmax, 100*$totok/$totmax;
c07a80fd 110 if ($bad == 1) {
6c31b336 111 die "Failed 1 test script, $pct% okay.$subpct\n";
c07a80fd 112 } else {
6c31b336 113 die "Failed $bad/$total test scripts, $pct% okay.$subpct\n";
c07a80fd 114 }
115 }
116 printf("Files=%d, Tests=%d, %s\n", $files, $totmax, timestr($t_total, 'nop'));
117}
118
119sub canonfailed ($@) {
120 my($max,@failed) = @_;
6c31b336 121 my %seen;
122 @failed = sort {$a <=> $b} grep !$seen{$_}++, @failed;
c07a80fd 123 my $failed = @failed;
124 my @result = ();
125 my @canon = ();
126 my $min;
127 my $last = $min = shift @failed;
128 if (@failed) {
129 for (@failed, $failed[-1]) { # don't forget the last one
130 if ($_ > $last+1 || $_ == $last) {
131 if ($min == $last) {
132 push @canon, $last;
133 } else {
134 push @canon, "$min-$last";
135 }
136 $min = $_;
137 }
138 $last = $_;
139 }
140 local $" = ", ";
141 push @result, "FAILED tests @canon\n";
a0d0e21e 142 } else {
c07a80fd 143 push @result, "FAILED test $last\n";
a0d0e21e 144 }
c07a80fd 145
146 push @result, "\tFailed $failed/$max tests, ";
147 push @result, sprintf("%.2f",100*(1-$failed/$max)), "% okay\n";
148 join "", @result;
a0d0e21e 149}
150
1511;
cb1a09d0 152__END__
153
154=head1 NAME
155
156Test::Harness - run perl standard test scripts with statistics
157
158=head1 SYNOPSIS
159
160use Test::Harness;
161
162runtests(@tests);
163
164=head1 DESCRIPTION
165
166Perl test scripts print to standard output C<"ok N"> for each single
167test, where C<N> is an increasing sequence of integers. The first line
168output by a standard test scxript is C<"1..M"> with C<M> being the
169number of tests that should be run within the test
170script. Test::Harness::runscripts(@tests) runs all the testscripts
171named as arguments and checks standard output for the expected
172C<"ok N"> strings.
173
174After all tests have been performed, runscripts() prints some
175performance statistics that are computed by the Benchmark module.
176
6c31b336 177=head2 The test script output
178
179Any output from the testscript to standard error is ignored and
180bypassed, thus will be seen by the user. Lines written to standard
181output that look like perl comments (start with C</^\s*\#/>) are
182discarded. Lines containing C</^(not\s+)?ok\b/> are interpreted as
183feedback for runtests().
184
185It is tolerated if the test numbers after C<ok> are omitted. In this
186case Test::Harness maintains temporarily its own counter until the
187script supplies test numbers again. So the following test script
188
189 print <<END;
190 1..6
191 not ok
192 ok
193 not ok
194 ok
195 ok
196 END
197
198will generate
199
200 FAILED tests 1, 3, 6
201 Failed 3/6 tests, 50.00% okay
202
203The global variable $Test::Harness::verbose is exportable and can be
204used to let runscripts() display the standard output of the script
205without altering the behavior otherwise.
206
cb1a09d0 207=head1 EXPORT
208
209C<&runscripts> is exported by Test::Harness per default.
210
211=head1 DIAGNOSTICS
212
213=over 4
214
215=item C<All tests successful.\nFiles=%d, Tests=%d, %s>
216
217If all tests are successful some statistics about the performance are
218printed.
219
6c31b336 220=item C<FAILED tests %s\n\tFailed %d/%d tests, %.2f%% okay.>
221
222For any single script that has failing subtests statistics like the
223above are printed.
224
225=item C<Test returned status %d (wstat %d)>
226
227Scripts that return a non-zero exit status, both $?>>8 and $? are
228printed in a message similar to the above.
229
230=item C<Failed 1 test, %.2f%% okay. %s>
cb1a09d0 231
6c31b336 232=item C<Failed %d/%d tests, %.2f%% okay. %s>
cb1a09d0 233
234If not all tests were successful, the script dies with one of the
235above messages.
236
237=back
238
239=head1 SEE ALSO
240
c07a80fd 241See L<Benchmark> for the underlying timing routines.
242
243=head1 AUTHORS
244
245Either Tim Bunce or Andreas Koenig, we don't know. What we know for
246sure is, that it was inspired by Larry Wall's TEST script that came
247with perl distributions for ages. Current maintainer is Andreas
248Koenig.
cb1a09d0 249
250=head1 BUGS
251
252Test::Harness uses $^X to determine the perl binary to run the tests
6c31b336 253with. Test scripts running via the shebang (C<#!>) line may not be
254portable because $^X is not consistent for shebang scripts across
cb1a09d0 255platforms. This is no problem when Test::Harness is run with an
6c31b336 256absolute path to the perl binary or when $^X can be found in the path.
cb1a09d0 257
258=cut