| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Test::Synopsis::Expectation; | 
| 2 | 10 |  |  | 10 |  | 22039 | use 5.008005; | 
|  | 10 |  |  |  |  | 26 |  | 
|  | 10 |  |  |  |  | 326 |  | 
| 3 | 10 |  |  | 10 |  | 67 | use strict; | 
|  | 10 |  |  |  |  | 14 |  | 
|  | 10 |  |  |  |  | 243 |  | 
| 4 | 10 |  |  | 10 |  | 42 | use warnings; | 
|  | 10 |  |  |  |  | 13 |  | 
|  | 10 |  |  |  |  | 256 |  | 
| 5 | 10 |  |  | 10 |  | 4939 | use parent qw/Test::Builder::Module/; | 
|  | 10 |  |  |  |  | 2671 |  | 
|  | 10 |  |  |  |  | 45 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | my @test_more_exports; | 
| 8 | 10 |  |  | 10 |  | 88077 | BEGIN { @test_more_exports = (qw/done_testing/) } | 
| 9 | 10 |  |  | 10 |  | 5596 | use PPI::Tokenizer; | 
|  | 10 |  |  |  |  | 842297 |  | 
|  | 10 |  |  |  |  | 372 |  | 
| 10 | 10 |  |  | 10 |  | 5571 | use ExtUtils::Manifest qw/maniread/; | 
|  | 10 |  |  |  |  | 72391 |  | 
|  | 10 |  |  |  |  | 818 |  | 
| 11 | 10 |  |  | 10 |  | 6663 | use Test::More import => \@test_more_exports; | 
|  | 10 |  |  |  |  | 38222 |  | 
|  | 10 |  |  |  |  | 116 |  | 
| 12 | 10 |  |  | 10 |  | 6610 | use Test::Synopsis::Expectation::Pod; | 
|  | 10 |  |  |  |  | 35 |  | 
|  | 10 |  |  |  |  | 9807 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | our $VERSION = "0.12"; | 
| 15 |  |  |  |  |  |  | our @EXPORT  = (@test_more_exports, qw/all_synopsis_ok synopsis_ok/); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | my $prepared = ''; | 
| 18 |  |  |  |  |  |  | my $ignorings = []; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub prepare { | 
| 21 | 1 |  |  | 1 | 1 | 7 | $prepared = shift; | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | sub set_ignorings { | 
| 25 | 1 |  |  | 1 | 1 | 8 | $ignorings = shift; | 
| 26 | 1 | 50 |  |  |  | 4 | $ignorings = [$ignorings] if ref $ignorings ne 'ARRAY'; | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 1 |  |  |  |  | 2 | return $ignorings; | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | sub all_synopsis_ok { | 
| 32 | 0 |  |  | 0 | 1 | 0 | my $builder = __PACKAGE__->builder; | 
| 33 | 0 |  |  |  |  | 0 | my @files   = _list_up_files_from_manifest($builder); | 
| 34 | 0 |  |  |  |  | 0 | for my $file (@files) { | 
| 35 | 0 |  |  |  |  | 0 | _synopsis_ok($file); | 
| 36 |  |  |  |  |  |  | } | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | sub synopsis_ok { | 
| 40 | 9 |  |  | 9 | 1 | 318 | my ($files) = @_; | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 9 | 100 |  |  |  | 52 | $files = [$files] if ref $files ne 'ARRAY'; | 
| 43 | 9 |  |  |  |  | 29 | for my $file (@$files) { | 
| 44 | 10 |  |  |  |  | 33 | _synopsis_ok($file); | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | sub _synopsis_ok { | 
| 49 | 10 |  |  | 10 |  | 21 | my ($file) = @_; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 10 |  |  |  |  | 53 | local $Test::Builder::Level = $Test::Builder::Level + 1; | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 10 |  |  |  |  | 115 | my $parser = Test::Synopsis::Expectation::Pod->new; | 
| 54 | 10 |  |  |  |  | 100 | $parser->parse_file($file); | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 10 |  |  |  |  | 164 | my $block_num = 1; | 
| 57 | 10 |  |  |  |  | 31 | for my $target_code (@{$parser->{target_codes}}) { | 
|  | 10 |  |  |  |  | 32 |  | 
| 58 | 12 |  |  |  |  | 43 | my ($expectations, $code) = _analyze_target_code($target_code); | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 12 |  |  |  |  | 69 | _check_syntax($code, $block_num, $file); | 
| 61 | 12 |  |  |  |  | 4542 | for my $expectation (@$expectations) { | 
| 62 | 24 |  |  |  |  | 3383 | _check_with_expectation($expectation, $block_num, $file); | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 12 |  |  |  |  | 3925 | $block_num++; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | sub _check_syntax { | 
| 70 |  |  |  |  |  |  | package Test::Synopsis::Expectation::Sandbox; | 
| 71 | 12 |  |  | 12 |  | 944 | eval $_[0]; ## no critic | 
|  | 1 |  |  | 1 |  | 6 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 72 | 12 | 50 |  |  |  | 49 | if ($@) { | 
| 73 | 0 |  |  |  |  | 0 | Test::More::fail("Syntax OK: $_[2] (SYNOPSIS Block: $_[1])"); | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  | else { | 
| 76 | 12 |  |  |  |  | 135 | Test::More::pass("Syntax OK: $_[2] (SYNOPSIS Block: $_[1])"); | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | sub _check_with_expectation { | 
| 81 |  |  |  |  |  |  | package Test::Synopsis::Expectation::Sandbox; | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | # $_[0] is expectation | 
| 84 | 24 |  |  | 24 |  | 1481 | my $got       = eval $_[0]->{code};     ## no critic | 
|  | 1 |  |  | 1 |  | 4 |  | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 1 |  | 29 |  | 
|  | 1 |  |  | 1 |  | 4 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 33 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 48 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 46 |  | 
| 85 | 24 |  |  |  |  | 939 | my $expected  = eval $_[0]->{expected}; ## no critic | 
| 86 | 24 |  |  |  |  | 68 | my $method    = $_[0]->{method}; | 
| 87 | 24 |  |  |  |  | 114 | my $test_name = "$_[2] (SYNOPSIS Block: $_[1], Line: $_[0]->{line_num})"; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 24 | 100 |  |  |  | 96 | if ($method eq 'is') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 90 | 19 |  |  |  |  | 62 | Test::More::is($got, $expected, $test_name); | 
| 91 |  |  |  |  |  |  | } elsif ($method eq 'isa') { | 
| 92 | 1 |  |  |  |  | 6 | Test::More::isa_ok($got, $expected, $test_name); | 
| 93 |  |  |  |  |  |  | } elsif ($method eq 'like') { | 
| 94 | 1 |  |  |  |  | 4 | Test::More::like($got, $expected, $test_name); | 
| 95 |  |  |  |  |  |  | } elsif ($method eq 'is_deeply') { | 
| 96 | 1 |  |  |  |  | 4 | Test::More::is_deeply($got, $expected, $test_name); | 
| 97 |  |  |  |  |  |  | } elsif ($method eq 'success') { | 
| 98 | 2 |  |  |  |  | 11 | Test::More::ok($got, $test_name); | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | sub _analyze_target_code { | 
| 103 | 12 |  |  | 12 |  | 21 | my ($target_code) = @_; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 12 |  |  |  |  | 19 | my $deficient_brace = 0; | 
| 106 | 12 |  | 100 |  |  | 66 | my $code = $prepared || ''; # code for test | 
| 107 | 12 |  |  |  |  | 19 | my @expectations; # store expectations for test | 
| 108 | 12 |  |  |  |  | 16 | my $line_num = 1; | 
| 109 | 12 |  |  |  |  | 77 | for my $line (split /\n\r?/, $target_code) { | 
| 110 | 44 |  |  |  |  | 522 | my $tokens = PPI::Tokenizer->new(\$line)->all_tokens; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 44 | 100 |  |  |  | 22609 | if (grep {$_->{content} eq '...' && $_->isa('PPI::Token::Operator')} @$tokens) { | 
|  | 256 | 100 |  |  |  | 1306 |  | 
| 113 | 1 |  |  |  |  | 6 | next; | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 | 43 |  |  |  |  | 80 | for my $ignoring (@$ignorings) { | 
| 117 | 4 |  |  |  |  | 27 | $line =~ s/\Q$ignoring\E//g; | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 43 |  |  |  |  | 84 | $code .= "$line\n"; | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | # Count the number of left braces to complete deficient right braces | 
| 123 | 43 | 100 |  |  |  | 62 | $deficient_brace++ if (grep {$_->{content} eq '{' && $_->isa('PPI::Token::Structure')}  @$tokens); | 
|  | 254 | 100 |  |  |  | 537 |  | 
| 124 | 43 | 100 |  |  |  | 55 | $deficient_brace-- if (grep {$_->{content} eq '}' && $_->isa('PPI::Token::Structure')}  @$tokens); | 
|  | 254 | 100 |  |  |  | 507 |  | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | # Extract comment statement | 
| 127 |  |  |  |  |  |  | # Tokens contain one comment token on a line, at the most | 
| 128 | 43 | 100 |  |  |  | 67 | if (my ($comment) = grep {$_->isa('PPI::Token::Comment')} @$tokens) { | 
|  | 254 |  |  |  |  | 925 |  | 
| 129 |  |  |  |  |  |  | # Accept special comment for this module | 
| 130 |  |  |  |  |  |  | # e.g. | 
| 131 |  |  |  |  |  |  | #     # => is 42 | 
| 132 | 24 |  |  |  |  | 140 | my ($expectation) = $comment->{content} =~ /#\s*=>\s*(.+)/; | 
| 133 | 24 | 50 |  |  |  | 65 | next unless $expectation; | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | # Accept test methods as string | 
| 136 | 24 |  |  |  |  | 31 | my $method; | 
| 137 | 24 | 100 |  |  |  | 76 | if ($expectation =~ s/^(?:(is|isa|is_deeply|like)\s|(success))//) { | 
| 138 | 7 |  | 66 |  |  | 27 | $method = $1 || $2; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 24 |  | 100 |  |  | 248 | push @expectations, +{ | 
| 142 |  |  |  |  |  |  | 'method'   => $method || 'is', | 
| 143 |  |  |  |  |  |  | 'expected' => $expectation, | 
| 144 |  |  |  |  |  |  | 'code'     => $code . ('}' x $deficient_brace), | 
| 145 |  |  |  |  |  |  | 'line_num' => $line_num, | 
| 146 |  |  |  |  |  |  | }; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 43 |  |  |  |  | 199 | $line_num++; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 12 |  |  |  |  | 251 | return (\@expectations, $code); | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | sub _list_up_files_from_manifest { | 
| 156 | 0 |  |  | 0 |  | 0 | my ($builder) = @_; | 
| 157 |  |  |  |  |  |  |  | 
| 158 | 0 |  |  |  |  | 0 | my $manifest = $ExtUtils::Manifest::MANIFEST; | 
| 159 | 0 | 0 |  |  |  | 0 | if ( not -f $manifest ) { | 
| 160 | 0 |  |  |  |  | 0 | $builder->plan( skip_all => "$manifest doesn't exist" ); | 
| 161 |  |  |  |  |  |  | } | 
| 162 | 0 |  |  |  |  | 0 | return grep { m!\Alib/.*\.pm\Z! } keys %{ maniread() }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | 1; | 
| 165 |  |  |  |  |  |  | __END__ |