File Coverage

blib/lib/Test2/Formatter/TAP.pm
Criterion Covered Total %
statement 216 228 94.7
branch 123 138 89.1
condition 90 111 81.0
subroutine 25 26 96.1
pod 2 16 12.5
total 456 519 87.8


line stmt bran cond sub pod time code
1             package Test2::Formatter::TAP;
2 245     245   3200 use strict;
  245         549  
  245         7374  
3 245     245   1249 use warnings;
  245         514  
  245         23974  
4              
5             our $VERSION = '1.302180';
6              
7 245     245   2467 use Test2::Util qw/clone_io/;
  245         496  
  245         13505  
8              
9 245         1708 use Test2::Util::HashBase qw{
10             no_numbers handles _encoding _last_fh
11             -made_assertion
12 245     245   2395 };
  245         523  
13              
14             sub OUT_STD() { 0 }
15             sub OUT_ERR() { 1 }
16              
17 245     245   102520 BEGIN { require Test2::Formatter; our @ISA = qw(Test2::Formatter) }
  245         122397  
18              
19             my $supports_tables;
20             sub supports_tables {
21 1 50   1 0 15 if (!defined $supports_tables) {
22 1         6 local $SIG{__DIE__} = 'DEFAULT';
23 1         3 local $@;
24             $supports_tables
25             = ($INC{'Term/Table.pm'} && $INC{'Term/Table/Util.pm'})
26 1   50     5 || eval { require Term::Table; require Term::Table::Util; 1 }
27             || 0;
28             }
29 1         6 return $supports_tables;
30             }
31              
32             sub _autoflush {
33 1196     1196   2481 my($fh) = pop;
34 1196         3462 my $old_fh = select $fh;
35 1196         2973 $| = 1;
36 1196         3121 select $old_fh;
37             }
38              
39             _autoflush(\*STDOUT);
40             _autoflush(\*STDERR);
41              
42 76     76 0 198 sub hide_buffered { 1 }
43              
44             sub init {
45 363     363 0 903 my $self = shift;
46              
47 363   66     4183 $self->{+HANDLES} ||= $self->_open_handles;
48 363 100       1793 if(my $enc = delete $self->{encoding}) {
49 1         2 $self->encoding($enc);
50             }
51             }
52              
53             sub _open_handles {
54 353     353   840 my $self = shift;
55              
56 353         2149 require Test2::API;
57 353         1798 my $out = clone_io(Test2::API::test2_stdout());
58 353         1682 my $err = clone_io(Test2::API::test2_stderr());
59              
60 353         1713 _autoflush($out);
61 353         1658 _autoflush($err);
62              
63 353         1882 return [$out, $err];
64             }
65              
66             sub encoding {
67 3     3 1 11 my $self = shift;
68              
69 3 100 66     13 if ($] ge "5.007003" and @_) {
70 2         4 my ($enc) = @_;
71 2         5 my $handles = $self->{+HANDLES};
72              
73             # https://rt.perl.org/Public/Bug/Display.html?id=31923
74             # If utf8 is requested we use ':utf8' instead of ':encoding(utf8)' in
75             # order to avoid the thread segfault.
76 2 50       15 if ($enc =~ m/^utf-?8$/i) {
77 2         34 binmode($_, ":utf8") for @$handles;
78             }
79             else {
80 0         0 binmode($_, ":encoding($enc)") for @$handles;
81             }
82 2         7 $self->{+_ENCODING} = $enc;
83             }
84              
85 3         9 return $self->{+_ENCODING};
86             }
87              
88             if ($^C) {
89 245     245   2150 no warnings 'redefine';
  245         537  
  245         53764  
90       4     *write = sub {};
91             }
92             sub write {
93 4983     4983 1 12750 my ($self, $e, $num, $f) = @_;
94              
95             # The most common case, a pass event with no amnesty and a normal name.
96 4983 100       15341 return if $self->print_optimal_pass($e, $num);
97              
98 2184   66     5505 $f ||= $e->facet_data;
99              
100 2184 50       5899 $self->encoding($f->{control}->{encoding}) if $f->{control}->{encoding};
101              
102 2184 100       6017 my @tap = $self->event_tap($f, $num) or return;
103              
104 1968 100       5134 $self->{+MADE_ASSERTION} = 1 if $f->{assert};
105              
106 1968   100     7386 my $nesting = $f->{trace}->{nested} || 0;
107 1968         3659 my $handles = $self->{+HANDLES};
108 1968         4542 my $indent = ' ' x $nesting;
109              
110             # Local is expensive! Only do it if we really need to.
111 1968 100 66     9374 local($\, $,) = (undef, '') if $\ || $,;
112 1968         4549 for my $set (@tap) {
113 245     245   1951 no warnings 'uninitialized';
  245         607  
  245         613616  
114 2861         6313 my ($hid, $msg) = @$set;
115 2861 50       6261 next unless $msg;
116 2861 50       6618 my $io = $handles->[$hid] or next;
117              
118             print $io "\n"
119             if $ENV{HARNESS_ACTIVE}
120             && $hid == OUT_ERR
121 2861 100 100     10750 && $self->{+_LAST_FH} != $io
      100        
      100        
122             && $msg =~ m/^#\s*Failed( \(TODO\))? test /;
123              
124 2861 100       8404 $msg =~ s/^/$indent/mg if $nesting;
125 2861         57942 print $io $msg;
126 2861         15764 $self->{+_LAST_FH} = $io;
127             }
128             }
129              
130             sub print_optimal_pass {
131 4995     4995 0 11316 my ($self, $e, $num) = @_;
132              
133 4995         9347 my $type = ref($e);
134              
135             # Only optimal if this is a Pass or a passing Ok
136 4995 100 100     24299 return unless $type eq 'Test2::Event::Pass' || ($type eq 'Test2::Event::Ok' && $e->{pass});
      100        
137              
138             # Amnesty requires further processing (todo is a form of amnesty)
139 2846 100 66     14897 return if ($e->{amnesty} && @{$e->{amnesty}}) || defined($e->{todo});
  13   100     62  
140              
141             # A name with a newline or hash symbol needs extra processing
142 2820 100 100     16899 return if defined($e->{name}) && (-1 != index($e->{name}, "\n") || -1 != index($e->{name}, '#'));
      100        
143              
144 2805         5026 my $ok = 'ok';
145 2805 100 66     14096 $ok .= " $num" if $num && !$self->{+NO_NUMBERS};
146 2805 100       8735 $ok .= defined($e->{name}) ? " - $e->{name}\n" : "\n";
147              
148 2805 100       7120 if (my $nesting = $e->{trace}->{nested}) {
149 89         208 my $indent = ' ' x $nesting;
150 89         243 $ok = "$indent$ok";
151             }
152              
153 2805         6149 my $io = $self->{+HANDLES}->[OUT_STD];
154              
155 2805 50 33     15360 local($\, $,) = (undef, '') if $\ || $,;
156 2805         254331 print $io $ok;
157 2805         12224 $self->{+_LAST_FH} = $io;
158              
159 2805         15275 return 1;
160             }
161              
162             sub event_tap {
163 2956     2956 0 5698 my ($self, $f, $num) = @_;
164              
165 2956         4648 my @tap;
166              
167             # If this IS the first event the plan should come first
168             # (plan must be before or after assertions, not in the middle)
169 2956 100 100     9720 push @tap => $self->plan_tap($f) if $f->{plan} && !$self->{+MADE_ASSERTION};
170              
171             # The assertion is most important, if present.
172 2956 100       6350 if ($f->{assert}) {
173 1338         3383 push @tap => $self->assert_tap($f, $num);
174 1338 100 100     4069 push @tap => $self->debug_tap($f, $num) unless $f->{assert}->{no_debug} || $f->{assert}->{pass};
175             }
176              
177             # Almost as important as an assertion
178 2956 100       6328 push @tap => $self->error_tap($f) if $f->{errors};
179              
180             # Now lets see the diagnostics messages
181 2956 100       7804 push @tap => $self->info_tap($f) if $f->{info};
182              
183             # If this IS NOT the first event the plan should come last
184             # (plan must be before or after assertions, not in the middle)
185 2956 100 100     9854 push @tap => $self->plan_tap($f) if $self->{+MADE_ASSERTION} && $f->{plan};
186              
187             # Bail out
188 2956 100       6664 push @tap => $self->halt_tap($f) if $f->{control}->{halt};
189              
190 2956 100       10850 return @tap if @tap;
191 217 100       1037 return @tap if $f->{control}->{halt};
192 215 100       780 return @tap if grep { $f->{$_} } qw/assert plan info errors/;
  860         2292  
193              
194             # Use the summary as a fallback if nothing else is usable.
195 211         1716 return $self->summary_tap($f, $num);
196             }
197              
198             sub error_tap {
199 4     4 0 94 my $self = shift;
200 4         7 my ($f) = @_;
201              
202 4 50 33     11 my $IO = ($f->{amnesty} && @{$f->{amnesty}}) ? OUT_STD : OUT_ERR;
203              
204             return map {
205 6         7 my $details = $_->{details};
206              
207 6         7 my $msg;
208 6 100       11 if (ref($details)) {
209 1         6 require Data::Dumper;
210 1         4 my $dumper = Data::Dumper->new([$details])->Indent(2)->Terse(1)->Pad('# ')->Useqq(1)->Sortkeys(1);
211 1         39 chomp($msg = $dumper->Dump);
212             }
213             else {
214 5         9 chomp($msg = $details);
215 5         17 $msg =~ s/^/# /;
216 5         11 $msg =~ s/\n/\n# /g;
217             }
218              
219 6         44 [$IO, "$msg\n"];
220 4         8 } @{$f->{errors}};
  4         10  
221             }
222              
223             sub plan_tap {
224 497     497 0 1151 my $self = shift;
225 497         1073 my ($f) = @_;
226 497 100       2949 my $plan = $f->{plan} or return;
227              
228 496 100       1720 return if $plan->{none};
229              
230 494 100       1617 if ($plan->{skip}) {
231 19 100       147 my $reason = $plan->{details} or return [OUT_STD, "1..0 # SKIP\n"];
232 16         367 chomp($reason);
233 16         142 return [OUT_STD, '1..0 # SKIP ' . $reason . "\n"];
234             }
235              
236 475         2383 return [OUT_STD, "1.." . $plan->{count} . "\n"];
237             }
238              
239 733     733 0 1287 sub no_subtest_space { 0 }
240             sub assert_tap {
241 1378     1378 0 2127 my $self = shift;
242 1378         2316 my ($f, $num) = @_;
243              
244 1378 50       3060 my $assert = $f->{assert} or return;
245 1378         2082 my $pass = $assert->{pass};
246 1378         2111 my $name = $assert->{details};
247              
248 1378 100       2754 my $ok = $pass ? 'ok' : 'not ok';
249 1378 100 66     5503 $ok .= " $num" if $num && !$self->{+NO_NUMBERS};
250              
251             # The regex form is ~250ms, the index form is ~50ms
252 1378         2021 my @extra;
253 1378 100 66     7525 defined($name) && (
      100        
254             (index($name, "\n") != -1 && (($name, @extra) = split(/\n\r?/, $name, -1))),
255             ((index($name, "#" ) != -1 || substr($name, -1) eq '\\') && (($name =~ s|\\|\\\\|g), ($name =~ s|#|\\#|g)))
256             );
257              
258 1378 100       3011 my $extra_space = @extra ? ' ' x (length($ok) + 2) : '';
259 1378         2082 my $extra_indent = '';
260              
261 1378         2136 my ($directives, $reason, $is_skip);
262 1378 100       3095 if ($f->{amnesty}) {
263 343         546 my %directives;
264              
265 343         465 for my $am (@{$f->{amnesty}}) {
  343         791  
266 356 50       820 next if $am->{inherited};
267 356 50       811 my $tag = $am->{tag} or next;
268 356 100       739 $is_skip = 1 if $tag eq 'skip';
269              
270 356   100     1378 $directives{$tag} ||= $am->{details};
271             }
272              
273 343         543 my %seen;
274              
275             # Sort so that TODO comes before skip even on systems where lc sorts
276             # before uc, as other code depends on that ordering.
277 343         1059 my @order = grep { !$seen{$_}++ } sort { lc $b cmp lc $a } keys %directives;
  353         1371  
  10         48  
278              
279 343         877 $directives = ' # ' . join ' & ' => @order;
280              
281 343         632 for my $tag ('skip', @order) {
282 644 100 100     2214 next unless defined($directives{$tag}) && length($directives{$tag});
283 286         507 $reason = $directives{$tag};
284 286         767 last;
285             }
286             }
287              
288 1378 100 100     5499 $ok .= " - $name" if defined $name && !($is_skip && !$name);
      100        
289              
290 1378         1977 my @subtap;
291 1378 100 100     3109 if ($f->{parent} && $f->{parent}->{buffered}) {
292 88         152 $ok .= ' {';
293              
294             # In a verbose harness we indent the extra since they will appear
295             # inside the subtest braces. This helps readability. In a non-verbose
296             # harness we do not do this because it is less readable.
297 88 50 33     433 if ($ENV{HARNESS_IS_VERBOSE} || !$ENV{HARNESS_ACTIVE}) {
298 0         0 $extra_indent = " ";
299 0         0 $extra_space = ' ';
300             }
301              
302             # Render the sub-events, we use our own counter for these.
303 88         149 my $count = 0;
304             @subtap = map {
305 763         1057 my $f2 = $_;
306              
307             # Bump the count for any event that should bump it.
308 763 100       1572 $count++ if $f2->{assert};
309              
310             # This indents all output lines generated for the sub-events.
311             # index 0 is the filehandle, index 1 is the message we want to indent.
312 763         1498 map { $_->[1] =~ s/^(.*\S.*)$/ $1/mg; $_ } $self->event_tap($f2, $count);
  889         5388  
  889         2285  
313 88         135 } @{$f->{parent}->{children}};
  88         237  
314              
315 88         246 push @subtap => [OUT_STD, "}\n"];
316             }
317              
318 1378 100       3054 if ($directives) {
319 343 100       684 $directives = ' # TODO & SKIP' if $directives eq ' # TODO & skip';
320 343         520 $ok .= $directives;
321 343 100       817 $ok .= " $reason" if defined($reason);
322             }
323              
324 1378 100       3327 $extra_space = ' ' if $self->no_subtest_space;
325              
326 1378         4406 my @out = ([OUT_STD, "$ok\n"]);
327 1378 100       3008 push @out => map {[OUT_STD, "${extra_indent}#${extra_space}$_\n"]} @extra if @extra;
  31         114  
328 1378         2329 push @out => @subtap;
329              
330 1378         3631 return @out;
331             }
332              
333             sub debug_tap {
334 23     23 0 60 my ($self, $f, $num) = @_;
335              
336             # Figure out the debug info, this is typically the file name and line
337             # number, but can also be a custom message. If no trace object is provided
338             # then we have nothing useful to display.
339 23         40 my $name = $f->{assert}->{details};
340 23         40 my $trace = $f->{trace};
341              
342 23         36 my $debug = "[No trace info available]";
343 23 100       66 if ($trace->{details}) {
    100          
344 1         2 $debug = $trace->{details};
345             }
346             elsif ($trace->{frame}) {
347 19         25 my ($pkg, $file, $line) = @{$trace->{frame}};
  19         44  
348 19 50 33     107 $debug = "at $file line $line." if $file && $line;
349             }
350              
351 23 100 100     65 my $amnesty = $f->{amnesty} && @{$f->{amnesty}}
352             ? ' (with amnesty)'
353             : '';
354              
355             # Create the initial diagnostics. If the test has a name we put the debug
356             # info on a second line, this behavior is inherited from Test::Builder.
357 23 100       80 my $msg = defined($name)
358             ? qq[# Failed test${amnesty} '$name'\n# $debug\n]
359             : qq[# Failed test${amnesty} $debug\n];
360              
361 23 100 100     58 my $IO = $f->{amnesty} && @{$f->{amnesty}} ? OUT_STD : OUT_ERR;
362              
363 23         90 return [$IO, $msg];
364             }
365              
366             sub halt_tap {
367 13     13 0 43 my ($self, $f) = @_;
368              
369 13 100 100     52 return if $f->{trace}->{nested} && !$f->{trace}->{buffered};
370 10         22 my $details = $f->{control}->{details};
371              
372 10 100 100     78 return [OUT_STD, "Bail out!\n"] unless defined($details) && length($details);
373 7         34 return [OUT_STD, "Bail out! $details\n"];
374             }
375              
376             sub info_tap {
377 914     914 0 2027 my ($self, $f) = @_;
378              
379             return map {
380 921         1619 my $details = $_->{details};
381 921         1597 my $table = $_->{table};
382              
383 921 100 100     3211 my $IO = $_->{debug} && !($f->{amnesty} && @{$f->{amnesty}}) ? OUT_ERR : OUT_STD;
384              
385 921         1346 my $msg;
386 921 50 33     2809 if ($table && $self->supports_tables) {
    100          
387 0         0 $msg = join "\n" => map { "# $_" } Term::Table->new(
388             header => $table->{header},
389             rows => $table->{rows},
390             collapse => $table->{collapse},
391             no_collapse => $table->{no_collapse},
392 0         0 sanitize => 1,
393             mark_tail => 1,
394             max_width => $self->calc_table_size($f),
395             )->render();
396             }
397             elsif (ref($details)) {
398 2         10 require Data::Dumper;
399 2         8 my $dumper = Data::Dumper->new([$details])->Indent(2)->Terse(1)->Pad('# ')->Useqq(1)->Sortkeys(1);
400 2         94 chomp($msg = $dumper->Dump);
401             }
402             else {
403 919         2192 chomp($msg = $details);
404 919         5097 $msg =~ s/^/# /;
405 919         3313 $msg =~ s/\n/\n# /g;
406             }
407              
408 921         5109 [$IO, "$msg\n"];
409 914         1390 } @{$f->{info}};
  914         2174  
410             }
411              
412             sub summary_tap {
413 214     214 0 795 my ($self, $f, $num) = @_;
414              
415 214 100       1222 return if $f->{about}->{no_display};
416              
417 213 100       1766 my $summary = $f->{about}->{details} or return;
418 2         5 chomp($summary);
419 2         10 $summary =~ s/^/# /smg;
420              
421 2         15 return [OUT_STD, "$summary\n"];
422             }
423              
424             sub calc_table_size {
425 0     0 0   my $self = shift;
426 0           my ($f) = @_;
427              
428 0           my $term = Term::Table::Util::term_size();
429 0   0       my $nesting = 2 + (($f->{trace}->{nested} || 0) * 4); # 4 spaces per level, also '# ' prefix
430 0           my $total = $term - $nesting;
431              
432             # Sane minimum width, any smaller and we are asking for pain
433 0 0         return 50 if $total < 50;
434              
435 0           return $total;
436             }
437              
438             1;
439              
440             __END__