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