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 |