File Coverage

blib/lib/Dist/Zilla/Plugin/Test/Compile/PerFile.pm
Criterion Covered Total %
statement 123 128 96.0
branch 22 32 68.7
condition 3 6 50.0
subroutine 31 31 100.0
pod 1 2 50.0
total 180 199 90.4


line stmt bran cond sub pod time code
1 9     9   16751001 use 5.006; # our
  9         23  
2 9     9   35 use strict;
  9         9  
  9         189  
3 9     9   42 use warnings;
  9         9  
  9         600  
4              
5             package Dist::Zilla::Plugin::Test::Compile::PerFile;
6              
7             our $VERSION = '0.004000';
8              
9             # ABSTRACT: Create a single .t for each compilable file in a distribution
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 9     9   34 use B ();
  9         12  
  9         481  
14              
15             BEGIN {
16             ## no critic (ProhibitCallsToUnexportedSubs)
17 9 50   9   219 *_HAVE_PERLSTRING = defined &B::perlstring ? sub() { 1 } : sub() { 0 };
18             }
19 9     9   576 use Moose qw( with around has );
  9         302743  
  9         71  
20 9     9   35657 use MooseX::LazyRequire;
  9         16366  
  9         71  
21              
22             with 'Dist::Zilla::Role::FileGatherer', 'Dist::Zilla::Role::TextTemplate';
23              
24 9     9   26080 use Path::Tiny qw(path);
  9         7539  
  9         526  
25 9     9   532 use File::ShareDir qw(dist_dir);
  9         4675  
  9         600  
26 9     9   42 use Moose::Util::TypeConstraints qw(enum);
  9         12  
  9         74  
