File Coverage

blib/lib/Pinwheel/DocTest.pm
Criterion Covered Total %
statement 257 258 99.6
branch 43 44 97.7
condition 22 23 95.6
subroutine 66 66 100.0
pod 0 4 0.0
total 388 395 98.2


line stmt bran cond sub pod time code
1             package Pinwheel::DocTest;
2              
3 3     3   28441 use strict;
  3         9  
  3         131  
4 3     3   20 use warnings;
  3         5  
  3         99  
5              
6 3     3   2686 use Data::Dumper;
  3         27906  
  3         236  
7 3     3   2032 use PPI;
  3         509598  
  3         103  
8 3     3   59 use Test::Builder;
  3         6  
  3         69  
9 3     3   16 use Test::More;
  3         8  
  3         32  
10              
11              
12             sub p
13             {
14 73     73 0 343 local $Data::Dumper::Indent = 0;
15 73         128 local $Data::Dumper::Sortkeys = 1;
16 73         447 local $Data::Dumper::Terse = 1;
17 73         111 local $Data::Dumper::Useqq = 1;
18 73         552 return Dumper(shift) . "\n";
19             }
20              
21             sub is_silent
22             {
23 68     68 0 120 my ($coderef, $d) = @_;
24              
25 68         590 $d = PPI::Document->new($coderef);
26             return $d->find_any(sub {
27 584 100   584   14794 $_[1]->isa('PPI::Token::Operator') and $_[1]->content eq '='
28 68         168277 });
29             }
30              
31             sub _expand_ellipsis
32             {
33 13     13   56 my ($s) = @_;
34 25         71 $s = join('.*',
35 13         275 map { s/([\$\^\.\*\+\?\(\)\{\}\[\]\|\\])/\\$1/g; $_ }
  25         82  
36             split(/\.\.\./, $s)
37             );
38 13         573 return qr/^$s$/s;
39             }
40              
41             sub run_tests
42             {
43 2     2 0 7 my ($pkg, $tests) = @_;
44 2         4 my ($test, $fh, $testfn, $output, $got);
45              
46 2         298 $test = Test::More->builder;
47 2         126 $fh = $test->todo_output;
48 2         19 local $Test::Builder::Level = 0;
49 2         6 foreach (@$tests) {
50 70         76652 my ($input, $expected, $line, $comment) = @$_;
51 70         789 tie(*STDOUT, 'Pinwheel::DocTest::CaptureOut');
52 2     2   19 $got = eval qq{
  2     2   4  
  2     2   448  
  2     2   20  
  2     2   4  
  2     2   115  
  2     2   16  
  2     2   4  
  2     2   64  
  2     2   13  
  2     2   4  
  2     2   67  
  2     2   14  
  2     2   6  
  2     2   61  
  2     2   13  
  2     2   4  
  2     2   64  
  2     2   14  
  2     2   5  
  2     2   74  
  2     2   16  
  2     2   5  
  2     2   79  
  2     2   15  
  2     2   4  
  2     1   82  
  2     1   86  
  2     1   5  
  2     1   94  
  2     1   14  
  2     1   4  
  2     1   83  
  2     1   16  
  2     1   5  
  2     1   73  
  2     1   16  
  2     1   4  
  2     1   153  
  2     1   15  
  2     1   5  
  2     1   69  
  2     1   61  
  2     1   4  
  2         143  
  2         14  
  2         4  
  2         85  
  2         17  
  2         4  
  2         99  
  2         16  
  2         5  
  2         80  
  2         14  
  2         5  
  2         91  
  2         19  
  2         6  
  2         82  
  2         13  
  2         6  
  2         78  
  2         16  
  2         6  
  2         96  
  2         17  
  2         5  
  2         90  
  2         94  
  2         5  
  2         99  
  2         18  
  2         6  
  2         139  
  2         15  
  2         5  
  2         66  
  1         7  
  1         3  
  1         27  
  1         8  
  1         2  
  1         75  
  1         9  
  1         4  
  1         39  
  1         8  
  1         3  
  1         52  
  1         9  
  1         3  
  1         181  
  1         10  
  1         1  
  1         48  
  1         8  
  1         2  
  1         41  
  1         8  
  1         2  
  1         58  
  1         7  
  1         3  
  1         44  
  1         8  
  1         2  
  1         35  
  1         9  
  1         2  
  1         173  
  1         7  
  1         3  
  1         45  
  1         9  
  1         2  
  1         56  
  1         7  
  1         2  
  1         34  
  1         15  
  1         2  
  1         49  
  1         8  
  1         3  
  1         34  
  1         6  
  1         2  
  1         46  
  1         12  
  1         2  
  1         39  
  70         11927  
53             package $pkg;
54             no strict qw(vars subs refs);
55             #line $line "console"
56             $input;
57             };
58 70         581 $output = ;
59 70         248 untie(*STDOUT);
60 70 100       681 if ($@) {
    100          
61 2         6 $got = $@;
62             } elsif (is_silent(\$input)) {
63 17         3376 $got = undef;
64             } else {
65 51         8238 $got = p($got);
66 51 100       6463 $got = $output . $got if defined($output);
67             }
68 70 100 100     675 if (defined($expected) && $expected =~ /\.\.\./) {
69 8         378 $expected = _expand_ellipsis($expected);
70 8         23 $testfn = 'like';
71             } else {
72 62         140 $testfn = 'is_eq';
73             }
74 70 100       187 if ($comment) {
75 19         106 $comment =~ s/^\s*\n//s;
76 19         163 $comment =~ s/\s*$//;
77 19         132 $comment =~ s/\n/\n# /g;
78 19         5676 print $fh "# $comment\n";
79             }
80 70         6970 eval qq{
81             package $pkg;
82             #line $line "console"
83             \$test->$testfn(\$got, \$expected);
84             };
85             }
86             }
87              
88              
89             sub test_file
90             {
91 2     2 0 17 my ($filename) = @_;
92 2         4 my ($fh, $pkg, $end, @tests);
93 0         0 my ($in_doctest, $indent, $input, $output, $line, $comment);
94              
95 2         85 open($fh, "< $filename");
96 2         7 $pkg = caller();
97 2         4 $end = 0;
98 2         3 $in_doctest = 0;
99              
100 2         8 while (!$end) {
101 271         6857 $_ = <$fh>;
102 271         317 $end = !defined($_);
103             # Fake a blank line at the end to ensure the final test is picked up
104 271 100       548 $_ = '' if $end;
105              
106 271 100 100     5154 if (/^=begin\s+doctest\b/) {
    100 66        
    100 100        
    100          
    100          
    100          
    100          
107 6         14 $in_doctest = 1;
108             } elsif (!$in_doctest) {
109 53         97 next;
110             } elsif (/^(\s*)>>> (.+)/) {
111 70 100       146 if (defined($input)) {
112 47         145 push @tests, [$input, $output, $line, $comment];
113 47         73 $comment = undef;
114             }
115 70         202 $indent = $1;
116 70         127 $input = $2 . "\n";
117 70         154 $output = undef;
118 70         165 $line = $.;
119             } elsif (defined($input) && (/^\s*$/ || /^=cut\b/)) {
120 23 100       50 $in_doctest = 0 if /^=cut\b/;
121 23         68 push @tests, [$input, $output, $line, $comment];
122 23         29 $input = undef;
123 23         47 $comment = undef;
124             } elsif (!defined($input)) {
125 40 100       67 if (/^=cut\b/) {
126 3         7 $in_doctest = 0;
127             } else {
128 37   100     228 $comment = ($comment || '') . $_;
129             }
130             } elsif (!defined($output) && /^$indent\.\.\. (.+)/) {
131 2         7 $input .= $1 . "\n";
132             } elsif (/^$indent\s*$/) {
133 2   100     13 $output = ($output || '') . "\n";
134             } else {
135 75         265 /^$indent(.+)/;
136 75   100     413 $output = ($output || '') . $1 . "\n";
137             }
138             }
139 2         35 run_tests($pkg, \@tests);
140              
141 2         2083 close($fh);
142             }
143              
144              
145              
146             package Pinwheel::DocTest::CaptureOut;
147              
148             sub TIEHANDLE
149             {
150 70     70   672 return bless([], $_[0]);
151             }
152              
153             sub PRINTF
154             {
155 1     1   90 my ($self, $format, @args) = @_;
156 1         10 push @$self, sprintf($format, @args);
157             }
158              
159             sub PRINT
160             {
161 17     17   1664 my ($self, @args) = @_;
162 17         204 push @$self, join('', @args);
163             }
164              
165             sub READLINE
166             {
167 70     70   126 my ($self) = @_;
168 70 100       294 return scalar(@$self) ? join('', @$self) : undef;
169             }
170              
171              
172              
173             package Pinwheel::DocTest::Mock;
174              
175 3     3   4726 use Carp;
  3         7  
  3         294  
