xref: /openbsd-src/gnu/usr.bin/perl/cpan/Test-Harness/t/parse.t (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1#!/usr/bin/perl -w
2
3use strict;
4
5use lib 't/lib';
6
7use Test::More tests => 294;
8use IO::c55Capture;
9
10use File::Spec;
11
12use TAP::Parser;
13use TAP::Parser::IteratorFactory;
14
15sub _get_results {
16    my $parser = shift;
17    my @results;
18    while ( defined( my $result = $parser->next ) ) {
19        push @results => $result;
20    }
21    return @results;
22}
23
24my ( $PARSER, $PLAN, $PRAGMA, $TEST, $COMMENT, $BAILOUT, $UNKNOWN, $YAML, $VERSION ) = qw(
25  TAP::Parser
26  TAP::Parser::Result::Plan
27  TAP::Parser::Result::Pragma
28  TAP::Parser::Result::Test
29  TAP::Parser::Result::Comment
30  TAP::Parser::Result::Bailout
31  TAP::Parser::Result::Unknown
32  TAP::Parser::Result::YAML
33  TAP::Parser::Result::Version
34);
35
36my $factory = TAP::Parser::IteratorFactory->new;
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 = $PARSER->new( { stream => $factory->make_iterator($aref) } );
347isa_ok $parser, $PARSER, '... and calling it should succeed';
348
349# results() is sane?
350
351ok @results = _get_results($parser), 'The parser should return results';
352is scalar @results, 5, '... and there should be one for each line';
353
354# check the test plan
355
356$result = shift @results;
357isa_ok $result, $PLAN;
358can_ok $result, 'type';
359is $result->type, 'plan', '... and it should report the correct type';
360ok $result->is_plan,   '... and it should identify itself as a plan';
361is $result->plan,      '1..2', '... and identify the plan';
362is $result->as_string, '1..2',
363  '... and have the correct string representation';
364is $result->raw, '1..2', '... and raw() should return the original line';
365
366# a normal, passing test
367
368$test = shift @results;
369isa_ok $test, $TEST;
370is $test->type, 'test', '... and it should report the correct type';
371ok $test->is_test, '... and it should identify itself as a test';
372is $test->ok,      'ok', '... and it should have the correct ok()';
373ok $test->is_ok,   '... and the correct boolean version of is_ok()';
374ok $test->is_actual_ok,
375  '... and the correct boolean version of is_actual_ok()';
376is $test->number, 1, '... and have the correct test number';
377is $test->description, '- input file opened',
378  '... and the correct description';
379ok !$test->directive,   '... and not have a directive';
380ok !$test->explanation, '... or a directive explanation';
381ok !$test->has_skip,    '... and it is not a SKIPped test';
382ok !$test->has_todo,    '... nor a TODO test';
383is $test->as_string, 'ok 1 - input file opened',
384  '... and its string representation should be correct';
385is $test->raw, 'ok 1 - input file opened',
386  '... and raw() should return the original line';
387
388# junk lines should be preserved
389
390$unknown = shift @results;
391isa_ok $unknown, $UNKNOWN;
392is $unknown->type, 'unknown', '... and it should report the correct type';
393ok $unknown->is_unknown, '... and it should identify itself as unknown';
394is $unknown->as_string,  '',
395  '... and its string representation should be returned verbatim';
396is $unknown->raw, '', '... and raw() should return the original line';
397
398# ... and the second empty line
399
400$unknown = shift @results;
401isa_ok $unknown, $UNKNOWN;
402is $unknown->type, 'unknown', '... and it should report the correct type';
403ok $unknown->is_unknown, '... and it should identify itself as unknown';
404is $unknown->as_string,  '',
405  '... and its string representation should be returned verbatim';
406is $unknown->raw, '', '... and raw() should return the original line';
407
408# a passing test
409
410$test = shift @results;
411isa_ok $test, $TEST;
412is $test->type, 'test', '... and it should report the correct type';
413ok $test->is_test, '... and it should identify itself as a test';
414is $test->ok,      'ok', '... and it should have the correct ok()';
415ok $test->is_ok,   '... and the correct boolean version of is_ok()';
416ok $test->is_actual_ok,
417  '... and the correct boolean version of is_actual_ok()';
418is $test->number, 2, '... and have the correct test number';
419is $test->description, '- read the rest of the file',
420  '... and the correct description';
421ok !$test->directive,   '... and not have a directive';
422ok !$test->explanation, '... or a directive explanation';
423ok !$test->has_skip,    '... and it is not a SKIPped test';
424ok !$test->has_todo,    '... nor a TODO test';
425is $test->as_string, 'ok 2 - read the rest of the file',
426  '... and its string representation should be correct';
427is $test->raw, 'ok 2 - read the rest of the file',
428  '... and raw() should return the original line';
429
430is scalar $parser->passed, 2,
431  'Empty junk lines should not affect the correct number of tests passed';
432
433# Check source => "tap content"
434can_ok $PARSER, 'new';
435$parser = $PARSER->new( { source => "1..1\nok 1\n" } );
436isa_ok $parser, $PARSER, '... and calling it should succeed';
437ok @results = _get_results($parser), 'The parser should return results';
438is( scalar @results, 2, "Got two lines of TAP" );
439
440# Check source => [array]
441can_ok $PARSER, 'new';
442$parser = $PARSER->new( { source => [ "1..1", "ok 1" ] } );
443isa_ok $parser, $PARSER, '... and calling it should succeed';
444ok @results = _get_results($parser), 'The parser should return results';
445is( scalar @results, 2, "Got two lines of TAP" );
446
447# Check source => $filehandle
448can_ok $PARSER, 'new';
449open my $fh, 't/data/catme.1';
450$parser = $PARSER->new( { source => $fh } );
451isa_ok $parser, $PARSER, '... and calling it should succeed';
452ok @results = _get_results($parser), 'The parser should return results';
453is( scalar @results, 2, "Got two lines of TAP" );
454
455{
456
457    # set a spool to write to
458    tie local *SPOOL, 'IO::c55Capture';
459
460    my $tap = <<'END_TAP';
461TAP version 13
4621..7
463ok 1 - input file opened
464... this is junk
465not ok first line of the input valid # todo some data
466# this is a comment
467ok 3 - read the rest of the file
468not ok 4 - this is a real failure
469  --- YAML!
470  ...
471ok 5 # skip we have no description
472ok 6 - you shall not pass! # TODO should have failed
473not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
474END_TAP
475
476    {
477        my $parser = $PARSER->new(
478            {   tap   => $tap,
479                spool => \*SPOOL,
480            }
481        );
482
483        _get_results($parser);
484
485        my @spooled = tied(*SPOOL)->dump();
486
487        is @spooled, 24, 'coverage testing for spool attribute of parser';
488        is join( '', @spooled ), $tap, "spooled tap matches";
489    }
490
491    {
492        my $parser = $PARSER->new(
493            {   tap   => $tap,
494                spool => \*SPOOL,
495            }
496        );
497
498        $parser->callback( 'ALL', sub { } );
499
500        _get_results($parser);
501
502        my @spooled = tied(*SPOOL)->dump();
503
504        is @spooled, 24, 'coverage testing for spool attribute of parser';
505        is join( '', @spooled ), $tap, "spooled tap matches";
506    }
507}
508
509{
510
511    # _initialize coverage
512
513    my $x = bless [], 'kjsfhkjsdhf';
514
515    my @die;
516
517    eval {
518        local $SIG{__DIE__} = sub { push @die, @_ };
519
520        $PARSER->new();
521    };
522
523    is @die, 1, 'coverage testing for _initialize';
524
525    like pop @die, qr/PANIC:\s+could not determine stream at/,
526      '...and it failed as expected';
527
528    @die = ();
529
530    eval {
531        local $SIG{__DIE__} = sub { push @die, @_ };
532
533        $PARSER->new(
534            {   stream => 'stream',
535                tap    => 'tap',
536                source => 'source',    # only one of these is allowed
537            }
538        );
539    };
540
541    is @die, 1, 'coverage testing for _initialize';
542
543    like pop @die,
544      qr/You may only choose one of 'exec', 'stream', 'tap' or 'source'/,
545      '...and it failed as expected';
546}
547
548{
549
550    # coverage of todo_failed
551
552    my $tap = <<'END_TAP';
553TAP version 13
5541..7
555ok 1 - input file opened
556... this is junk
557not ok first line of the input valid # todo some data
558# this is a comment
559ok 3 - read the rest of the file
560not ok 4 - this is a real failure
561  --- YAML!
562  ...
563ok 5 # skip we have no description
564ok 6 - you shall not pass! # TODO should have failed
565not ok 7 - Gandalf wins.  Game over.  # TODO 'bout time!
566END_TAP
567
568    my $parser = $PARSER->new( { tap => $tap } );
569
570    _get_results($parser);
571
572    my @warn;
573
574    eval {
575        local $SIG{__WARN__} = sub { push @warn, @_ };
576
577        $parser->todo_failed;
578    };
579
580    is @warn, 1, 'coverage testing of todo_failed';
581
582    like pop @warn,
583      qr/"todo_failed" is deprecated.  Please use "todo_passed".  See the docs[.]/,
584      '..and failed as expected'
585}
586
587{
588
589    # coverage testing for T::P::_initialize
590
591    # coverage of the source argument paths
592
593    # ref argument to source
594
595    my $parser = TAP::Parser->new( { source => [ split /$/, $tap ] } );
596
597    isa_ok $parser, 'TAP::Parser';
598
599    isa_ok $parser->_stream, 'TAP::Parser::Iterator::Array';
600
601    # uncategorisable argument to source
602    my @die;
603
604    eval {
605        local $SIG{__DIE__} = sub { push @die, @_ };
606
607        $parser = TAP::Parser->new( { source => 'nosuchfile' } );
608    };
609
610    is @die, 1, 'uncategorisable source';
611
612    like pop @die, qr/Cannot determine source for nosuchfile/,
613      '... and we died as expected';
614}
615
616{
617
618    # coverage test of perl source with switches
619
620    my $parser = TAP::Parser->new(
621        {   source => File::Spec->catfile(
622                't',
623                'sample-tests',
624                'simple'
625            ),
626        }
627    );
628
629    isa_ok $parser, 'TAP::Parser';
630
631    isa_ok $parser->_stream, 'TAP::Parser::Iterator::Process';
632
633    # Workaround for Mac OS X problem wrt closing the iterator without
634    # reading from it.
635    $parser->next;
636}
637
638{
639
640    # coverage testing for TAP::Parser::has_problems
641
642    # we're going to need to test lots of fragments of tap
643    # to cover all the different boolean tests
644
645    # currently covered are no problems and failed, so let's next test
646    # todo_passed
647
648    my $tap = <<'END_TAP';
649TAP version 13
6501..2
651ok 1 - input file opened
652ok 2 - Gandalf wins.  Game over.  # TODO 'bout time!
653END_TAP
654
655    my $parser = TAP::Parser->new( { tap => $tap } );
656
657    _get_results($parser);
658
659    ok !$parser->failed, 'parser didnt fail';
660    ok $parser->todo_passed, '... and todo_passed is true';
661
662    ok !$parser->has_problems, '... and has_problems is false';
663
664    # now parse_errors
665
666    $tap = <<'END_TAP';
667TAP version 13
6681..2
669SMACK
670END_TAP
671
672    $parser = TAP::Parser->new( { tap => $tap } );
673
674    _get_results($parser);
675
676    ok !$parser->failed,      'parser didnt fail';
677    ok !$parser->todo_passed, '... and todo_passed is false';
678    ok $parser->parse_errors, '... and parse_errors is true';
679
680    ok $parser->has_problems, '... and has_problems';
681
682    # Now wait and exit are hard to do in an OS platform-independent way, so
683    # we won't even bother
684
685    $tap = <<'END_TAP';
686TAP version 13
6871..2
688ok 1 - input file opened
689ok 2 - Gandalf wins
690END_TAP
691
692    $parser = TAP::Parser->new( { tap => $tap } );
693
694    _get_results($parser);
695
696    $parser->wait(1);
697
698    ok !$parser->failed,       'parser didnt fail';
699    ok !$parser->todo_passed,  '... and todo_passed is false';
700    ok !$parser->parse_errors, '... and parse_errors is false';
701
702    ok $parser->wait, '... and wait is set';
703
704    ok $parser->has_problems, '... and has_problems';
705
706    # and use the same for exit
707
708    $parser->wait(0);
709    $parser->exit(1);
710
711    ok !$parser->failed,       'parser didnt fail';
712    ok !$parser->todo_passed,  '... and todo_passed is false';
713    ok !$parser->parse_errors, '... and parse_errors is false';
714    ok !$parser->wait,         '... and wait is not set';
715
716    ok $parser->exit, '... and exit is set';
717
718    ok $parser->has_problems, '... and has_problems';
719}
720
721{
722
723    # coverage testing of the version states
724
725    my $tap = <<'END_TAP';
726TAP version 12
7271..2
728ok 1 - input file opened
729ok 2 - Gandalf wins
730END_TAP
731
732    my $parser = TAP::Parser->new( { tap => $tap } );
733
734    _get_results($parser);
735
736    my @errors = $parser->parse_errors;
737
738    is @errors, 1, 'test too low version number';
739
740    like pop @errors,
741      qr/Explicit TAP version must be at least 13. Got version 12/,
742      '... and trapped expected version error';
743
744    # now too high a version
745    $tap = <<'END_TAP';
746TAP version 14
7471..2
748ok 1 - input file opened
749ok 2 - Gandalf wins
750END_TAP
751
752    $parser = TAP::Parser->new( { tap => $tap } );
753
754    _get_results($parser);
755
756    @errors = $parser->parse_errors;
757
758    is @errors, 1, 'test too high version number';
759
760    like pop @errors,
761      qr/TAP specified version 14 but we don't know about versions later than 13/,
762      '... and trapped expected version error';
763}
764
765{
766
767    # coverage testing of TAP version in the wrong place
768
769    my $tap = <<'END_TAP';
7701..2
771ok 1 - input file opened
772TAP version 12
773ok 2 - Gandalf wins
774END_TAP
775
776    my $parser = TAP::Parser->new( { tap => $tap } );
777
778    _get_results($parser);
779
780    my @errors = $parser->parse_errors;
781
782    is @errors, 1, 'test TAP version number in wrong place';
783
784    like pop @errors,
785      qr/If TAP version is present it must be the first line of output/,
786      '... and trapped expected version error';
787
788}
789
790{
791
792    # we're going to bash the internals a bit (but using the API as
793    # much as possible) to force grammar->tokenise() to fail
794
795  # firstly we'll create a stream that dies when its next_raw method is called
796
797    package TAP::Parser::Iterator::Dies;
798
799    use strict;
800    use vars qw(@ISA);
801
802    @ISA = qw(TAP::Parser::Iterator);
803
804    sub next_raw {
805        die 'this is the dying iterator';
806    }
807
808    # required as part of the TPI interface
809    sub exit { }
810    sub wait { }
811
812    package main;
813
814    # now build a standard parser
815
816    my $tap = <<'END_TAP';
8171..2
818ok 1 - input file opened
819ok 2 - Gandalf wins
820END_TAP
821
822    {
823        my $parser = TAP::Parser->new( { tap => $tap } );
824
825        # build a dying stream
826        my $stream = TAP::Parser::Iterator::Dies->new;
827
828        # now replace the stream - we're forced to us an T::P intenal
829        # method for this
830        $parser->_stream($stream);
831
832        # build a new grammar
833        my $grammar = TAP::Parser::Grammar->new(
834            {   stream => $stream,
835                parser => $parser
836            }
837        );
838
839        # replace our grammar with this new one
840        $parser->_grammar($grammar);
841
842        # now call next on the parser, and the grammar should die
843        my $result = $parser->next;    # will die in iterator
844
845        is $result, undef, 'iterator dies';
846
847        my @errors = $parser->parse_errors;
848        is @errors, 2, '...and caught expected errrors';
849
850        like shift @errors, qr/this is the dying iterator/,
851          '...and it was what we expected';
852    }
853
854    # Do it all again with callbacks to exercise the other code path in
855    # the unrolled iterator
856    {
857        my $parser = TAP::Parser->new( { tap => $tap } );
858
859        $parser->callback( 'ALL', sub { } );
860
861        # build a dying stream
862        my $stream = TAP::Parser::Iterator::Dies->new;
863
864        # now replace the stream - we're forced to us an T::P intenal
865        # method for this
866        $parser->_stream($stream);
867
868        # build a new grammar
869        my $grammar = TAP::Parser::Grammar->new(
870            {   stream => $stream,
871                parser => $parser
872            }
873        );
874
875        # replace our grammar with this new one
876        $parser->_grammar($grammar);
877
878        # now call next on the parser, and the grammar should die
879        my $result = $parser->next;    # will die in iterator
880
881        is $result, undef, 'iterator dies';
882
883        my @errors = $parser->parse_errors;
884        is @errors, 2, '...and caught expected errrors';
885
886        like shift @errors, qr/this is the dying iterator/,
887          '...and it was what we expected';
888    }
889}
890
891{
892
893    # coverage testing of TAP::Parser::_next_state
894
895    package TAP::Parser::WithBrokenState;
896    use vars qw(@ISA);
897
898    @ISA = qw( TAP::Parser );
899
900    sub _make_state_table {
901        return { INIT => { plan => { goto => 'FOO' } } };
902    }
903
904    package main;
905
906    my $tap = <<'END_TAP';
9071..2
908ok 1 - input file opened
909ok 2 - Gandalf wins
910END_TAP
911
912    my $parser = TAP::Parser::WithBrokenState->new( { tap => $tap } );
913
914    my @die;
915
916    eval {
917        local $SIG{__DIE__} = sub { push @die, @_ };
918
919        $parser->next;
920        $parser->next;
921    };
922
923    is @die, 1, 'detect broken state machine';
924
925    like pop @die, qr/Illegal state: FOO/,
926      '...and the message is as we expect';
927}
928
929{
930
931    # coverage testing of TAP::Parser::_iter
932
933    package TAP::Parser::WithBrokenIter;
934    use vars qw(@ISA);
935
936    @ISA = qw( TAP::Parser );
937
938    sub _iter {return}
939
940    package main;
941
942    my $tap = <<'END_TAP';
9431..2
944ok 1 - input file opened
945ok 2 - Gandalf wins
946END_TAP
947
948    my $parser = TAP::Parser::WithBrokenIter->new( { tap => $tap } );
949
950    my @die;
951
952    eval {
953        local $SIG{__WARN__} = sub { };
954        local $SIG{__DIE__} = sub { push @die, @_ };
955
956        $parser->next;
957    };
958
959    is @die, 1, 'detect broken iter';
960
961    like pop @die, qr/Can't use/, '...and the message is as we expect';
962}
963
964SKIP: {
965
966    # http://markmail.org/message/rkxbo6ft7yorgnzb
967    skip "Crashes on older Perls", 2 if $] <= 5.008004 || $] == 5.009;
968
969    # coverage testing of TAP::Parser::_finish
970
971    my $tap = <<'END_TAP';
9721..2
973ok 1 - input file opened
974ok 2 - Gandalf wins
975END_TAP
976
977    my $parser = TAP::Parser->new( { tap => $tap } );
978
979    $parser->tests_run(999);
980
981    my @die;
982
983    eval {
984        local $SIG{__DIE__} = sub { push @die, @_ };
985
986        _get_results $parser;
987    };
988
989    is @die, 1, 'detect broken test counts';
990
991    like pop @die,
992      qr/Panic: planned test count [(]1001[)] did not equal sum of passed [(]0[)] and failed [(]2[)] tests!/,
993      '...and the message is as we expect';
994}
995
996{
997
998    # Sanity check on state table
999
1000    my $parser      = TAP::Parser->new( { tap => "1..1\nok 1\n" } );
1001    my $state_table = $parser->_make_state_table;
1002    my @states      = sort keys %$state_table;
1003    my @expect      = sort qw(
1004      bailout comment plan pragma test unknown version yaml
1005    );
1006
1007    my %reachable = ( INIT => 1 );
1008
1009    for my $name (@states) {
1010        my $state      = $state_table->{$name};
1011        my @can_handle = sort keys %$state;
1012        is_deeply \@can_handle, \@expect, "token types handled in $name";
1013        for my $type (@can_handle) {
1014            $reachable{$_}++
1015              for grep {defined}
1016              map      { $state->{$type}->{$_} } qw(goto continue);
1017        }
1018    }
1019
1020    is_deeply [ sort keys %reachable ], [@states], "all states reachable";
1021}
1022
1023{
1024
1025    # exit, wait, ignore_exit interactions
1026
1027    my @truth = (
1028        [ 0, 0, 0, 0 ],
1029        [ 0, 0, 1, 0 ],
1030        [ 1, 0, 0, 1 ],
1031        [ 1, 0, 1, 0 ],
1032        [ 1, 1, 0, 1 ],
1033        [ 1, 1, 1, 0 ],
1034        [ 0, 1, 0, 1 ],
1035        [ 0, 1, 1, 0 ],
1036    );
1037
1038    for my $t (@truth) {
1039        my ( $wait, $exit, $ignore_exit, $has_problems ) = @$t;
1040        my $test_parser = sub {
1041            my $parser = shift;
1042            $parser->wait($wait);
1043            $parser->exit($exit);
1044            ok $has_problems ? $parser->has_problems : !$parser->has_problems,
1045              "exit=$exit, wait=$wait, ignore=$ignore_exit";
1046        };
1047
1048        my $parser = TAP::Parser->new( { tap => "1..1\nok 1\n" } );
1049        $parser->ignore_exit($ignore_exit);
1050        $test_parser->($parser);
1051
1052        $test_parser->(
1053            TAP::Parser->new(
1054                { tap => "1..1\nok 1\n", ignore_exit => $ignore_exit }
1055            )
1056        );
1057    }
1058}
1059