xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Harness/t/parse.t (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1#!/usr/bin/perl -w
2
3use strict;
4
5BEGIN {
6    use lib 't/lib';
7}
8
9use Test::More tests => 294;
10use IO::c55Capture;
11
12use File::Spec;
13
14use TAP::Parser;
15use TAP::Parser::Iterator::Array;
16
17sub _get_results {
18    my $parser = shift;
19    my @results;
20    while ( defined( my $result = $parser->next ) ) {
21        push @results => $result;
22    }
23    return @results;
24}
25
26my ( $PARSER, $PLAN, $PRAGMA, $TEST, $COMMENT, $BAILOUT, $UNKNOWN, $YAML, $VERSION ) = qw(
27  TAP::Parser
28  TAP::Parser::Result::Plan
29  TAP::Parser::Result::Pragma
30  TAP::Parser::Result::Test
31  TAP::Parser::Result::Comment
32  TAP::Parser::Result::Bailout
33  TAP::Parser::Result::Unknown
34  TAP::Parser::Result::YAML
35  TAP::Parser::Result::Version
36);
37
38my $tap = <<'END_TAP';
39TAP version 13
401..7
41ok 1 - input file opened
42... this is junk
43not ok first line of the input valid # todo some data
44# this is a comment
45ok 3 - read the rest of the file
46not ok 4 - this is a real failure
47  --- YAML!
48  ...
49ok 5 # skip we have no description
50ok 6 - you shall not pass! # TODO should have failed
51not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
52END_TAP
53
54can_ok $PARSER, 'new';
55my $parser = $PARSER->new( { tap => $tap } );
56isa_ok $parser, $PARSER, '... and the object it returns';
57
58ok $ENV{TAP_VERSION}, 'TAP_VERSION env variable should be set';
59
60# results() is sane?
61
62my @results = _get_results($parser);
63is scalar @results, 12, '... and there should be one for each line';
64
65my $version = shift @results;
66isa_ok $version, $VERSION;
67is $version->version, '13', '... and the version should be 13';
68
69# check the test plan
70
71my $result = shift @results;
72isa_ok $result, $PLAN;
73can_ok $result, 'type';
74is $result->type, 'plan', '... and it should report the correct type';
75ok $result->is_plan, '... and it should identify itself as a plan';
76is $result->plan, '1..7', '... and identify the plan';
77ok !$result->directive,   '... and this plan should not have a directive';
78ok !$result->explanation, '... or a directive explanation';
79is $result->as_string, '1..7',
80  '... and have the correct string representation';
81is $result->raw, '1..7', '... and raw() should return the original line';
82
83# a normal, passing test
84
85my $test = shift @results;
86isa_ok $test, $TEST;
87is $test->type, 'test', '... and it should report the correct type';
88ok $test->is_test, '... and it should identify itself as a test';
89is $test->ok,      'ok', '... and it should have the correct ok()';
90ok $test->is_ok,   '... and the correct boolean version of is_ok()';
91ok $test->is_actual_ok,
92  '... and the correct boolean version of is_actual_ok()';
93is $test->number, 1, '... and have the correct test number';
94is $test->description, '- input file opened',
95  '... and the correct description';
96ok !$test->directive,   '... and not have a directive';
97ok !$test->explanation, '... or a directive explanation';
98ok !$test->has_skip,    '... and it is not a SKIPped test';
99ok !$test->has_todo,    '... nor a TODO test';
100is $test->as_string, 'ok 1 - input file opened',
101  '... and its string representation should be correct';
102is $test->raw, 'ok 1 - input file opened',
103  '... and raw() should return the original line';
104
105# junk lines should be preserved
106
107my $unknown = shift @results;
108isa_ok $unknown, $UNKNOWN;
109is $unknown->type, 'unknown', '... and it should report the correct type';
110ok $unknown->is_unknown, '... and it should identify itself as unknown';
111is $unknown->as_string,  '... this is junk',
112  '... and its string representation should be returned verbatim';
113is $unknown->raw, '... this is junk',
114  '... and raw() should return the original line';
115
116# a failing test, which also happens to have a directive
117
118my $failed = shift @results;
119isa_ok $failed, $TEST;
120is $failed->type, 'test', '... and it should report the correct type';
121ok $failed->is_test, '... and it should identify itself as a test';
122is $failed->ok,      'not ok', '... and it should have the correct ok()';
123ok $failed->is_ok,   '... and TODO tests should always pass';
124ok !$failed->is_actual_ok,
125  '... and the correct boolean version of is_actual_ok ()';
126is $failed->number, 2, '... and have the correct failed number';
127is $failed->description, 'first line of the input valid',
128  '... and the correct description';
129is $failed->directive, 'TODO', '... and should have the correct directive';
130is $failed->explanation, 'some data',
131  '... and the correct directive explanation';
132ok !$failed->has_skip, '... and it is not a SKIPped failed';
133ok $failed->has_todo, '... but it is a TODO succeeded';
134is $failed->as_string,
135  'not ok 2 first line of the input valid # TODO some data',
136  '... and its string representation should be correct';
137is $failed->raw, 'not ok first line of the input valid # todo some data',
138  '... and raw() should return the original line';
139
140# comments
141
142my $comment = shift @results;
143isa_ok $comment, $COMMENT;
144is $comment->type, 'comment', '... and it should report the correct type';
145ok $comment->is_comment, '... and it should identify itself as a comment';
146is $comment->comment,    'this is a comment',
147  '... and you should be able to fetch the comment';
148is $comment->as_string, '# this is a comment',
149  '... and have the correct string representation';
150is $comment->raw, '# this is a comment',
151  '... and raw() should return the original line';
152
153# another normal, passing test
154
155$test = shift @results;
156isa_ok $test, $TEST;
157is $test->type, 'test', '... and it should report the correct type';
158ok $test->is_test, '... and it should identify itself as a test';
159is $test->ok,      'ok', '... and it should have the correct ok()';
160ok $test->is_ok,   '... and the correct boolean version of is_ok()';
161ok $test->is_actual_ok,
162  '... and the correct boolean version of is_actual_ok()';
163is $test->number, 3, '... and have the correct test number';
164is $test->description, '- read the rest of the file',
165  '... and the correct description';
166ok !$test->directive,   '... and not have a directive';
167ok !$test->explanation, '... or a directive explanation';
168ok !$test->has_skip,    '... and it is not a SKIPped test';
169ok !$test->has_todo,    '... nor a TODO test';
170is $test->as_string, 'ok 3 - read the rest of the file',
171  '... and its string representation should be correct';
172is $test->raw, 'ok 3 - read the rest of the file',
173  '... and raw() should return the original line';
174
175# a failing test
176
177$failed = shift @results;
178isa_ok $failed, $TEST;
179is $failed->type, 'test', '... and it should report the correct type';
180ok $failed->is_test, '... and it should identify itself as a test';
181is $failed->ok, 'not ok', '... and it should have the correct ok()';
182ok !$failed->is_ok, '... and the tests should not have passed';
183ok !$failed->is_actual_ok,
184  '... and the correct boolean version of is_actual_ok ()';
185is $failed->number, 4, '... and have the correct failed number';
186is $failed->description, '- this is a real failure',
187  '... and the correct description';
188ok !$failed->directive,   '... and should have no directive';
189ok !$failed->explanation, '... and no directive explanation';
190ok !$failed->has_skip,    '... and it is not a SKIPped failed';
191ok !$failed->has_todo,    '... and not a TODO test';
192is $failed->as_string, 'not ok 4 - this is a real failure',
193  '... and its string representation should be correct';
194is $failed->raw, 'not ok 4 - this is a real failure',
195  '... and raw() should return the original line';
196
197# Some YAML
198my $yaml = shift @results;
199isa_ok $yaml, $YAML;
200is $yaml->type, 'yaml', '... and it should report the correct type';
201ok $yaml->is_yaml, '... and it should identify itself as yaml';
202is_deeply $yaml->data, 'YAML!', '... and data should be correct';
203
204# ok 5 # skip we have no description
205# skipped test
206
207$test = shift @results;
208isa_ok $test, $TEST;
209is $test->type, 'test', '... and it should report the correct type';
210ok $test->is_test, '... and it should identify itself as a test';
211is $test->ok,      'ok', '... and it should have the correct ok()';
212ok $test->is_ok,   '... and the correct boolean version of is_ok()';
213ok $test->is_actual_ok,
214  '... and the correct boolean version of is_actual_ok()';
215is $test->number, 5, '... and have the correct test number';
216ok !$test->description, '... and skipped tests have no description';
217is $test->directive, 'SKIP', '... and the correct directive';
218is $test->explanation, 'we have no description',
219  '... but we should have an explanation';
220ok $test->has_skip, '... and it is a SKIPped test';
221ok !$test->has_todo, '... but not a TODO test';
222is $test->as_string, 'ok 5 # SKIP we have no description',
223  '... and its string representation should be correct';
224is $test->raw, 'ok 5 # skip we have no description',
225  '... and raw() should return the original line';
226
227# a failing test, which also happens to have a directive
228# ok 6 - you shall not pass! # TODO should have failed
229
230my $bonus = shift @results;
231isa_ok $bonus, $TEST;
232can_ok $bonus, 'todo_passed';
233is $bonus->type, 'test', 'TODO tests should parse correctly';
234ok $bonus->is_test, '... and it should identify itself as a test';
235is $bonus->ok,      'ok', '... and it should have the correct ok()';
236ok $bonus->is_ok,   '... and TODO tests should not always pass';
237ok $bonus->is_actual_ok,
238  '... and the correct boolean version of is_actual_ok ()';
239is $bonus->number, 6, '... and have the correct failed number';
240is $bonus->description, '- you shall not pass!',
241  '... and the correct description';
242is $bonus->directive, 'TODO', '... and should have the correct directive';
243is $bonus->explanation, 'should have failed',
244  '... and the correct directive explanation';
245ok !$bonus->has_skip, '... and it is not a SKIPped failed';
246ok $bonus->has_todo,  '... but it is a TODO succeeded';
247is $bonus->as_string, 'ok 6 - you shall not pass! # TODO should have failed',
248  '... and its string representation should be correct';
249is $bonus->raw, 'ok 6 - you shall not pass! # TODO should have failed',
250  '... and raw() should return the original line';
251ok $bonus->todo_passed,
252  '... todo_bonus() should pass for TODO tests which unexpectedly succeed';
253
254# not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
255
256my $passed = shift @results;
257isa_ok $passed, $TEST;
258can_ok $passed, 'todo_passed';
259is $passed->type, 'test', 'TODO tests should parse correctly';
260ok $passed->is_test, '... and it should identify itself as a test';
261is $passed->ok,      'not ok', '... and it should have the correct ok()';
262ok $passed->is_ok,   '... and TODO tests should always pass';
263ok !$passed->is_actual_ok,
264  '... and the correct boolean version of is_actual_ok ()';
265is $passed->number, 7, '... and have the correct passed number';
266is $passed->description, '- Gandalf wins.  Game over.',
267  '... and the correct description';
268is $passed->directive, 'TODO', '... and should have the correct directive';
269is $passed->explanation, "'bout time!",
270  '... and the correct directive explanation';
271ok !$passed->has_skip, '... and it is not a SKIPped passed';
272ok $passed->has_todo, '... but it is a TODO succeeded';
273is $passed->as_string,
274  "not ok 7 - Gandalf wins.  Game over. # TODO 'bout time!",
275  '... and its string representation should be correct';
276is $passed->raw, "not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!",
277  '... and raw() should return the original line';
278ok !$passed->todo_passed,
279  '... todo_passed() should not pass for TODO tests which failed';
280
281# test parse results
282
283can_ok $parser, 'passed';
284is $parser->passed, 6,
285  '... and we should have the correct number of passed tests';
286is_deeply [ $parser->passed ], [ 1, 2, 3, 5, 6, 7 ],
287  '... and get a list of the passed tests';
288
289can_ok $parser, 'failed';
290is $parser->failed, 1, '... and the correct number of failed tests';
291is_deeply [ $parser->failed ], [4], '... and get a list of the failed tests';
292
293can_ok $parser, 'actual_passed';
294is $parser->actual_passed, 4,
295  '... and we should have the correct number of actually passed tests';
296is_deeply [ $parser->actual_passed ], [ 1, 3, 5, 6 ],
297  '... and get a list of the actually passed tests';
298
299can_ok $parser, 'actual_failed';
300is $parser->actual_failed, 3,
301  '... and the correct number of actually failed tests';
302is_deeply [ $parser->actual_failed ], [ 2, 4, 7 ],
303  '... or get a list of the actually failed tests';
304
305can_ok $parser, 'todo';
306is $parser->todo, 3,
307  '... and we should have the correct number of TODO tests';
308is_deeply [ $parser->todo ], [ 2, 6, 7 ],
309  '... and get a list of the TODO tests';
310
311can_ok $parser, 'skipped';
312is $parser->skipped, 1,
313  '... and we should have the correct number of skipped tests';
314is_deeply [ $parser->skipped ], [5],
315  '... and get a list of the skipped tests';
316
317# check the plan
318
319can_ok $parser, 'plan';
320is $parser->plan,          '1..7', '... and we should have the correct plan';
321is $parser->tests_planned, 7,      '... and the correct number of tests';
322
323# "Unexpectedly succeeded"
324can_ok $parser, 'todo_passed';
325is scalar $parser->todo_passed, 1,
326  '... and it should report the number of tests which unexpectedly succeeded';
327is_deeply [ $parser->todo_passed ], [6],
328  '... or *which* tests unexpectedly succeeded';
329
330#
331# Bug report from Torsten Schoenfeld
332# Makes sure parser can handle blank lines
333#
334
335$tap = <<'END_TAP';
3361..2
337ok 1 - input file opened
338
339
340ok 2 - read the rest of the file
341END_TAP
342
343my $aref = [ split /\n/ => $tap ];
344
345can_ok $PARSER, 'new';
346$parser
347  = $PARSER->new( { iterator => TAP::Parser::Iterator::Array->new($aref) } );
348isa_ok $parser, $PARSER, '... and calling it should succeed';
349
350# results() is sane?
351
352ok @results = _get_results($parser), 'The parser should return results';
353is scalar @results, 5, '... and there should be one for each line';
354
355# check the test plan
356
357$result = shift @results;
358isa_ok $result, $PLAN;
359can_ok $result, 'type';
360is $result->type, 'plan', '... and it should report the correct type';
361ok $result->is_plan,   '... and it should identify itself as a plan';
362is $result->plan,      '1..2', '... and identify the plan';
363is $result->as_string, '1..2',
364  '... and have the correct string representation';
365is $result->raw, '1..2', '... and raw() should return the original line';
366
367# a normal, passing test
368
369$test = shift @results;
370isa_ok $test, $TEST;
371is $test->type, 'test', '... and it should report the correct type';
372ok $test->is_test, '... and it should identify itself as a test';
373is $test->ok,      'ok', '... and it should have the correct ok()';
374ok $test->is_ok,   '... and the correct boolean version of is_ok()';
375ok $test->is_actual_ok,
376  '... and the correct boolean version of is_actual_ok()';
377is $test->number, 1, '... and have the correct test number';
378is $test->description, '- input file opened',
379  '... and the correct description';
380ok !$test->directive,   '... and not have a directive';
381ok !$test->explanation, '... or a directive explanation';
382ok !$test->has_skip,    '... and it is not a SKIPped test';
383ok !$test->has_todo,    '... nor a TODO test';
384is $test->as_string, 'ok 1 - input file opened',
385  '... and its string representation should be correct';
386is $test->raw, 'ok 1 - input file opened',
387  '... and raw() should return the original line';
388
389# junk lines should be preserved
390
391$unknown = shift @results;
392isa_ok $unknown, $UNKNOWN;
393is $unknown->type, 'unknown', '... and it should report the correct type';
394ok $unknown->is_unknown, '... and it should identify itself as unknown';
395is $unknown->as_string,  '',
396  '... and its string representation should be returned verbatim';
397is $unknown->raw, '', '... and raw() should return the original line';
398
399# ... and the second empty line
400
401$unknown = shift @results;
402isa_ok $unknown, $UNKNOWN;
403is $unknown->type, 'unknown', '... and it should report the correct type';
404ok $unknown->is_unknown, '... and it should identify itself as unknown';
405is $unknown->as_string,  '',
406  '... and its string representation should be returned verbatim';
407is $unknown->raw, '', '... and raw() should return the original line';
408
409# a passing test
410
411$test = shift @results;
412isa_ok $test, $TEST;
413is $test->type, 'test', '... and it should report the correct type';
414ok $test->is_test, '... and it should identify itself as a test';
415is $test->ok,      'ok', '... and it should have the correct ok()';
416ok $test->is_ok,   '... and the correct boolean version of is_ok()';
417ok $test->is_actual_ok,
418  '... and the correct boolean version of is_actual_ok()';
419is $test->number, 2, '... and have the correct test number';
420is $test->description, '- read the rest of the file',
421  '... and the correct description';
422ok !$test->directive,   '... and not have a directive';
423ok !$test->explanation, '... or a directive explanation';
424ok !$test->has_skip,    '... and it is not a SKIPped test';
425ok !$test->has_todo,    '... nor a TODO test';
426is $test->as_string, 'ok 2 - read the rest of the file',
427  '... and its string representation should be correct';
428is $test->raw, 'ok 2 - read the rest of the file',
429  '... and raw() should return the original line';
430
431is scalar $parser->passed, 2,
432  'Empty junk lines should not affect the correct number of tests passed';
433
434# Check source => "tap content"
435can_ok $PARSER, 'new';
436$parser = $PARSER->new( { source => "1..1\nok 1\n" } );
437isa_ok $parser, $PARSER, '... and calling it should succeed';
438ok @results = _get_results($parser), 'The parser should return results';
439is( scalar @results, 2, "Got two lines of TAP" );
440
441# Check source => [array]
442can_ok $PARSER, 'new';
443$parser = $PARSER->new( { source => [ "1..1", "ok 1" ] } );
444isa_ok $parser, $PARSER, '... and calling it should succeed';
445ok @results = _get_results($parser), 'The parser should return results';
446is( scalar @results, 2, "Got two lines of TAP" );
447
448# Check source => $filehandle
449can_ok $PARSER, 'new';
450open my $fh, 't/data/catme.1';
451$parser = $PARSER->new( { source => $fh } );
452isa_ok $parser, $PARSER, '... and calling it should succeed';
453ok @results = _get_results($parser), 'The parser should return results';
454is( scalar @results, 2, "Got two lines of TAP" );
455
456{
457
458    # set a spool to write to
459    tie local *SPOOL, 'IO::c55Capture';
460
461    my $tap = <<'END_TAP';
462TAP version 13
4631..7
464ok 1 - input file opened
465... this is junk
466not ok first line of the input valid # todo some data
467# this is a comment
468ok 3 - read the rest of the file
469not ok 4 - this is a real failure
470  --- YAML!
471  ...
472ok 5 # skip we have no description
473ok 6 - you shall not pass! # TODO should have failed
474not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
475END_TAP
476
477    {
478        my $parser = $PARSER->new(
479            {   tap   => $tap,
480                spool => \*SPOOL,
481            }
482        );
483
484        _get_results($parser);
485
486        my @spooled = tied(*SPOOL)->dump();
487
488        is @spooled, 24, 'coverage testing for spool attribute of parser';
489        is join( '', @spooled ), $tap, "spooled tap matches";
490    }
491
492    {
493        my $parser = $PARSER->new(
494            {   tap   => $tap,
495                spool => \*SPOOL,
496            }
497        );
498
499        $parser->callback( 'ALL', sub { } );
500
501        _get_results($parser);
502
503        my @spooled = tied(*SPOOL)->dump();
504
505        is @spooled, 24, 'coverage testing for spool attribute of parser';
506        is join( '', @spooled ), $tap, "spooled tap matches";
507    }
508}
509
510{
511
512    # _initialize coverage
513
514    my $x = bless [], 'kjsfhkjsdhf';
515
516    my @die;
517
518    eval {
519        local $SIG{__DIE__} = sub { push @die, @_ };
520
521        $PARSER->new();
522    };
523
524    is @die, 1, 'coverage testing for _initialize';
525
526    like pop @die, qr/PANIC:\s+could not determine iterator for input\s*at/,
527      '...and it failed as expected';
528
529    @die = ();
530
531    eval {
532        local $SIG{__DIE__} = sub { push @die, @_ };
533
534        $PARSER->new(
535            {   iterator => 'iterator',
536                tap      => 'tap',
537                source   => 'source',     # only one of these is allowed
538            }
539        );
540    };
541
542    is @die, 1, 'coverage testing for _initialize';
543
544    like pop @die,
545      qr/You may only choose one of 'exec', 'tap', 'source' or 'iterator'/,
546      '...and it failed as expected';
547}
548
549{
550
551    # coverage of todo_failed
552
553    my $tap = <<'END_TAP';
554TAP version 13
5551..7
556ok 1 - input file opened
557... this is junk
558not ok first line of the input valid # todo some data
559# this is a comment
560ok 3 - read the rest of the file
561not ok 4 - this is a real failure
562  --- YAML!
563  ...
564ok 5 # skip we have no description
565ok 6 - you shall not pass! # TODO should have failed
566not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
567END_TAP
568
569    my $parser = $PARSER->new( { tap => $tap } );
570
571    _get_results($parser);
572
573    my @warn;
574
575    eval {
576        local $SIG{__WARN__} = sub { push @warn, @_ };
577
578        $parser->todo_failed;
579    };
580
581    is @warn, 1, 'coverage testing of todo_failed';
582
583    like pop @warn,
584      qr/"todo_failed" is deprecated.  Please use "todo_passed".  See the docs[.]/,
585      '..and failed as expected'
586}
587
588{
589
590    # coverage testing for T::P::_initialize
591
592    # coverage of the source argument paths
593
594    # ref argument to source
595
596    my $parser = TAP::Parser->new( { source => [ split /$/, $tap ] } );
597
598    isa_ok $parser, 'TAP::Parser';
599
600    isa_ok $parser->_iterator, 'TAP::Parser::Iterator::Array';
601
602    SKIP: {
603        skip 'Segfaults Perl 5.6.0' => 2 if $] <= 5.006000;
604
605        # uncategorisable argument to source
606        my @die;
607
608        eval {
609            local $SIG{__DIE__} = sub { push @die, @_ };
610
611            $parser = TAP::Parser->new( { source => 'nosuchfile' } );
612        };
613
614        is @die, 1, 'uncategorisable source';
615
616        like pop @die, qr/Cannot detect source of 'nosuchfile'/,
617          '... and we died as expected';
618    }
619}
620
621{
622
623    # coverage test of perl source with switches
624
625    my $parser = TAP::Parser->new(
626        {   source => File::Spec->catfile(
627                't',
628                'sample-tests',
629                'simple'
630            ),
631        }
632    );
633
634    isa_ok $parser, 'TAP::Parser';
635
636    isa_ok $parser->_iterator, 'TAP::Parser::Iterator::Process';
637
638    # Workaround for Mac OS X problem wrt closing the iterator without
639    # reading from it.
640    $parser->next;
641}
642
643{
644
645    # coverage testing for TAP::Parser::has_problems
646
647    # we're going to need to test lots of fragments of tap
648    # to cover all the different boolean tests
649
650    # currently covered are no problems and failed, so let's next test
651    # todo_passed
652
653    my $tap = <<'END_TAP';
654TAP version 13
6551..2
656ok 1 - input file opened
657ok 2 - Gandalf wins.  Game over.  # TODO 'bout time!
658END_TAP
659
660    my $parser = TAP::Parser->new( { tap => $tap } );
661
662    _get_results($parser);
663
664    ok !$parser->failed, 'parser didnt fail';
665    ok $parser->todo_passed, '... and todo_passed is true';
666
667    ok !$parser->has_problems, '... and has_problems is false';
668
669    # now parse_errors
670
671    $tap = <<'END_TAP';
672TAP version 13
6731..2
674SMACK
675END_TAP
676
677    $parser = TAP::Parser->new( { tap => $tap } );
678
679    _get_results($parser);
680
681    ok !$parser->failed,      'parser didnt fail';
682    ok !$parser->todo_passed, '... and todo_passed is false';
683    ok $parser->parse_errors, '... and parse_errors is true';
684
685    ok $parser->has_problems, '... and has_problems';
686
687    # Now wait and exit are hard to do in an OS platform-independent way, so
688    # we won't even bother
689
690    $tap = <<'END_TAP';
691TAP version 13
6921..2
693ok 1 - input file opened
694ok 2 - Gandalf wins
695END_TAP
696
697    $parser = TAP::Parser->new( { tap => $tap } );
698
699    _get_results($parser);
700
701    $parser->wait(1);
702
703    ok !$parser->failed,       'parser didnt fail';
704    ok !$parser->todo_passed,  '... and todo_passed is false';
705    ok !$parser->parse_errors, '... and parse_errors is false';
706
707    ok $parser->wait, '... and wait is set';
708
709    ok $parser->has_problems, '... and has_problems';
710
711    # and use the same for exit
712
713    $parser->wait(0);
714    $parser->exit(1);
715
716    ok !$parser->failed,       'parser didnt fail';
717    ok !$parser->todo_passed,  '... and todo_passed is false';
718    ok !$parser->parse_errors, '... and parse_errors is false';
719    ok !$parser->wait,         '... and wait is not set';
720
721    ok $parser->exit, '... and exit is set';
722
723    ok $parser->has_problems, '... and has_problems';
724}
725
726{
727
728    # coverage testing of the version states
729
730    my $tap = <<'END_TAP';
731TAP version 12
7321..2
733ok 1 - input file opened
734ok 2 - Gandalf wins
735END_TAP
736
737    my $parser = TAP::Parser->new( { tap => $tap } );
738
739    _get_results($parser);
740
741    my @errors = $parser->parse_errors;
742
743    is @errors, 1, 'test too low version number';
744
745    like pop @errors,
746      qr/Explicit TAP version must be at least 13. Got version 12/,
747      '... and trapped expected version error';
748
749    # now too high a version
750    $tap = <<'END_TAP';
751TAP version 14
7521..2
753ok 1 - input file opened
754ok 2 - Gandalf wins
755END_TAP
756
757    $parser = TAP::Parser->new( { tap => $tap } );
758
759    _get_results($parser);
760
761    @errors = $parser->parse_errors;
762
763    is @errors, 1, 'test too high version number';
764
765    like pop @errors,
766      qr/TAP specified version 14 but we don't know about versions later than 13/,
767      '... and trapped expected version error';
768}
769
770{
771
772    # coverage testing of TAP version in the wrong place
773
774    my $tap = <<'END_TAP';
7751..2
776ok 1 - input file opened
777TAP version 12
778ok 2 - Gandalf wins
779END_TAP
780
781    my $parser = TAP::Parser->new( { tap => $tap } );
782
783    _get_results($parser);
784
785    my @errors = $parser->parse_errors;
786
787    is @errors, 1, 'test TAP version number in wrong place';
788
789    like pop @errors,
790      qr/If TAP version is present it must be the first line of output/,
791      '... and trapped expected version error';
792
793}
794
795{
796
797    # we're going to bash the internals a bit (but using the API as
798    # much as possible) to force grammar->tokenise() to fail
799
800# firstly we'll create a iterator that dies when its next_raw method is called
801
802    package TAP::Parser::Iterator::Dies;
803
804    use strict;
805    use vars qw(@ISA);
806
807    @ISA = qw(TAP::Parser::Iterator);
808
809    sub next_raw {
810        die 'this is the dying iterator';
811    }
812
813    # required as part of the TPI interface
814    sub exit { }
815    sub wait { }
816
817    package main;
818
819    # now build a standard parser
820
821    my $tap = <<'END_TAP';
8221..2
823ok 1 - input file opened
824ok 2 - Gandalf wins
825END_TAP
826
827    {
828        my $parser = TAP::Parser->new( { tap => $tap } );
829
830        # build a dying iterator
831        my $iterator = TAP::Parser::Iterator::Dies->new;
832
833        # now replace the iterator - we're forced to us an T::P intenal
834        # method for this
835        $parser->_iterator($iterator);
836
837        # build a new grammar
838        my $grammar = TAP::Parser::Grammar->new(
839            {   iterator => $iterator,
840                parser   => $parser
841            }
842        );
843
844        # replace our grammar with this new one
845        $parser->_grammar($grammar);
846
847        # now call next on the parser, and the grammar should die
848        my $result = $parser->next;    # will die in iterator
849
850        is $result, undef, 'iterator dies';
851
852        my @errors = $parser->parse_errors;
853        is @errors, 2, '...and caught expected errrors';
854
855        like shift @errors, qr/this is the dying iterator/,
856          '...and it was what we expected';
857    }
858
859    # Do it all again with callbacks to exercise the other code path in
860    # the unrolled iterator
861    {
862        my $parser = TAP::Parser->new( { tap => $tap } );
863
864        $parser->callback( 'ALL', sub { } );
865
866        # build a dying iterator
867        my $iterator = TAP::Parser::Iterator::Dies->new;
868
869        # now replace the iterator - we're forced to us an T::P intenal
870        # method for this
871        $parser->_iterator($iterator);
872
873        # build a new grammar
874        my $grammar = TAP::Parser::Grammar->new(
875            {   iterator => $iterator,
876                parser   => $parser
877            }
878        );
879
880        # replace our grammar with this new one
881        $parser->_grammar($grammar);
882
883        # now call next on the parser, and the grammar should die
884        my $result = $parser->next;    # will die in iterator
885
886        is $result, undef, 'iterator dies';
887
888        my @errors = $parser->parse_errors;
889        is @errors, 2, '...and caught expected errrors';
890
891        like shift @errors, qr/this is the dying iterator/,
892          '...and it was what we expected';
893    }
894}
895
896{
897
898    # coverage testing of TAP::Parser::_next_state
899
900    package TAP::Parser::WithBrokenState;
901    use vars qw(@ISA);
902
903    @ISA = qw( TAP::Parser );
904
905    sub _make_state_table {
906        return { INIT => { plan => { goto => 'FOO' } } };
907    }
908
909    package main;
910
911    my $tap = <<'END_TAP';
9121..2
913ok 1 - input file opened
914ok 2 - Gandalf wins
915END_TAP
916
917    my $parser = TAP::Parser::WithBrokenState->new( { tap => $tap } );
918
919    my @die;
920
921    eval {
922        local $SIG{__DIE__} = sub { push @die, @_ };
923
924        $parser->next;
925        $parser->next;
926    };
927
928    is @die, 1, 'detect broken state machine';
929
930    like pop @die, qr/Illegal state: FOO/,
931      '...and the message is as we expect';
932}
933
934{
935
936    # coverage testing of TAP::Parser::_iter
937
938    package TAP::Parser::WithBrokenIter;
939    use vars qw(@ISA);
940
941    @ISA = qw( TAP::Parser );
942
943    sub _iter {return}
944
945    package main;
946
947    my $tap = <<'END_TAP';
9481..2
949ok 1 - input file opened
950ok 2 - Gandalf wins
951END_TAP
952
953    my $parser = TAP::Parser::WithBrokenIter->new( { tap => $tap } );
954
955    my @die;
956
957    eval {
958        local $SIG{__WARN__} = sub { };
959        local $SIG{__DIE__} = sub { push @die, @_ };
960
961        $parser->next;
962    };
963
964    is @die, 1, 'detect broken iter';
965
966    like pop @die, qr/Can't use/, '...and the message is as we expect';
967}
968
969SKIP: {
970
971    # http://markmail.org/message/rkxbo6ft7yorgnzb
972    skip "Crashes on older Perls", 2 if $] <= 5.008004 || $] == 5.009;
973
974    # coverage testing of TAP::Parser::_finish
975
976    my $tap = <<'END_TAP';
9771..2
978ok 1 - input file opened
979ok 2 - Gandalf wins
980END_TAP
981
982    my $parser = TAP::Parser->new( { tap => $tap } );
983
984    $parser->tests_run(999);
985
986    my @die;
987
988    eval {
989        local $SIG{__DIE__} = sub { push @die, @_ };
990
991        _get_results $parser;
992    };
993
994    is @die, 1, 'detect broken test counts';
995
996    like pop @die,
997      qr/Panic: planned test count [(]1001[)] did not equal sum of passed [(]0[)] and failed [(]2[)] tests!/,
998      '...and the message is as we expect';
999}
1000
1001{
1002
1003    # Sanity check on state table
1004
1005    my $parser      = TAP::Parser->new( { tap => "1..1\nok 1\n" } );
1006    my $state_table = $parser->_make_state_table;
1007    my @states      = sort keys %$state_table;
1008    my @expect      = sort qw(
1009      bailout comment plan pragma test unknown version yaml
1010    );
1011
1012    my %reachable = ( INIT => 1 );
1013
1014    for my $name (@states) {
1015        my $state      = $state_table->{$name};
1016        my @can_handle = sort keys %$state;
1017        is_deeply \@can_handle, \@expect, "token types handled in $name";
1018        for my $type (@can_handle) {
1019            $reachable{$_}++
1020              for grep {defined}
1021              map      { $state->{$type}->{$_} } qw(goto continue);
1022        }
1023    }
1024
1025    is_deeply [ sort keys %reachable ], [@states], "all states reachable";
1026}
1027
1028{
1029
1030    # exit, wait, ignore_exit interactions
1031
1032    my @truth = (
1033        [ 0, 0, 0, 0 ],
1034        [ 0, 0, 1, 0 ],
1035        [ 1, 0, 0, 1 ],
1036        [ 1, 0, 1, 0 ],
1037        [ 1, 1, 0, 1 ],
1038        [ 1, 1, 1, 0 ],
1039        [ 0, 1, 0, 1 ],
1040        [ 0, 1, 1, 0 ],
1041    );
1042
1043    for my $t (@truth) {
1044        my ( $wait, $exit, $ignore_exit, $has_problems ) = @$t;
1045        my $test_parser = sub {
1046            my $parser = shift;
1047            $parser->wait($wait);
1048            $parser->exit($exit);
1049            ok $has_problems ? $parser->has_problems : !$parser->has_problems,
1050              "exit=$exit, wait=$wait, ignore=$ignore_exit";
1051        };
1052
1053        my $parser = TAP::Parser->new( { tap => "1..1\nok 1\n" } );
1054        $parser->ignore_exit($ignore_exit);
1055        $test_parser->($parser);
1056
1057        $test_parser->(
1058            TAP::Parser->new(
1059                { tap => "1..1\nok 1\n", ignore_exit => $ignore_exit }
1060            )
1061        );
1062    }
1063}
1064