File Coverage

blib/lib/Test/PerlTidy.pm
Criterion Covered Total %
statement 108 119 90.7
branch 23 38 60.5
condition 13 14 92.8
subroutine 19 19 100.0
pod 4 4 100.0
total 167 194 86.0


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