This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update ExtUtils-Manifest to CPAN version 1.65
[perl5.git] / cpan / ExtUtils-Manifest / t / Manifest.t
1 #!/usr/bin/perl -w
2
3 BEGIN {
4     if( $ENV{PERL_CORE} ) {
5         chdir 't' if -d 't';
6         unshift @INC, '../lib';
7     }
8     else {
9         unshift @INC, 't/lib';
10     }
11 }
12 chdir 't';
13
14 use strict;
15
16 use Test::More tests => 97;
17 use Cwd;
18
19 use File::Spec;
20 use File::Path;
21 use File::Find;
22 use Config;
23
24 my $Is_VMS = $^O eq 'VMS';
25 my $Is_VMS_noefs = $Is_VMS;
26 if ($Is_VMS) {
27     my $vms_efs = 0;
28     if (eval 'require VMS::Feature') {
29         $vms_efs = VMS::Feature::current("efs_charset");
30     } else {
31         my $efs_charset = $ENV{'DECC$EFS_CHARSET'} || '';
32         $vms_efs = $efs_charset =~ /^[ET1]/i;
33     }
34     $Is_VMS_noefs = 0 if $vms_efs;
35 }
36
37
38 # We're going to be chdir'ing and modules are sometimes loaded on the
39 # fly in this test, so we need an absolute @INC.
40 @INC = map { File::Spec->rel2abs($_) } @INC;
41
42 # keep track of everything added so it can all be deleted
43 my %Files;
44 sub add_file {
45     my ($file, $data) = @_;
46     $data ||= 'foo';
47     $file =~ s/ /^_/g if $Is_VMS_noefs; # escape spaces
48     1 while unlink $file;  # or else we'll get multiple versions on VMS
49     open( T, '> '.$file) or return;
50     binmode T, ':raw'; # no CRLFs please
51     print T $data;
52     close T;
53     return 0 unless -e $file;  # exists under the name we gave it ?
54     ++$Files{$file};
55 }
56
57 sub read_manifest {
58     open( M, 'MANIFEST' ) or return;
59     chomp( my @files = <M> );
60     close M;
61     return @files;
62 }
63
64 sub catch_warning {
65     my $warn = '';
66     local $SIG{__WARN__} = sub { $warn .= $_[0] };
67     return join('', $_[0]->() ), $warn;
68 }
69
70 sub remove_dir {
71     ok( rmdir( $_ ), "remove $_ directory" ) for @_;
72 }
73
74 # use module, import functions
75 BEGIN {
76     use_ok( 'ExtUtils::Manifest',
77             qw( mkmanifest manicheck filecheck fullcheck
78                 maniread manicopy skipcheck maniadd maniskip) );
79 }
80
81 my $cwd = Cwd::getcwd();
82
83 # Just in case any old files were lying around.
84 rmtree('mantest');
85
86 ok( mkdir( 'mantest', 0777 ), 'make mantest directory' );
87 ok( chdir( 'mantest' ), 'chdir() to mantest' );
88 ok( add_file('foo'), 'add a temporary file' );
89
90 # This ensures the -x check for manicopy means something
91 # Some platforms don't have chmod or an executable bit, in which case
92 # this call will do nothing or fail, but on the platforms where chmod()
93 # works, we test the executable bit is copied
94 chmod( 0744, 'foo') if $Config{'chmod'};
95
96 # there shouldn't be a MANIFEST there
97 my ($res, $warn) = catch_warning( \&mkmanifest );
98 # Canonize the order.
99 $warn = join("", map { "$_|" }
100                  sort { lc($a) cmp lc($b) } split /\r?\n/, $warn);
101 is( $warn, "Added to MANIFEST: foo|Added to MANIFEST: MANIFEST|",
102     "mkmanifest() displayed its additions" );
103
104 # and now you see it
105 ok( -e 'MANIFEST', 'create MANIFEST file' );
106
107 my @list = read_manifest();
108 is( @list, 2, 'check files in MANIFEST' );
109 ok( ! ExtUtils::Manifest::filecheck(), 'no additional files in directory' );
110
111 # after adding bar, the MANIFEST is out of date
112 ok( add_file( 'bar' ), 'add another file' );
113 ok( ! manicheck(), 'MANIFEST now out of sync' );
114
115 # it reports that bar has been added and throws a warning
116 ($res, $warn) = catch_warning( \&filecheck );
117
118 like( $warn, qr/^Not in MANIFEST: bar/, 'warning that bar has been added' );
119 is( $res, 'bar', 'bar reported as new' );
120
121 # now quiet the warning that bar was added and test again
122 ($res, $warn) = do { local $ExtUtils::Manifest::Quiet = 1;
123                      catch_warning( \&skipcheck )
124                 };
125 is( $warn, '', 'disabled warnings' );
126
127 # add a skip file with a rule to skip itself (and the nonexistent glob '*baz*')
128 add_file( 'MANIFEST.SKIP', "baz\n.SKIP" );
129
130 # this'll skip the new file
131 ($res, $warn) = catch_warning( \&skipcheck );
132 like( $warn, qr/^Skipping MANIFEST\.SKIP/i, 'got skipping warning' );
133
134 my @skipped;
135 catch_warning( sub {
136         @skipped = skipcheck()
137 });
138
139 is( join( ' ', @skipped ), 'MANIFEST.SKIP', 'listed skipped files' );
140
141 {
142         local $ExtUtils::Manifest::Quiet = 1;
143         is( join(' ', filecheck() ), 'bar', 'listing skipped with filecheck()' );
144 }
145
146 # add a subdirectory and a file there that should be found
147 ok( mkdir( 'moretest', 0777 ), 'created moretest directory' );
148 add_file( File::Spec->catfile('moretest', 'quux'), 'quux' );
149 ok( exists( ExtUtils::Manifest::manifind()->{'moretest/quux'} ),
150                                         "manifind found moretest/quux" );
151
152 # only MANIFEST and foo are in the manifest
153 $_ = 'foo';
154 my $files = maniread();
155 is( keys %$files, 2, 'two files found' );
156 is( join(' ', sort { lc($a) cmp lc($b) } keys %$files), 'foo MANIFEST',
157                                         'both files found' );
158 is( $_, 'foo', q{maniread() doesn't clobber $_} );
159
160 ok( mkdir( 'copy', 0777 ), 'made copy directory' );
161
162 # Check that manicopy copies files.
163 manicopy( $files, 'copy', 'cp' );
164 my @copies = ();
165 find( sub { push @copies, $_ if -f }, 'copy' );
166 @copies = map { s/\.$//; $_ } @copies if $Is_VMS;  # VMS likes to put dots on
167                                                    # the end of files.
168 # Have to compare insensitively for non-case preserving VMS
169 is_deeply( [sort map { lc } @copies], [sort map { lc } keys %$files] );
170
171 # cp would leave files readonly, so check permissions.
172 foreach my $orig (@copies) {
173     my $copy = "copy/$orig";
174     ok( -r $copy,               "$copy: must be readable" );
175     is( -w $copy, -w $orig,     "       writable if original was" );
176     is( -x $copy, -x $orig,     "       executable if original was" );
177 }
178 rmtree('copy');
179
180
181 # poison the manifest, and add a comment that should be reported
182 add_file( 'MANIFEST', 'none #none' );
183 is( ExtUtils::Manifest::maniread()->{none}, '#none',
184                                         'maniread found comment' );
185
186 ok( mkdir( 'copy', 0777 ), 'made copy directory' );
187 $files = maniread();
188 eval { (undef, $warn) = catch_warning( sub {
189                 manicopy( $files, 'copy', 'cp' ) })
190 };
191
192 # a newline comes through, so get rid of it
193 chomp($warn);
194 # the copy should have given a warning
195 like($warn, qr/^none not found/, 'carped about none' );
196 ($res, $warn) = catch_warning( \&skipcheck );
197 like($warn, qr/^Skipping MANIFEST.SKIP/i, 'warned about MANIFEST.SKIP' );
198
199 # tell ExtUtils::Manifest to use a different file
200 {
201         local $ExtUtils::Manifest::MANIFEST = 'albatross';
202         ($res, $warn) = catch_warning( \&mkmanifest );
203         like( $warn, qr/Added to albatross: /, 'using a new manifest file' );
204
205         # add the new file to the list of files to be deleted
206         $Files{'albatross'}++;
207 }
208
209
210 # Make sure MANIFEST.SKIP is using complete relative paths
211 add_file( 'MANIFEST.SKIP' => "^moretest/q\n" );
212
213 # This'll skip moretest/quux
214 ($res, $warn) = catch_warning( \&skipcheck );
215 like( $warn, qr{^Skipping moretest/quux$}i, 'got skipping warning again' );
216
217
218 # There was a bug where entries in MANIFEST would be blotted out
219 # by MANIFEST.SKIP rules.
220 add_file( 'MANIFEST.SKIP' => 'foo' );
221 add_file( 'MANIFEST'      => "foobar\n"   );
222 add_file( 'foobar'        => '123' );
223 ($res, $warn) = catch_warning( \&manicheck );
224 is( $res,  '',      'MANIFEST overrides MANIFEST.SKIP' );
225 is( $warn, '',   'MANIFEST overrides MANIFEST.SKIP, no warnings' );
226
227 $files = maniread;
228 ok( !$files->{wibble},     'MANIFEST in good state' );
229 maniadd({ wibble => undef });
230 maniadd({ yarrow => "hock" });
231 $files = maniread;
232 is( $files->{wibble}, '',    'maniadd() with undef comment' );
233 is( $files->{yarrow}, 'hock','          with comment' );
234 is( $files->{foobar}, '',    '          preserved old entries' );
235
236 my $manicontents = do {
237   local $/;
238   open my $fh, "MANIFEST" or die;
239   binmode $fh, ':raw';
240   <$fh>
241 };
242 is index($manicontents, "\015\012"), -1, 'MANIFEST no CRLF';
243
244 {
245     # EOL normalization in maniadd()
246
247     # move manifest away:
248     rename "MANIFEST", "MANIFEST.bak" or die "Could not rename MANIFEST to MANIFEST.bak: $!";
249     my $prev_maniaddresult;
250     my @eol = ("\012","\015","\015\012");
251     # for all line-endings:
252     for my $i (0..$#eol) {
253         my $eol = $eol[$i];
254         #   cp the backup of the manifest to MANIFEST, line-endings adjusted
255         my $content = do { local $/; open my $fh, "MANIFEST.bak" or die; <$fh> };
256     SPLITTER: for my $eol2 (@eol) {
257             if ( index($content, $eol2) > -1 ) {
258                 my @lines = split /$eol2/, $content;
259                 pop @lines while $lines[-1] eq "";
260                 open my $fh, ">", "MANIFEST" or die "Could not open >MANIFEST: $!";
261                 print $fh map { "$_$eol" } @lines;
262                 close $fh or die "Could not close: $!";
263                 last SPLITTER;
264             }
265         }
266         #   try maniadd
267         maniadd({eoltest => "end of line normalization test"});
268         #   slurp result and compare to previous result
269         my $maniaddresult = do { local $/; open my $fh, "MANIFEST" or die; <$fh> };
270         if ($prev_maniaddresult) {
271             if ( $maniaddresult eq $prev_maniaddresult ) {
272                 pass "normalization success with i=$i";
273             } else {
274                 require Data::Dumper;
275                 no warnings "once";
276                 local $Data::Dumper::Useqq = 1;
277                 local $Data::Dumper::Terse = 1;
278                 is Data::Dumper::Dumper($maniaddresult), Data::Dumper::Dumper($prev_maniaddresult), "eol normalization failed with i=$i";
279             }
280         }
281         $prev_maniaddresult = $maniaddresult;
282     }
283     # move backup over MANIFEST
284     rename "MANIFEST.bak", "MANIFEST" or die "Could not rename MANIFEST.bak to MANIFEST: $!";
285 }
286
287 my %funky_files;
288 # test including a filename with a space
289 SKIP: {
290     add_file( 'foo bar' => "space" )
291         or skip "couldn't create spaced test file", 2;
292     local $ExtUtils::Manifest::MANIFEST = "albatross";
293     maniadd({ 'foo bar' => "contains space"});
294     is( maniread()->{'foo bar'}, "contains space",
295         'spaced manifest filename' );
296     add_file( 'albatross.bak', '' );
297     ($res, $warn) = catch_warning( \&mkmanifest );
298     like( $warn, qr/\A(Added to.*\n)+\z/m,
299           'no warnings about funky filename' );
300     $funky_files{'space'} = 'foo bar';
301 }
302
303 # test including a filename with a space and a quote
304 SKIP: {
305     add_file( 'foo\' baz\'quux' => "quote" )
306         or skip "couldn't create quoted test file", 1;
307     local $ExtUtils::Manifest::MANIFEST = "albatross";
308     maniadd({ 'foo\' baz\'quux' => "contains quote"});
309     is( maniread()->{'foo\' baz\'quux'}, "contains quote",
310         'quoted manifest filename' );
311     $funky_files{'space_quote'} = 'foo\' baz\'quux';
312 }
313
314 # test including a filename with a space and a backslash
315 SKIP: {
316     add_file( 'foo bar\\baz' => "backslash" )
317         or skip "couldn't create backslash test file", 1;
318     local $ExtUtils::Manifest::MANIFEST = "albatross";
319     maniadd({ 'foo bar\\baz' => "contains backslash"});
320     is( maniread()->{'foo bar\\baz'}, "contains backslash",
321         'backslashed manifest filename' );
322     $funky_files{'space_backslash'} = 'foo bar\\baz';
323 }
324
325 # test including a filename with a space, quote, and a backslash
326 SKIP: {
327     add_file( 'foo bar\\baz\'quux' => "backslash/quote" )
328         or skip "couldn't create backslash/quote test file", 1;
329     local $ExtUtils::Manifest::MANIFEST = "albatross";
330     maniadd({ 'foo bar\\baz\'quux' => "backslash and quote"});
331     is( maniread()->{'foo bar\\baz\'quux'}, "backslash and quote",
332         'backslashed and quoted manifest filename' );
333     $funky_files{'space_quote_backslash'} = 'foo bar\\baz\'quux';
334 }
335
336 my @funky_keys = qw(space space_quote space_backslash space_quote_backslash);
337 # test including an external manifest.skip file in MANIFEST.SKIP
338 {
339     maniadd({ foo => undef , albatross => undef,
340               'mymanifest.skip' => undef, 'mydefault.skip' => undef});
341     for (@funky_keys) {
342         maniadd( {$funky_files{$_} => $_} ) if defined $funky_files{$_};
343     }
344
345     add_file('mymanifest.skip' => "^foo\n");
346     add_file('mydefault.skip'  => "^my\n");
347     local $ExtUtils::Manifest::DEFAULT_MSKIP =
348          File::Spec->catfile($cwd, qw(mantest mydefault.skip));
349     my $skip = File::Spec->catfile($cwd, qw(mantest mymanifest.skip));
350     add_file('MANIFEST.SKIP' =>
351              "albatross\n#!include $skip\n#!include_default");
352     my ($res, $warn) = catch_warning( \&skipcheck );
353     for (qw(albatross foo foobar mymanifest.skip mydefault.skip)) {
354         like( $warn, qr/Skipping \b$_\b/,
355               "Skipping $_" );
356     }
357     for my $funky_key (@funky_keys) {
358         SKIP: {
359             my $funky_file = $funky_files{$funky_key};
360             skip "'$funky_key' not created", 1 unless $funky_file;
361             like( $warn, qr/Skipping \b\Q$funky_file\E\b/,
362               "Skipping $funky_file");
363         }
364     }
365     ($res, $warn) = catch_warning( \&mkmanifest );
366     for (qw(albatross foo foobar mymanifest.skip mydefault.skip)) {
367         like( $warn, qr/Removed from MANIFEST: \b$_\b/,
368               "Removed $_ from MANIFEST" );
369     }
370     for my $funky_key (@funky_keys) {
371         SKIP: {
372             my $funky_file = $funky_files{$funky_key};
373             skip "'$funky_key' not created", 1 unless $funky_file;
374             like( $warn, qr/Removed from MANIFEST: \b\Q$funky_file\E\b/,
375               "Removed $funky_file from MANIFEST");
376         }
377     }
378     my $files = maniread;
379     ok( ! exists $files->{albatross}, 'albatross excluded via MANIFEST.SKIP' );
380     ok( exists $files->{yarrow},      'yarrow included in MANIFEST' );
381     ok( exists $files->{bar},         'bar included in MANIFEST' );
382     ok( ! exists $files->{foobar},    'foobar excluded via mymanifest.skip' );
383     ok( ! exists $files->{foo},       'foo excluded via mymanifest.skip' );
384     ok( ! exists $files->{'mymanifest.skip'},
385         'mymanifest.skip excluded via mydefault.skip' );
386     ok( ! exists $files->{'mydefault.skip'},
387         'mydefault.skip excluded via mydefault.skip' );
388
389     # test exclusion of funky files
390     for my $funky_key (@funky_keys) {
391         SKIP: {
392             my $funky_file = $funky_files{$funky_key};
393             skip "'$funky_key' not created", 1 unless $funky_file;
394             ok( ! exists $files->{$funky_file},
395                   "'$funky_file' excluded via mymanifest.skip" );
396         }
397     }
398
399     # tests for maniskip
400     my $skipchk = maniskip();
401     is ( $skipchk->('albatross'), 1,
402         'albatross excluded via MANIFEST.SKIP' );
403     is( $skipchk->('yarrow'), '',
404         'yarrow included in MANIFEST' );
405     is( $skipchk->('bar'), '',
406         'bar included in MANIFEST' );
407     $skipchk = maniskip('mymanifest.skip');
408     is( $skipchk->('foobar'), 1,
409         'foobar excluded via mymanifest.skip' );
410     is( $skipchk->('foo'), 1,
411         'foo excluded via mymanifest.skip' );
412     is( $skipchk->('mymanifest.skip'), '',
413         'mymanifest.skip included via mydefault.skip' );
414     is( $skipchk->('mydefault.skip'), '',
415         'mydefault.skip included via mydefault.skip' );
416     $skipchk = maniskip('mydefault.skip');
417     is( $skipchk->('foobar'), '',
418         'foobar included via mydefault.skip' );
419     is( $skipchk->('foo'), '',
420         'foo included via mydefault.skip' );
421     is( $skipchk->('mymanifest.skip'), 1,
422         'mymanifest.skip excluded via mydefault.skip' );
423     is( $skipchk->('mydefault.skip'), 1,
424         'mydefault.skip excluded via mydefault.skip' );
425
426     my $extsep = $Is_VMS_noefs ? '_' : '.';
427     $Files{"$_.bak"}++ for ('MANIFEST', "MANIFEST${extsep}SKIP");
428 }
429
430 add_file('MANIFEST'   => 'Makefile.PL');
431 maniadd({ foo  => 'bar' });
432 $files = maniread;
433 # VMS downcases the MANIFEST.  We normalize it here to match.
434 %$files = map { (lc $_ => $files->{$_}) } keys %$files;
435 my %expect = ( 'makefile.pl' => '',
436                'foo'    => 'bar'
437              );
438 is_deeply( $files, \%expect, 'maniadd() vs MANIFEST without trailing newline');
439
440 #add_file('MANIFEST'   => 'Makefile.PL');
441 #maniadd({ foo => 'bar' });
442
443 SKIP: {
444     chmod( 0400, 'MANIFEST' );
445     skip "Can't make MANIFEST read-only", 2 if -w 'MANIFEST';
446
447     eval {
448         maniadd({ 'foo' => 'bar' });
449     };
450     is( $@, '',  "maniadd() won't open MANIFEST if it doesn't need to" );
451
452     eval {
453         maniadd({ 'grrrwoof' => 'yippie' });
454     };
455     like( $@, qr/^\Qmaniadd() could not open MANIFEST:\E/,
456                  "maniadd() dies if it can't open the MANIFEST" );
457
458     chmod( 0600, 'MANIFEST' );
459 }
460
461
462 END {
463         is( unlink( keys %Files ), keys %Files, 'remove all added files' );
464         for my $file ( keys %Files ) { 1 while unlink $file; } # all versions
465         remove_dir( 'moretest', 'copy' );
466
467         # now get rid of the parent directory
468         ok( chdir( $cwd ), 'return to parent directory' );
469         remove_dir( 'mantest' );
470 }