File Coverage

blib/lib/Test/Inline/Script.pm
Criterion Covered Total %
statement 128 134 95.5
branch 39 54 72.2
condition 10 12 83.3
subroutine 27 28 96.4
pod 14 14 100.0
total 218 242 90.0


line stmt bran cond sub pod time code
1             package Test::Inline::Script;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Test::Inline::Script - Generate the test file for a single source file
8              
9             =head1 DESCRIPTION
10              
11             This class is where the heavy lifting happens to actually generating a
12             test file takes place. Given a source filename, this modules will load
13             it, parse out the relavent bits, put them into order based on the tags,
14             and then merge them into a test file.
15              
16             =head1 METHODS
17              
18             =cut
19              
20 12     12   64 use strict;
  12         24  
  12         406  
21 12     12   62 use List::Util ();
  12         22  
  12         405  
22 12     12   59 use Params::Util qw{_ARRAY _INSTANCE};
  12         22  
  12         693  
23 12     12   65 use Algorithm::Dependency::Item ();
  12         22  
  12         158  
24 12     12   63 use Algorithm::Dependency::Source ();
  12         26  
  12         158  
25 12     12   10609 use Algorithm::Dependency::Ordered ();
  12         5959  
  12         563  
26              
27             use overload 'bool' => sub () { 1 },
28 12     12   79 '""' => 'filename';
  12         30  
  12         125  
29              
30 12     12   987 use vars qw{$VERSION @ISA};
  12         23  
  12         754  
31             BEGIN {
32 12     12   27 $VERSION = '2.213';
33 12         683 @ISA = qw{
34             Algorithm::Dependency::Source
35             Algorithm::Dependency::Item
36             };
37             }
38              
39             # Special case, for when doing unit tests ONLY.
40             # Don't throw the missing files warning.
41 12     12   64 use vars qw{$NO_MISSING_DEPENDENCIES_WARNING};
  12         22  
  12         455  
42             BEGIN {
43 12     12   17292 $NO_MISSING_DEPENDENCIES_WARNING = '';
44             }
45              
46              
47              
48              
49              
50             #####################################################################
51             # Constructor and Accessors
52              
53             =pod
54              
55             =head2 new
56              
57             my $File = Test::Inline::Script->new( $class, \@sections, $check_count );
58              
59             The C constructor takes a class name, set of Section objects and
60             an optional C flag.
61              
62             Returns a Test::Inline::Script object on success.
63             Returns C on error.
64              
65             =cut
66              
67             sub new {
68 20     20 1 36 my $class = shift;
69 20 50       87 my $_class = defined $_[0] ? shift : return undef;
70 20 50       95 my $Sections = _ARRAY(shift) or return undef;
71 20   100     62 my $check_count = shift || 0;
72              
73             # Create the object
74 49         129 my $self = bless {
75             class => $_class,
76 49         121 setup => [ grep { $_->setup } @$Sections ],
77 20         55 sections => [ grep { ! $_->setup } @$Sections ],
78             filename => lc "$_class.t",
79             check_count => $check_count,
80             # tests => undef,
81             }, $class;
82 20         1216 $self->{filename} =~ s/::/_/g;
83              
84             # Verify the uniqueness of the names
85 20 50       71 $self->_duplicate_names and return undef;
86              
87             # Warn if we have missing dependencies
88 20         169 my $missing = $self->missing_dependencies;
89 20 100       231 if ( $missing ) {
90 1         3 foreach ( @$missing ) {
91 1 50       4 next if $NO_MISSING_DEPENDENCIES_WARNING;
92 0         0 print "Warning: Missing dependency '$_' in $self->{class}\n";
93             }
94             }
95              
96             # Quickly predetermine if there will be an unknown number
97             # of unit tests in the file
98 20         44 my $unknown = grep { ! defined $_->tests } @$Sections;
  49         128  
99 20 50 66     87 unless ( $unknown or grep { $_->tests } @$Sections ) {
  27         65  
100 0         0 $unknown = 1;
101             }
102              
103             # Flag all sections that need count checking in advance
104 20 100       56 if ( $check_count ) {
105 19         41 foreach my $Section ( @$Sections ) {
106 44 100       122 next unless defined $Section->tests;
107 34 100 100     163 next unless $unknown or $check_count > 1;
108              
109             # Each count check is itself a test, so
110             # increment the number of tests for the section
111             # when we enable the check flag.
112 12         20 $Section->{check_count} = 1;
113 12         21 $Section->{tests}++;
114             }
115             }
116              
117 20         144 $self;
118             }
119              
120             =pod
121              
122             =head2 class
123              
124             Returns the class that the test file will test
125              
126             =head2 filename
127              
128             my $filename = $File->filename;
129              
130             The C method returns the name of the output file that the tests
131             should be written to. For example, the class C would have the
132             filename value C.
133              
134             =head2 config
135              
136             my $config = $File->config;
137              
138             The C method returns the config object for the file, assuming that
139             it has one. If more than one are found, the first will be used, and any
140             additional config sections discarded.
141              
142             Returns a L object on success, or false if the
143             file does not contain a config section.
144              
145             =head2 setup
146              
147             my @setup = $File->setup;
148              
149             The C method returns the setup sections from the file, in the same
150             order as in the file.
151              
152             Returns a list of setup L objects, the null
153             array C<()> if the file does not contain any setup objects.
154              
155             =head2 sections
156              
157             my @sections = $File->sections;
158              
159             The C method returns all normal sections from the file, in the
160             same order as in the file. This may not be the order they will be written
161             to the test file, for that you should see the C method.
162              
163             Returns a list of L objects, or the null array
164             C<()> if the file does not contain any non-setup sections.
165              
166             =cut
167              
168 2     2 1 488 sub class { $_[0]->{class} }
169 42     42 1 2388 sub filename { $_[0]->{filename} }
170 0 0   0 1 0 sub config { $_[0]->{config} || '' }
171 65     65 1 73 sub setup { @{$_[0]->{setup}} }
  65         284  
