File Coverage

blib/lib/Test/PerlTidy.pm
Criterion Covered Total %
statement 103 114 90.3
branch 21 36 58.3
condition 8 9 88.8
subroutine 19 19 100.0
pod 4 4 100.0
total 155 182 85.1


line stmt bran cond sub pod time code
1             package Test::PerlTidy;
2             $Test::PerlTidy::VERSION = '20210709';
3 5     5   221622 use 5.014;
  5         113  
4 5     5   32 use strict;
  5         7  
  5         132  
5 5     5   26 use warnings;
  5         7  
  5         146  
6 5     5   2885 use English qw( -no_match_vars );
  5         19206  
  5         28  
7              
8 5     5   4548 use parent 'Exporter';
  5         1620  
  5         30  
9              
10 5     5   295 use vars qw( @EXPORT ); ## no critic (Modules::ProhibitAutomaticExportation)
  5         10  
  5         232  
11             @EXPORT = qw( run_tests );
12              
13 5     5   27 use Carp qw( croak );
  5         12  
  5         260  
14 5     5   4477 use Path::Tiny 0.100 qw( path );
  5         72028  
  5         354  
15 5     5   48 use File::Spec ();
  5         12  
  5         72  
16 5     5   3041 use IO::File ();
  5         46638  
  5         164  
17 5     5   5874 use Perl::Tidy 20201001;
  5         1661473  
  5         687  
18 5     5   1867 use Test::Builder ();
  5         130169  
  5         163  
19 5     5   3194 use Text::Diff qw( diff );
  5         42727  
  5         5532  
