File Coverage

blib/lib/Test2/Harness/Parser/TAP.pm
Criterion Covered Total %
statement 195 199 97.9
branch 87 96 90.6
condition 21 25 84.0
subroutine 34 34 100.0
pod 2 13 15.3
total 339 367 92.3


line stmt bran cond sub pod time code
1             package Test2::Harness::Parser::TAP;
2 24     24   116918 use strict;
  24         28  
  24         903  
3 24     24   93 use warnings;
  24         46  
  24         1961  
4              
5             our $VERSION = '0.000012';
6              
7 24     24   115 use Test2::Event::Bail;
  24         45  
  24         846  
8 24     24   179 use Test2::Event::Diag;
  24         110  
  24         719  
9 24     24   819 use Test2::Event::Encoding;
  24         563  
  24         497  
10 24     24   99 use Test2::Event::Exception;
  24         23  
  24         498  
11 24     24   73 use Test2::Event::Note;
  24         69  
  24         492  
12 24     24   115 use Test2::Event::Ok;
  24         25  
  24         549  
13 24     24   775 use Test2::Event::ParseError;
  24         27  
  24         448  
14 24     24   115 use Test2::Event::Plan;
  24         45  
  24         470  
15 24     24   72 use Test2::Event::Skip;
  24         47  
  24         472  
16 24     24   93 use Test2::Event::Subtest;
  24         24  
  24         555  
17 24     24   10512 use Test2::Event::TAP::Version;
  24         8799  
  24         622  
18 24     24   726 use Test2::Event::UnknownStderr;
  24         27  
  24         451  
19 24     24   677 use Test2::Event::UnknownStdout;
  24         25  
  24         643  
20 24     24   94 use Test2::Event::Waiting;
  24         25  
  24         470  
21 24     24   8871 use Test2::Harness::Parser::TAP::SubtestState;
  24         69  
  24         568  
22              
23 24     24   957 use Time::HiRes qw/sleep/;
  24         2527  
  24         292  
24              
25 24     24   4020 use base 'Test2::Harness::Parser';
  24         25  
  24         2478  
26 24     24   97 use Test2::Util::HashBase qw/_subtest_state/;
  24         24  
  24         76  
