File Coverage

blib/lib/TAP/DOM.pm
Criterion Covered Total %
statement 227 229 99.1
branch 123 144 85.4
condition 68 83 81.9
subroutine 24 24 100.0
pod 5 5 100.0
total 447 485 92.1


line stmt bran cond sub pod time code
1             package TAP::DOM;
2             # git description: v0.97-7-g357ca13
3              
4             our $AUTHORITY = 'cpan:SCHWIGON';
5             # ABSTRACT: TAP as Document Object Model.
6             $TAP::DOM::VERSION = '0.98';
7 19     19   1199667 use 5.006;
  19         235  
8 19     19   114 use strict;
  19         43  
  19         529  
9 19     19   115 use warnings;
  19         49  
  19         659  
10              
11 19     19   8102 use TAP::DOM::Entry;
  19         48  
  19         570  
12 19     19   7865 use TAP::DOM::Summary;
  19         46  
  19         567  
13 19     19   8183 use TAP::DOM::DocumentData;
  19         230  
  19         563  
14 19     19   7550 use TAP::DOM::Config;
  19         44  
  19         550  
15 19     19   11755 use TAP::Parser;
  19         996553  
  19         720  
16 19     19   8961 use TAP::Parser::Aggregator;
  19         122928  
  19         554  
17 19     19   8714 use YAML::Syck;
  19         35879  
  19         1199  
18 19     19   11495 use Data::Dumper;
  19         122416  
  19         3294  
19              
20             our $IS_PLAN = 1;
21             our $IS_OK = 2;
22             our $IS_TEST = 4;
23             our $IS_COMMENT = 8;
24             our $IS_UNKNOWN = 16;
25             our $IS_ACTUAL_OK = 32;
26             our $IS_VERSION = 64;
27             our $IS_PRAGMA = 128;
28             our $IS_UNPLANNED = 256;
29             our $IS_BAILOUT = 512;
30             our $IS_YAML = 1024;
31             our $HAS_SKIP = 2048;
32             our $HAS_TODO = 4096;
33              
34             our @tap_dom_args = (qw(ignore
35             ignorelines
36             dontignorelines
37             usebitsets
38             disable_global_kv_data
39             put_dangling_kv_data_under_lazy_plan
40             document_data_prefix
41             document_data_ignore
42             preprocess_ignorelines
43             preprocess_tap
44             noempty_tap
45             lowercase_fieldnames
46             lowercase_fieldvalues
47             trim_fieldvalues
48             ));
49              
50 19     19   8352 use parent 'Exporter';
  19         5660  
  19         116  
