1 package Test2::Formatter::TAP;
6 our $VERSION = '1.302059';
9 use Test2::Util::HashBase qw{
10 no_numbers handles _encoding
18 BEGIN { require Test2::Formatter; our @ISA = qw(Test2::Formatter) }
21 'Test2::Event::Ok' => 'event_ok',
22 'Test2::Event::Skip' => 'event_skip',
23 'Test2::Event::Note' => 'event_note',
24 'Test2::Event::Diag' => 'event_diag',
25 'Test2::Event::Bail' => 'event_bail',
26 'Test2::Event::Exception' => 'event_exception',
27 'Test2::Event::Subtest' => 'event_subtest',
28 'Test2::Event::Plan' => 'event_plan',
31 # Initial list of converters are safe for direct hash access cause we control them.
32 my %SAFE_TO_ACCESS_HASH = %CONVERTERS;
36 my ($type, $convert) = @_;
37 croak "Event type is a required argument" unless $type;
38 croak "Event type '$type' already registered" if $CONVERTERS{$type};
39 croak "The second argument to register_event() must be a code reference or method name"
40 unless $convert && (ref($convert) eq 'CODE' || $class->can($convert));
41 $CONVERTERS{$type} = $convert;
50 $self->{+HANDLES} ||= $self->_open_handles;
51 if(my $enc = delete $self->{encoding}) {
52 $self->encoding($enc);
56 sub hide_buffered { 1 }
63 my $handles = $self->{+HANDLES};
65 # https://rt.perl.org/Public/Bug/Display.html?id=31923
66 # If utf8 is requested we use ':utf8' instead of ':encoding(utf8)' in
67 # order to avoid the thread segfault.
68 if ($enc =~ m/^utf-?8$/i) {
69 binmode($_, ":utf8") for @$handles;
72 binmode($_, ":encoding($enc)") for @$handles;
74 $self->{+_ENCODING} = $enc;
77 return $self->{+_ENCODING};
81 no warnings 'redefine';
85 my ($self, $e, $num) = @_;
89 my $converter = $CONVERTERS{$type} || 'event_other';
90 my @tap = $self->$converter($e, $self->{+NO_NUMBERS} ? undef : $num) or return;
92 my $handles = $self->{+HANDLES};
93 my $nesting = ($SAFE_TO_ACCESS_HASH{$type} ? $e->{nested} : $e->nested) || 0;
94 my $indent = ' ' x $nesting;
96 # Local is expensive! Only do it if we really need to.
97 local($\, $,) = (undef, '') if $\ || $,;
99 no warnings 'uninitialized';
100 my ($hid, $msg) = @$set;
102 my $io = $handles->[$hid] or next;
104 $msg =~ s/^/$indent/mg if $nesting;
113 open(my $out, '>&', STDOUT) or die "Can't dup STDOUT: $!";
114 binmode($out, join(":", "", "raw", grep { $_ ne 'unix' and !$seen{$_}++ } PerlIO::get_layers(STDOUT)));
117 open(my $err, '>&', STDERR) or die "Can't dup STDERR: $!";
118 binmode($err, join(":", "", "raw", grep { $_ ne 'unix' and !$seen{$_}++ } PerlIO::get_layers(STDERR)));
128 my $old_fh = select $fh;
137 my $converter = $CONVERTERS{ref($e)} or return;
139 $num = undef if $self->{+NO_NUMBERS};
141 return $self->$converter($e, $num);
148 # We use direct hash access for performance. OK events are so common we
149 # need this to be fast.
150 my ($name, $todo) = @{$e}{qw/name todo/};
151 my $in_todo = defined($todo);
154 $out .= "not " unless $e->{pass};
156 $out .= " $num" if defined($num);
158 # The regex form is ~250ms, the index form is ~50ms
161 (index($name, "\n") != -1 && (($name, @extra) = split(/\n\r?/, $name, -1))),
162 ((index($name, "#" ) != -1 || substr($name, -1) eq '\\') && (($name =~ s|\\|\\\\|g), ($name =~ s|#|\\#|g)))
165 my $space = @extra ? ' ' x (length($out) + 2) : '';
167 $out .= " - $name" if defined $name;
168 $out .= " # TODO" if $in_todo;
169 $out .= " $todo" if defined($todo) && length($todo);
171 # The primary line of TAP, if the test passed this is all we need.
172 return([OUT_STD, "$out\n"]) unless @extra;
174 return $self->event_ok_multiline($out, $space, @extra);
177 sub event_ok_multiline {
179 my ($out, $space, @extra) = @_;
183 map {[OUT_STD, "#${space}$_\n"]} @extra,
192 my $reason = $e->reason;
196 $out .= "not " unless $e->{pass};
198 $out .= " $num" if defined $num;
199 $out .= " - $name" if $name;
200 if (defined($todo)) {
201 $out .= " # TODO & SKIP"
206 $out .= " $reason" if defined($reason) && length($reason);
208 return([OUT_STD, "$out\n"]);
215 chomp(my $msg = $e->message);
219 return [OUT_STD, "$msg\n"];
226 chomp(my $msg = $e->message);
230 return [OUT_ERR, "$msg\n"];
237 return if $e->nested;
241 "Bail out! " . $e->reason . "\n",
245 sub event_exception {
248 return [ OUT_ERR, $e->error ];
255 # A 'subtest' is a subclass of 'ok'. Let the code that renders 'ok' render
257 my ($ok, @diag) = $self->event_ok($e, $num);
259 # If the subtest is not buffered then the sub-events have already been
260 # rendered, we can go ahead and return.
261 return ($ok, @diag) unless $e->buffered;
263 # In a verbose harness we indent the diagnostics from the 'Ok' event since
264 # they will appear inside the subtest braces. This helps readability. In a
265 # non-verbose harness we do not do this because it is less readable.
266 if ($ENV{HARNESS_IS_VERBOSE}) {
267 # index 0 is the filehandle, index 1 is the message we want to indent.
268 $_->[1] =~ s/^(.*\S.*)$/ $1/mg for @diag;
271 # Add the trailing ' {' to the 'ok' line of TAP output.
272 $ok->[1] =~ s/\n/ {\n/;
274 # Render the sub-events, we use our own counter for these.
277 # Bump the count for any event that should bump it.
278 $count++ if $_->increments_count;
280 # This indents all output lines generated for the sub-events.
281 # index 0 is the filehandle, index 1 is the message we want to indent.
282 map { $_->[1] =~ s/^(.*\S.*)$/ $1/mg; $_ } $self->event_tap($_, $count);
286 $ok, # opening ok - name {
287 @diag, # diagnostics if the subtest failed
288 @subs, # All the inner-event lines
289 [OUT_STD(), "}\n"], # } (closing brace)
297 my $directive = $e->directive;
298 return if $directive && $directive eq 'NO PLAN';
300 my $reason = $e->reason;
301 $reason =~ s/\n/\n# /g if $reason;
303 my $plan = "1.." . $e->max;
305 $plan .= " # $directive";
306 $plan .= " $reason" if defined $reason;
309 return [OUT_STD, "$plan\n"];
315 return if $e->no_display;
319 if (my ($max, $directive, $reason) = $e->sets_plan) {
320 my $plan = "1..$max";
321 $plan .= " # $directive" if $directive;
322 $plan .= " $reason" if defined $reason;
323 push @out => [OUT_STD, "$plan\n"];
326 if ($e->increments_count) {
328 $ok .= "not " if $e->causes_fail;
330 $ok .= " $num" if defined($num);
331 $ok .= " - " . $e->summary if $e->summary;
333 push @out => [OUT_STD, "$ok\n"];
336 my $handle = ($e->causes_fail || $e->diagnostics) ? OUT_ERR : OUT_STD;
337 my $summary = $e->summary || ref($e);
339 $summary =~ s/^/# /smg;
340 push @out => [$handle, "$summary\n"];
356 Test2::Formatter::TAP - Standard TAP formatter
360 This is what takes events and turns them into TAP.
364 use Test2::Formatter::TAP;
365 my $tap = Test2::Formatter::TAP->new();
368 $tap->encoding('utf8');
370 $tap->write($event, $number); # Output an event
376 =item $bool = $tap->no_numbers
378 =item $tap->set_no_numbers($bool)
380 Use to turn numbers on and off.
382 =item $arrayref = $tap->handles
384 =item $tap->set_handles(\@handles);
386 Can be used to get/set the filehandles. Indexes are identified by the
387 C<OUT_STD> and C<OUT_ERR> constants.
389 =item $encoding = $tap->encoding
391 =item $tap->encoding($encoding)
393 Get or set the encoding. By default no encoding is set, the original settings
394 of STDOUT and STDERR are used.
396 This directly modifies the stored filehandles, it does not create new ones.
398 =item $tap->write($e, $num)
400 Write an event to the console.
402 =item Test2::Formatter::TAP->register_event($pkg, sub { ... });
404 In general custom events are not supported. There are however occasions where
405 you might want to write a custom event type that results in TAP output. In
406 order to do this you use the C<register_event()> class method.
409 use Test2::Formatter::TAP;
411 use base 'Test2::Event';
412 use Test2::Util::HashBase qw/pass name diag note/;
414 Test2::Formatter::TAP->register_event(
420 [Test2::Formatter::TAP::OUT_STD, "ok $num - " . $e->name . "\n"],
421 [Test2::Formatter::TAP::OUT_ERR, "# " . $e->name . " " . $e->diag . "\n"],
422 [Test2::Formatter::TAP::OUT_STD, "# " . $e->name . " " . $e->note . "\n"],
433 All these methods require the event itself. Optionally they can all except a
436 All methods return a list of array-refs. Each array-ref will have 2 items, the
437 first is an integer identifying an output handle, the second is a string that
438 should be written to the handle.
442 =item @out = $TAP->event_ok($e)
444 =item @out = $TAP->event_ok($e, $num)
446 Process an L<Test2::Event::Ok> event.
448 =item @out = $TAP->event_plan($e)
450 =item @out = $TAP->event_plan($e, $num)
452 Process an L<Test2::Event::Plan> event.
454 =item @out = $TAP->event_note($e)
456 =item @out = $TAP->event_note($e, $num)
458 Process an L<Test2::Event::Note> event.
460 =item @out = $TAP->event_diag($e)
462 =item @out = $TAP->event_diag($e, $num)
464 Process an L<Test2::Event::Diag> event.
466 =item @out = $TAP->event_bail($e)
468 =item @out = $TAP->event_bail($e, $num)
470 Process an L<Test2::Event::Bail> event.
472 =item @out = $TAP->event_exception($e)
474 =item @out = $TAP->event_exception($e, $num)
476 Process an L<Test2::Event::Exception> event.
478 =item @out = $TAP->event_skip($e)
480 =item @out = $TAP->event_skip($e, $num)
482 Process an L<Test2::Event::Skip> event.
484 =item @out = $TAP->event_subtest($e)
486 =item @out = $TAP->event_subtest($e, $num)
488 Process an L<Test2::Event::Subtest> event.
490 =item @out = $TAP->event_other($e, $num)
492 Fallback for unregistered event types. It uses the L<Test2::Event> API to
493 convert the event to TAP.
499 The source code repository for Test2 can be found at
500 F<http://github.com/Test-More/test-more/>.
506 =item Chad Granum E<lt>exodist@cpan.orgE<gt>
514 =item Chad Granum E<lt>exodist@cpan.orgE<gt>
516 =item Kent Fredric E<lt>kentnl@cpan.orgE<gt>
522 Copyright 2016 Chad Granum E<lt>exodist@cpan.orgE<gt>.
524 This program is free software; you can redistribute it and/or
525 modify it under the same terms as Perl itself.
527 See F<http://dev.perl.org/licenses/>