27              
28             ## no critic (ProhibitPackageVars)
29             our %path_translators;
30              
31             $path_translators{base64_filter} = sub {
32             my ($file) = @_;
33             $file =~ s/[^-[:alnum:]_]+/_/msxg;
34             return $file;
35             };
36              
37             $path_translators{mimic_source} = sub {
38             my ($file) = @_;
39             return $file;
40             };
41              
42             ##
43             #
44             # This really example code, because this notation is so unrecommended, as Colons in file names
45             # are highly non-portable.
46             #
47             # Edit this to = 1 if you're 100% serious you want this.
48             #
49             ##
50              
51             if (0) {
52             $path_translators{module_names} = sub {
53             my ($file) = @_;
54             return $file if $file !~ /\Alib\//msx;
55             return $file if $file !~ /[.]pm\z/msx;
56             $file =~ s{\Alib/}{}msx;
57             $file =~ s{[.]pm\z}{}msx;
58             $file =~ s{/}{::}msxg;
59             $file = 'module/' . $file;
60             return $file;
61             };
62             }
63              
64             our %templates = ();
65              
66             {
67             my $dist_dir = dist_dir('Dist-Zilla-Plugin-Test-Compile-PerFile');
68             my $template_dir = path($dist_dir);
69             for my $file ( $template_dir->children ) {
70             next if $file =~ /\A[.]/msx; # Skip hidden files
71             next if -d $file; # Skip directories
72             $templates{ $file->basename } = $file;
73             }
74             }
75              
76             around mvp_multivalue_args => sub {
77             my ( $orig, $self, @args ) = @_;
78             return ( 'finder', 'file', 'skip', $self->$orig(@args) );
79             };
80              
81             around mvp_aliases => sub {
82             my ( $orig, $self, @args ) = @_;
83             my $hash = $self->$orig(@args);
84             $hash = {} if not defined $hash;
85             $hash->{files} = 'file';
86             return $hash;
87             };
88              
89             around dump_config => sub {
90             my ( $orig, $self, @args ) = @_;
91             my $config = $self->$orig(@args);
92             my $localconf = $config->{ +__PACKAGE__ } = {};
93              
94             $localconf->{finder} = $self->finder if $self->has_finder;
95             $localconf->{xt_mode} = $self->xt_mode;
96             $localconf->{prefix} = $self->prefix;
97             $localconf->{file} = [ sort @{ $self->file } ];
98             $localconf->{skip} = $self->skip;
99             $localconf->{path_translator} = $self->path_translator;
100             $localconf->{test_template} = $self->test_template;
101              
102             $localconf->{ q[$] . __PACKAGE__ . '::VERSION' } = $VERSION
103             unless __PACKAGE__ eq ref $self;
104              
105             return $config;
106             };
107              
108              
109              
110              
111              
112              
113              
114              
115              
116             sub BUILD {
117 8     8 0 14 my ($self) = @_;
118 8 100       318 return if $self->has_file;
119 7 100       250 return if $self->has_finder;
120 6         197 $self->_finder_objects;
121 6         185 return;
122             }
123              
124              
125              
126              
127              
128              
129              
130              
131              
132              
133              
134              
135              
136             has xt_mode => ( is => ro =>, isa => Bool =>, lazy_build => 1 );
137              
138              
139              
140              
141              
142              
143              
144              
145              
146              
147              
148              
149              
150             has prefix => ( is => ro =>, isa => Str =>, lazy_build => 1 );
151              
152              
153              
154              
155              
156              
157              
158              
159              
160              
161              
162              
163              
164              
165              
166              
167              
168              
169             has file => ( is => ro =>, isa => 'ArrayRef[Str]', lazy_build => 1, );
170              
171              
172              
173              
174              
175              
176              
177              
178              
179              
180              
181             has skip => ( is => ro =>, isa => 'ArrayRef[Str]', lazy_build => 1, );
182              
183              
184              
185              
186              
187              
188              
189              
190              
191              
192              
193              
194              
195              
196             has finder => ( is => ro =>, isa => 'ArrayRef[Str]', lazy_required => 1, predicate => 'has_finder' );
197              
198              
199              
200              
201              
202              
203              
204              
205              
206              
207              
208              
209              
210              
211              
212              
213              
214              
215              
216              
217              
218              
219              
220              
221              
222              
223              
224              
225              
226              
227              
228              
229              
230              
231              
232              
233              
234              
235              
236              
237              
238              
239              
240              
241              
242              
243              
244              
245              
246              
247              
248              
249              
250              
251              
252              
253              
254              
255              
256              
257              
258              
259              
260              
261              
262              
263              
264             has path_translator => ( is => ro =>, isa => enum( [ sort keys %path_translators ] ), lazy_build => 1 );
265              
266              
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278              
279              
280              
281              
282              
283              
284              
285              
286              
287              
288              
289              
290              
291              
292              
293              
294              
295              
296              
297             has test_template => ( is => ro =>, isa => enum( [ sort keys %templates ] ), lazy_build => 1 );
298              
299             sub _quoted {
300 9     9   7765 no warnings 'numeric';
  9         13  
  9         3799  
301             ## no critic (ProhibitBitwiseOperators,ProhibitCallsToUndeclaredSubs)
302             ## no critic (ProhibitCallsToUnexportedSubs,ProhibitUnusedVarsStricter)
303 8 50 33 8   9098 !defined $_[0]
    50          
304             ? 'undef()'
305             : ( length( ( my $dummy = q[] ) & $_[0] ) && 0 + $_[0] eq $_[0] && $_[0] * 0 == 0 ) ? $_[0] # numeric detection
306             : _HAVE_PERLSTRING ? B::perlstring( $_[0] )
307             : qq["\Q$_[0]\E"];
308             }
309              
310             sub _generate_file {
311 8     8   17 my ( $self, $name, $file ) = @_;
312 8 50       51 my $relpath = ( $file =~ /\Alib\/(.*)\z/msx ? $1 : q[./] . $file );
313              
314 8         46 $self->log_debug("relpath for $file is: $relpath");
315              
316             my $code = sub {
317 8 50   8   323517 return $self->fill_in_string(
318             $self->_test_template_content,
319             {
320             file => $file,
321             relpath => $relpath,
322             plugin_module => $self->meta->name,
323             plugin_name => $self->plugin_name,
324             plugin_version => ( $self->VERSION ? $self->VERSION : '<self>' ),
325             test_more_version => '0.89',
326             quoted => \&_quoted,
327             },
328             );
329 8         1693 };
330 8         218 return Dist::Zilla::File::FromCode->new(
331             name => $name,
332             code_return_type => 'text',
333             code => $code,
334             );
335             }
336              
337              
338              
339              
340              
341              
342              
343              
344              
345              
346              
347             sub gather_files {
348 8     8 1 647938 my ($self) = @_;
349 8         4473 require Dist::Zilla::File::FromCode;
350              
351 8         389554 my $prefix = $self->prefix;
352 8         39 $prefix =~ s{/?\z}{/}msx;
353              
354 8         314 my $translator = $self->_path_translator;
355              
356 8 50       13 if ( not @{ $self->file } ) {
  8         256  
357 0         0 $self->log_debug('Did not find any files to add tests for, did you add any files yet?');
358 0         0 return;
359             }
360 8         15 my $skiplist = {};
361 8         13 for my $skip ( @{ $self->skip } ) {
  8         291  
362 1         3 $skiplist->{$skip} = 1;
363             }
364 8         12 for my $file ( @{ $self->file } ) {
  8         263  
365 9 100       680 if ( exists $skiplist->{$file} ) {
366 1         7 $self->log_debug("Skipping compile test generation for $file");
367 1         208 next;
368             }
369 8         35 my $name = sprintf q[%s%s.t], $prefix, $translator->($file);
370 8         62 $self->log_debug("Adding $name for $file");
371 8         2089 $self->add_file( $self->_generate_file( $name, $file ) );
372             }
373 8         4480 return;
374             }
375              
376             has _path_translator => ( is => ro =>, isa => CodeRef =>, lazy_build => 1, init_arg => undef );
377             has _test_template => ( is => ro =>, isa => Defined =>, lazy_build => 1, init_arg => undef );
378             has _test_template_content => ( is => ro =>, isa => Defined =>, lazy_build => 1, init_arg => undef );
379             has _finder_objects => ( is => ro =>, isa => 'ArrayRef', lazy_build => 1, init_arg => undef );
380              
381             __PACKAGE__->meta->make_immutable;
382 9     9   44 no Moose;
  9         10  
  9         41  
