| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package TAP::Harness::ReportByDescription; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
25975
|
use strict; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
148
|
|
|
4
|
3
|
|
|
3
|
|
18
|
use base 'TAP::Harness'; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
3539
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.06'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
TAP::Harness::ReportByDescription - Report TAP output by test file description rather than test file name |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 VERSION |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
0.01 |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use TAP::Harness::ReportByDescription; |
|
18
|
|
|
|
|
|
|
my $harness = TAP::Harness::ReportByDescription->new(); |
|
19
|
|
|
|
|
|
|
$harness->aggregate_tests($aggregator, @tests); |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
This package subclasses TAP::Harness for the purpose of enabling a user to |
|
24
|
|
|
|
|
|
|
report the TAP output for a test file by a user-provided description rather |
|
25
|
|
|
|
|
|
|
than by the name of the test file itself. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Why would you want to do this? Three reasons come to mind. |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=over 4 |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=item * One Master Summary Rather Than Summaries for Individual Subharnesses |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
Suppose that you had a F testing target that is in essence nothing more |
|
34
|
|
|
|
|
|
|
than a sequential run of several smaller testing targets, each of which is a |
|
35
|
|
|
|
|
|
|
separate invocation of a test harness. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
make fulltest : test_prep \ |
|
38
|
|
|
|
|
|
|
compilers \ |
|
39
|
|
|
|
|
|
|
src \ |
|
40
|
|
|
|
|
|
|
oo \ |
|
41
|
|
|
|
|
|
|
codingstd \ |
|
42
|
|
|
|
|
|
|
examples |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Other things being equal, you would get a B at the end of each of the |
|
45
|
|
|
|
|
|
|
five targets or subharnesses. Under some circumstances, you might prefer to |
|
46
|
|
|
|
|
|
|
get a single master Summary at the end of the overall program. |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=item * Multiple Runs of Same Tests in Different Environments |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Suppose that you had a set of tests that you wanted to run several times, each |
|
51
|
|
|
|
|
|
|
time in a slightly different environment. You could write a program which |
|
52
|
|
|
|
|
|
|
executes multiple runs, writing a summary after each run and then modifying |
|
53
|
|
|
|
|
|
|
the environment for the next run. |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
perl t/harness --gc-debug --runcore=bounds |
|
56
|
|
|
|
|
|
|
perl t/harness --gc-debug --runcore=fast |
|
57
|
|
|
|
|
|
|
perl t/harness --gc-debug --run-pbc |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
As the TAP output flowed by, you would see three instances of each test: |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
t/pmc/arrayiterator.t ............................ ok |
|
62
|
|
|
|
|
|
|
# ... |
|
63
|
|
|
|
|
|
|
t/pmc/arrayiterator.t ............................ ok |
|
64
|
|
|
|
|
|
|
# ... |
|
65
|
|
|
|
|
|
|
t/pmc/arrayiterator.t ............................ ok |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
... but you would not be able to tell from the test file's report itself which |
|
68
|
|
|
|
|
|
|
harness it was a part of. Under certain circumstances it would be nice to be |
|
69
|
|
|
|
|
|
|
able to differentiate among the different runs: |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
bounds__t/pmc/arrayiterator.t .................... ok |
|
72
|
|
|
|
|
|
|
# ... |
|
73
|
|
|
|
|
|
|
fast__t/pmc/arrayiterator.t ...................... ok |
|
74
|
|
|
|
|
|
|
# ... |
|
75
|
|
|
|
|
|
|
pbc__t/pmc/arrayiterator.t ....................... ok |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Here you're providing a B of each run of each test which provides |
|
78
|
|
|
|
|
|
|
an observer with more information. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=item * Preparation of a Test Harness Archive |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
The ability to provide a specific description for a different run of the same |
|
83
|
|
|
|
|
|
|
test becomes crucial when preparing a test harness archive. Currently, CPAN |
|
84
|
|
|
|
|
|
|
distribution Test::Harness::Archive stores the TAP for a particular test file |
|
85
|
|
|
|
|
|
|
in a file with the name of the test file itself. If you do multiple runs of |
|
86
|
|
|
|
|
|
|
the same file in different environments, a later run of a test will overwrite |
|
87
|
|
|
|
|
|
|
the TAP file from an earlier run. You would therefore only be able to include |
|
88
|
|
|
|
|
|
|
the TAP from the last subharness in an archive. That would impede you from |
|
89
|
|
|
|
|
|
|
sharing the full results of testing via a smoke-test aggregator such as |
|
90
|
|
|
|
|
|
|
Smolder. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=back |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
In short, we need (a) a way to run multiple harnesses as if they were one, (b) |
|
95
|
|
|
|
|
|
|
run the same tests through multiple harnesses and be able to quickly identify |
|
96
|
|
|
|
|
|
|
which harness we were running it through, and (c) store multiple versions of a |
|
97
|
|
|
|
|
|
|
file's TAP output in a test harness archive. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
Need (a) can actually be fulfilled with existing TAP::Parser::Aggregator |
|
100
|
|
|
|
|
|
|
functionality. Let's build on that to meet needs (b) and (c). To do that we |
|
101
|
|
|
|
|
|
|
need one package to subclass TAP::Harness and one to subclass |
|
102
|
|
|
|
|
|
|
TAP::Harness::Archive. TAP::Harness::ReportByDescription and |
|
103
|
|
|
|
|
|
|
TAP::Harness::Archive::MultiplesHarnesses are these packages. |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 METHODS |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=head2 C |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
Inherited from TAP::Harness. |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head2 C |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Replicated, along with methods called internally from this method, from |
|
114
|
|
|
|
|
|
|
TAP::Harness. The only change occurs in an internal method |
|
115
|
|
|
|
|
|
|
C<_get_parser_args()>, which now assigns the individual test's B to |
|
116
|
|
|
|
|
|
|
one variable and a user-provided B to a second variable. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
my $test_prog = $job->filename; |
|
119
|
|
|
|
|
|
|
my $spool_prog = $job->description; |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
It is the latter variable which will appear on the console and in a test |
|
122
|
|
|
|
|
|
|
archive. Since this occurs within an internal method, the user need make no |
|
123
|
|
|
|
|
|
|
change in how C is called. |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub aggregate_tests { |
|
128
|
16
|
|
|
16
|
1
|
241061
|
my ( $self, $aggregate, @tests ) = @_; |
|
129
|
|
|
|
|
|
|
|
|
130
|
16
|
|
|
|
|
650
|
my $scheduler = $self->make_scheduler(@tests); |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# #12458 |
|
133
|
16
|
50
|
|
|
|
18483
|
local $ENV{HARNESS_IS_VERBOSE} = 1 |
|
134
|
|
|
|
|
|
|
if $self->{formatter}->{verbosity} > 0; |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# Formatter gets only names. |
|
137
|
16
|
|
|
|
|
90
|
$self->{formatter}->prepare( map { $_->description } $scheduler->get_all ); |
|
|
32
|
|
|
|
|
1057
|
|
|
138
|
|
|
|
|
|
|
|
|
139
|
16
|
100
|
|
|
|
1328
|
if ( $self->{formatter}->{jobs} > 1 ) { |
|
140
|
1
|
|
|
|
|
12
|
$self->_aggregate_parallel( $aggregate, $scheduler ); |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
else { |
|
143
|
15
|
|
|
|
|
67
|
$self->_aggregate_single( $aggregate, $scheduler ); |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
16
|
|
|
|
|
1535
|
return; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _aggregate_parallel { |
|
150
|
1
|
|
|
1
|
|
3
|
my ( $self, $aggregate, $scheduler ) = @_; |
|
151
|
|
|
|
|
|
|
|
|
152
|
1
|
|
|
|
|
8
|
my $jobs = $self->jobs; |
|
153
|
1
|
|
|
|
|
13
|
my $mux = $self->_construct( $self->multiplexer_class ); |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
RESULT: { |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Keep multiplexer topped up |
|
158
|
1
|
|
|
|
|
2420
|
FILL: |
|
159
|
11
|
|
|
|
|
46
|
while ( $mux->parsers < $jobs ) { |
|
160
|
13
|
|
|
|
|
443
|
my $job = $scheduler->get_job; |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# If we hit a spinner stop filling and start running. |
|
163
|
13
|
100
|
66
|
|
|
999
|
last FILL if !defined $job || $job->is_spinner; |
|
164
|
|
|
|
|
|
|
|
|
165
|
2
|
|
|
|
|
53
|
my ( $parser, $session ) = $self->make_parser($job); |
|
166
|
2
|
|
|
|
|
27
|
$mux->add( $parser, [ $session, $job ] ); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
11
|
100
|
|
|
|
48
|
if ( my ( $parser, $stash, $result ) = $mux->next ) { |
|
170
|
10
|
|
|
|
|
45197
|
my ( $session, $job ) = @$stash; |
|
171
|
10
|
100
|
|
|
|
37
|
if ( defined $result ) { |
|
172
|
8
|
|
|
|
|
59
|
$session->result($result); |
|
173
|
8
|
50
|
|
|
|
982
|
$self->_bailout($result) if $result->is_bailout; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
else { |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# End of parser. Automatically removed from the mux. |
|
178
|
2
|
|
|
|
|
30
|
$self->finish_parser( $parser, $session ); |
|
179
|
2
|
|
|
|
|
798
|
$self->_after_test( $aggregate, $job, $parser ); |
|
180
|
2
|
|
|
|
|
527
|
$job->finish; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
10
|
|
|
|
|
217
|
redo RESULT; |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
1
|
|
|
|
|
29
|
return; |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub _aggregate_single { |
|
190
|
15
|
|
|
15
|
|
31
|
my ( $self, $aggregate, $scheduler ) = @_; |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
JOB: |
|
193
|
15
|
|
|
|
|
71
|
while ( my $job = $scheduler->get_job ) { |
|
194
|
30
|
50
|
|
|
|
4015
|
next JOB if $job->is_spinner; |
|
195
|
|
|
|
|
|
|
|
|
196
|
30
|
|
|
|
|
309
|
my ( $parser, $session ) = $self->make_parser($job); |
|
197
|
|
|
|
|
|
|
|
|
198
|
30
|
|
|
|
|
411
|
while ( defined( my $result = $parser->next ) ) { |
|
199
|
120
|
|
|
|
|
1774336
|
$session->result($result); |
|
200
|
120
|
50
|
|
|
|
35022
|
if ( $result->is_bailout ) { |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# Keep reading until input is exhausted in the hope |
|
203
|
|
|
|
|
|
|
# of allowing any pending diagnostics to show up. |
|
204
|
0
|
|
|
|
|
0
|
1 while $parser->next; |
|
205
|
0
|
|
|
|
|
0
|
$self->_bailout($result); |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
30
|
|
|
|
|
70096
|
$self->finish_parser( $parser, $session ); |
|
210
|
30
|
|
|
|
|
11939
|
$self->_after_test( $aggregate, $job, $parser ); |
|
211
|
30
|
|
|
|
|
10113
|
$job->finish; |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
15
|
|
|
|
|
3616
|
return; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub make_parser { |
|
218
|
32
|
|
|
32
|
1
|
70
|
my ( $self, $job ) = @_; |
|
219
|
|
|
|
|
|
|
|
|
220
|
32
|
|
|
|
|
161
|
my $args = $self->_get_parser_args($job); |
|
221
|
32
|
|
|
|
|
161
|
$self->_make_callback( 'parser_args', $args, $job->as_array_ref ); |
|
222
|
32
|
|
|
|
|
1506
|
my $parser = $self->_construct( $self->parser_class, $args ); |
|
223
|
|
|
|
|
|
|
|
|
224
|
32
|
|
|
|
|
592962
|
$self->_make_callback( 'made_parser', $parser, $job->as_array_ref ); |
|
225
|
32
|
|
|
|
|
3180
|
my $session = $self->{formatter}->open_test( $job->description, $parser ); |
|
226
|
|
|
|
|
|
|
|
|
227
|
32
|
|
|
|
|
11966
|
return ( $parser, $session ); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub _get_parser_args { |
|
231
|
32
|
|
|
32
|
|
57
|
my ( $self, $job ) = @_; |
|
232
|
32
|
|
|
|
|
98
|
my $test_prog = $job->filename; |
|
233
|
32
|
|
|
|
|
229
|
my $spool_prog = $job->description; |
|
234
|
32
|
|
|
|
|
157
|
my %args = (); |
|
235
|
|
|
|
|
|
|
|
|
236
|
32
|
50
|
|
|
|
560
|
$args{sources} = $self->sources if $self->sources; |
|
237
|
|
|
|
|
|
|
|
|
238
|
32
|
|
|
|
|
220
|
my @switches; |
|
239
|
32
|
50
|
|
|
|
289
|
@switches = $self->lib if $self->lib; |
|
240
|
32
|
50
|
|
|
|
1069
|
push @switches => $self->switches if $self->switches; |
|
241
|
32
|
|
|
|
|
576
|
$args{switches} = \@switches; |
|
242
|
32
|
|
|
|
|
199
|
$args{spool} = $self->_open_spool($spool_prog); |
|
243
|
32
|
|
|
|
|
16085
|
$args{merge} = $self->merge; |
|
244
|
32
|
|
|
|
|
465
|
$args{ignore_exit} = $self->ignore_exit; |
|
245
|
32
|
50
|
|
|
|
406
|
$args{version} = $self->version if $self->version; |
|
246
|
|
|
|
|
|
|
|
|
247
|
32
|
50
|
|
|
|
304
|
if ( my $exec = $self->exec ) { |
|
248
|
0
|
0
|
|
|
|
0
|
$args{exec} |
|
249
|
|
|
|
|
|
|
= ref $exec eq 'CODE' |
|
250
|
|
|
|
|
|
|
? $exec->( $self, $test_prog ) |
|
251
|
|
|
|
|
|
|
: [ @$exec, $test_prog ]; |
|
252
|
0
|
0
|
0
|
|
|
0
|
if ( not defined $args{exec} ) { |
|
|
|
0
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
0
|
$args{source} = $test_prog; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
elsif ( ( ref( $args{exec} ) || "" ) ne "ARRAY" ) { |
|
256
|
0
|
|
|
|
|
0
|
$args{source} = delete $args{exec}; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
else { |
|
260
|
32
|
|
|
|
|
262
|
$args{source} = $test_prog; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
32
|
50
|
|
|
|
161
|
if ( defined( my $test_args = $self->test_args ) ) { |
|
264
|
|
|
|
|
|
|
|
|
265
|
0
|
0
|
|
|
|
0
|
if ( ref($test_args) eq 'HASH' ) { |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# different args for each test |
|
268
|
0
|
0
|
|
|
|
0
|
if ( exists( $test_args->{ $job->description } ) ) { |
|
269
|
0
|
|
|
|
|
0
|
$test_args = $test_args->{ $job->description }; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
else { |
|
272
|
0
|
|
|
|
|
0
|
$self->_croak( "TAP::Harness Can't find test_args for " |
|
273
|
|
|
|
|
|
|
. $job->description ); |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
0
|
|
|
|
|
0
|
$args{test_args} = $test_args; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
32
|
|
|
|
|
436
|
return \%args; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head1 EXAMPLE |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
See C. |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head1 AUTHOR |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
99% of the code in this module comes from TAP::Harness, written by Andy |
|
291
|
|
|
|
|
|
|
Armstrong and generations of Perl QA hackers. Documentation and the one small |
|
292
|
|
|
|
|
|
|
code tweak needed were written by James E Keenan. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head1 LICENSE |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
This is free software and is released under the same terms as Perl itself. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=cut |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# vim:ts=4:sw=4:et:sta |