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   935 use strict;
  57         60  
  57         1440  
3 57     57   179 use warnings;
  57         61  
  57         2328  
4              
5             our $VERSION = '0.000044';
6              
7 57         318 use Test2::Util::HashBase qw{
8             no_numbers handles _encoding
9 57     57   841 };
  57         67  
10              
11             sub OUT_STD() { 0 }
12             sub OUT_ERR() { 1 }
13              
14 57     57   258 use Carp qw/croak/;
  57         76  
  57         2806  
15              
16 57     57   210 use base 'Test2::Formatter';
  57         87  
  57         20870  
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         1 my ($type, $convert) = @_;
35 1 50       3 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 80     80 0 117 my $self = shift;
47              
48 80   66     544 $self->{+HANDLES} ||= $self->_open_handles;
49 80 100       278 if(my $enc = delete $self->{encoding}) {
50 1         3 $self->encoding($enc);
51             }
52             }
53              
54 32     32 0 36 sub hide_buffered { 1 }
55              
56             sub encoding {
57 3     3 1 8 my $self = shift;
58              
59 3 100       5 if (@_) {
60 2         2 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       13 if ($enc =~ m/^utf-?8$/i) {
67 2         12 binmode($_, ":utf8") for @$handles;
68             }
69             else {
70 0         0 binmode($_, ":encoding($enc)") for @$handles;
71             }
72 2         2 $self->{+_ENCODING} = $enc;
73             }
74              
75 3         7 return $self->{+_ENCODING};
76             }
77              
78             if ($^C) {
79 57     57   257 no warnings 'redefine';
  57         79  
  57         7769  
80             *write = sub {};
81             }
82             sub write {
83 946     946 1 851 my ($self, $e, $num) = @_;
84              
85 946         839 my $type = ref($e);
86              
87 946   100     1647 my $converter = $CONVERTERS{$type} || 'event_other';
88 946 50       2604 my @tap = $self->$converter($e, $self->{+NO_NUMBERS} ? undef : $num) or return;
    100          
89              
90 945         939 my $handles = $self->{+HANDLES};
91 945   100     2962 my $nesting = ($SAFE_TO_ACCESS_HASH{$type} ? $e->{nested} : $e->nested) || 0;
92 945         1122 my $indent = ' ' x $nesting;
93              
94             # Local is expensive! Only do it if we really need to.
95 945 50 33     2682 local($\, $,) = (undef, '') if $\ || $,;
96 945         1192 for my $set (@tap) {
97 57     57   219 no warnings 'uninitialized';
  57         73  
  57         63301  
98 1165         1644 my ($hid, $msg) = @$set;
99 1165 50       1749 next unless $msg;
100 1165 50       1860 my $io = $handles->[$hid] or next;
101              
102 1165 100       1489 $msg =~ s/^/$indent/mg if $nesting;
103 1165         105268 print $io $msg;
104             }
105             }
106              
107             sub _open_handles {
108 79     79   90 my $self = shift;
109              
110 79 50       1568 open( my $out, '>&', STDOUT ) or die "Can't dup STDOUT: $!";
111 79 50       582 open( my $err, '>&', STDERR ) or die "Can't dup STDERR: $!";
112              
113 79         169 _autoflush($out);
114 79         151 _autoflush($err);
115              
116 79         292 return [$out, $err];
117             }
118              
119             sub _autoflush {
120 272     272   380 my($fh) = pop;
121 272         523 my $old_fh = select $fh;
122 272         406 $| = 1;
123 272         491 select $old_fh;
124             }
125              
126             sub event_tap {
127 227     227 0 213 my $self = shift;
128 227         173 my ($e, $num) = @_;
129              
130 227 50       346 my $converter = $CONVERTERS{ref($e)} or return;
131              
132 227 50       284 $num = undef if $self->{+NO_NUMBERS};
133              
134 227         307 return $self->$converter($e, $num);
135             }
136              
137             sub event_ok {
138 1008     1008 1 752 my $self = shift;
139 1008         792 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 1008         861 my ($name, $todo) = @{$e}{qw/name todo/};
  1008         1352  
144 1008         907 my $in_todo = defined($todo);
145              
146 1008         811 my $out = "";
147 1008 100       1450 $out .= "not " unless $e->{pass};
148 1008         987 $out .= "ok";
149 1008 100       1688 $out .= " $num" if defined($num);
150 1008 100       1499 $out .= " - $name" if defined $name;
151 1008 100       1276 $out .= " # TODO" if $in_todo;
152 1008 100 100     1607 $out .= " $todo" if defined($todo) && length($todo);
153              
154             # The primary line of TAP, if the test passed this is all we need.
155 1008         3081 return([OUT_STD, "$out\n"]);
156             }
157              
158             sub event_skip {
159 3     3 1 3 my $self = shift;
160 3         4 my ($e, $num) = @_;
161              
162 3         8 my $name = $e->name;
163 3         5 my $reason = $e->reason;
164 3         7 my $todo = $e->todo;
165              
166 3         3 my $out = "";
167 3 100       8 $out .= "not " unless $e->{pass};
168 3         2 $out .= "ok";
169 3 50       7 $out .= " $num" if defined $num;
170 3 50       5 $out .= " - $name" if $name;
171 3 100       6 if (defined($todo)) {
172 1         1 $out .= " # TODO & SKIP"
173             }
174             else {
175 2         2 $out .= " # skip";
176             }
177 3 50 33     13 $out .= " $reason" if defined($reason) && length($reason);
178              
179 3         9 return([OUT_STD, "$out\n"]);
180             }
181              
182             sub event_note {
183 24     24 1 24 my $self = shift;
184 24         25 my ($e, $num) = @_;
185              
186 24         37 chomp(my $msg = $e->message);
187 24         76 $msg =~ s/^/# /;
188 24         38 $msg =~ s/\n/\n# /g;
189              
190 24         120 return [OUT_STD, "$msg\n"];
191             }
192              
193             sub event_diag {
194 32     32 1 29 my $self = shift;
195 32         31 my ($e, $num) = @_;
196              
197 32         51 chomp(my $msg = $e->message);
198 32         102 $msg =~ s/^/# /;
199 32         45 $msg =~ s/\n/\n# /g;
200              
201 32         101 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       4 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 2 my $self = shift;
218 1         2 my ($e, $num) = @_;
219 1         2 return [ OUT_ERR, $e->error ];
220             }
221              
222             sub event_subtest {
223 37     37 1 28 my $self = shift;
224 37         40 my ($e, $num) = @_;
225              
226             # A 'subtest' is a subclass of 'ok'. Let the code that renders 'ok' render
227             # this event.
228 37         53 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 37 100       73 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 34 100       67 if ($ENV{HARNESS_IS_VERBOSE}) {
238             # index 0 is the filehandle, index 1 is the message we want to indent.
239 9         15 $_->[1] =~ s/^(.*\S.*)$/ $1/mg for @diag;
240             }
241              
242             # Add the trailing ' {' to the 'ok' line of TAP output.
243 34         97 $ok->[1] =~ s/\n/ {\n/;
244              
245             # Render the sub-events, we use our own counter for these.
246 34         30 my $count = 0;
247             my @subs = map {
248             # Bump the count for any event that should bump it.
249 195 100       303 $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 195         220 map { $_->[1] =~ s/^(.*\S.*)$/ $1/mg; $_ } $self->event_tap($_, $count);
  195         765  
  195         350  
254 34         126 } @{$e->subevents};
  34         59  
255              
256             return (
257 34         151 $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 90     90 1 215 my $self = shift;
266 90         98 my ($e, $num) = @_;
267              
268 90         250 my $directive = $e->directive;
269 90 100 100     278 return if $directive && $directive eq 'NO PLAN';
270              
271 88         255 my $reason = $e->reason;
272 88 100       199 $reason =~ s/\n/\n# /g if $reason;
273              
274 88         226 my $plan = "1.." . $e->max;
275 88 100       194 if ($directive) {
276 5         16 $plan .= " # $directive";
277 5 100       12 $plan .= " $reason" if defined $reason;
278             }
279              
280 88         296 return [OUT_STD, "$plan\n"];
281             }
282              
283             sub event_other {
284 12     12 1 16 my $self = shift;
285 12         17 my ($e, $num) = @_;
286 12 50       50 return if $e->no_display;
287              
288 12         13 my @out;
289              
290 12 50       35 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       25 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     21 my $handle = ($e->causes_fail || $e->diagnostics) ? OUT_ERR : OUT_STD;
308 12   33     40 my $summary = $e->summary || ref($e);
309 12         19 chomp($summary);
310 12         65 $summary =~ s/^/# /smg;
311 12         33 push @out => [$handle, "$summary\n"];
312             }
313              
314 12         45 return @out;
315             }
316              
317             1;
318              
319             __END__