172 243     243 1 293 sub sections { @{$_[0]->{sections}} }
  243         809  
173              
174              
175              
176              
177              
178             #####################################################################
179             # Main Methods
180              
181             =pod
182              
183             =head2 sorted
184              
185             The C method returns all normal sections from the file, in an order
186             that satisfies any dependencies in the sections.
187              
188             Returns a reference to an array of L objects,
189             C<0> if the file does not contain any non-setup sections, or C on
190             error.
191              
192             =cut
193              
194             sub sorted {
195 18     18 1 27 my $self = shift;
196 18 50       64 return $self->{sorted} if $self->{sorted};
197              
198             # Handle the simple case there there are no dependencies,
199             # so we don't have to load the dependency algorithm code.
200 18 100       47 unless ( map { $_->depends } $self->sections ) {
  37         118  
201 12         30 return $self->{sorted} = [ $self->setup, $self->sections ];
202             }
203              
204             # Create the dependency algorithm object
205 6 50       80 my $Algorithm = Algorithm::Dependency::Ordered->new(
206             source => $self,
207             ignore_orphans => 1, # Be lenient to non-existant dependencies
208             ) or return undef;
209              
210             # Pull the schedule from the algorithm. If we get an error back, it
211             # should be because there is a circular dependency.
212 6         233 my $schedule = $Algorithm->schedule_all;
213 6 50       102 unless ( $schedule ) {
214 0         0 warn " (Failed to build $self->{class} test schedule) ";
215 0         0 return undef;
216             }
217              
218             # Index the sections by name
219 6         21 my %hash = map { $_->name => $_ } grep { $_->name } $self->sections;
  18         41  
  24         52  
220              
221             # Merge together the setup, schedule, and anonymous parts into a
222             # single sorted list.
223 18         40 my @sorted = (
224             $self->setup,
225 24         157 ( map { $hash{$_} } @$schedule ),
226 6         27 ( grep { $_->anonymous } $self->sections )
227             );
228              
229 6         77 $self->{sorted} = \@sorted;
230             }
231              
232             =pod
233              
234             =head2 tests
235              
236             If the number of tests for all of the sections within the file are known,
237             then the number of tests for the entire file can also be determined.
238              
239             The C method determines if the number of tests can be known, and
240             if so, calculates and returns the number of tests. Returns false if the
241             number of tests is not known.
242              
243             =cut
244              
245             sub tests {
246 22     22 1 35 my $self = shift;
247 22 100       76 return $self->{tests} if exists $self->{tests};
248              
249             # Add up the tests
250 19         27 my $total = 0;
251 19         46 foreach my $Section ( $self->setup, $self->sections ) {
252             # Return undef if section has an unknown number of tests
253 37 100       101 return undef unless defined $Section->tests;
254 28         72 $total += $Section->tests;
255             }
256              
257             # If the total is zero, it's probably screwed, go with "unknown"
258 10   50     61 $self->{tests} = $total || undef;
259             }
260              
261             =pod
262              
263             =head2 merged_content
264              
265             The C method generates and returns the merged contents of all
266             the sections in the file, including the setup sections at the beginning. The
267             method does not return the entire file, merely the part contained in the
268             sections. For the full file contents, see the C method.
269              
270             Returns a string containing the merged section content on success, false
271             if there is no content, despite the existance of sections ( which would
272             have been empty ), or C on error.
273              
274             =cut
275              
276             sub merged_content {
277 22     22 1 37 my $self = shift;
278 22 100       92 return $self->{content} if exists $self->{content};
279              
280             # Get the sorted Test::Inline::Section objects
281 18 50       58 my $sorted = $self->sorted or return undef;
282              
283             # Prepare
284 18         116 $self->{_example_count} = 0;
285              
286             # Strip out empty sections
287 18         34 @$sorted = grep { $_->content =~ /\S/ } @$sorted;
  43         116  
288              
289             # Generate wrapped code chunks
290 18         38 my @content = map { $self->_wrap_content($_) } @$sorted;
  43         102  
291 18 50       54 return '' unless @content;
292              
293             # Merge to create the core testing code
294 18         87 $self->{content} = join "\n\n\n", @content;
295              
296             # Clean up and return
297 18         41 delete $self->{_example_count};
298 18         75 $self->{content};
299             }
300              
301             # Take a single generated section of code, and wrap it
302             # in the standard boilerplate.
303             sub _wrap_content {
304 43     43   48 my $self = shift;
305 43 50       285 my $Section = _INSTANCE(shift, 'Test::Inline::Section') or return undef;
306 43         119 my $code = $Section->content;
307              
308             # Wrap in compilation test code if an example
309 43 100       113 if ( $Section->example ) {
310 1         3 $self->{_example_count}++;
311 1         6 $code =~ s/^/ /mg;
312 1         4 $code = "eval q{\n"
313             . " my \$example = sub {\n"
314             . " local \$^W = 0;\n"
315             . $code
316             . " };\n"
317             . "};\n"
318             . "is(\$@, '', 'Example $self->{_example_count} compiles cleanly');\n";
319             }
320              
321             # Wrap in scope braces unless it is a setup section
322 43 100       110 unless ( $Section->setup ) {
323 37         83 $code = "{\n"
324             . $code
325             . "}\n";
326             }
327              
328             # Add the count-checking code if needed
329 43 100       106 if ( $Section->{check_count} ) {
330 12         30 my $increase = $Section->tests - 1;
331 12 100       32 my $were = $increase == 1 ? 'test was' : 'tests were';
332 12   100     72 my $section =
333             $code = "\$::__tc = Test::Builder->new->current_test;\n"
334             . $code
335             . "is( Test::Builder->new->current_test - \$::__tc, "
336             . ($increase || '0')
337             . ",\n"
338             . "\t'$increase $were run in the section' );\n";
339             }
340              
341             # Add the section header
342 43         133 $code = "# $Section->{begin}\n"
343             . $code;
344              
345             # Aaaaaaaand we're done
346 43         135 $code;
347             }
348              
349              
350              
351              
352              
353             #####################################################################
354             # Implement the Algorithm::Dependency::Source Interface
355             # This is used for section-level dependency.
356             # These methods, though public, are undocumented.
357              
358             # Our implementation of Algorithm::Dependency::Source->load is a no-op
359 21     21 1 663 sub load { 1 }
360              
361             # Pull a single item by name, section in the sections for it
362             sub item {
363 107     107 1 1074 my $self = shift;
364 107 50       333 my $name = shift or return undef;
365 107     310   810 List::Util::first { $_->name eq $name } $self->sections;
  310         826  
366             }
367              
368             # Return, in their original order, all the items ( named sections )
369 27     27 1 200 sub items { grep { $_->name } $_[0]->sections }
  67         173  
