File Coverage

blib/lib/Perl/ToPerl6/TestUtils.pm
Criterion Covered Total %
statement 69 225 30.6
branch 0 94 0.0
condition 0 5 0.0
subroutine 22 37 59.4
pod 13 13 100.0
total 104 374 27.8


line stmt bran cond sub pod time code
1             package Perl::ToPerl6::TestUtils;
2              
3 11     11   141865 use 5.006001;
  11         34  
4 11     11   50 use strict;
  11         26  
  11         235  
5 11     11   46 use warnings;
  11         17  
  11         341  
6              
7 11     11   41 use English qw(-no_match_vars);
  11         328  
  11         66  
8 11     11   5409 use Readonly;
  11         5928  
  11         628  
9              
10 11     11   60 use Exporter 'import';
  11         15  
  11         269  
11              
12 11     11   45 use File::Path ();
  11         14  
  11         158  
13 11     11   42 use File::Spec ();
  11         16  
  11         127  
14 11     11   42 use File::Spec::Unix ();
  11         15  
  11         143  
15 11     11   8297 use File::Temp ();
  11         119949  
  11         349  
16 11     11   73 use File::Find qw( find );
  11         22  
  11         708  
17              
18 11     11   5377 use Perl::ToPerl6;
  11         43  
  11         785  
19 11     11   76 use Perl::ToPerl6::Config;
  11         20  
  11         370  
20 11     11   60 use Perl::ToPerl6::Exception::Fatal::Generic qw{ &throw_generic };
  11         20  
  11         1204  
21 11     11   66 use Perl::ToPerl6::Exception::Fatal::Internal qw{ &throw_internal };
  11         21  
  11         1149  
22 11     11   64 use Perl::ToPerl6::Utils qw{ :severities :data_conversion transformer_long_name };
  11         21  
  11         669  
23 11     11   2042 use Perl::ToPerl6::TransformerFactory (-test => 1);
  11         20  
  11         155  
