File Coverage

blib/lib/Test/Steering/Wheel.pm
Criterion Covered Total %
statement 137 142 96.4
branch 35 42 83.3
condition 5 9 55.5
subroutine 24 25 96.0
pod 5 5 100.0
total 206 223 92.3


line stmt bran cond sub pod time code
1             package Test::Steering::Wheel;
2              
3 5     5   80841 use warnings;
  5         10  
  5         224  
4 5     5   33 use strict;
  5         17  
  5         143  
5 5     5   30 use Carp;
  5         10  
  5         322  
6 5     5   5807 use TAP::Harness;
  5         88899  
  5         177  
7 5     5   51 use Scalar::Util qw(refaddr);
  5         13  
  5         759  
8              
9             =head1 NAME
10              
11             Test::Steering::Wheel - Execute tests and renumber the resulting TAP.
12              
13             =head1 VERSION
14              
15             This document describes Test::Steering::Wheel version 0.02
16              
17             =cut
18              
19             our $VERSION = '0.02';
20              
21             =head1 SYNOPSIS
22              
23             use Test::Steering::Wheel;
24              
25             my $wheel = Test::Steering::Wheel->new;
26             $wheel->include_tests( 'xt/vms/*.t' ) if $^O eq 'VMS';
27             $wheel->include_tests( 'xt/windows/*.t' ) if $^O =~ 'MSWin32';
28              
29             =head1 DESCRIPTION
30              
31             Behind the scenes in L is a singleton instance of
32             C.
33              
34             See L for more information.
35              
36             =head1 INTERFACE
37              
38             =head2 C<< new >>
39              
40             Create a new C.
41              
42             =over
43              
44             =item C<< add_prefix >>
45              
46             =item C<< announce >>
47              
48             =item C<< defaults >>
49              
50             =item C<< harness >>
51              
52             =back
53              
54             =cut
55              
56             {
57             my %DEFAULTS;
58              
59             BEGIN {
60 5     5   47 %DEFAULTS = (
61             add_prefix => 0,
62             announce => 0,
63             defaults => {},
64             harness => 'TAP::Harness',
65             );
66              
67 5         23 for my $method ( keys %DEFAULTS ) {
68 5     5   40 no strict 'refs';
  5         8  
  5         435  
69 17         7620 *{ __PACKAGE__ . '::' . $method } = sub {
70 58     58   97 my $self = shift;
71 58 50       335 croak "$method may not be set" if @_;
72 58         410 return $self->{$method};
73 17         84 };
74             }
75             }
76              
77             sub new {
78 16     16 1 36660 my $class = shift;
79 16 50       121 croak "Must supply an even number of arguments" if @_ % 1;
80 16         201 my %args = ( %DEFAULTS, @_ );
81              
82 16         88 my @bad = grep { !exists $DEFAULTS{$_} } keys %args;
  61         164  
83 16 50       74 croak "Illegal option(s): ", join ', ', sort @bad if @bad;
84              
85 16         164 return bless { _test_number_adjust => 0, %args }, $class;
86             }
87              
88             # Documentation lower down
89             sub option_names {
90 3     3 1 32 my $class = shift;
91 2         18 return sort keys %DEFAULTS;
92             }
93             }
94              
95             # Output demultiplexer. Handles output associated with multiple parsers.
96             # If parsers output sequentially no buffering is done. If, however,
97             # output from multiple parsers is interleaved output from the first
98             # encountered will be echoed directly and output from all the others
99             # will be buffered.
100             #
101             # After a parser finishes (calls $done) the next parser to generate
102             # output will have its buffer flushed and will start output directly.
103             #
104             # The upshot of all this is that we output from multiple parsers doing
105             # the minimum amount of buffering necessary to keep per-parser output
106             # ordered.
107              
108             sub _output_demux {
109 14     15   1024 my ( $self, $printer, $complete ) = @_;
110 14         32 my $current_id = undef;
111 14         37 my %queue_for = ();
112 14         29 my @completed = ();
113              
114             my $finish = sub {
115 31     31   1978 while ( my $job = shift @completed ) {
116 4         24 my ( $parser, $buffered ) = @$job;
117 4         23 $printer->( $parser, @$_ ) for @$buffered;
118 4         22 $complete->( $parser );
119             }
120 14         89 };
121              
122             return (
123             # demux
124             sub {
125 88     88   2305 my ( $parser, $type, $line ) = @_;
126 88         363 my $id = refaddr $parser;
127              
128 88 100       311 unless ( defined $current_id ) {
129             # Our chance to take over...
130 18 100       105 if ( $self->announce ) {
131 1         15 my $name = $self->_name_for_parser( $parser );
132 1         94 print STDERR "# Running $name\n";
133             }
134 18 100       104 if ( my $buffered = delete $queue_for{$id} ) {
135 1         5 $printer->( $parser, @$_ ) for @$buffered;
136             }
137 18         74 $current_id = $id;
138             }
139              
140 88 100       274 if ( $current_id == $id ) {
141 87         299 $printer->( $parser, $type, $line );
142             }
143             else {
144 1         2 push @{ $queue_for{$id} }, [ $type, $line ];
  1         8  
145             }
146              
147             },
148             # done
149             sub {
150 21     21   855 my $parser = shift;
151 21         259 my $id = refaddr $parser;
152 21 100 66     507 if ( defined $current_id && $current_id == $id ) {
153             # Finished the current one so allow another to
154             # take over
155 17         67 $complete->( $parser );
156 17         107 undef $current_id;
157             # Flush any others that have completed in the mean time
158 17         56 $finish->();
159             }
160             else {
161             # Add to completed list
162 4         37 push @completed, [ $parser, delete $queue_for{$id} ];
163             }
164             },
165             # finish
166 14         201 $finish,
167             );
168             }
169              
170             sub _name_for_parser {
171 51     51   157 my $self = shift;
172 51         392 my $parser = shift;
173 51         198 my $id = refaddr $parser;
174 51 100       304 return $self->{parser_name}->{$id} unless @_;
175 20         184 return $self->{parser_name}->{$id} = shift;
176             }
177              
178             # Like ok
179             sub _output_result {
180 22     22   45 my ( $self, $ok, $description ) = @_;
181 22 100       2191 printf( "%sok %d %s\n",
182             $ok ? '' : 'not ',
183             ++$self->{_test_number_adjust}, $description );
184             }
185              
186             # Output additional test failures if our subtest had problems.
187              
188             sub _parser_postmortem {
189 20     20   41 my ( $self, $parser ) = @_;
190              
191 20         86 my $test = $self->_name_for_parser( $parser );
192              
193 20         45 my @errs = ();
194              
195 20         70 push @errs, "$test: Parse error: $_" for $parser->parse_errors;
196              
197 20         177 my ( $wait, $exit ) = ( $parser->wait, $parser->exit );
198 20 100 66     328 push @errs, "$test: Non-zero status: exit=$exit, wait=$wait"
199             if $exit || $wait;
200              
201 20 100       63 if ( @errs ) {
202 2         14 $self->_output_result( 0, $_ ) for @errs;
203             }
204             else {
205 18         93 $self->_output_result( 1, "$test done" );
206             }
207             }
208              
209             sub _load {
210 13     13   22 my $class = shift;
211 13 50 33 2   2575 unless ( $INC{$class} || eval "use $class; 1" ) {
  2         15  
  2         5  
  2         47  
212 0         0 croak "Can't load $class: $@";
213             }
214 13         126 return $class;
215             }
216              
217             =head2 C<< include_tests >>
218              
219             Run one or more tests. Wildcards will be expanded.
220              
221             include_tests( 'xt/vms/*.t' ) if $^O eq 'VMS';
222             include_tests( 'xt/windows/*.t' ) if $^O =~ 'MSWin32';
223              
224             =cut
225              
226             sub include_tests {
227 13     13 1 31192 my ( $self, @tests ) = @_;
228              
229 13         35 my %options = ( verbosity => -9, %{ $self->defaults } );
  13         52  
230 13         29 my @real_tests = ();
231              
232             # Split options hashes from tests
233 13 100       33 for my $t (
  21         341  
234 20 100       882 map { 'ARRAY' eq ref $_ ? $_ : [ $_, $_ ] }
235             map { ref $_ ? $_ : glob $_ } @tests
236             ) {
237 21 50       96 if ( 'HASH' eq ref $t ) {
238 0         0 %options = ( %options, %$t );
239             }
240             else {
241 21         158 push @real_tests,
242 21         38 grep { !$self->{_seen}->{ $_->[1] }++ } $t;
243             }
244             }
245              
246 13         61 my $harness = _load( $self->harness )->new( \%options );
247 13         51798 my $add_prefix = $self->add_prefix;
248              
249             my $printer = sub {
250 84     84   149 my ( $parser, $type, $line ) = @_;
251 84 100       1345 print "TAP version 13\n" unless $self->{_started}++;
252 84 100       214 if ( $type eq 'test' ) {
253 80         704 $line =~ s/(\d+)/$1 + $self->{_test_number_adjust}/e;
  80         351  
254 80 100       222 if ( $add_prefix ) {
255 10         35 my $name = $self->_name_for_parser( $parser );
256 10         63 $line =~ s/(\d+)[ \t]*(\S+)/$1: $2/;
257 10         61 $line =~ s/(\d+)/$1 $name/;
258             }
259             }
260 84         2266 print $line;
261 13         149 };
262              
263             my $complete = sub {
264 20     20   42 my $parser = shift;
265 20         69 my $tests_run = $parser->tests_run;
266 20         146 $self->{_test_number_adjust} += $parser->tests_run;
267 13         112 };
268              
269 13         454 my ( $demux, $done, $finish )
270             = $self->_output_demux( $printer, $complete );
271              
272             $harness->callback(
273             made_parser => sub {
274 20     20   402646 my ( $parser, $test_desc ) = @_;
275              
276 20         570 $self->_name_for_parser( $parser, $test_desc->[1] );
277              
278 20         498 $parser->callback( plan => sub { } );
  18         143752  
279 20         1267 $parser->callback( version => sub { } );
  2         23738  
280             $parser->callback(
281             test => sub {
282 80         30280 my $test = shift;
283 80         237 my $raw = $test->as_string;
284 80         2891 $demux->( $parser, 'test', "$raw\n" );
285             }
286 20         559 );
287             $parser->callback(
288             ELSE => sub {
289 4         5885 my $result = shift;
290 4         38 $demux->( $parser, 'raw', $result->raw . "\n" );
291             }
292 20         491 );
293             $parser->callback(
294             EOF => sub {
295 20         22040 $done->( $parser );
296 20         112 $self->_parser_postmortem( $parser );
297             }
298 20         588 );
299             }
300 13         180 );
301              
302 13         324 my $aggregator = $harness->runtests( @real_tests );
303 13         10693 $finish->();
304             }
305              
306             =head2 C
307              
308             Output the trailing plan.
309              
310             =cut
311              
312             sub end_plan {
313 14     14 1 461 my $self = shift;
314 14 100       103 if ( my $plan = $self->{_test_number_adjust} ) {
315 13         613 print "1..$plan\n";
316 13         669 $self->{_test_number_adjust} = 0;
317             }
318             }
319              
320             =head2 C<< tests_run >>
321              
322             Get a list of tests that have been run.
323              
324             my @tests = $wheel->tests_run();
325              
326             =cut
327              
328             sub tests_run {
329 0     0 1 0 my $self = shift;
330 0 0       0 return sort keys %{ $self->{_seen} || {} };
  0         0  
331             }
332              
333             =head2 C<< option_names >>
334              
335             Get the names of the supported options to C. Used by L
336             to validate its arguments.
337              
338             =cut
339              
340             1;
341              
342             __END__