176 3     3   18 use overload '&{}' => \&getfn;
  3         6  
  3         45  
177              
178             our $AUTOLOAD;
179              
180              
181             sub new
182             {
183 4     4   782 my ($class, $name) = @_;
184 4         339 return bless({name => $name, results => {}}, $class);
185             }
186              
187             sub getfn
188             {
189 5     5   32 my ($self) = @_;
190              
191             return sub {
192 13     13   20 my $result;
193 13         97 print "Called $self->{name} with " . Pinwheel::DocTest::p(\@_);
194 13         2972 $result = $self->{results}{''};
195 13 100       65 return $result->(@_) if (ref($result) eq 'CODE');
196 3         19 return $result;
197 5         37 };
198             }
199              
200             sub AUTOLOAD
201             {
202 9     9   402 my $self = shift;
203 9         22 my ($name, $result);
204              
205 9         19 $name = $AUTOLOAD;
206 9         60 $name =~ s/.*:://;
207 9 50       53 return unless ($name =~ /[a-z]/);
208 9 100 100     73 if ($name =~ /(.+)_returns$/ || $name =~ /^returns$/) {
209 5   100     85 $self->{results}{$1 || ''} = shift;
210 5         31 return;
211             }
212              
213 4         68 print "Called $self->{name}\->$name with " . Pinwheel::DocTest::p(\@_);
214 4         38 $result = $self->{results}{$name};
215 4 100       20 return $result->(@_) if (ref($result) eq 'CODE');
216 3         23 return $result;
217             }
218              
219              
220             1;