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