really add the new files
[gitmo/moose-presentations.git] / moose-class / exercises / t / lib / Test / Harness.pm
CommitLineData
4920168e 1package Test::Harness;
2
3require 5.00405;
4
5use strict;
6
7use constant IS_WIN32 => ( $^O =~ /^(MS)?Win32$/ );
8use constant IS_VMS => ( $^O eq 'VMS' );
9
10use TAP::Harness ();
11use TAP::Parser::Aggregator ();
12use TAP::Parser::Source::Perl ();
13
14use TAP::Parser::Utils qw( split_shell );
15
16use Config;
17use Exporter;
18
19# TODO: Emulate at least some of these
20use vars qw(
21 $VERSION
22 @ISA @EXPORT @EXPORT_OK
23 $Verbose $Switches $Debug
24 $verbose $switches $debug
25 $Columns
26 $Color
27 $Directives
28 $Timer
29 $Strap
30 $has_time_hires
31 $IgnoreExit
32);
33
34# $ML $Last_ML_Print
35
36BEGIN {
37 eval q{use Time::HiRes 'time'};
38 $has_time_hires = !$@;
39}
40
41=head1 NAME
42
43Test::Harness - Run Perl standard test scripts with statistics
44
45=head1 VERSION
46
47Version 3.17
48
49=cut
50
51$VERSION = '3.17';
52
53# Backwards compatibility for exportable variable names.
54*verbose = *Verbose;
55*switches = *Switches;
56*debug = *Debug;
57
58$ENV{HARNESS_ACTIVE} = 1;
59$ENV{HARNESS_VERSION} = $VERSION;
60
61END {
62
63 # For VMS.
64 delete $ENV{HARNESS_ACTIVE};
65 delete $ENV{HARNESS_VERSION};
66}
67
68@ISA = ('Exporter');
69@EXPORT = qw(&runtests);
70@EXPORT_OK = qw(&execute_tests $verbose $switches);
71
72$Verbose = $ENV{HARNESS_VERBOSE} || 0;
73$Debug = $ENV{HARNESS_DEBUG} || 0;
74$Switches = '-w';
75$Columns = $ENV{HARNESS_COLUMNS} || $ENV{COLUMNS} || 80;
76$Columns--; # Some shells have trouble with a full line of text.
77$Timer = $ENV{HARNESS_TIMER} || 0;
78$Color = $ENV{HARNESS_COLOR} || 0;
79$IgnoreExit = $ENV{HARNESS_IGNORE_EXIT} || 0;
80
81=head1 SYNOPSIS
82
83 use Test::Harness;
84
85 runtests(@test_files);
86
87=head1 DESCRIPTION
88
89Although, for historical reasons, the L<Test::Harness> distribution
90takes its name from this module it now exists only to provide
91L<TAP::Harness> with an interface that is somewhat backwards compatible
92with L<Test::Harness> 2.xx. If you're writing new code consider using
93L<TAP::Harness> directly instead.
94
95Emulation is provided for C<runtests> and C<execute_tests> but the
96pluggable 'Straps' interface that previous versions of L<Test::Harness>
97supported is not reproduced here. Straps is now available as a stand
98alone module: L<Test::Harness::Straps>.
99
100See L<TAP::Parser>, L<TAP::Harness> for the main documentation for this
101distribution.
102
103=head1 FUNCTIONS
104
105The following functions are available.
106
107=head2 runtests( @test_files )
108
109This runs all the given I<@test_files> and divines whether they passed
110or failed based on their output to STDOUT (details above). It prints
111out each individual test which failed along with a summary report and
112a how long it all took.
113
114It returns true if everything was ok. Otherwise it will C<die()> with
115one of the messages in the DIAGNOSTICS section.
116
117=cut
118
119sub _has_taint {
120 my $test = shift;
121 return TAP::Parser::Source::Perl->get_taint(
122 TAP::Parser::Source::Perl->shebang($test) );
123}
124
125sub _aggregate {
126 my ( $harness, $aggregate, @tests ) = @_;
127
128 # Don't propagate to our children
129 local $ENV{HARNESS_OPTIONS};
130
131 _apply_extra_INC($harness);
132 _aggregate_tests( $harness, $aggregate, @tests );
133}
134
135# Make sure the child seens all the extra junk in @INC
136sub _apply_extra_INC {
137 my $harness = shift;
138
139 $harness->callback(
140 parser_args => sub {
141 my ( $args, $test ) = @_;
142 push @{ $args->{switches} }, map {"-I$_"} _filtered_inc();
143 }
144 );
145}
146
147sub _aggregate_tests {
148 my ( $harness, $aggregate, @tests ) = @_;
149 $aggregate->start();
150 $harness->aggregate_tests( $aggregate, @tests );
151 $aggregate->stop();
152
153}
154
155sub runtests {
156 my @tests = @_;
157
158 # shield against -l
159 local ( $\, $, );
160
161 my $harness = _new_harness();
162 my $aggregate = TAP::Parser::Aggregator->new();
163
164 _aggregate( $harness, $aggregate, @tests );
165
166 $harness->formatter->summary($aggregate);
167
168 my $total = $aggregate->total;
169 my $passed = $aggregate->passed;
170 my $failed = $aggregate->failed;
171
172 my @parsers = $aggregate->parsers;
173
174 my $num_bad = 0;
175 for my $parser (@parsers) {
176 $num_bad++ if $parser->has_problems;
177 }
178
179 die(sprintf(
180 "Failed %d/%d test programs. %d/%d subtests failed.\n",
181 $num_bad, scalar @parsers, $failed, $total
182 )
183 ) if $num_bad;
184
185 return $total && $total == $passed;
186}
187
188sub _canon {
189 my @list = sort { $a <=> $b } @_;
190 my @ranges = ();
191 my $count = scalar @list;
192 my $pos = 0;
193
194 while ( $pos < $count ) {
195 my $end = $pos + 1;
196 $end++ while $end < $count && $list[$end] <= $list[ $end - 1 ] + 1;
197 push @ranges, ( $end == $pos + 1 )
198 ? $list[$pos]
199 : join( '-', $list[$pos], $list[ $end - 1 ] );
200 $pos = $end;
201 }
202
203 return join( ' ', @ranges );
204}
205
206sub _new_harness {
207 my $sub_args = shift || {};
208
209 my ( @lib, @switches );
210 my @opt = split_shell( $Switches, $ENV{HARNESS_PERL_SWITCHES} );
211 while ( my $opt = shift @opt ) {
212 if ( $opt =~ /^ -I (.*) $ /x ) {
213 push @lib, length($1) ? $1 : shift @opt;
214 }
215 else {
216 push @switches, $opt;
217 }
218 }
219
220 # Do things the old way on VMS...
221 push @lib, _filtered_inc() if IS_VMS;
222
223 # If $Verbose isn't numeric default to 1. This helps core.
224 my $verbosity = ( $Verbose ? ( $Verbose !~ /\d/ ) ? 1 : $Verbose : 0 );
225
226 my $args = {
227 timer => $Timer,
228 directives => $Directives,
229 lib => \@lib,
230 switches => \@switches,
231 color => $Color,
232 verbosity => $verbosity,
233 ignore_exit => $IgnoreExit,
234 };
235
236 $args->{stdout} = $sub_args->{out}
237 if exists $sub_args->{out};
238
239 if ( defined( my $env_opt = $ENV{HARNESS_OPTIONS} ) ) {
240 for my $opt ( split /:/, $env_opt ) {
241 if ( $opt =~ /^j(\d*)$/ ) {
242 $args->{jobs} = $1 || 9;
243 }
244 elsif ( $opt eq 'c' ) {
245 $args->{color} = 1;
246 }
247 else {
248 die "Unknown HARNESS_OPTIONS item: $opt\n";
249 }
250 }
251 }
252
253 return TAP::Harness->new($args);
254}
255
256# Get the parts of @INC which are changed from the stock list AND
257# preserve reordering of stock directories.
258sub _filtered_inc {
259 my @inc = grep { !ref } @INC; #28567
260
261 if (IS_VMS) {
262
263 # VMS has a 255-byte limit on the length of %ENV entries, so
264 # toss the ones that involve perl_root, the install location
265 @inc = grep !/perl_root/i, @inc;
266
267 }
268 elsif (IS_WIN32) {
269
270 # Lose any trailing backslashes in the Win32 paths
271 s/[\\\/]+$// foreach @inc;
272 }
273
274 my @default_inc = _default_inc();
275
276 my @new_inc;
277 my %seen;
278 for my $dir (@inc) {
279 next if $seen{$dir}++;
280
281 if ( $dir eq ( $default_inc[0] || '' ) ) {
282 shift @default_inc;
283 }
284 else {
285 push @new_inc, $dir;
286 }
287
288 shift @default_inc while @default_inc and $seen{ $default_inc[0] };
289 }
290
291 return @new_inc;
292}
293
294{
295
296 # Cache this to avoid repeatedly shelling out to Perl.
297 my @inc;
298
299 sub _default_inc {
300 return @inc if @inc;
301
302 local $ENV{PERL5LIB};
303 local $ENV{PERLLIB};
304
305 my $perl = $ENV{HARNESS_PERL} || $^X;
306
307 # Avoid using -l for the benefit of Perl 6
308 chomp( @inc = `$perl -e "print join qq[\\n], \@INC, q[]"` );
309 return @inc;
310 }
311}
312
313sub _check_sequence {
314 my @list = @_;
315 my $prev;
316 while ( my $next = shift @list ) {
317 return if defined $prev && $next <= $prev;
318 $prev = $next;
319 }
320
321 return 1;
322}
323
324sub execute_tests {
325 my %args = @_;
326
327 my $harness = _new_harness( \%args );
328 my $aggregate = TAP::Parser::Aggregator->new();
329
330 my %tot = (
331 bonus => 0,
332 max => 0,
333 ok => 0,
334 bad => 0,
335 good => 0,
336 files => 0,
337 tests => 0,
338 sub_skipped => 0,
339 todo => 0,
340 skipped => 0,
341 bench => undef,
342 );
343
344 # Install a callback so we get to see any plans the
345 # harness executes.
346 $harness->callback(
347 made_parser => sub {
348 my $parser = shift;
349 $parser->callback(
350 plan => sub {
351 my $plan = shift;
352 if ( $plan->directive eq 'SKIP' ) {
353 $tot{skipped}++;
354 }
355 }
356 );
357 }
358 );
359
360 _aggregate( $harness, $aggregate, @{ $args{tests} } );
361
362 $tot{bench} = $aggregate->elapsed;
363 my @tests = $aggregate->descriptions;
364
365 # TODO: Work out the circumstances under which the files
366 # and tests totals can differ.
367 $tot{files} = $tot{tests} = scalar @tests;
368
369 my %failedtests = ();
370 my %todo_passed = ();
371
372 for my $test (@tests) {
373 my ($parser) = $aggregate->parsers($test);
374
375 my @failed = $parser->failed;
376
377 my $wstat = $parser->wait;
378 my $estat = $parser->exit;
379 my $planned = $parser->tests_planned;
380 my @errors = $parser->parse_errors;
381 my $passed = $parser->passed;
382 my $actual_passed = $parser->actual_passed;
383
384 my $ok_seq = _check_sequence( $parser->actual_passed );
385
386 # Duplicate exit, wait status semantics of old version
387 $estat ||= '' unless $wstat;
388 $wstat ||= '';
389
390 $tot{max} += ( $planned || 0 );
391 $tot{bonus} += $parser->todo_passed;
392 $tot{ok} += $passed > $actual_passed ? $passed : $actual_passed;
393 $tot{sub_skipped} += $parser->skipped;
394 $tot{todo} += $parser->todo;
395
396 if ( @failed || $estat || @errors ) {
397 $tot{bad}++;
398
399 my $huh_planned = $planned ? undef : '??';
400 my $huh_errors = $ok_seq ? undef : '??';
401
402 $failedtests{$test} = {
403 'canon' => $huh_planned
404 || $huh_errors
405 || _canon(@failed)
406 || '??',
407 'estat' => $estat,
408 'failed' => $huh_planned
409 || $huh_errors
410 || scalar @failed,
411 'max' => $huh_planned || $planned,
412 'name' => $test,
413 'wstat' => $wstat
414 };
415 }
416 else {
417 $tot{good}++;
418 }
419
420 my @todo = $parser->todo_passed;
421 if (@todo) {
422 $todo_passed{$test} = {
423 'canon' => _canon(@todo),
424 'estat' => $estat,
425 'failed' => scalar @todo,
426 'max' => scalar $parser->todo,
427 'name' => $test,
428 'wstat' => $wstat
429 };
430 }
431 }
432
433 return ( \%tot, \%failedtests, \%todo_passed );
434}
435
436=head2 execute_tests( tests => \@test_files, out => \*FH )
437
438Runs all the given C<@test_files> (just like C<runtests()>) but
439doesn't generate the final report. During testing, progress
440information will be written to the currently selected output
441filehandle (usually C<STDOUT>), or to the filehandle given by the
442C<out> parameter. The I<out> is optional.
443
444Returns a list of two values, C<$total> and C<$failed>, describing the
445results. C<$total> is a hash ref summary of all the tests run. Its
446keys and values are this:
447
448 bonus Number of individual todo tests unexpectedly passed
449 max Number of individual tests ran
450 ok Number of individual tests passed
451 sub_skipped Number of individual tests skipped
452 todo Number of individual todo tests
453
454 files Number of test files ran
455 good Number of test files passed
456 bad Number of test files failed
457 tests Number of test files originally given
458 skipped Number of test files skipped
459
460If C<< $total->{bad} == 0 >> and C<< $total->{max} > 0 >>, you've
461got a successful test.
462
463C<$failed> is a hash ref of all the test scripts that failed. Each key
464is the name of a test script, each value is another hash representing
465how that script failed. Its keys are these:
466
467 name Name of the test which failed
468 estat Script's exit value
469 wstat Script's wait status
470 max Number of individual tests
471 failed Number which failed
472 canon List of tests which failed (as string).
473
474C<$failed> should be empty if everything passed.
475
476=cut
477
4781;
479__END__
480
481=head1 EXPORT
482
483C<&runtests> is exported by C<Test::Harness> by default.
484
485C<&execute_tests>, C<$verbose>, C<$switches> and C<$debug> are
486exported upon request.
487
488=head1 ENVIRONMENT VARIABLES THAT TAP::HARNESS::COMPATIBLE SETS
489
490C<Test::Harness> sets these before executing the individual tests.
491
492=over 4
493
494=item C<HARNESS_ACTIVE>
495
496This is set to a true value. It allows the tests to determine if they
497are being executed through the harness or by any other means.
498
499=item C<HARNESS_VERSION>
500
501This is the version of C<Test::Harness>.
502
503=back
504
505=head1 ENVIRONMENT VARIABLES THAT AFFECT TEST::HARNESS
506
507=over 4
508
509=item C<HARNESS_TIMER>
510
511Setting this to true will make the harness display the number of
512milliseconds each test took. You can also use F<prove>'s C<--timer>
513switch.
514
515=item C<HARNESS_VERBOSE>
516
517If true, C<Test::Harness> will output the verbose results of running
518its tests. Setting C<$Test::Harness::verbose> will override this,
519or you can use the C<-v> switch in the F<prove> utility.
520
521=item C<HARNESS_OPTIONS>
522
523Provide additional options to the harness. Currently supported options are:
524
525=over
526
527=item C<< j<n> >>
528
529Run <n> (default 9) parallel jobs.
530
531=item C<< f >>
532
533Use forked parallelism.
534
535=back
536
537Multiple options may be separated by colons:
538
539 HARNESS_OPTIONS=j9:f make test
540
541=back
542
543=head1 Taint Mode
544
545Normally when a Perl program is run in taint mode the contents of the
546C<PERL5LIB> environment variable do not appear in C<@INC>.
547
548Because C<PERL5LIB> is often used during testing to add build
549directories to C<@INC> C<Test::Harness> (actually
550L<TAP::Parser::Source::Perl>) passes the names of any directories found
551in C<PERL5LIB> as -I switches. The net effect of this is that
552C<PERL5LIB> is honoured even in taint mode.
553
554=head1 SEE ALSO
555
556L<TAP::Harness>
557
558=head1 BUGS
559
560Please report any bugs or feature requests to
561C<bug-test-harness at rt.cpan.org>, or through the web interface at
562L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Harness>. I will be
563notified, and then you'll automatically be notified of progress on your bug
564as I make changes.
565
566=head1 AUTHORS
567
568Andy Armstrong C<< <andy@hexten.net> >>
569
570L<Test::Harness> 2.64 (maintained by Andy Lester and on which this
571module is based) has this attribution:
572
573 Either Tim Bunce or Andreas Koenig, we don't know. What we know for
574 sure is, that it was inspired by Larry Wall's F<TEST> script that came
575 with perl distributions for ages. Numerous anonymous contributors
576 exist. Andreas Koenig held the torch for many years, and then
577 Michael G Schwern.
578
579=head1 LICENCE AND COPYRIGHT
580
581Copyright (c) 2007-2008, Andy Armstrong C<< <andy@hexten.net> >>. All rights reserved.
582
583This module is free software; you can redistribute it and/or
584modify it under the same terms as Perl itself. See L<perlartistic>.
585