27              
28 10     10 0 26265 sub init { $_[0]->_init }
29 265     265 1 111507 sub morph { $_[0]->_init }
30              
31             sub _init {
32 275     275   522 my $self = shift;
33 275         2938 $self->{+_SUBTEST_STATE} = Test2::Harness::Parser::TAP::SubtestState->new;
34             }
35              
36             sub step {
37 9713     9713 1 94204 my $self = shift;
38              
39 9713         32242 my @events = ($self->parse_stdout, $self->parse_stderr);
40             # If in_subtest is defined then the object is part of a buffered subtest
41             # and therefore cannot be starting a streaming subtest.
42 9713 100       33404 return map { defined $_->in_subtest ? $_ : $self->{+_SUBTEST_STATE}->maybe_start_streaming_subtest($_) } @events;
  5536         15968  
43             }
44              
45             sub parse_stderr {
46 9723     9723 0 22228 my $self = shift;
47              
48 9723 100       25273 my $line = $self->proc->get_err_line(peek => 1) or return;
49              
50 50 100       774 return $self->slurp_comments('STDERR')
51             if $line =~ m/^\s*#/;
52              
53 4         9 $line = $self->proc->get_err_line();
54 4         25 chomp(my $out = $line);
55 4 50       9 return unless length($out);
56 4         30 return Test2::Event::UnknownStderr->new(output => $out);
57             }
58              
59             sub parse_stdout {
60 9713     9713 0 14695 my $self = shift;
61              
62 9713 100       48074 my $line = $self->proc->get_out_line(peek => 1) or return;
63              
64 1265 100       5604 return $self->slurp_comments('STDOUT')
65             if $line =~ m/^\s*#/;
66              
67 1210         2456 $line = $self->proc->get_out_line();
68 1210         3488 my @events = $self->parse_tap_line($line);
69 1210 100       4671 return @events if @events;
70              
71 6         9 chomp(my $out = $line);
72 6 50       26 return unless length($out);
73 0         0 return Test2::Event::UnknownStdout->new(output => $out);
74             }
75              
76             sub parse_tap_line {
77 5779     5779 0 5236 my $self = shift;
78 5779         6036 my ($line) = @_;
79              
80 5779         7026 chomp($line);
81 5779 100       25723 my ($lead, $str) = ($line =~ m/^(\s+)(.+)$/) ? ($1, $2) : ('', $line);
82 5779         5905 $lead =~ s/\t/ /g;
83 5779         7891 my $nest = length($lead) / 4;
84              
85 5779         3740 my @events;
86             # The buffered_subtest parsing always starts by trying to parse an "ok"
87             # line, so we don't need to try parsing that _again_.
88 5779         9943 my @types = qw/buffered_subtest plan bail version/;
89 5779         6555 for my $type (@types) {
90 7760         9242 my $sub = "parse_tap_$type";
91 7760 100       14674 if (@events = $self->$sub($str, $nest)) {
92 5439         6266 last;
93             }
94             }
95              
96 5779         10108 return @events;
97             }
98              
99             sub parse_tap_buffered_subtest {
100 5779     5779 0 5500 my $self = shift;
101 5779         5352 my ($line, $nest) = @_;
102              
103 5779 100       7458 my ($st_ok, @errors) = $self->parse_tap_ok($line, $nest) or return;
104 4478 100       17324 return ($st_ok, @errors) unless $line =~ /\s*\{\s*\)?\s*$/;
105              
106 686         2708 my $id = $self->{+_SUBTEST_STATE}->next_id;
107              
108 686         706 my @events;
109             my @subevents;
110 686         660 my $count = 0;
111 686         530 while (1) {
112 5255         19896 my $line = $self->proc->get_out_line();
113 5255 50       8048 unless (defined $line) {
114 0         0 sleep 0.1;
115 0 0       0 die "Abrupt end to buffered subtest?" if $count++ > 10;
116 0         0 next;
117             }
118              
119 5255 100       10313 last if $line =~ m/^\s*\}\s*$/;
120 4569         6281 my @e = $self->parse_tap_line($line);
121 4569         4879 push @events => @e;
122             # We might have events where in_subtest is already set in the case of
123             # nested buffered subtests. In that case, we want those nested
124             # subevents to _not_ be part of this particular subtest. Instead, they
125             # are part of the child subtest contained in the parent. However, we
126             # still want to emit _all_ the events as we see them, so we need to do
127             # some filtering here.
128 4569         4239 push @subevents => grep { !defined $_->in_subtest } @e;
  4307         7861  
129             }
130              
131 686         2585 $_->set_in_subtest($id) for @subevents;
132              
133             # If this is a buffered subtest marked as todo, then the "{" marking the
134             # subtest ends up in the todo field instead of the name;
135 686         10933 my $todo = $st_ok->todo;
136 686 100       2474 $todo =~ s/\s*\{\s*$// if defined $todo;
137              
138 686         1583 my $name = $st_ok->name;
139 686 50       4101 $name =~ s/\s*\{\s*$// if defined $name;
140              
141 686         1649 my %st = (
142             subtest_id => $id,
143             name => $name,
144             pass => $st_ok->pass,
145             todo => $todo,
146             nested => $nest,
147             subevents => \@subevents,
148             );
149              
150 686         6946 my $st = Test2::Event::Subtest->new(%st);
151              
152 686         29088 return (@events, $st, @errors);
153             }
154              
155             sub parse_tap_ok {
156 5827     5827 0 56700 my $self = shift;
157 5827         4551 my ($line, $nest) = @_;
158              
159 5827         3975 my ($pass, $todo, $skip, $num, @errors);
160              
161 5827 100       26924 return unless $line =~ s/^(not )?ok\b//;
162 4526         7564 $pass = !$1;
163              
164 4526 100 100     18168 push @errors => "'ok' is not immediately followed by a space."
165             if $line && !($line =~ m/^ /);
166              
167 4526 100       15188 if ($line =~ s/^(\s*)(\d+)\b//) {
168 4492         5788 my $space = $1;
169 4492         4535 $num = $2;
170              
171 4492 100       7596 push @errors => "Extra space after 'ok'"
172             if length($space) > 1;
173             }
174              
175             # Not strictly compliant, but compliant with what Test-Simple does...
176             # Standard does not have a todo & skip.
177 4526 100       7555 if ($line =~ s/#\s*(todo & skip|todo|skip)(.*)$//i) {
178 75         150 my ($directive, $reason) = ($1, $2);
179              
180 75 100       354 push @errors => "No space before the '#' for the '$directive' directive."
181             unless $line =~ s/\s+$//;
182              
183 75 100 100     972 push @errors => "No space between '$directive' directive and reason."
184             if $reason && !($reason =~ s/^\s+//);
185              
186 75 100       211 $skip = $reason if $directive =~ m/skip/i;
187 75 100       232 $todo = $reason if $directive =~ m/todo/i;
188             }
189              
190             # Standard says that everything after the ok (except the number) is part of
191             # the name. Most things add a dash between them, and I am deviating from
192             # standards by stripping it and surrounding whitespace.
193 4526         10793 $line =~ s/\s*-\s*//;
194              
195 4526         6008 $line =~ s/^\s+//;
196 4526         6345 $line =~ s/\s+$//;
197              
198 4526         3172 my $event;
199 4526 100       8767 if ($line =~ /^Subtest: (.+)$/) {
    100          
200 12         55 $event = Test2::Event::Subtest->new($self->{+_SUBTEST_STATE}->finish_streaming_subtest($pass, $line, $nest));
201             }
202             elsif (defined $skip) {
203 29         276 $event = Test2::Event::Skip->new(
204             reason => $skip,
205             pass => $pass,
206             name => $line,
207             nested => $nest,
208             );
209             }
210             else {
211 4485 100       14500 $event = Test2::Event::Ok->new(
212             defined($todo) ? (todo => $todo) : (),
213             pass => $pass,
214             name => $line,
215             nested => $nest,
216             );
217             }
218              
219             return (
220             $event,
221 4526         66992 map { Test2::Event::ParseError->new(parse_error => $_) } @errors,
  8         21  
222             );
223             }
224              
225             sub parse_tap_version {
226 346     346 0 3688 my $self = shift;
227 346         311 my ($line, $nest) = @_;
228              
229 346 100       962 return unless $line =~ s/^TAP version\s*//;
230              
231 4         21 return Test2::Event::TAP::Version->new(
232             version => $line,
233             nested => $nest,
234             );
235             }
236              
237             sub parse_tap_plan {
238 1313     1313 0 7898 my $self = shift;
239 1313         1450 my ($line, $nest) = @_;
240              
241 1313 100       5373 return unless $line =~ s/^1\.\.(\d+)//;
242 969         1834 my $max = $1;
243              
244 969         910 my ($directive, $reason);
245              
246 969 100       2406 if ($max == 0) {
247 23 100       274 if ($line =~ s/^\s*#\s*//) {
248 19 50       145 if ($line =~ s/^(skip)\S*\s*//i) {
249 19         112 $directive = uc($1);
250 19         71 $reason = $line;
251 19         54 $line = "";
252             }
253             }
254              
255 23   100     125 $directive ||= "SKIP";
256 23   100     141 $reason ||= "no reason given";
257             }
258              
259 969         3503 my $event = Test2::Event::Plan->new(
260             max => $max,
261             directive => $directive,
262             reason => $reason,
263             nested => $nest,
264             );
265              
266 969 100       23282 return $event unless $line =~ m/\S/;
267              
268             return (
269 2         24 $event,
270             Test2::Event::ParseError->new(
271             parse_error => 'Extra characters after plan.',
272             ),
273             );
274             }
275              
276             sub parse_tap_bail {
277 346     346 0 5878 my $self = shift;
278 346         278 my ($line, $nest) = @_;
279              
280 346 100       875 return unless $line =~ s/^Bail out! *//;
281              
282 4         15 return Test2::Event::Bail->new(
283             reason => $line,
284             nested => $nest,
285             );
286             }
287              
288             sub slurp_comments {
289 115     115 0 7540 my $self = shift;
290 115         332 my ($io) = @_;
291              
292 115 100       649 my $meth = $io eq 'STDERR' ? 'get_err_line' : 'get_out_line';
293              
294 115         354 my $raw = $self->proc->$meth;
295 115         547 my ($nest, $diag) = strip_comment($raw);
296              
297 115 50 33     740 die "Not a comment? ($raw)"
298             unless defined($nest) && defined($diag);
299              
300 115 100       593 my $failed = $diag =~ m/^Failed test/ ? 1 : 0;
301              
302 115         173 while (1) {
303 1062 100       1875 my $line = $self->proc->$meth(peek => 1) or last;
304 992         1557 my ($lnest, $msg) = strip_comment($line);
305 992 100 66     3314 last unless defined($lnest) && defined($msg);
306 959 100       1536 last if $lnest != $nest;
307 955 100       1584 last if $msg =~ m/^Failed test/;
308 953 100 100     1673 last if $failed && $msg !~ m/^at /;
309              
310 951         1879 $raw .= $self->proc->$meth;
311              
312 951 50       2447 $diag .= "\n$msg" if $msg;
313 951 100       1409 last if $failed;
314             }
315              
316 115 100       533 my $class = $io eq 'STDERR' ? 'Test2::Event::Diag' : 'Test2::Event::Note';
317 115         919 return $class->new(
318             message => $diag,
319             nested => $nest,
320             );
321             }
322              
323             sub strip_comment {
324 1115     1115 0 4831 my $line = shift;
325 1115         1122 chomp($line);
326 1115         4796 my ($nest, $hash, $space, $msg) = split /(#)(\s*)/, $line, 2;
327 1115 100 100     2686 return unless $msg || $hash || $space;
      66        
328              
329 1080         1184 $nest = length($nest) / 4;
330             # We want to preserve any space in the comment _after_ the first space,
331             # since proper TAP is formatted as "# $msg". So the first space is part of
332             # the comment marker, while subsequent space is significant.
333 1080         2627 $space =~ s/^ //;
334 1080         2048 return ($nest, $space . $msg);
335             }
336              
337             1;
338              
339             __END__