File Coverage

blib/lib/Test2/Formatter/TAP.pm
Criterion Covered Total %
statement 154 165 93.3
branch 56 86 65.1
condition 18 31 58.0
subroutine 24 24 100.0
pod 12 15 80.0
total 264 321 82.2


line stmt bran cond sub pod time code
1             package Test2::Formatter::TAP;
2 54     54   990 use strict;
  54         60  
  54         1231  
3 54     54   156 use warnings;
  54         51  
  54         1978  
4              
5             our $VERSION = '0.000042';
6              
7 54         251 use Test2::Util::HashBase qw{
8             no_numbers handles _encoding
9 54     54   820 };
  54         63  
10              
11             sub OUT_STD() { 0 }
12             sub OUT_ERR() { 1 }
13              
14 54     54   213 use Carp qw/croak/;
  54         60  
  54         2387  
15              
16 54     54   191 use base 'Test2::Formatter';
  54         59  
  54         17883  
17              
18             my %CONVERTERS = (
19             'Test2::Event::Ok' => 'event_ok',
20             'Test2::Event::Skip' => 'event_skip',
21             'Test2::Event::Note' => 'event_note',
22             'Test2::Event::Diag' => 'event_diag',
23             'Test2::Event::Bail' => 'event_bail',
24             'Test2::Event::Exception' => 'event_exception',
25             'Test2::Event::Subtest' => 'event_subtest',
26             'Test2::Event::Plan' => 'event_plan',
27             );
28              
29             # Initial list of converters are safe for direct hash access cause we control them.
30             my %SAFE_TO_ACCESS_HASH = %CONVERTERS;
31              
32             sub register_event {
33 1     1 1 10 my $class = shift;
34 1         3 my ($type, $convert) = @_;
35 1 50       7 croak "Event type is a required argument" unless $type;
36 1 50       3 croak "Event type '$type' already registered" if $CONVERTERS{$type};
37 1 50 33     6 croak "The second argument to register_event() must be a code reference or method name"
      33        
38             unless $convert && (ref($convert) eq 'CODE' || $class->can($convert));
39 1         2 $CONVERTERS{$type} = $convert;
40             }
41              
42             _autoflush(\*STDOUT);
43             _autoflush(\*STDERR);
44              
45             sub init {
46 77     77 0 94 my $self = shift;
47              
48 77   66     446 $self->{+HANDLES} ||= $self->_open_handles;
49 77 100       271 if(my $enc = delete $self->{encoding}) {
50 1         3 $self->encoding($enc);
51             }
52             }
53              
54 31     31 0 36 sub hide_buffered { 1 }
55              
56             sub encoding {
57 3     3 1 6 my $self = shift;
58              
59 3 100       6 if (@_) {
60 2         3 my ($enc) = @_;
61 2         2 my $handles = $self->{+HANDLES};
62              
63             # https://rt.perl.org/Public/Bug/Display.html?id=31923
64             # If utf8 is requested we use ':utf8' instead of ':encoding(utf8)' in
65             # order to avoid the thread segfault.
66 2 50       11 if ($enc =~ m/^utf-?8$/i) {
67 2         14 binmode($_, ":utf8") for @$handles;
68             }
69             else {
70 0         0 binmode($_, ":encoding($enc)") for @$handles;
71             }
72 2         3 $self->{+_ENCODING} = $enc;
73             }
74              
75 3         6 return $self->{+_ENCODING};
76             }
77              
78             if ($^C) {
79 54     54   249 no warnings 'redefine';
  54         70  
  54         6997  
80             *write = sub {};
81             }
82             sub write {
83 924     924 1 852 my ($self, $e, $num) = @_;
84              
85 924         803 my $type = ref($e);
86              
87 924   100     1627 my $converter = $CONVERTERS{$type} || 'event_other';
88 924 50       2282 my @tap = $self->$converter($e, $self->{+NO_NUMBERS} ? undef : $num) or return;
    100          
89              
90 923         882 my $handles = $self->{+HANDLES};
91 923   100     2921 my $nesting = ($SAFE_TO_ACCESS_HASH{$type} ? $e->{nested} : $e->nested) || 0;
92 923         1040 my $indent = ' ' x $nesting;
93              
94             # Local is expensive! Only do it if we really need to.
95 923 50 33     2609 local($\, $,) = (undef, '') if $\ || $,;
96 923         1152 for my $set (@tap) {
97 54     54   185 no warnings 'uninitialized';
  54         61  
  54         58114  
98 1139         1555 my ($hid, $msg) = @$set;
99 1139 50       1713 next unless $msg;
100 1139 50       1706 my $io = $handles->[$hid] or next;
101              
102 1139 100       1434 $msg =~ s/^/$indent/mg if $nesting;
103 1139         73296 print $io $msg;
104             }
105             }
106              
107             sub _open_handles {
108 76     76   76 my $self = shift;
109              
110 76 50       1460 open( my $out, '>&', STDOUT ) or die "Can't dup STDOUT: $!";
111 76 50       631 open( my $err, '>&', STDERR ) or die "Can't dup STDERR: $!";
112              
113 76         142 _autoflush($out);
114 76         125 _autoflush($err);
115              
116 76         272 return [$out, $err];
117             }
118              
119             sub _autoflush {
120 260     260   310 my($fh) = pop;
121 260         486 my $old_fh = select $fh;
122 260         365 $| = 1;
123 260         452 select $old_fh;
124             }
125              
126             sub event_tap {
127 224     224 0 224 my $self = shift;
128 224         170 my ($e, $num) = @_;
129              
130 224 50       365 my $converter = $CONVERTERS{ref($e)} or return;
131              
132 224 50       281 $num = undef if $self->{+NO_NUMBERS};
133              
134 224         293 return $self->$converter($e, $num);
135             }
136              
137             sub event_ok {
138 987     987 1 778 my $self = shift;
139 987         806 my ($e, $num) = @_;
140              
141             # We use direct hash access for performance. OK events are so common we
142             # need this to be fast.
143 987         784 my ($name, $todo) = @{$e}{qw/name todo/};
  987         1517  
144 987         845 my $in_todo = defined($todo);
145              
146 987         729 my $out = "";
147 987 100       1363 $out .= "not " unless $e->{pass};
148 987         888 $out .= "ok";
149 987 100       1572 $out .= " $num" if defined($num);
150 987 100       1562 $out .= " - $name" if defined $name;
151 987 100       1204 $out .= " # TODO" if $in_todo;
152 987 100 100     1547 $out .= " $todo" if defined($todo) && length($todo);
153              
154             # The primary line of TAP, if the test passed this is all we need.
155 987         3064 return([OUT_STD, "$out\n"]);
156             }
157              
158             sub event_skip {
159 3     3 1 4 my $self = shift;
160 3         2 my ($e, $num) = @_;
161              
162 3         8 my $name = $e->name;
163 3         6 my $reason = $e->reason;
164 3         7 my $todo = $e->todo;
165              
166 3         2 my $out = "";
167 3 100       7 $out .= "not " unless $e->{pass};
168 3         4 $out .= "ok";
169 3 50       5 $out .= " $num" if defined $num;
170 3 50       7 $out .= " - $name" if $name;
171 3 100       5 if (defined($todo)) {
172 1         1 $out .= " # TODO & SKIP"
173             }
174             else {
175 2         2 $out .= " # skip";
176             }
177 3 50 33     11 $out .= " $reason" if defined($reason) && length($reason);
178              
179 3         12 return([OUT_STD, "$out\n"]);
180             }
181              
182             sub event_note {
183 24     24 1 18 my $self = shift;
184 24         25 my ($e, $num) = @_;
185              
186 24         41 chomp(my $msg = $e->message);
187 24         74 $msg =~ s/^/# /;
188 24         31 $msg =~ s/\n/\n# /g;
189              
190 24         84 return [OUT_STD, "$msg\n"];
191             }
192              
193             sub event_diag {
194 32     32 1 27 my $self = shift;
195 32         27 my ($e, $num) = @_;
196              
197 32         52 chomp(my $msg = $e->message);
198 32         102 $msg =~ s/^/# /;
199 32         42 $msg =~ s/\n/\n# /g;
200              
201 32         106 return [OUT_ERR, "$msg\n"];
202             }
203              
204             sub event_bail {
205 1     1 1 1 my $self = shift;
206 1         1 my ($e, $num) = @_;
207              
208 1 50       3 return if $e->nested;
209              
210             return [
211 1         3 OUT_STD,
212             "Bail out! " . $e->reason . "\n",
213             ];
214             }
215              
216             sub event_exception {
217 1     1 1 1 my $self = shift;
218 1         2 my ($e, $num) = @_;
219 1         3 return [ OUT_ERR, $e->error ];
220             }
221              
222             sub event_subtest {
223 36     36 1 27 my $self = shift;
224 36         30 my ($e, $num) = @_;
225              
226             # A 'subtest' is a subclass of 'ok'. Let the code that renders 'ok' render
227             # this event.
228 36         46 my ($ok, @diag) = $self->event_ok($e, $num);
229              
230             # If the subtest is not buffered then the sub-events have already been
231             # rendered, we can go ahead and return.
232 36 100       69 return ($ok, @diag) unless $e->buffered;
233              
234             # In a verbose harness we indent the diagnostics from the 'Ok' event since
235             # they will appear inside the subtest braces. This helps readability. In a
236             # non-verbose harness we do nto do this because it is less readable.
237 33 100       62 if ($ENV{HARNESS_IS_VERBOSE}) {
238             # index 0 is the filehandle, index 1 is the message we want to indent.
239 9         13 $_->[1] =~ s/^(.*\S)$/ $1/mg for @diag;
240             }
241              
242             # Add the trailing ' {' to the 'ok' line of TAP output.
243 33         96 $ok->[1] =~ s/\n/ {\n/;
244              
245             # Render the sub-events, we use our own counter for these.
246 33         27 my $count = 0;
247             my @subs = map {
248             # Bump the count for any event that should bump it.
249 192 100       419 $count++ if $_->increments_count;
250              
251             # This indents all output lines generated for the sub-events.
252             # index 0 is the filehandle, index 1 is the message we want to indent.
253 192         199 map { $_->[1] =~ s/^(.*\S)$/ $1/mg; $_ } $self->event_tap($_, $count);
  192         723  
  192         295  
254 33         28 } @{$e->subevents};
  33         58  
255              
256             return (
257 33         132 $ok, # opening ok - name {
258             @diag, # diagnostics if the subtest failed
259             @subs, # All the inner-event lines
260             [OUT_STD(), "}\n"], # } (closing brace)
261             );
262             }
263              
264             sub event_plan {
265 86     86 1 88 my $self = shift;
266 86         89 my ($e, $num) = @_;
267              
268 86         305 my $directive = $e->directive;
269 86 100 100     246 return if $directive && $directive eq 'NO PLAN';
270              
271 84         245 my $reason = $e->reason;
272 84 100       219 $reason =~ s/\n/\n# /g if $reason;
273              
274 84         192 my $plan = "1.." . $e->max;
275 84 100       171 if ($directive) {
276 5         10 $plan .= " # $directive";
277 5 100       14 $plan .= " $reason" if defined $reason;
278             }
279              
280 84         303 return [OUT_STD, "$plan\n"];
281             }
282              
283             sub event_other {
284 12     12 1 17 my $self = shift;
285 12         13 my ($e, $num) = @_;
286 12 50       54 return if $e->no_display;
287              
288 12         13 my @out;
289              
290 12 50       41 if (my ($max, $directive, $reason) = $e->sets_plan) {
291 0         0 my $plan = "1..$max";
292 0 0       0 $plan .= " # $directive" if $directive;
293 0 0       0 $plan .= " $reason" if defined $reason;
294 0         0 push @out => [OUT_STD, "$plan\n"];
295             }
296              
297 12 50       23 if ($e->increments_count) {
298 0         0 my $ok = "";
299 0 0       0 $ok .= "not " if $e->causes_fail;
300 0         0 $ok .= "ok";
301 0 0       0 $ok .= " $num" if defined($num);
302 0 0       0 $ok .= " - " . $e->summary if $e->summary;
303              
304 0         0 push @out => [OUT_STD, "$ok\n"];
305             }
306             else { # Comment
307 12 50 33     23 my $handle = ($e->causes_fail || $e->diagnostics) ? OUT_ERR : OUT_STD;
308 12   33     42 my $summary = $e->summary || ref($e);
309 12         24 chomp($summary);
310 12         67 $summary =~ s/^/# /smg;
311 12         35 push @out => [$handle, "$summary\n"];
312             }
313              
314 12         43 return @out;
315             }
316              
317             1;
318              
319             __END__