20              
21             my $test = Test::Builder->new;
22              
23             our $MUTE = 0;
24              
25             sub run_tests {
26 2     2 1 267 my %args = @_;
27 2         5 my @opts;
28 2 50       11 if ( my $perltidy_options = delete( $args{perltidy_options} ) ) {
29 0         0 push @opts, +{ perltidy_options => $perltidy_options, };
30             }
31              
32             # Skip all tests if instructed to.
33 2 50       9 $test->skip_all('All tests skipped.') if $args{skip_all};
34              
35 2 50       9 $MUTE = $args{mute} if exists $args{mute};
36              
37             # Get files to work with and set the plan.
38 2         11 my @files = list_files(%args);
39 2         23 $test->plan( tests => scalar @files );
40              
41             # Check each file in turn.
42 2         2264 foreach my $file (@files) {
43 13         5742 $test->ok( is_file_tidy( $file, $args{perltidyrc}, @opts, ),
44             "'$file'" );
45             }
46              
47 2         1452 return;
48             }
49              
50             sub is_file_tidy {
51 16     16 1 192 my ( $file_to_tidy, $perltidyrc, $named_args ) = @_;
52              
53 16   100     163 $named_args //= { perltidy_options => {}, };
54 16         72 my $code_to_tidy = load_file($file_to_tidy);
55              
56 16         69 my $tidied_code = q{};
57 16         47 my $logfile = q{};
58 16         124 my $errorfile = q{};
59              
60 16 50       3436 my $stderr_fh = IO::File->new_tmpfile or croak "IO::File->new_tmpfile: $!";
61 16         206 $stderr_fh->autoflush(1);
62              
63             Perl::Tidy::perltidy(
64             source => \$code_to_tidy,
65             destination => \$tidied_code,
66             stderr => $stderr_fh,
67             logfile => \$logfile,
68             errorfile => \$errorfile,
69             perltidyrc => $perltidyrc,
70 16         1090 %{ $named_args->{perltidy_options} },
  16         147  
71             );
72              
73             # If there were perltidy errors report them and return.
74 16         7388205 $stderr_fh->seek( 0, 0 );
75 16 50   3   1451 binmode $stderr_fh, ':encoding(UTF-8)' or croak "error setting binmode $!";
  3         49  
  3         9  
  3         37  
76 16         6200 my $stderr = do {
77 16         169 local $INPUT_RECORD_SEPARATOR = undef;
78 16         4080 <$stderr_fh>;
79             };
80 16 50       133 if ($stderr) {
81 0 0       0 unless ($MUTE) {
82 0         0 $test->diag("perltidy reported the following errors:\n");
83 0         0 $test->diag($stderr);
84             }
85 0         0 return 0;
86             }
87              
88             # Compare the pre and post tidy code and return result.
89             # Do not worry about trailing newlines.
90             #
91 16         1203 $code_to_tidy =~ s/[\r\n]+$//;
92 16         1151 $tidied_code =~ s/[\r\n]+$//;
93 16 100       113 if ( $code_to_tidy eq $tidied_code ) {
94 15         266 return 1;
95             }
96             else {
97 1 50       6 unless ($MUTE) {
98 0         0 $test->diag("The file '$file_to_tidy' is not tidy\n");
99 0         0 $test->diag(
100             diff( \$code_to_tidy, \$tidied_code, { STYLE => 'Table' } ) );
101             }
102              
103 1         13 return 0;
104             }
105             }
106              
107             sub list_files {
108 4     4 1 211 my (@args) = @_;
109              
110 4         11 my %args;
111             my $path;
112              
113             # Expect either a hashref of args, or a single "path" argument:
114             #
115             # The only reason for allowing a single path argument is for
116             # backward compatibility with Test::PerlTidy::list_files, on the
117             # off chance that someone was calling it directly...
118             #
119 4 50       19 if ( @args > 1 ) {
120 4         14 %args = @args;
121 4         12 $path = $args{path};
122             }
123             else {
124 0         0 %args = ();
125 0         0 $path = $args[0];
126             }
127              
128 4   100     19 $path ||= q{.};
129              
130 4 50       80 $test->BAIL_OUT(qq{The directory "$path" does not exist}) unless -d $path;
131              
132             my $excludes = $args{exclude}
133 4   50     24 || [ $OSNAME eq 'MSWin32' ? qr{^blib[/\\]} : 'blib/' ]
134             ; # exclude blib by default
135              
136 4 50       22 $test->BAIL_OUT('exclude should be an array')
137             unless ref $excludes eq 'ARRAY';
138              
139 4         10 my @files;
140             path($path)
141 281 100 100 281   38257 ->visit( sub { push @files, $_ if $_->is_file && /[.](?:pl|pm|PL|t)\z/; },
142 4         27 { recurse => 1 } );
143              
144 4         352 my %keep = map { File::Spec->canonpath($_) => 1 } @files;
  68         383  
145 4         96 my @excluded = ();
146              
147 4         31 foreach my $file ( keys %keep ) {
148              
149 68         91 foreach my $exclude ( @{$excludes} ) {
  68         107  
150              
151 166 100       855 my $exclude_me =
152             ref $exclude ? ( $file =~ $exclude ) : ( $file =~ /^$exclude/ );
153              
154 166 100       374 if ($exclude_me) {
155 38         59 delete $keep{$file};
156 38 50       74 push @excluded, $file if $args{debug};
157 38         78 last; # no need to check more exclusions...
158             }
159             }
160             }
161              
162             # Sort the output so that it is repeatable
163 4         66 @files = sort keys %keep;
164              
165 4 50       22 if ( $args{debug} ) {
166 0         0 $test->diag( 'Files excluded: ', join( "\n\t", sort @excluded ), "\n" );
167 0         0 $test->diag( 'Files remaining ', join( "\n\t", @files ), "\n" );
168             }
169              
170 4         32 return @files;
171             }
172              
173             sub load_file {
174 16     16 1 47 my $filename = shift;
175              
176             # If the file is not regular then return undef.
177 16 50       505 return unless -f $filename;
178              
179             # Slurp the file.
180 16         143 my $content = path($filename)->slurp_utf8;
181 16         10353 return $content;
182             }
183              
184             1;
185              
186             __END__
187              
188             =pod
189              
190             =encoding UTF-8
191              
192             =head1 NAME
193              
194             Test::PerlTidy - check that all your files are tidy.
195              
196             =head1 VERSION
197              
198             version 20210709
199              
200             =head1 SYNOPSIS
201              
202             # In a file like 't/perltidy.t':
203              
204             use Test::PerlTidy qw( run_tests );
205              
206             run_tests();
207              
208             =head1 DESCRIPTION
209              
210             This rather unflattering comment was made in a piece by Ken Arnold:
211              
212             "Perl is a vast swamp of lexical and syntactic swill and nobody
213             knows how to format even their own code well, but it's the only
214             major language I can think of (with the possible exception of the
215             recent, yet very Java-like C#) that doesn't have at least one
216             style that's good enough."
217             http://www.artima.com/weblogs/viewpost.jsp?thread=74230
218              
219             Hmmm... He is sort of right in a way. Then again the piece he wrote
220             was related to Python which is somewhat strict about formatting
221             itself.
222              
223             Fear not though - now you too can have your very own formatting
224             gestapo in the form of Test::PerlTidy! Simply add a test file as
225             suggested above and any file ending in .pl, .pm, .t or .PL will cause
226             a test fail unless it is exactly as perltidy would like it to be.
227              
228             =for stopwords Hmmm perltidy cvs perltidyrc subdirectories listref canonified pre von der Leszczynski perl
229              
230             =head1 REASONS TO DO THIS
231              
232             If the style is mandated in tests then it will be adhered to.
233              
234             If perltidy decides what is a good style then there should be no
235             quibbling.
236              
237             If the style never changes then cvs diffs stop catching changes that
238             are not really there.
239              
240             Readability might even improve.
241              
242             =head1 HINTS
243              
244             If you want to change the default style then muck around with
245             '.perltidyrc';
246              
247             To quickly make a file work then try 'perltidy -b the_messy_file.pl'.
248              
249             =head1 HOW IT WORKS
250              
251             Runs B<perltidy> on files and reports errors if any of the files
252             differ after having been tidied. Does not permanently modify the
253             files being tested.
254              
255             By default, B<perltidy> will be run on files under the current
256             directory and its subdirectories with extensions matching:
257             C<.pm .pl .PL .t>
258              
259             =head1 METHODS
260              
261             =head2 run_tests ( [ I<%args> ] )
262              
263             This is the main entry point for running tests.
264              
265             A number of options can be specified when running the tests, e.g.:
266              
267             run_tests(
268             path => $start_dir,
269             perltidyrc => $path_to_config_file,
270             exclude => [ qr{\.t$}, 'inc/'],
271             );
272              
273             =over 4
274              
275             =item debug
276              
277             Set C<debug> to a true value to enable additional diagnostic
278             output, in particular info about any processing done as a result of
279             specifying the C<exclude> option. Default is false.
280              
281             =item exclude
282              
283             C<run_tests()> will look for files to test under the current
284             directory and its subdirectories. By default, it will exclude files
285             in the "C<./blib/>" directory. Set C<exclude> to a listref of
286             exclusion criteria if you need to specify additional rules by which
287             files will be excluded.
288              
289             If an item in the C<exclude> list is a string, e.g. "C<./blib/>",
290             it will be assumed to be a path prefix. Files will be excluded if
291             that string matches their path at the beginning.
292              
293             If an item in the C<exclude> list is a regex object, e.g.
294             "C<qr{\.t$}>", files will be excluded if that regex matches their
295             path.
296              
297             Note that the paths of files to be tested are canonified using
298             L<File::Spec|File::Spec>C<< ->canonpath >> before any matching is
299             attempted, which can impact how the exclusion rules apply. If your
300             exclusion rules do not seem to be working, turn on the C<debug>
301             option to see the paths of the files that are being kept/excluded.
302              
303             =item path
304              
305             Set C<path> to the path to the top-level directory which contains
306             the files to be tested. Defaults to the current directory (i.e.
307             "C<.>").
308              
309             =item perltidyrc
310              
311             By default, B<perltidy> will attempt to read its options from the
312             F<.perltidyrc> file on your system. Set C<perltidyrc> to the path
313             to a custom file if you would like to control the B<perltidy>
314             options used during testing.
315              
316             =item mute
317              
318             By default, C<run_tests()> will output diagnostics about any errors
319             reported by B<perltidy>, as well as any actual differences found
320             between the pre-tidied and post-tidied files. Set C<mute> to a
321             true value to turn off that diagnostic output.
322              
323             =item skip_all
324              
325             Set C<skip_all> to a true value to skip all tests. Default is false.
326              
327             =item perltidy_options
328              
329             Pass these to Perl::Tidy::perltidy().
330             (Added in version 20200411 .)
331              
332             =back
333              
334             =head2 list_files ( [ I<start_path> | I<%args> ] )
335              
336             Generate the list of files to be tested. Generally not called directly.
337              
338             =head2 load_file ( I<path_to_file> )
339              
340             Load the file to be tested from disk and return the contents.
341             Generally not called directly.
342              
343             =head2 is_file_tidy ( I<path_to_file> [ , I<path_to_perltidyrc> ] [, I<$named_args>] )
344              
345             Test if a file is tidy or not. Generally not called directly.
346              
347             $named_args can be a hash ref which may have a key called 'perltidy_options'
348             that refers to a hash ref of options that will be passed to Perl::Tidy::perltidy().
349             ($named_args was added in version 20200411).
350              
351             =head1 SEE ALSO
352              
353             L<Perl::Tidy>
354              
355             =head1 ORIGINAL AUTHOR
356              
357             Edmund von der Burg, C<< <evdb at ecclestoad.co.uk> >>
358              
359             =head1 CONTRIBUTORS
360              
361             Duncan J. Ferguson, C<< <duncan_j_ferguson at yahoo.co.uk> >>
362              
363             Stephen, C<< <stephen at enterity.com> >>
364              
365             Larry Leszczynski, C<< <larryl at cpan.org> >>
366              
367             Shlomi Fish, L<https://www.shlomifish.org/>
368              
369             =head1 SUGGESTIONS
370              
371             Please let me know if you have any comments or suggestions.
372              
373             L<http://ecclestoad.co.uk/>
374              
375             =head1 COPYRIGHT
376              
377             Copyright 2007 Edmund von der Burg, all rights reserved.
378              
379             =head1 LICENSE
380              
381             This library is free software . You can redistribute it and/or modify
382             it under the same terms as perl itself.
383              
384             =for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
385              
386             =head1 SUPPORT
387              
388             =head2 Websites
389              
390             The following websites have more information about this module, and may be of help to you. As always,
391             in addition to those websites please use your favorite search engine to discover more resources.
392              
393             =over 4
394              
395             =item *
396              
397             MetaCPAN
398              
399             A modern, open-source CPAN search engine, useful to view POD in HTML format.
400              
401             L<https://metacpan.org/release/Test-PerlTidy>
402              
403             =item *
404              
405             RT: CPAN's Bug Tracker
406              
407             The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN.
408              
409             L<https://rt.cpan.org/Public/Dist/Display.html?Name=Test-PerlTidy>
410              
411             =item *
412              
413             CPANTS
414              
415             The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution.
416              
417             L<http://cpants.cpanauthors.org/dist/Test-PerlTidy>
418              
419             =item *
420              
421             CPAN Testers
422              
423             The CPAN Testers is a network of smoke testers who run automated tests on uploaded CPAN distributions.
424              
425             L<http://www.cpantesters.org/distro/T/Test-PerlTidy>
426              
427             =item *
428              
429             CPAN Testers Matrix
430              
431             The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms.
432              
433             L<http://matrix.cpantesters.org/?dist=Test-PerlTidy>
434              
435             =item *
436              
437             CPAN Testers Dependencies
438              
439             The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution.
440              
441             L<http://deps.cpantesters.org/?module=Test::PerlTidy>
442              
443             =back
444              
445             =head2 Bugs / Feature Requests
446              
447             Please report any bugs or feature requests by email to C<bug-test-perltidy at rt.cpan.org>, or through
448             the web interface at L<https://rt.cpan.org/Public/Bug/Report.html?Queue=Test-PerlTidy>. You will be automatically notified of any
449             progress on the request by the system.
450              
451             =head2 Source Code
452              
453             The code is open to the world, and available for you to hack on. Please feel free to browse it and play
454             with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull
455             from your repository :)
456              
457             L<https://github.com/shlomif/test-perltidy>
458              
459             git clone https://github.com/shlomif/Test-PerlTidy
460              
461             =head1 AUTHOR
462              
463             Shlomi Fish <shlomif@cpan.org>
464              
465             =head1 BUGS
466              
467             Please report any bugs or feature requests on the bugtracker website
468             L<https://github.com/shlomif/test-perltidy/issues>
469              
470             When submitting a bug or request, please include a test-file or a
471             patch to an existing test-file that illustrates the bug or desired
472             feature.
473              
474             =head1 COPYRIGHT AND LICENSE
475              
476             This software is copyright (c) 2021 by Edmund von der Burg.
477              
478             This is free software; you can redistribute it and/or modify it under
479             the same terms as the Perl 5 programming language system itself.
480              
481             =cut