383 9     9   887 no Moose::Util::TypeConstraints;
  9         11  
  9         34  
384              
385             sub _build_xt_mode {
386 7     7   213 return;
387             }
388              
389             sub _build_prefix {
390 8     8   13 my ($self) = @_;
391 8 100       278 if ( $self->xt_mode ) {
392 1         46 return 'xt/author/00-compile';
393             }
394 7         216 return 't/00-compile';
395             }
396              
397             sub _build_path_translator {
398 7     7   15 my ( undef, ) = @_;
399 7         226 return 'base64_filter';
400             }
401              
402             sub _build__path_translator {
403 8     8   18 my ($self) = @_;
404 8         277 my $translator = $self->path_translator;
405 8         266 return $path_translators{$translator};
406             }
407              
408             sub _build_test_template {
409 8     8   281 return '01-basic.t.tpl';
410             }
411              
412             sub _build__test_template {
413 8     8   16 my ($self) = @_;
414 8         301 my $template = $self->test_template;
415 8         290 return $templates{$template};
416             }
417              
418             sub _build__test_template_content {
419 8     8   17 my ($self) = @_;
420 8         319 my $template = $self->_test_template;
421 8         40 return $template->slurp_utf8;
422             }
423              
424             sub _build_file {
425 7     7   11 my ($self) = @_;
426 7         14 return [ map { $_->name } @{ $self->_found_files } ];
  8         52  
  7         26  
427             }
428              
429             sub _build_skip {
430 7     7   223 return [];
431             }
432              
433             sub _build__finder_objects {
434 7     7   12 my ($self) = @_;
435 7 100       252 if ( $self->has_finder ) {
436 1         1 my @out;
437 1         2 for my $finder ( @{ $self->finder } ) {
  1         33  
438 1         27 my $plugin = $self->zilla->plugin_named($finder);
439 1 50       158 if ( not $plugin ) {
440 0         0 $self->log_fatal("no plugin named $finder found");
441             }
442 1 50       7 if ( not $plugin->does('Dist::Zilla::Role::FileFinder') ) {
443 0         0 $self->log_fatal("plugin $finder is not a FileFinder");
444             }
445 1         50 push @out, $plugin;
446             }
447 1         35 return \@out;
448             }
449 6         22 return [ $self->_vivify_installmodules_pm_finder ];
450             }
451              
452             sub _vivify_installmodules_pm_finder {
453 6     6   12 my ($self) = @_;
454 6         151 my $name = $self->plugin_name;
455 6         42 $name .= '/AUTOVIV/:InstallModulesPM';
456 6 50       149 if ( my $plugin = $self->zilla->plugin_named($name) ) {
457 0         0 return $plugin;
458             }
459 6         4279 require Dist::Zilla::Plugin::FinderCode;
460             my $plugin = Dist::Zilla::Plugin::FinderCode->new(
461             {
462             plugin_name => $name,
463             zilla => $self->zilla,
464             style => 'grep',
465             code => sub {
466 13     13   864 my ( $file, $self ) = @_;
467 13         49 local $_ = $file->name;
468             ## no critic (RegularExpressions)
469 13 100 66     552 return 1 if m{\Alib/} and m{\.(pm)$};
470 6 50       132 return 1 if $_ eq $self->zilla->main_module;
471 6         8662 return;
472             },
473             },
474 6         201361 );
475 6         440 push @{ $self->zilla->plugins }, $plugin;
  6         152  
476 6         352 return $plugin;
477             }
478              
479             sub _found_files {
480 7     7   10 my ($self) = @_;
481 7         11 my %by_name;
482 7         10 for my $plugin ( @{ $self->_finder_objects } ) {
  7         243  
483 7         13 for my $file ( @{ $plugin->find_files } ) {
  7         37  
484 8         303 $by_name{ $file->name } = $file;
485             }
486             }
487 7         235 return [ values %by_name ];
488             }
489              
490              
491             1;
492              
493             __END__
494              
495             =pod
496              
497             =encoding UTF-8
498              
499             =head1 NAME
500              
501             Dist::Zilla::Plugin::Test::Compile::PerFile - Create a single .t for each compilable file in a distribution
502              
503             =head1 VERSION
504              
505             version 0.004000
506              
507             =head1 SYNOPSIS
508              
509             ; in dist.ini
510             [Test::Compile::PerFile]
511              
512             =head1 DESCRIPTION
513              
514             This module is inspired by its earlier sibling L<< C<[Test::Compile]>|Dist::Zilla::Plugin::Test::Compile >>.
515              
516             Test::Compile is awesome, however, in the process of its development, we discovered it might be useful
517             to run compilation tests in parallel.
518              
519             This lead to the realization that implementing said functions are kinda messy.
520              
521             However, a further realization is, that parallelism should not be codified in the test itself, because platform parallelism is
522             rather not very portable, so parallelism should only be enabled when asked for.
523              
524             And this lead to the realization that C<prove> and C<Test::Harness> B<ALREADY> implement parallelism, and B<ALREADY> provide a
525             safe way for platforms to indicate parallelism is wanted.
526              
527             Which means implementing another layer of parallelism is unwanted and unproductive effort ( which may be also filled with messy
528             parallelism-induced bugs )
529              
530             So, here is the Test::Compile model based on how development is currently proceeding.
531              
532             prove
533             \ ----- 00_compile.t
534             | \ ----- Compile Module 1
535             | \ ----- Compile Module 2
536             |
537             \ ----- 01_basic.t
538              
539             That may be fine for some people, but this approach has several fundamental limits:
540              
541             =over 4
542              
543             =item 1. Sub-Tasks of compile don't get load balanced by the master harness.
544              
545             =item 2. Parallelism is developer side, not deployment side governed.
546              
547             =item 3. This approach means C<prove -s> will have no impact.
548              
549             =item 4. This approach means C<prove -j> will have no impact.
550              
551             =item 5. This approach inhibits other features of C<prove> such as the C<--state=slow>
552              
553             =back
554              
555             So this variation aims to employ one test file per module, to leverage C<prove> power.
556              
557             One initial concern cropped up on the notion of having excessive numbers of C<perl> instances, e.g:
558              
559             prove
560             \ ----- 00_compile/01_Module_1.t
561             | \ ----- Compile Module 1
562             |
563             \ ----- 00_compile/02_Module_2.t
564             | \ ----- Compile Module 2
565             |
566             \ ----- 01_basic.t
567              
568             If we were to implement it this way, we'd have the fun overhead of having to spawn B<2> C<perl> instances
569             per module tested, which on C<Win32>, would roughly double the test time and give nothing in return.
570              
571             However, B<Most> of the reason for having a C<perl> process per compile, was to separate the modules from each other
572             to assure they could be loaded independently.
573              
574             So because we already have a basically empty compile-state per test, we can reduce the number of C<perl> processes to as many
575             modules as we have.
576              
577             prove
578             \ ----- 00_compile/01_Module_1.t
579             |
580             \ ----- 00_compile/02_Module_2.t
581             |
582             \ ----- 01_basic.t
583              
584             Granted, there is still some bleed here, because doing it like this means you have some modules preloaded prior to compiling the
585             module in question, namely, that C<Test::*> will be in scope.
586              
587             However, "testing these modules compile without C<Test::> loaded" is not the real purpose of the compile tests,
588             the compile tests are to make sure the modules load.
589              
590             So this is an acceptable caveat for this module, and if you wish to be distinct from C<Test::*>, then you're encouraged to use the
591             much more proven C<[Test::Compile]>.
592              
593             Though we may eventually provide an option to spawn additional C<perl> processes to more closely mimic C<Test::*>'s behaviour,
594             the cost of doing so should not be understated, and as this module exist to attempt to improve efficiency of tests, not to
595             decrease them, that would be an approach counter-productive to this modules purpose.
596              
597             =head1 METHODS
598              
599             =head2 C<gather_files>
600              
601             This plugin operates B<ONLY> during C<gather_files>, unlike other plugins which have multiple phase involvement, this only
602             happens at this phase.
603              
604             The intrinsic dependence of this plugin on other files in your dist, means that in order for it to generate a test for any given
605             file, the test itself must be included B<after> that file is gathered.
606              
607             =head1 ATTRIBUTES
608              
609             =head2 C<xt_mode>
610              
611             I<optional> B<< C<Bool> >>
612              
613             xt_mode = 1
614              
615             If set, C<prefix> defaults to C<xt/author/00-compile>
616              
617             I<Default> is B<NOT SET>
618              
619             =head2 C<prefix>
620              
621             I<optional> B<< C<Str> >>
622              
623             prefix = t/99-compilerthingys
624              
625             If set, sets the prefix path for generated tests to go in.
626              
627             I<Defaults> to C<t/00-compile>
628              
629             =head2 C<file>
630              
631             I<optional> B<< C<multivalue_arg> >> B<< C<ArrayRef[Str]> >>
632              
633             B<< C<mvp_aliases> >>: C<files>
634              
635             file = lib/Foo.pm
636             file = lib/Bar.pm
637             files = lib/Quux.pm
638             file = script/whatever.pl
639              
640             Specifies the list of source files to generate compile tests for.
641              
642             I<If not specified>, defaults are populated from the file finder C<finder>
643              
644             =head2 C<skip>
645              
646             I<optional> B<< C<multivalue_arg> >> B<< C<ArrayRef[Str]> >>
647              
648             skip = lib/Foo.pm
649              
650             Specifies the list of source files to skip compile tests for.
651              
652             =head2 C<finder>
653              
654             I<optional> B<< C<multivalue_arg> >> B<< C<ArrayRef[Str]> >>
655              
656             finder = :InstallModules
657              
658             Specifies a L<< C<FileFinder>|Dist::Zilla::Role::FileFinder >> plugin name
659             to query for a list of files to build compile tests for.
660              
661             I<If not specified>, a custom one is autovivified, and matches only C<*.pm> in C<lib/>
662              
663             =head2 C<path_translator>
664              
665             I<optional> B<< C<Str> >>
666              
667             A Name of a routine to translate source paths ( i.e: Paths to modules/scripts that are to be compiled )
668             into test file names.
669              
670             I<Default> is C<base64_filter>
671              
672             Supported Values:
673              
674             =over 4
675              
676             =item * C<base64_filter>
677              
678             Paths are L<< mangled so that they contain only base64 web-safe elements|http://tools.ietf.org/html/rfc3548#section-4 >>
679              
680             That is to say, if you were building tests for a distribution with this layout:
681              
682             lib/Foo/Bar.pm
683             lib/Foo.pm
684             lib/Foo_Quux.pm
685              
686             That the generated test files will be in the C<prefix> directory named:
687              
688             lib_Foo_Bar_pm.t
689             lib_Foo_pm.t
690             lib_Foo_Quux.t
691              
692             This is the default, but not necessarily the most sane if you have unusual file naming.
693              
694             lib/Foo/Bar.pm
695             lib/Foo_Bar.pm
696              
697             This configuration will not work with this translator.
698              
699             =item * C<mimic_source>
700              
701             This is mostly a 1:1 mapping, it doesn't translate source names in any way, other than prefixing and suffixing,
702             which is standard regardless of translation chosen.
703              
704             lib/Foo/Bar.pm
705             lib/Foo.pm
706             lib/Foo_Quux.pm
707              
708             Will emit a prefix directory populated as such
709              
710             lib/Foo/Bar.pm.t
711             lib/Foo.pm.t
712             lib/Foo_Quux.pm.t
713              
714             Indeed, if you had a death wish, you could set C<prefix = lib> and your final layout would be:
715              
716             lib/Foo/Bar.pm
717             lib/Foo/Bar.pm.t
718             lib/Foo.pm
719             lib/Foo.pm.t
720             lib/Foo_Quux.pm
721             lib/Foo_Quux.pm.t
722              
723             Though this is not advised, and is only given for an example.
724              
725             =back
726              
727             =head2 C<test_template>
728              
729             Contains the string of the template file you wish to use as a reference point.
730              
731             Unlike most plugins, which use L<< C<Data::Section>|Data::Section >> to provide their templates,
732             this plugin uses a L<< C<File::ShareDir> C<dist_dir>|File::ShareDir >> to distribute templates.
733              
734             This means there will always be a predetermined list of templates shipped by this plugin,
735             however, if you wish to modify these templates and store them with a non-colliding name, for your personal convenience,
736             you are entirely free to so.
737              
738             As such, this field takes as its parameter, the name of any file that happened to be in the C<dist_dir> at compile time.
739              
740             Provided Templates:
741              
742             =over 4
743              
744             =item * C<01-basic.t.tpl>
745              
746             A very basic standard template, which C<use>'s C<Test::More>, does a C<requires_ok($file)> for the requested file, and nothing
747             else.
748              
749             =item * C<02-raw-require.t.tpl>
750              
751             A minimalist spartan C<require_ok> implementation, but without using C<Test::More>. Subsequently faster under Test2 and can expose
752             more issues where modules have implicit C<use>
753              
754             =back
755              
756             =for Pod::Coverage BUILD
757              
758             =head1 Other Important Differences to Test::Compile
759              
760             =head2 Finders useful, but not required
761              
762             C<[Test::Compile::PerFile]> supports providing an arbitrary list of files to generate compile tests
763              
764             [Test::Compile::PerFile]
765             file = lib/Foo.pm
766             file = lib/Quux.pm
767              
768             Using this will supersede using finders to find things.
769              
770             =head2 Single finder only, not multiple
771              
772             C<[Test::Compile]> supports 2 finder keys, C<module_finder> and C<script_finder>.
773              
774             This module only supports one key, C<finder>, and it is expected
775             that if you want to test 2 different sets of files, you'll create a separate instance for that:
776              
777             -[Test::Compile]
778             -module_finder = Foo
779             -script_finder = bar
780             +[Test::Compile::PerFile / module compile tests]
781             +finder = Foo
782             +[Test::Compile::PerFile / script compile tests]
783             +finder = bar
784              
785             This is harder to do with C<[Test::Compile]>, because you'd have to declare a separate file name for it to work,
786             where-as C<[Test::Compile::PerFile]> generates a unique file name for each source it tests.
787              
788             Collisions are still possible, but harder to hit by accident.
789              
790             =head2 File Oriented, not Module Oriented
791              
792             Under the hood, C<Test::Compile> is really file oriented too, it just doesn't give that impression on the box.
793              
794             It just seemed fundamentally less complex to deal only in file paths for this module, as it gives
795             no illusions as to what it can, and cannot do.
796              
797             ( For example, by being clearly file oriented, there's no ambiguity of how it will behave when a file name and a module name are
798             miss-matching in some way, by simply not caring about the latter , it will also never attempt to probe and load modules that can't
799             be automatically resolved to files )
800              
801             =head1 Performance
802              
803             A rough comparison on the C<dzil> git tree, with C<HARNESS_OPTIONS=j4:c> where C<4> is the number of logical C<CPUs> I have:
804              
805             Test::Compile - Files= 42, Tests=577, 57 wallclock secs ( 0.32 usr 0.11 sys + 109.29 cusr 11.13 csys = 120.85 CPU)
806             Test::Compile::PerFile - Files=176, Tests=576, 44 wallclock secs ( 0.83 usr 0.39 sys + 127.34 cusr 13.27 csys = 141.83 CPU)
807              
808             So a 20% saving for a 300% growth in file count, a 500k growth in unpacked tar size, and a 4k growth in C<tar.gz> size.
809              
810             Hmm, that's a pretty serious trade off. Might not really be worth the savings.
811              
812             Though, comparing compile tests alone:
813              
814             # Test::Compile
815             prove -j4lr --timer t/00-compile.t
816             Files=1, Tests=135, 41 wallclock secs ( 0.07 usr 0.01 sys + 36.82 cusr 3.58 csys = 40.48 CPU)
817              
818             # Test::Compile::PerFile
819             prove -j4lr --timer t/00-compile/
820             Files=135, Tests=135, 22 wallclock secs ( 0.58 usr 0.32 sys + 64.45 cusr 6.74 csys = 72.09 CPU)
821              
822             That's not bad, considering that although I have 4 logical C<CPUs>, that's really just 2 physical C<CPUs> with hyper-threading ;)
823              
824             =head1 AUTHOR
825              
826             Kent Fredric <kentnl@cpan.org>
827              
828             =head1 COPYRIGHT AND LICENSE
829              
830             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
831              
832             This is free software; you can redistribute it and/or modify it under
833             the same terms as the Perl 5 programming language system itself.
834              
835             =cut