370              
371              
372              
373              
374              
375             #####################################################################
376             # Implement the Algorithm::Dependency::Item Interface
377             # This is used for class-level dependency.
378             # These methods, though public, are undocumented.
379              
380             sub id {
381 14     14 1 90 $_[0]->{class};
382             }
383              
384             sub depends {
385 28     28 1 145 my $self = shift;
386 0         0 my %depends = map { $_ => 1 }
  46         132  
387 28         65 map { $_->classes }
388             ($self->setup, $self->sections);
389 28         163 keys %depends;
390             }
391              
392              
393              
394              
395              
396             #####################################################################
397             # Utility Functions
398              
399             sub _duplicate_names(@) {
400 20     20   33 my $self = shift;
401 20         43 my %seen = ();
402 20         59 foreach ( map { $_->name } $self->sections ) {
  42         134  
403 42 100       101 next unless $_;
404 33 50       114 return 1 if $seen{$_}++;
405             }
406 20         105 undef;
407             }
408              
409             1;
410              
411             =pod
412              
413             =head1 SUPPORT
414              
415             See the main L section.
416              
417             =head1 AUTHOR
418              
419             Adam Kennedy Eadamk@cpan.orgE, L
420              
421             =head1 COPYRIGHT
422              
423             Copyright 2004 - 2013 Adam Kennedy.
424              
425             This program is free software; you can redistribute
426             it and/or modify it under the same terms as Perl itself.
427              
428             The full text of the license can be found in the
429             LICENSE file included with this module.
430              
431             =cut