24              
25             our $VERSION = '0.03';
26              
27             Readonly::Array our @EXPORT_OK => qw(
28             ptransform ptransform_with_transformations
29             transform transform_with_transformations
30             ftransform ftransform_with_transformations
31             subtests_in_tree
32             should_skip_author_tests
33             get_author_test_skip_message
34             starting_points_including_examples
35             bundled_transformer_names
36             names_of_transformers_willing_to_work
37             );
38              
39             #-----------------------------------------------------------------------------
40             # If the user already has an existing perlmogrifyrc file, it will get
41             # in the way of these test. This little tweak to ensures that we
42             # don't find the perlmogrifyrc file.
43              
44             sub block_perlmogrifyrc {
45 11     11   66 no warnings 'redefine';
  11         19  
  11         24263  
46 25     25 1 64 *Perl::ToPerl6::UserProfile::_find_profile_path = sub { return };
  11     11   596  
47 11         30 return 1;
48             }
49              
50             #-----------------------------------------------------------------------------
51             # ToPerl6 a code snippet using only one transformer. Returns the transformations.
52             #
53             # Also uses a secret escape hatch in $c->transform() so we can get at the
54             # raw PPI::Document object without breaking AUTOLOAD.
55              
56             sub ptransform_with_transformations {
57 0     0 1 0 my($transformer, $code_ref, $config_ref) = @_;
58 0         0 my $c = Perl::ToPerl6->new( -profile => 'NONE' );
59 0         0 $c->apply_transform(-transformer => $transformer, -config => $config_ref);
60 0         0 my $doc;
61 0         0 $code_ref = \join("\n",@{$code_ref});
  0         0  
62 0         0 my @rv = $c->transform($code_ref, doc => \$doc);
63 0         0 return ($doc, @rv);
64             }
65              
66             #-----------------------------------------------------------------------------
67             # Mogrifyize a code snippet using only one transformer. Returns the number
68             # of transformations
69              
70             sub ptransform { ##no mogrify(ArgUnpacking)
71 0     0 1 0 return scalar ptransform_with_transformations(@_);
72             }
73              
74             #-----------------------------------------------------------------------------
75             # Mogrifyize a code snippet using a specified config. Returns the transformations.
76              
77             sub transform_with_transformations {
78 0     0 1 0 my ($code_ref, $config_ref) = @_;
79 0         0 my $c = Perl::ToPerl6->new( %{$config_ref} );
  0         0  
80 0         0 return $c->transform($code_ref);
81             }
82              
83             #-----------------------------------------------------------------------------
84             # Mogrifyize a code snippet using a specified config. Returns the
85             # number of transformations
86              
87             sub transform { ##no mogrify(ArgUnpacking)
88 0     0 1 0 return scalar transform_with_transformations(@_);
89             }
90              
91             #-----------------------------------------------------------------------------
92             # Like ptransform_with_transformations, but forces a PPI::Document::File context.
93             # The $filename arg is a Unix-style relative path, like 'Foo/Bar.pm'
94              
95             Readonly::Scalar my $TEMP_FILE_PERMISSIONS => oct 700;
96              
97             sub ftransform_with_transformations {
98 0     0 1 0 my($transformer, $code_ref, $filename, $config_ref) = @_;
99 0         0 my $c = Perl::ToPerl6->new( -profile => 'NONE' );
100 0         0 $c->apply_transform(-transformer => $transformer, -config => $config_ref);
101              
102 0         0 my $dir = File::Temp::tempdir( 'PerlMogrify-tmpXXXXXX', TMPDIR => 1 );
103 0   0     0 $filename ||= 'Temp.pm';
104 0         0 my @fileparts = File::Spec::Unix->splitdir($filename);
105 0 0       0 if (@fileparts > 1) {
106 0         0 my $subdir = File::Spec->catdir($dir, @fileparts[0..$#fileparts-1]);
107 0         0 File::Path::mkpath($subdir, 0, $TEMP_FILE_PERMISSIONS);
108             }
109 0         0 my $file = File::Spec->catfile($dir, @fileparts);
110 0 0       0 if (open my $fh, '>', $file) {
111 0         0 print {$fh} ${$code_ref};
  0         0  
  0         0  
112 0 0       0 close $fh or throw_generic "unable to close $file: $OS_ERROR";
113             }
114              
115             # Use eval so we can clean up before throwing an exception in case of
116             # error.
117 0         0 my @v = eval {$c->transform($file)};
  0         0  
118 0         0 my $err = $EVAL_ERROR;
119 0         0 File::Path::rmtree($dir, 0, 1);
120 0 0       0 if ($err) {
121 0         0 throw_generic $err;
122             }
123 0         0 return @v;
124             }
125              
126             #-----------------------------------------------------------------------------
127             # Like ptransform, but forces a PPI::Document::File context. The
128             # $filename arg is a Unix-style relative path, like 'Foo/Bar.pm'
129              
130             sub ftransform { ##no mogrify(ArgUnpacking)
131 0     0 1 0 return scalar ftransform_with_transformations(@_);
132             }
133              
134             # Note: $include_extras is not documented in the POD because I'm not
135             # committing to the interface yet.
136             sub subtests_in_tree {
137 0     0 1 0 my ($start, $include_extras) = @_;
138              
139 0         0 my %subtests;
140              
141             find(
142             {
143             wanted => sub {
144 0 0   0   0 return if not -f;
145              
146 0         0 my ($fileroot) = m{(.+)[.]run\z}xms;
147              
148 0 0       0 return if not $fileroot;
149              
150 0         0 my @pathparts = File::Spec->splitdir($fileroot);
151 0 0       0 if (@pathparts < 2) {
152 0         0 throw_internal 'confusing transformer test filename ' . $_;
153             }
154              
155             # my $transformer = join q{::}, @pathparts[-2, -1];
156 0         0 my @remaining_path = @pathparts;
157              
158 0 0       0 shift @remaining_path if $remaining_path[0] eq 't';
159 0 0       0 shift @remaining_path if $remaining_path[0] eq 'Perl';
160 0 0       0 shift @remaining_path if $remaining_path[0] eq 'ToPerl6';
161 0         0 my $transformer = join q{::}, @remaining_path;
162              
163 0         0 my $globals = _globals_from_file( $_ );
164 0 0       0 if ( my $prerequisites = $globals->{prerequisites} ) {
165 0         0 foreach my $prerequisite ( keys %{$prerequisites} ) {
  0         0  
166 0 0       0 eval "require $prerequisite; 1" or return;
167             }
168             }
169              
170 0         0 my @subtests = _subtests_from_file( $_ );
171              
172 0 0       0 if ($include_extras) {
173 0         0 $subtests{$transformer} =
174             { subtests => [ @subtests ], globals => $globals };
175             }
176             else {
177 0         0 $subtests{$transformer} = [ @subtests ];
178             }
179              
180 0         0 return;
181             },
182 0         0 no_chdir => 1,
183             },
184             $start
185             );
186              
187 0         0 return \%subtests;
188             }
189              
190             # Answer whether author test should be run.
191             #
192             # Note: this code is duplicated in
193             # t/tlib/Perl/ToPerl6/TestUtilitiesWithMinimalDependencies.pm.
194             # If you change this here, make sure to change it there.
195              
196             sub should_skip_author_tests {
197             return not $ENV{TEST_AUTHOR_PERL_MOGRIFY}
198 0     0 1 0 }
199              
200             sub get_author_test_skip_message {
201 0     0 1 0 return 'Author test. Set $ENV{TEST_AUTHOR_PERL_MOGRIFY} to a true value to run.';
202             }
203              
204              
205             sub starting_points_including_examples {
206 0 0   0 1 0 return (-e 'blib' ? 'blib' : 'lib', 'examples');
207             }
208              
209             sub _globals_from_file {
210 0     0   0 my $test_file = shift;
211              
212 0         0 my %valid_keys = hashify qw< prerequisites >;
213              
214 0 0       0 return if -z $test_file; # Skip if the Transformer has a regular .t file.
215              
216 0         0 my %globals;
217              
218 0 0       0 open my $handle, '<', $test_file
219             or throw_internal "Couldn't open $test_file: $OS_ERROR";
220              
221 0         0 while ( my $line = <$handle> ) {
222 0         0 chomp;
223              
224 0 0       0 if (
225             my ($key,$value) =
226             $line =~ m<\A [#][#] [ ] global [ ] (\S+) (?:\s+(.+))? >xms
227             ) {
228 0 0       0 next if not $key;
229 0 0       0 if ( not $valid_keys{$key} ) {
230 0         0 throw_internal "Unknown global key $key in $test_file";
231             }
232              
233 0 0       0 if ( $key eq 'prerequisites' ) {
234 0         0 $value = { hashify( words_from_string($value) ) };
235             }
236 0         0 $globals{$key} = $value;
237             }
238             }
239 0 0       0 close $handle or throw_generic "unable to close $test_file: $OS_ERROR";
240              
241 0         0 return \%globals;
242             }
243              
244             # The internal representation of a subtest is just a hash with some
245             # named keys. It could be an object with accessors for safety's sake,
246             # but at this point I don't see why.
247             sub _subtests_from_file {
248 0     0   0 my $test_file = shift;
249              
250 0         0 my %valid_keys = hashify qw( name failures parms TODO error filename optional_modules );
251              
252 0 0       0 return if -z $test_file; # Skip if the Transformer has a regular .t file.
253              
254 0 0       0 open my $fh, '<', $test_file
255             or throw_internal "Couldn't open $test_file: $OS_ERROR";
256              
257 0         0 my @subtests;
258              
259 0         0 my $incode = 0;
260 0         0 my $cut_in_code = 0;
261 0         0 my $subtest;
262             my $lineno;
263 0         0 while ( <$fh> ) {
264 0         0 ++$lineno;
265 0         0 chomp;
266 0         0 my $inheader = /^## name/ .. /^## cut/;
267              
268 0         0 my $line = $_;
269              
270 0 0       0 if ( $inheader ) {
    0          
    0          
271 0 0       0 $line =~ m/\A [#]/xms or throw_internal "Code before cut: $test_file";
272 0         0 my ($key,$value) = $line =~ m/\A [#][#] [ ] (\S+) (?:\s+(.+))? /xms;
273 0 0       0 next if !$key;
274 0 0       0 next if $key eq 'cut';
275 0 0       0 if ( not $valid_keys{$key} ) {
276 0         0 throw_internal "Unknown key $key in $test_file";
277             }
278              
279 0 0       0 if ( $key eq 'name' ) {
280 0 0       0 if ( $subtest ) { # Stash any current subtest
281 0         0 push @subtests, _finalize_subtest( $subtest );
282 0         0 undef $subtest;
283             }
284 0         0 $subtest->{lineno} = $lineno;
285 0         0 $incode = 0;
286 0         0 $cut_in_code = 0;
287             }
288 0 0       0 if ($incode) {
289 0         0 throw_internal "Header line found while still in code: $test_file";
290             }
291 0         0 $subtest->{$key} = $value;
292             }
293             elsif ( $subtest ) {
294 0         0 $incode = 1;
295 0   0     0 $cut_in_code ||= $line =~ m/ \A [#][#] [ ] cut \z /smx;
296             # Don't start a subtest if we're not in one.
297             # Don't add to the test if we have seen a '## cut'.
298 0 0       0 $cut_in_code or push @{$subtest->{code}}, $line;
  0         0  
299             }
300             elsif (@subtests) {
301             ## don't complain if we have not yet hit the first test
302 0         0 throw_internal "Got some code but I'm not in a subtest: $test_file";
303             }
304             }
305 0 0       0 close $fh or throw_generic "unable to close $test_file: $OS_ERROR";
306 0 0       0 if ( $subtest ) {
307 0 0       0 if ( $incode ) {
308 0         0 push @subtests, _finalize_subtest( $subtest );
309             }
310             else {
311 0         0 throw_internal "Incomplete subtest in $test_file";
312             }
313             }
314              
315 0         0 return @subtests;
316             }
317              
318             sub _split_subtest {
319 0     0   0 my $code = shift;
320              
321 0         0 my (@original, @sample);
322 0         0 my $original_done;
323 0         0 for ( @{ $code } ) {
  0         0  
324 0 0       0 if ( /^#-->/ ) {
    0          
325 0         0 $original_done = 1;
326 0         0 next;
327             }
328             elsif ( $original_done ) {
329 0         0 push @sample, $_;
330             }
331             else {
332 0         0 push @original, $_;
333             }
334             }
335 0         0 return (\@original, \@sample);
336             }
337              
338             sub _finalize_subtest {
339 0     0   0 my $subtest = shift;
340              
341 0 0       0 if ( $subtest->{code} ) {
342 0         0 my $code = delete $subtest->{code};
343 0         0 @{$subtest}{qw(original sample)} = _split_subtest($code);
  0         0  
344 0         0 delete $subtest->{code};
345             }
346             else {
347 0         0 throw_internal "$subtest->{name} has no code lines";
348             }
349 0 0       0 if ( !defined $subtest->{failures} ) {
350 0         0 throw_internal "$subtest->{name} does not specify failures";
351             }
352 0 0       0 if ($subtest->{parms}) {
353 0         0 $subtest->{parms} = eval $subtest->{parms};
354 0 0       0 if ($EVAL_ERROR) {
355 0         0 throw_internal
356             "$subtest->{name} has an error in the 'parms' property:\n"
357             . $EVAL_ERROR;
358             }
359 0 0       0 if ('HASH' ne ref $subtest->{parms}) {
360 0         0 throw_internal
361             "$subtest->{name} 'parms' did not evaluate to a hashref";
362             }
363             } else {
364 0         0 $subtest->{parms} = {};
365             }
366              
367 0 0       0 if (defined $subtest->{error}) {
368 0 0       0 if ( $subtest->{error} =~ m{ \A / (.*) / \z }xms) {
369 0         0 $subtest->{error} = eval {qr/$1/};
  0         0  
370 0 0       0 if ($EVAL_ERROR) {
371 0         0 throw_internal
372             "$subtest->{name} 'error' has a malformed regular expression";
373             }
374             }
375             }
376              
377 0         0 return $subtest;
378             }
379              
380             sub bundled_transformer_names {
381 5     5 1 3614 require ExtUtils::Manifest;
382 5         36561 my $manifest = ExtUtils::Manifest::maniread();
383 5         7413 my @transformer_paths = map {m{\A lib/(Perl/ToPerl6/Transformer/.*).pm \z}xms} keys %{$manifest};
  950         932  
  5         114  
384 5         56 my @transformers = map { join q{::}, split m{/}xms } @transformer_paths;
  190         541  
385 5         97 my @sorted_transformers = sort @transformers;
386 5         231 return @sorted_transformers;
387             }
388              
389             sub names_of_transformers_willing_to_work {
390 1     1 1 16 my %configuration = @_;
391              
392 1         25 my @transformers_willing_to_work =
393             Perl::ToPerl6::Config
394             ->new( %configuration )
395             ->transformers();
396              
397 1         48 return map { ref } @transformers_willing_to_work;
  38         272  
398             }
399              
400             1;
401              
402             __END__
403              
404             #-----------------------------------------------------------------------------
405              
406             =pod
407              
408             =for stopwords RCS subtest subtests
409              
410             =head1 NAME
411              
412             Perl::ToPerl6::TestUtils - Utility functions for testing new Transformers.
413              
414              
415             =head1 INTERFACE SUPPORT
416              
417             This is considered to be a public module. Any changes to its
418             interface will go through a deprecation cycle.
419              
420              
421             =head1 SYNOPSIS
422              
423             use Perl::ToPerl6::TestUtils qw(transform ptransform ftransform);
424              
425             my $code = '<<END_CODE';
426             package Foo::Bar;
427             $foo = frobulator();
428             $baz = $foo ** 2;
429             1;
430             END_CODE
431              
432             # Critique code against all loaded transformers...
433             my $perl_mogrify_config = { -severity => 2 };
434             my $transformation_count = transform( \$code, $perl_mogrify_config);
435              
436             # Critique code against one transformer...
437             my $custom_transformer = 'Miscellanea::ProhibitFrobulation'
438             my $transformation_count = ptransform( $custom_transformer, \$code );
439              
440             # Critique code against one filename-related transformer...
441             my $custom_transformer = 'Modules::RequireFilenameMatchesPackage'
442             my $transformation_count = ftransform( $custom_transformer, \$code, 'Foo/Bar.pm' );
443              
444              
445             =head1 DESCRIPTION
446              
447             This module is used by L<Perl::ToPerl6|Perl::Critic> only for
448             self-testing. It provides a few handy subroutines for testing new
449             Perl::ToPerl6::Transformer modules. Look at the test programs that ship with
450             Perl::ToPerl6 for more examples of how to use these subroutines.
451              
452              
453             =head1 EXPORTS
454              
455             =over
456              
457             =item block_perlmogrifyrc()
458              
459             If a user has a F<~/.perlmogrifyrc> file, this can interfere with
460             testing. This handy method disables the search for that file --
461             simply call it at the top of your F<.t> program. Note that this is
462             not easily reversible, but that should not matter.
463              
464              
465             =item transform_with_transformations( $code_string_ref, $config_ref )
466              
467             Test a block of code against the specified Perl::ToPerl6::Config
468             instance (or C<undef> for the default). Returns the transformations that
469             occurred.
470              
471              
472             =item transform( $code_string_ref, $config_ref )
473              
474             Test a block of code against the specified Perl::ToPerl6::Config
475             instance (or C<undef> for the default). Returns the number of
476             transformations that occurred.
477              
478              
479             =item ptransform_with_transformations( $transformer_name, $code_string_ref, $config_ref )
480              
481             Like C<transform_with_transformations()>, but tests only a single transformer
482             instead of the whole bunch.
483              
484              
485             =item ptransform( $transformer_name, $code_string_ref, $config_ref )
486              
487             Like C<transform()>, but tests only a single transformer instead of the
488             whole bunch.
489              
490              
491             =item ftransform_with_transformations( $transformer_name, $code_string_ref, $filename, $config_ref )
492              
493             Like C<ptransform_with_transformations()>, but pretends that the code was
494             loaded from the specified filename. This is handy for testing
495             transformers like C<Modules::RequireFilenameMatchesPackage> which care
496             about the filename that the source derived from.
497              
498             The C<$filename> parameter must be a relative path, not absolute. The
499             file and all necessary subdirectories will be created via
500             L<File::Temp|File::Temp> and will be automatically deleted.
501              
502              
503             =item ftransform( $transformer_name, $code_string_ref, $filename, $config_ref )
504              
505             Like C<ptransform()>, but pretends that the code was loaded from the
506             specified filename. This is handy for testing transformers like
507             C<Modules::RequireFilenameMatchesPackage> which care about the
508             filename that the source derived from.
509              
510             The C<$filename> parameter must be a relative path, not absolute. The
511             file and all necessary subdirectories will be created via
512             L<File::Temp|File::Temp> and will be automatically deleted.
513              
514              
515             =item subtests_in_tree( $dir )
516              
517             Searches the specified directory recursively for F<.run> files. Each
518             one found is parsed and a hash-of-list-of-hashes is returned. The
519             outer hash is keyed on transformer short name, like
520             C<Modules::RequireEndWithOne>. The inner hash specifies a single test
521             to be handed to C<ptransform()> or C<ftransform()>, including the code
522             string, test name, etc. See below for the syntax of the F<.run>
523             files.
524              
525              
526             =item should_skip_author_tests()
527              
528             Answers whether author tests should run.
529              
530              
531             =item get_author_test_skip_message()
532              
533             Returns a string containing the message that should be emitted when a
534             test is skipped due to it being an author test when author tests are
535             not enabled.
536              
537              
538             =item starting_points_including_examples()
539              
540             Returns a list of the directories contain code that needs to be tested
541             when it is desired that the examples be included.
542              
543              
544             =item bundled_transformer_names()
545              
546             Returns a list of Transformer packages that come bundled with this package.
547             This functions by searching F<MANIFEST> for
548             F<lib/Perl/ToPerl6/Transformer/*.pm> and converts the results to package
549             names.
550              
551              
552             =item names_of_transformers_willing_to_work( %configuration )
553              
554             Returns a list of the packages of transformers that are willing to
555             function on the current system using the specified configuration.
556              
557              
558             =back
559              
560              
561             =head1 F<.run> file information
562              
563             Testing a transformer follows a very simple pattern:
564              
565             * Transformer name
566             * Subtest name
567             * Optional parameters
568             * Number of failures expected
569             * Optional exception expected
570             * Optional filename for code
571              
572             Each of the subtests for a transformer is collected in a single F<.run>
573             file, with test properties as comments in front of each code block
574             that describes how we expect Perl::ToPerl6 to react to the code. For
575             example, say you have a transformer called Variables::ProhibitVowels:
576              
577             (In file t/Variables/ProhibitVowels.run)
578              
579             ## name Basics
580             ## failures 1
581             ## cut
582              
583             my $vrbl_nm = 'foo'; # Good, vowel-free name
584             my $wango = 12; # Bad, pronouncable name
585              
586              
587             ## name Sometimes Y
588             ## failures 1
589             ## cut
590              
591             my $yllw = 0; # "y" not a vowel here
592             my $rhythm = 12; # But here it is
593              
594             These are called "subtests", and two are shown above. The beauty of
595             incorporating multiple subtests in a file is that the F<.run> is
596             itself a (mostly) valid Perl file, and not hidden in a HEREDOC, so
597             your editor's color-coding still works, and it is much easier to work
598             with the code and the POD.
599              
600             If you need to pass any configuration parameters for your subtest, do
601             so like this:
602              
603             ## parms { allow_y => '0' }
604              
605             Note that all the values in this hash must be strings because that's
606             what Perl::ToPerl6 will hand you from a F<.perlmogrifyrc>.
607              
608             If it's a TODO subtest (probably because of some weird corner of PPI
609             that we exercised that Adam is getting around to fixing, right?), then
610             make a C<##TODO> entry.
611              
612             ## TODO Should pass when PPI 1.xxx comes out
613              
614             If the code is expected to trigger an exception in the transformer,
615             indicate that like so:
616              
617             ## error 1
618              
619             If you want to test the error message, mark it with C</.../> to
620             indicate a C<like()> test:
621              
622             ## error /Can't load Foo::Bar/
623              
624             If the transformer you are testing cares about the filename of the code,
625             you can indicate that C<ftransform> should be used like so (see
626             C<ftransform> for more details):
627              
628             ## filename lib/Foo/Bar.pm
629              
630             The value of C<parms> will get C<eval>ed and passed to C<ptransform()>,
631             so be careful.
632              
633             In general, a subtest document runs from the C<## cut> that starts it to
634             either the next C<## name> or the end of the file. In very rare circumstances
635             you may need to end the test document earlier. A second C<## cut> will do
636             this. The only known need for this is in
637             F<t/Miscellanea/RequireRcsKeywords.run>, where it is used to prevent the RCS
638             keywords in the file footer from producing false positives or negatives in the
639             last test.
640              
641             Note that nowhere within the F<.run> file itself do you specify the
642             transformer that you're testing. That's implicit within the filename.
643              
644              
645             =head1 BUGS AND CAVEATS AND TODO ITEMS
646              
647             Test that we have a t/*/*.run for each lib/*/*.pm
648              
649             Allow us to specify the nature of the failures, and which one. If
650             there are 15 lines of code, and six of them fail, how do we know
651             they're the right six?
652              
653              
654             =head1 AUTHOR
655              
656             Chris Dolan <cdolan@cpan.org>
657             and the rest of the L<Perl::ToPerl6|Perl::Critic> team.
658              
659              
660             =head1 COPYRIGHT
661              
662             Copyright (c) 2005-2011 Chris Dolan.
663              
664             This program is free software; you can redistribute it and/or modify
665             it under the same terms as Perl itself. The full text of this license
666             can be found in the LICENSE file included with this module.
667              
668             =cut
669              
670             # Local Variables:
671             # mode: cperl
672             # cperl-indent-level: 4
673             # fill-column: 78
674             # indent-tabs-mode: nil
675             # c-indentation-style: bsd
676             # End:
677             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :