File Coverage

blib/lib/Test/PerlTidy.pm
Criterion Covered Total %
statement 108 119 90.7
branch 23 38 60.5
condition 10 11 90.9
subroutine 19 19 100.0
pod 4 4 100.0
total 164 191 85.8


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