51             our @EXPORT_OK = qw( $IS_PLAN
52             $IS_OK
53             $IS_TEST
54             $IS_COMMENT
55             $IS_UNKNOWN
56             $IS_ACTUAL_OK
57             $IS_VERSION
58             $IS_PRAGMA
59             $IS_UNPLANNED
60             $IS_BAILOUT
61             $IS_YAML
62             $HAS_SKIP
63             $HAS_TODO
64             );
65             our %EXPORT_TAGS = (constants => [ qw( $IS_PLAN
66             $IS_OK
67             $IS_TEST
68             $IS_COMMENT
69             $IS_UNKNOWN
70             $IS_ACTUAL_OK
71             $IS_VERSION
72             $IS_PRAGMA
73             $IS_UNPLANNED
74             $IS_BAILOUT
75             $IS_YAML
76             $HAS_SKIP
77             $HAS_TODO
78             ) ] );
79              
80             our %mnemonic = (
81             severity => {
82             1 => 'ok',
83             2 => 'ok_todo',
84             3 => 'ok_skip',
85             4 => 'notok_todo',
86             5 => 'notok',
87             6 => 'notok_skip', # forbidden TAP semantic, should never happen
88             },
89             );
90              
91             # TAP severity level definition:
92             #
93             # |--------+---------------+----------+--------------+----------+------------+----------|
94             # | *type* | is_ok | has_todo | is_actual_ok | has_skip | *mnemonic* | *tapcon* |
95             # |--------+---------------+----------+--------------+----------+------------+----------|
96             # | plan | undef | undef | undef | 1 | ok_skip | 3 |
97             # |--------+---------------+----------+--------------+----------+------------+----------|
98             # | test | 1 | 0 | 0 | 0 | ok | 1 |
99             # | test | 1 | 1 | 1 | 0 | ok_todo | 2 |
100             # | test | 1 | 0 | 0 | 1 | ok_skip | 3 |
101             # | test | 1 | 1 | 0 | 0 | notok_todo | 4 |
102             # | test | 0 | 0 | 0 | 0 | notok | 5 |
103             # | test | 0 | 0 | 0 | 1 | notok_skip | 6 |
104             # |--------+---------------+----------+--------------+----------+------------+----------|
105             # | | | | | | missing | 0 |
106             # |--------+---------------+----------+--------------+----------+------------+----------|
107             # | *type* | *value* | | | | | |
108             # |--------+---------------+----------+--------------+----------+------------+----------|
109             # | pragma | +tapdom_error | | | | notok | 5 |
110             # |--------+---------------+----------+--------------+----------+------------+----------|
111              
112             our $severity = {};
113             #
114             # {type} {is_ok} {has_todo} {is_actual_ok} {has_skip} = $severity;
115             #
116             $severity->{plan} {''} {0} {0} {1} = 3; # ok_skip
117             $severity->{test} {1} {0} {0} {0} = 1; # ok
118             $severity->{test} {1} {1} {1} {0} = 2; # ok_todo
119             $severity->{test} {1} {0} {0} {1} = 3; # ok_skip
120             $severity->{test} {1} {1} {0} {0} = 4; # notok_todo
121             $severity->{test} {0} {0} {0} {0} = 5; # notok
122             $severity->{test} {0} {0} {0} {1} = 6; # notok_skip
123              
124             our $obvious_tap_line = qr/(1\.\.|ok\s|not\s+ok\s|#|\s|tap\s+version|pragma|Bail out!)/i;
125              
126             our $noempty_tap = "pragma +tapdom_error\n# document was empty";
127              
128             use Class::XSAccessor
129 19         259 chained => 1,
130             accessors => [qw( plan
131             lines
132             pragmas
133             tests_planned
134             tests_run
135             version
136             is_good_plan
137             skip_all
138             start_time
139             end_time
140             has_problems
141             exit
142             parse_errors
143             parse_errors_msgs
144             summary
145             tapdom_config
146             document_data
147 19     19   6128 )];
  19         59  
148              
149             sub _capture_group {
150 146     146   605 my ($s, $n) = @_; substr($s, $-[$n], $+[$n] - $-[$n]);
  146         879  
151             }
152              
153             # Optimize the TAP text before parsing it.
154             sub preprocess_ignorelines {
155 4     4 1 14 my %args = @_;
156              
157 4 50       15 if ($args{tap}) {
158              
159 4 50       16 if (my $ignorelines = $args{ignorelines}) {
160 4         9 my $dontignorelines = $args{dontignorelines};
161 4         11 my $tap = $args{tap};
162 4 100       13 if ($dontignorelines) {
163             # HIGHLY EXPERIMENTAL!
164             #
165             # We convert the 'dontignorelines' regex into a negative-lookahead
166             # condition and prepend it before the 'ignorelines'.
167             #
168             # Why? Because we want to utilize the cleanup in one single
169             # operation as fast as the regex engine can do it.
170 2 50       8 my $re_dontignorelines = $dontignorelines ? "(?!$dontignorelines)" : '';
171 2         43 my $re_filter = qr/^$re_dontignorelines$ignorelines.*[\r\n]*/m; # the /m scope needs to be here!
172 2         78 $tap =~ s/$re_filter//g;
173             } else {
174 2         68 $tap =~ s/^$ignorelines.*[\r\n]*//mg;
175             }
176 4         11 $args{tap} = $tap;
177 4         11 delete $args{ignorelines}; # don't try it again during parsing later
178             }
179             }
180              
181 4         21 return %args
182             }
183              
184             # Filter away obvious non-TAP lines before parsing it.
185             sub preprocess_tap {
186 4     4 1 15 my %args = @_;
187              
188 4 50       15 if ($args{tap}) {
189 4         7 my $tap = $args{tap};
190 4         186 $tap =~ s/^(?!$obvious_tap_line).*[\r\n]*//mg;
191 4         18 $args{tap} = $tap;
192             }
193              
194 4         20 return %args
195             }
196              
197             # Mark empty TAP with replacement lines
198             sub noempty_tap {
199 21     21 1 50 my %args = @_;
200              
201 21 100 100     172 if (defined($args{tap}) and $args{tap} eq '') {
    100 100        
202 3         7 $args{tap} = $noempty_tap;
203             }
204             elsif (defined($args{source}) and -z $args{source}) {
205 1         4 $args{tap} = $noempty_tap;
206 1         3 delete $args{source};
207             }
208              
209 21         86 return %args
210             }
211              
212             sub new {
213             # hash or hash ref
214 69     69 1 189772 my $class = shift;
215 69 50       379 my %args = @_ == 1 ? %{$_[0]} : @_;
  0         0  
216              
217 69         425 my @lines;
218             my $plan;
219 69         0 my $version;
220 69         0 my @pragmas;
221 69         0 my $bailout;
222 69         0 my %document_data;
223 69         0 my %dangling_kv_data;
224              
225 69 100       217 %args = preprocess_ignorelines(%args) if $args{preprocess_ignorelines};
226 69 100       202 %args = preprocess_tap(%args) if $args{preprocess_tap};
227 69 100       204 %args = noempty_tap(%args) if $args{noempty_tap};
228              
229 69         123 my %IGNORE = map { $_ => 1 } @{$args{ignore}};
  3         9  
  69         216  
230 69         141 my $IGNORELINES = $args{ignorelines};
231 69         110 my $DONTIGNORELINES = $args{dontignorelines};
232 69         104 my $USEBITSETS = $args{usebitsets};
233 69         113 my $DISABLE_GLOBAL_KV_DATA = $args{disable_global_kv_data};
234 69         103 my $PUT_DANGLING_KV_DATA_UNDER_LAZY_PLAN = $args{put_dangling_kv_data_under_lazy_plan};
235 69   50     294 my $DOC_DATA_PREFIX = $args{document_data_prefix} || 'Test-';
236 69         112 my $DOC_DATA_IGNORE = $args{document_data_ignore};
237 69         116 my $LOWERCASE_FIELDNAMES = $args{lowercase_fieldnames};
238 69         100 my $LOWERCASE_FIELDVALUES = $args{lowercase_fieldvalues};
239 69         139 my $TRIM_FIELDVALUES = $args{trim_fieldvalues};
240 69         382 my $NOEMPTY_TAP = $args{noempty_tap};
241 69         146 delete $args{ignore};
242 69         97 delete $args{ignorelines};
243 69         109 delete $args{dontignorelines};
244 69         110 delete $args{usebitsets};
245 69         214 delete $args{disable_global_kv_data};
246 69         101 delete $args{put_dangling_kv_data_under_lazy_plan};
247 69         93 delete $args{document_data_prefix};
248 69         100 delete $args{document_data_ignore};
249 69         93 delete $args{preprocess_ignorelines};
250 69         102 delete $args{preprocess_tap};
251 69         108 delete $args{noempty_tap};
252 69         95 delete $args{lowercase_fieldnames};
253 69         106 delete $args{lowercase_fieldvalues};
254 69         91 delete $args{trim_fieldvalues};
255              
256 69         869 my $document_data_regex = qr/^#\s*$DOC_DATA_PREFIX([^:]+)\s*:\s*(.*)$/;
257 69 100       241 my $document_data_ignore = defined($DOC_DATA_IGNORE) ? qr/$DOC_DATA_IGNORE/ : undef;
258              
259 69         549 my $parser = TAP::Parser->new( { %args } );
260              
261 69         38462 my $aggregate = TAP::Parser::Aggregator->new;
262 69         4332 $aggregate->start;
263              
264 69         1825 my $count_tap_lines = 0;
265 69         129 my $found_pragma_tapdom_error = 0;
266 69         241 while ( my $result = $parser->next ) {
267 19     19   30997 no strict 'refs';
  19         50  
  19         11613  
268              
269 667 100 100     149852 next if $IGNORELINES && $result->raw =~ m/$IGNORELINES/ && !($DONTIGNORELINES && $result->raw =~ m/$DONTIGNORELINES/);
      100        
      100        
270              
271 641         2008 my $entry = TAP::DOM::Entry->new;
272 641 100       1215 $entry->{is_has} = 0 if $USEBITSETS;
273              
274             # test info
275 641         1104 foreach (qw(type raw as_string )) {
276 1923 100       8293 $entry->{$_} = $result->$_ unless $IGNORE{$_};
277             }
278              
279 641 100       5716 if ($result->is_test) {
280 174         988 foreach (qw(directive explanation number description )) {
281 696 100       2774 $entry->{$_} = $result->$_ unless $IGNORE{$_};
282             }
283 174         709 foreach (qw(is_ok is_unplanned )) {
284 348 100       2875 if ($USEBITSETS) {
285 8 100       25 $entry->{is_has} |= $result->$_ ? ${uc $_} : 0 unless $IGNORE{$_};
  4 50       58  
286             } else {
287 340 100       979 $entry->{$_} = $result->$_ ? 1 : 0 unless $IGNORE{$_};
    50          
288             }
289             }
290             }
291              
292             # plan
293 641 100       4171 if ($result->is_plan) {
294 60         379 $plan = $result->as_string;
295 60         225 foreach (qw(directive explanation)) {
296 120 100       519 $entry->{$_} = $result->$_ unless $IGNORE{$_};
297             }
298              
299             # save Dangling kv_data to plan entry. The situation
300             # that we already collected kv_data but haven't got
301             # a plan yet should only happen in documents with
302             # lazy plans (plan at the end).
303 60 50 33     332 if ($PUT_DANGLING_KV_DATA_UNDER_LAZY_PLAN and keys %dangling_kv_data) {
304 0         0 $entry->{kv_data}{$_} = $dangling_kv_data{$_} foreach keys %dangling_kv_data;
305             }
306             }
307              
308             # meta info
309 641         3166 foreach ((qw(has_skip has_todo))) {
310 1282 100       3984 if ($USEBITSETS) {
311 14 100       39 $entry->{is_has} |= $result->$_ ? ${uc $_} : 0 unless $IGNORE{$_};
  2 50       10  
312             } else {
313 1268 100       3446 $entry->{$_} = $result->$_ ? 1 : 0 unless $IGNORE{$_};
    50          
314             }
315             }
316             # Idea:
317             # use constants
318             # map to constants
319             # then loop
320 641         2976 foreach (qw( is_pragma is_comment is_bailout is_plan
321             is_version is_yaml is_unknown is_test))
322             {
323 5128 100       24688 if ($USEBITSETS) {
324 56 100       133 $entry->{is_has} |= $result->$_ ? ${uc $_} : 0 unless $IGNORE{$_};
  7 50       42  
325             } else {
326 5072 100       12920 $entry->{$_} = $result->$_ ? 1 : 0 unless $IGNORE{$_};
    50          
327             }
328             }
329 641 50       3670 if (! $IGNORE{is_actual_ok}) {
330             # XXX:
331             # I think it's confusing when the value of
332             # "is_actual_ok" only has a meaning when
333             # "has_todo" is true.
334             # This makes it difficult to evaluate later.
335             # But it's aligned with TAP::Parser
336             # which also sets this only on "has_todo".
337             #
338             # Maybe the problem is a general philosophical one
339             # in TAP::DOM to always have each hashkey existing.
340             # Hmmm...
341 641 100 100     1244 my $is_actual_ok = ($result->has_todo && $result->is_actual_ok) ? 1 : 0;
342 641 100       3096 if ($USEBITSETS) {
343 7 100       14 $entry->{is_has} |= $is_actual_ok ? $IS_ACTUAL_OK : 0;
344             } else {
345 634         1215 $entry->{is_actual_ok} = $is_actual_ok;
346             }
347             }
348 641 100 66     1226 $entry->{data} = $result->data if $result->is_yaml && !$IGNORE{data};
349              
350 641 100 100     4085 if ($result->is_comment and $result->as_string =~ $document_data_regex)
351             {{ # extra block for 'last'
352             # we can't use $1, $2 because the regex could contain configured other groups
353 73         1104 my ($key, $value) = (_capture_group($result->as_string, -2), _capture_group($result->as_string, -1));
  73         160  
354 73         288 $key =~ s/^\s+//; # strip leading whitespace
355 73         161 $key =~ s/\s+$//; # strip trailing whitespace
356              
357             # optional lowercase
358 73 100       193 $key = lc $key if $LOWERCASE_FIELDNAMES;
359 73 100       143 $value = lc $value if $LOWERCASE_FIELDVALUES;
360              
361             # optional value trimming
362 73 100       134 $value =~ s/\s+$// if $TRIM_FIELDVALUES; # there can be no leading whitespace
363              
364             # skip this field according to regex
365 73 100 66     291 last if $DOC_DATA_IGNORE and $document_data_ignore and $key =~ $document_data_ignore;
      100        
366              
367             # Store "# Test-key: value" entries also as
368             # 'kv_data' under their parent line.
369             # That line should be a test or a plan line, so that its
370             # place (or "data path") is structurally always the same.
371 66 100 100     205 if ($lines[-1]->is_test or $lines[-1]->is_plan or $lines[-1]->is_pragma) {
      100        
372 56         171 $lines[-1]->{kv_data}{$key} = $value;
373             } else {
374 10 50       25 if (!$plan) {
375             # We haven't got a plan yet, so that
376             # kv_data entry would get lost. As we
377             # might still get a lazy plan at end
378             # of document, so we save it up for
379             # that potential plan entry.
380 10         26 $dangling_kv_data{$key} = $value;
381             }
382             }
383 66 50 66     143 $document_data{$key} = $value unless $lines[-1]->is_test && $DISABLE_GLOBAL_KV_DATA;
384             }}
385              
386             # calculate severity
387 641 100 100     5759 if ($entry->{is_test} or $entry->{is_plan}) {
388 19     19   154 no warnings 'uninitialized';
  19         46  
  19         2221  
389 229         310 $count_tap_lines++;
390             $entry->{severity} = $severity
391             ->{$entry->{type}}
392             ->{$entry->{is_ok}}
393             ->{$entry->{has_todo}}
394             ->{$entry->{is_actual_ok}}
395 229         1424 ->{$entry->{has_skip}};
396             }
397              
398 641 100 100     2125 if ($entry->{is_pragma} or $entry->{is_unknown}) {
399 19     19   151 no warnings 'uninitialized';
  19         48  
  19         22529  
400 31 100       135 if ($entry->{raw} =~ /^pragma\s+\+tapdom_error\s*$/) {
401 6         12 $found_pragma_tapdom_error=1;
402 6         14 $entry->{severity} = 5;
403 6         10 $entry->{is_unknown} = 0;
404 6         11 $entry->{is_pragma} = 1;
405 6         13 $entry->{type} = 'pragma';
406             } else {
407 25         47 $entry->{severity} = 0;
408             }
409             }
410 641 100       1429 $entry->{severity} = 0 if not defined $entry->{severity};
411              
412             # yaml and comments are taken as children of the line before
413 641 100 100     1263 if ($result->is_yaml or $result->is_comment and @lines)
      100        
414             {
415 341         3549 push @{ $lines[-1]->{_children} }, $entry;
  341         1763  
416             }
417             else
418             {
419 300         4528 push @lines, $entry;
420             }
421             }
422 69         16048 @pragmas = $parser->pragmas;
423              
424 69 100 100     741 if (!$count_tap_lines and !$found_pragma_tapdom_error and $NOEMPTY_TAP) {
      100        
425             # pragma +tapdom_error
426 2         30 my $error_entry = TAP::DOM::Entry->new(
427             'is_version' => 0,
428             'is_plan' => 0,
429             'is_test' => 0,
430             'is_comment' => 0,
431             'is_yaml' => 0,
432             'is_unknown' => 0,
433             'is_bailout' => 0,
434             'is_actual_ok' => 0,
435             'is_pragma' => 1,
436             'type' => 'pragma',
437             'raw' => 'pragma +tapdom_error',
438             'as_string' => 'pragma +tapdom_error',
439             'severity' => 5,
440             'has_todo' => 0,
441             'has_skip' => 0,
442             );
443 2 50       9 $error_entry->{is_has} = $IS_PRAGMA if $USEBITSETS;
444 2 50       11 foreach (qw(raw type as_string explanation)) { delete $error_entry->{$_} if $IGNORE{$_} }
  8         18  
445             # pragma +tapdom_error
446 2         21 my $error_comment = TAP::DOM::Entry->new(
447             'is_version' => 0,
448             'is_plan' => 0,
449             'is_test' => 0,
450             'is_comment' => 1,
451             'is_yaml' => 0,
452             'is_unknown' => 0,
453             'is_bailout' => 0,
454             'is_actual_ok' => 0,
455             'is_pragma' => 0,
456             'type' => 'comment',
457             'raw' => '# no tap lines',
458             'as_string' => '# no tap lines',
459             'severity' => 0,
460             'has_todo' => 0,
461             'has_skip' => 0,
462             );
463 2 50       7 $error_comment->{is_has} = $IS_COMMENT if $USEBITSETS;
464 2 50       7 foreach (qw(raw type as_string explanation)) { delete $error_comment->{$_} if $IGNORE{$_} }
  8         18  
465 2   50     16 $error_entry->{_children} //= [];
466 2         4 push @{$error_entry->{_children}}, $error_comment;
  2         6  
467 2         4 push @lines, $error_entry;
468 2         5 push @pragmas, 'tapdom_error';
469             }
470              
471 69         293 $aggregate->add( main => $parser );
472 69         8846 $aggregate->stop;
473              
474 69 100       1657 my $summary = TAP::DOM::Summary->new
    100          
    100          
475             (
476             failed => scalar $aggregate->failed,
477             parse_errors => scalar $aggregate->parse_errors,
478             planned => scalar $aggregate->planned,
479             passed => scalar $aggregate->passed,
480             skipped => scalar $aggregate->skipped,
481             todo => scalar $aggregate->todo,
482             todo_passed => scalar $aggregate->todo_passed,
483             wait => scalar $aggregate->wait,
484             exit => scalar $aggregate->exit,
485             elapsed => $aggregate->elapsed,
486             elapsed_timestr => $aggregate->elapsed_timestr,
487             all_passed => $aggregate->all_passed ? 1 : 0,
488             status => $aggregate->get_status,
489             total => $aggregate->total,
490             has_problems => $aggregate->has_problems ? 1 : 0,
491             has_errors => $aggregate->has_errors ? 1 : 0,
492             );
493              
494 69         17443 my $tapdom_config = TAP::DOM::Config->new
495             (
496             ignore => \%IGNORE,
497             ignorelines => $IGNORELINES,
498             dontignorelines => $DONTIGNORELINES,
499             usebitsets => $USEBITSETS,
500             disable_global_kv_data => $DISABLE_GLOBAL_KV_DATA,
501             put_dangling_kv_data_under_lazy_plan => $PUT_DANGLING_KV_DATA_UNDER_LAZY_PLAN,
502             document_data_prefix => $DOC_DATA_PREFIX,
503             document_data_ignore => $DOC_DATA_IGNORE,
504             lowercase_fieldnames => $LOWERCASE_FIELDNAMES,
505             lowercase_fieldvalues => $LOWERCASE_FIELDVALUES,
506             trim_fieldvalues => $TRIM_FIELDVALUES,
507             noempty_tap => $NOEMPTY_TAP,
508             );
509              
510 69         728 my $document_data = TAP::DOM::DocumentData->new(%document_data);
511              
512 69         270 my $tapdata = {
513             plan => $plan,
514             lines => \@lines,
515             pragmas => \@pragmas,
516             tests_planned => $parser->tests_planned,
517             tests_run => $parser->tests_run,
518             version => $parser->version,
519             is_good_plan => $parser->is_good_plan,
520             skip_all => $parser->skip_all,
521             start_time => $parser->start_time,
522             end_time => $parser->end_time,
523             has_problems => $parser->has_problems,
524             exit => $parser->exit,
525             parse_errors => scalar $parser->parse_errors,
526             parse_errors_msgs => [ $parser->parse_errors ],
527             summary => $summary,
528             tapdom_config => $tapdom_config,
529             document_data => $document_data,
530             };
531 69         6174 return bless $tapdata, $class;
532             }
533              
534             sub _entry_to_tapline
535             {
536 40     40   57 my ($self, $entry) = @_;
537              
538 40         44 my %IGNORE = %{$self->{tapdom_config}{ignore}};
  40         67  
539              
540 40         51 my $tapline = "";
541              
542             # ok/notok test lines
543 40 100 100     148 if ($entry->{is_test})
    100 66        
      33        
544             {
545             $tapline = join(" ",
546             # the original "NOT" is more difficult to reconstruct than it should...
547             ($entry->{has_todo}
548             ? $entry->{is_actual_ok} ? () : "not"
549             : $entry->{is_ok} ? () : "not"),
550             "ok",
551             ($entry->{number} || ()),
552             ($entry->{description} || ()),
553             ($entry->{has_skip} ? "# SKIP ".($entry->{explanation} || "")
554 16 100 33     120 : $entry->{has_todo }? "# TODO ".($entry->{explanation} || "")
    50 66        
    100 50        
    100 50        
    100          
555             : ()),
556             );
557             }
558             # pragmas and meta lines, but no version nor plan
559             elsif ($entry->{is_pragma} ||
560             $entry->{is_comment} ||
561             $entry->{is_bailout} ||
562             $entry->{is_yaml})
563             {
564 20 50       33 $tapline = $IGNORE{raw} ? $entry->{as_string} : $entry->{raw}; # if "raw" was 'ignored' try "as_string"
565             }
566 40         67 return $tapline;
567             }
568              
569             sub _lines_to_tap
570             {
571 8     8   14 my ($self, $lines) = @_;
572              
573 8         11 my @taplines;
574 8         15 foreach my $entry (@$lines)
575             {
576 40         66 my $tapline = $self->_entry_to_tapline($entry);
577 40 100       80 push @taplines, $tapline if $tapline;
578 40 100       82 push @taplines, $self->_lines_to_tap($entry->{_children}) if $entry->{_children};
579             }
580 8         27 return @taplines;
581             }
582              
583             sub to_tap
584             {
585 2     2 1 1005 my ($self) = @_;
586              
587 2         6 my @taplines = $self->_lines_to_tap($self->{lines});
588 2         6 unshift @taplines, $self->{plan};
589 2         7 unshift @taplines, "TAP version ".$self->{version};
590              
591             return wantarray
592             ? @taplines
593 2 100       16 : join("\n", @taplines)."\n";
594             }
595              
596             1; # End of TAP::DOM
597              
598             __END__