File Coverage

blib/lib/Test/Portability/Files.pm
Criterion Covered Total %
statement 65 95 68.4
branch 23 64 35.9
condition 3 18 16.6
subroutine 11 12 91.6
pod 3 3 100.0
total 105 192 54.6


line stmt bran cond sub pod time code
1             package Test::Portability::Files;
2             $Test::Portability::Files::VERSION = '0.07';
3             # ABSTRACT: Check file names portability
4 2     2   1094 use strict;
  2         2  
  2         54  
5 2     2   6 use warnings;
  2         2  
  2         65  
6 2     2   943 use ExtUtils::Manifest qw(maniread);
  2         12635  
  2         126  
7 2     2   14 use File::Basename;
  2         3  
  2         86  
8 2     2   7 use File::Find;
  2         2  
  2         64  
9 2     2   7 use File::Spec;
  2         2  
  2         25  
10 2     2   6 use Test::Builder;
  2         2  
  2         142  
11             require Exporter;
12              
13             our @ISA = qw(Exporter);
14             our @EXPORT = qw(&options &run_tests);
15             our @EXPORT_OK = @EXPORT;
16              
17             my $Test = Test::Builder->new;
18              
19             sub import {
20 1     1   6 my $self = shift;
21 1         1 my $caller = caller;
22              
23             {
24             ## no critic
25 2     2   7 no strict 'refs';
  2         2  
  2         1576  
  1         1  
26 1         2 *{ $caller . '::options' } = \&options;
  1         4  
27 1         1 *{ $caller . '::run_tests' } = \&run_tests;
  1         3  
28             ## use critic
29             }
30              
31 1         5 $Test->exported_to($caller);
32 1 50       9 $Test->plan( tests => 1 ) unless $Test->has_plan;
33             }
34              
35             my %options = ( use_file_find => 0, );
36              
37             my %tests = (
38             ansi_chars => 1,
39             one_dot => 1,
40             dir_noext => 1,
41             special_chars => 1,
42             space => 1,
43             mac_length => 0,
44             amiga_length => 0,
45             vms_length => 1,
46             dos_length => 0,
47             case => 1,
48             'symlink' => 1,
49             );
50              
51             my %errors_text =
52             ( # wrap the text at this column --------------------------------> |
53             ansi_chars =>
54             "These files does not respect the portable filename characters\n"
55             . "as defined by ANSI C and perlport:\n",
56              
57             one_dot => "These files contain more than one dot in their name:\n",
58              
59             dir_noext => "These directories have an extension in their name:\n",
60              
61             special_chars =>
62             "These files contain special characters that may break on\n"
63             . "several systems, please correct:\n",
64              
65             space => "These files contain space in their name, which is not well\n"
66             . "handled on several systems:\n",
67              
68             mac_length =>
69             "These files have a name more than 31 characters long, which\n"
70             . "will be truncated on Mac OS Classic and old AmigaOS:\n",
71              
72             amiga_length =>
73             "These files have a name more than 107 characters long, which\n"
74             . "will be truncated on recent AmigaOS:\n",
75              
76             vms_length =>
77             "These files have a name or extension too long for VMS (both\n"
78             . "are limited to 39 characters):\n",
79              
80             dos_length =>
81             "These files have a name too long for MS-DOS and compatible\n"
82             . "systems:\n",
83              
84             case => "The name of these files differ only by the case, which can\n"
85             . "cause real problems on case-insensitive filesystems:",
86              
87             'symlink' => "The following files are symbolic links, which are not\n"
88             . "supported on several operating systems:",
89             );
90              
91             my %bad_names = ();
92             my %lc_names = ();
93              
94              
95             sub options {
96 0     0 1 0 my %opts = @_;
97 0         0 for my $test ( keys %tests ) {
98 0 0       0 $tests{$test} = $opts{"test_$test"} if exists $opts{"test_$test"};
99             }
100 0         0 for my $opt ( keys %options ) {
101 0 0       0 $options{$opt} = $opts{$opt} if exists $opts{$opt};
102             }
103             @tests{ keys %tests } = ( $opts{all_tests} ) x ( keys %tests )
104 0 0       0 if exists $opts{all_tests};
105             }
106              
107              
108             sub test_name_portability {
109 30     30 1 16 my ( $file, $file_name, $file_path, $file_ext );
110              
111             # extract path, base name and extension
112 30 50       34 if ( $options{use_file_find} ) { # using Find::File
113             # skip generated files
114 0 0 0     0 return if $_ eq File::Spec->curdir or $_ eq 'pm_to_blib';
115 0         0 my $firstdir = (
116             File::Spec->splitdir( File::Spec->canonpath($File::Find::name) ) )
117             [0];
118 0 0 0     0 return if $firstdir eq 'blib' or $firstdir eq '_build';
119              
120 0         0 $file = $File::Find::name;
121 0         0 ( $file_name, $file_path, $file_ext ) =
122             fileparse( $file, '\\.[^.]+?' );
123              
124             }
125             else { # only check against MANIFEST
126 30         27 $file = shift;
127 30         396 ( $file_name, $file_path, $file_ext ) =
128             fileparse( $file, '\\.[^.]+?' );
129              
130             #for my $dir (File::Spec->splitdir(File::Spec->canonpath($file_path))) {
131             # test_name_portability($dir)
132             #}
133              
134 30         42 $_ = $file_name . $file_ext;
135             }
136              
137             #print STDERR "file $file\t=> path='$file_path', name='$file_name', ext='$file_ext'\n";
138              
139             # After this point, the following variables are expected to hold these semantics
140             # $file must contain the path to the file (t/00load.t)
141             # $_ must contain the full name of the file (00load.t)
142             # $file_name must contain the base name of the file (00load)
143             # $file_path must contain the path to the directory containing the file (t/)
144             # $file_ext must contain the extension (if any) of the file (.t)
145              
146             # check if the name only uses portable filename characters, as defined by ANSI C
147 30 50       49 if ( $tests{ansi_chars} ) {
148 30 50       64 /^[A-Za-z0-9._][A-Za-z0-9._-]*$/ or $bad_names{$file} .= 'ansi_chars,';
149             }
150              
151             # check if the name contains more than one dot
152 30 50       37 if ( $tests{one_dot} ) {
153 30 50       55 tr/.// > 1 and $bad_names{$file} .= 'one_dot,';
154             }
155              
156             # check if the name contains special chars
157 30 50       34 if ( $tests{special_chars} ) {
158             m-[!"#\$%&'\(\)\*\+/:;<>\?@\[\\\]^`\{\|\}~]-
159 30 50       51 and $bad_names{$file} .= 'special_chars,';
160             }
161              
162             # check if the name contains a space char
163 30 50       34 if ( $tests{space} ) {
164 30 50       36 m/ / and $bad_names{$file} .= 'space,';
165             }
166              
167             # check the length of the name, compared to Mac OS Classic max length
168 30 50       33 if ( $tests{mac_length} ) {
169 0 0       0 length > 31 and $bad_names{$file} .= 'mac_length,';
170             }
171              
172             # check the length of the name, compared to AmigaOS max length
173 30 50       31 if ( $tests{amiga_length} ) {
174 0 0       0 length > 107 and $bad_names{$file} .= 'amiga_length,';
175             }
176              
177             # check the length of the name, compared to VMS max length
178 30 50       33 if ( $tests{vms_length} ) {
179             ( length($file_name) <= 39 and length($file_ext) <= 40 )
180 30 50 33     88 or $bad_names{$file} .= 'vms_length,';
181             }
182              
183             # check the length of the name, compared to DOS max length
184 30 50       35 if ( $tests{dos_length} ) {
185             ( length($file_name) <= 8 and length($file_ext) <= 4 )
186 0 0 0     0 or $bad_names{$file} .= 'dos_length,';
187             }
188              
189             # check if the name is unique on case-insensitive filesystems
190 30 50       32 if ( $tests{case} ) {
191 30 50 33     87 if ( not $lc_names{$file} and $lc_names{ lc $file } ) {
192 0         0 $bad_names{$file} .= 'case,';
193 0         0 $bad_names{ $lc_names{ lc $file } } .= 'case,';
194             }
195             else {
196 30         43 $lc_names{ lc $file } = $file;
197             }
198             }
199              
200             # check if the file is a symbolic link
201 30 50       34 if ( $tests{'symlink'} ) {
202 30 50       258 -l $file and $bad_names{$file} .= 'symlink,';
203             }
204              
205             # if it's a directory, check that it has no extension
206 30 50       41 if ( $tests{'dir_noext'} ) {
207 30 50 33     207 -d $file and tr/.// > 0 and $bad_names{$file} .= 'dir_noext,';
208             }
209             }
210              
211              
212             sub run_tests {
213 1     1 1 434 fileparse_set_fstype('Unix');
214              
215 1 50       3 if ( $options{use_file_find} ) {
216              
217             # check all files found using File::Find
218 0         0 find( \&test_name_portability, File::Spec->curdir );
219              
220             }
221             else {
222             # check only against files listed in MANIFEST
223 1         3 my $manifest = maniread();
224 1         296 map { test_name_portability($_) } keys %$manifest;
  30         37  
225             }
226              
227             # check the results
228 1 50       5 if ( keys %bad_names ) {
229 0         0 $Test->ok( 0, "File names portability" );
230              
231 0         0 my %errors_list = ();
232 0         0 for my $file ( keys %bad_names ) {
233 0         0 for my $error ( split ',', $bad_names{$file} ) {
234 0 0       0 $errors_list{$error} = [] if not ref $errors_list{$error};
235 0         0 push @{ $errors_list{$error} }, $file;
  0         0  
236             }
237             }
238              
239 0         0 for my $error ( sort keys %errors_list ) {
240 0         0 $Test->diag( $errors_text{$error} );
241              
242 0         0 for my $file ( sort @{ $errors_list{$error} } ) {
  0         0  
243 0         0 $Test->diag(" $file");
244             }
245              
246 0         0 $Test->diag(' ');
247             }
248             }
249             else {
250 1         5 $Test->ok( 1, "File names portability" );
251             }
252             }
253              
254              
255             1;
256              
257             __END__