summaryrefslogtreecommitdiff
path: root/dist/ExtUtils-Manifest/t/Manifest.t
blob: 8d2ff8b91e899d800b339e8c023f95b55c567209 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
#!/usr/bin/perl -w

BEGIN {
    if( $ENV{PERL_CORE} ) {
        chdir 't' if -d 't';
        unshift @INC, '../lib';
    }
    else {
        unshift @INC, 't/lib';
    }
}
chdir 't';

use strict;

use Test::More tests => 96;
use Cwd;

use File::Spec;
use File::Path;
use File::Find;
use Config;

my $Is_VMS = $^O eq 'VMS';
my $Is_VMS_noefs = $Is_VMS;
if ($Is_VMS) {
    my $vms_efs = 0;
    if (eval 'require VMS::Feature') {
        $vms_efs = VMS::Feature::current("efs_charset");
    } else {
        my $efs_charset = $ENV{'DECC$EFS_CHARSET'} || '';
        $vms_efs = $efs_charset =~ /^[ET1]/i;
    }
    $Is_VMS_noefs = 0 if $vms_efs;
}


# We're going to be chdir'ing and modules are sometimes loaded on the
# fly in this test, so we need an absolute @INC.
@INC = map { File::Spec->rel2abs($_) } @INC;

# keep track of everything added so it can all be deleted
my %Files;
sub add_file {
    my ($file, $data) = @_;
    $data ||= 'foo';
    1 while unlink $file;  # or else we'll get multiple versions on VMS
    open( T, '> '.$file) or return;
    print T $data;
    close T;
    return 0 unless -e $file;  # exists under the name we gave it ?
    ++$Files{$file};
}

sub read_manifest {
    open( M, 'MANIFEST' ) or return;
    chomp( my @files = <M> );
    close M;
    return @files;
}

sub catch_warning {
    my $warn = '';
    local $SIG{__WARN__} = sub { $warn .= $_[0] };
    return join('', $_[0]->() ), $warn;
}

sub remove_dir {
    ok( rmdir( $_ ), "remove $_ directory" ) for @_;
}

# use module, import functions
BEGIN {
    use_ok( 'ExtUtils::Manifest',
            qw( mkmanifest manicheck filecheck fullcheck
                maniread manicopy skipcheck maniadd maniskip) );
}

my $cwd = Cwd::getcwd();

# Just in case any old files were lying around.
rmtree('mantest');

ok( mkdir( 'mantest', 0777 ), 'make mantest directory' );
ok( chdir( 'mantest' ), 'chdir() to mantest' );
ok( add_file('foo'), 'add a temporary file' );

# This ensures the -x check for manicopy means something
# Some platforms don't have chmod or an executable bit, in which case
# this call will do nothing or fail, but on the platforms where chmod()
# works, we test the executable bit is copied
chmod( 0744, 'foo') if $Config{'chmod'};

# there shouldn't be a MANIFEST there
my ($res, $warn) = catch_warning( \&mkmanifest );
# Canonize the order.
$warn = join("", map { "$_|" }
                 sort { lc($a) cmp lc($b) } split /\r?\n/, $warn);
is( $warn, "Added to MANIFEST: foo|Added to MANIFEST: MANIFEST|",
    "mkmanifest() displayed its additions" );

# and now you see it
ok( -e 'MANIFEST', 'create MANIFEST file' );

my @list = read_manifest();
is( @list, 2, 'check files in MANIFEST' );
ok( ! ExtUtils::Manifest::filecheck(), 'no additional files in directory' );

# after adding bar, the MANIFEST is out of date
ok( add_file( 'bar' ), 'add another file' );
ok( ! manicheck(), 'MANIFEST now out of sync' );

# it reports that bar has been added and throws a warning
($res, $warn) = catch_warning( \&filecheck );

like( $warn, qr/^Not in MANIFEST: bar/, 'warning that bar has been added' );
is( $res, 'bar', 'bar reported as new' );

# now quiet the warning that bar was added and test again
($res, $warn) = do { local $ExtUtils::Manifest::Quiet = 1;
                     catch_warning( \&skipcheck )
                };
is( $warn, '', 'disabled warnings' );

# add a skip file with a rule to skip itself (and the nonexistent glob '*baz*')
add_file( 'MANIFEST.SKIP', "baz\n.SKIP" );

# this'll skip the new file
($res, $warn) = catch_warning( \&skipcheck );
like( $warn, qr/^Skipping MANIFEST\.SKIP/i, 'got skipping warning' );

my @skipped;
catch_warning( sub {
	@skipped = skipcheck()
});

is( join( ' ', @skipped ), 'MANIFEST.SKIP', 'listed skipped files' );

{
	local $ExtUtils::Manifest::Quiet = 1;
	is( join(' ', filecheck() ), 'bar', 'listing skipped with filecheck()' );
}

# add a subdirectory and a file there that should be found
ok( mkdir( 'moretest', 0777 ), 'created moretest directory' );
add_file( File::Spec->catfile('moretest', 'quux'), 'quux' );
ok( exists( ExtUtils::Manifest::manifind()->{'moretest/quux'} ),
                                        "manifind found moretest/quux" );

# only MANIFEST and foo are in the manifest
$_ = 'foo';
my $files = maniread();
is( keys %$files, 2, 'two files found' );
is( join(' ', sort { lc($a) cmp lc($b) } keys %$files), 'foo MANIFEST',
                                        'both files found' );
is( $_, 'foo', q{maniread() doesn't clobber $_} );

ok( mkdir( 'copy', 0777 ), 'made copy directory' );

# Check that manicopy copies files.
manicopy( $files, 'copy', 'cp' );
my @copies = ();
find( sub { push @copies, $_ if -f }, 'copy' );
@copies = map { s/\.$//; $_ } @copies if $Is_VMS;  # VMS likes to put dots on
                                                   # the end of files.
# Have to compare insensitively for non-case preserving VMS
is_deeply( [sort map { lc } @copies], [sort map { lc } keys %$files] );

# cp would leave files readonly, so check permissions.
foreach my $orig (@copies) {
    my $copy = "copy/$orig";
    ok( -r $copy,               "$copy: must be readable" );
    is( -w $copy, -w $orig,     "       writable if original was" );
    is( -x $copy, -x $orig,     "       executable if original was" );
}
rmtree('copy');


# poison the manifest, and add a comment that should be reported
add_file( 'MANIFEST', 'none #none' );
is( ExtUtils::Manifest::maniread()->{none}, '#none',
                                        'maniread found comment' );

ok( mkdir( 'copy', 0777 ), 'made copy directory' );
$files = maniread();
eval { (undef, $warn) = catch_warning( sub {
		manicopy( $files, 'copy', 'cp' ) })
};

# a newline comes through, so get rid of it
chomp($warn);
# the copy should have given a warning
like($warn, qr/^none not found/, 'carped about none' );
($res, $warn) = catch_warning( \&skipcheck );
like($warn, qr/^Skipping MANIFEST.SKIP/i, 'warned about MANIFEST.SKIP' );

# tell ExtUtils::Manifest to use a different file
{
	local $ExtUtils::Manifest::MANIFEST = 'albatross';
	($res, $warn) = catch_warning( \&mkmanifest );
	like( $warn, qr/Added to albatross: /, 'using a new manifest file' );

	# add the new file to the list of files to be deleted
	$Files{'albatross'}++;
}


# Make sure MANIFEST.SKIP is using complete relative paths
add_file( 'MANIFEST.SKIP' => "^moretest/q\n" );

# This'll skip moretest/quux
($res, $warn) = catch_warning( \&skipcheck );
like( $warn, qr{^Skipping moretest/quux$}i, 'got skipping warning again' );


# There was a bug where entries in MANIFEST would be blotted out
# by MANIFEST.SKIP rules.
add_file( 'MANIFEST.SKIP' => 'foo' );
add_file( 'MANIFEST'      => "foobar\n"   );
add_file( 'foobar'        => '123' );
($res, $warn) = catch_warning( \&manicheck );
is( $res,  '',      'MANIFEST overrides MANIFEST.SKIP' );
is( $warn, '',   'MANIFEST overrides MANIFEST.SKIP, no warnings' );

$files = maniread;
ok( !$files->{wibble},     'MANIFEST in good state' );
maniadd({ wibble => undef });
maniadd({ yarrow => "hock" });
$files = maniread;
is( $files->{wibble}, '',    'maniadd() with undef comment' );
is( $files->{yarrow}, 'hock','          with comment' );
is( $files->{foobar}, '',    '          preserved old entries' );

{
    # EOL normalization in maniadd()

    # move manifest away:
    rename "MANIFEST", "MANIFEST.bak" or die "Could not rename MANIFEST to MANIFEST.bak: $!";
    my $prev_maniaddresult;
    my @eol = ("\012","\015","\015\012");
    # for all line-endings:
    for my $i (0..$#eol) {
        my $eol = $eol[$i];
        #   cp the backup of the manifest to MANIFEST, line-endings adjusted
        my $content = do { local $/; open my $fh, "MANIFEST.bak" or die; <$fh> };
    SPLITTER: for my $eol2 (@eol) {
            if ( index($content, $eol2) > -1 ) {
                my @lines = split /$eol2/, $content;
                pop @lines while $lines[-1] eq "";
                open my $fh, ">", "MANIFEST" or die "Could not open >MANIFEST: $!";
                print $fh map { "$_$eol" } @lines;
                close $fh or die "Could not close: $!";
                last SPLITTER;
            }
        }
        #   try maniadd
        maniadd({eoltest => "end of line normalization test"});
        #   slurp result and compare to previous result
        my $maniaddresult = do { local $/; open my $fh, "MANIFEST" or die; <$fh> };
        if ($prev_maniaddresult) {
            if ( $maniaddresult eq $prev_maniaddresult ) {
                pass "normalization success with i=$i";
            } else {
                require Data::Dumper;
                local $Data::Dumper::Useqq = 1;
                local $Data::Dumper::Terse = 1;
                is Data::Dumper::Dumper($maniaddresult), Data::Dumper::Dumper($prev_maniaddresult), "eol normalization failed with i=$i";
            }
        }
        $prev_maniaddresult = $maniaddresult;
    }
    # move backup over MANIFEST
    rename "MANIFEST.bak", "MANIFEST" or die "Could not rename MANIFEST.bak to MANIFEST: $!";
}

my %funky_files;
# test including a filename with a space
SKIP: {
    add_file( 'foo bar' => "space" )
        or skip "couldn't create spaced test file", 2;
    local $ExtUtils::Manifest::MANIFEST = "albatross";
    maniadd({ 'foo bar' => "contains space"});
    is( maniread()->{'foo bar'}, "contains space",
	'spaced manifest filename' );
    add_file( 'albatross.bak', '' );
    ($res, $warn) = catch_warning( \&mkmanifest );
    like( $warn, qr/\A(Added to.*\n)+\z/m,
	  'no warnings about funky filename' );
    $funky_files{'space'} = 'foo bar';
}

# test including a filename with a space and a quote
SKIP: {
    add_file( 'foo\' baz\'quux' => "quote" )
        or skip "couldn't create quoted test file", 1;
    local $ExtUtils::Manifest::MANIFEST = "albatross";
    maniadd({ 'foo\' baz\'quux' => "contains quote"});
    is( maniread()->{'foo\' baz\'quux'}, "contains quote",
	'quoted manifest filename' );
    $funky_files{'space_quote'} = 'foo\' baz\'quux';
}

# test including a filename with a space and a backslash
SKIP: {
    add_file( 'foo bar\\baz' => "backslash" )
        or skip "couldn't create backslash test file", 1;
    local $ExtUtils::Manifest::MANIFEST = "albatross";
    maniadd({ 'foo bar\\baz' => "contains backslash"});
    is( maniread()->{'foo bar\\baz'}, "contains backslash",
	'backslashed manifest filename' );
    $funky_files{'space_backslash'} = 'foo bar\\baz';
}

# test including a filename with a space, quote, and a backslash
SKIP: {
    add_file( 'foo bar\\baz\'quux' => "backslash/quote" )
        or skip "couldn't create backslash/quote test file", 1;
    local $ExtUtils::Manifest::MANIFEST = "albatross";
    maniadd({ 'foo bar\\baz\'quux' => "backslash and quote"});
    is( maniread()->{'foo bar\\baz\'quux'}, "backslash and quote",
	'backslashed and quoted manifest filename' );
    $funky_files{'space_quote_backslash'} = 'foo bar\\baz\'quux';
}

my @funky_keys = qw(space space_quote space_backslash space_quote_backslash);
# test including an external manifest.skip file in MANIFEST.SKIP
{
    maniadd({ foo => undef , albatross => undef,
              'mymanifest.skip' => undef, 'mydefault.skip' => undef});
    for (@funky_keys) {
        maniadd( {$funky_files{$_} => $_} ) if defined $funky_files{$_};
    }

    add_file('mymanifest.skip' => "^foo\n");
    add_file('mydefault.skip'  => "^my\n");
    local $ExtUtils::Manifest::DEFAULT_MSKIP =
         File::Spec->catfile($cwd, qw(mantest mydefault.skip));
    my $skip = File::Spec->catfile($cwd, qw(mantest mymanifest.skip));
    add_file('MANIFEST.SKIP' =>
             "albatross\n#!include $skip\n#!include_default");
    my ($res, $warn) = catch_warning( \&skipcheck );
    for (qw(albatross foo foobar mymanifest.skip mydefault.skip)) {
        like( $warn, qr/Skipping \b$_\b/,
              "Skipping $_" );
    }
    for my $funky_key (@funky_keys) {
        SKIP: {
            my $funky_file = $funky_files{$funky_key};
	    skip "'$funky_key' not created", 1 unless $funky_file;
	    like( $warn, qr/Skipping \b\Q$funky_file\E\b/,
	      "Skipping $funky_file");
	}
    }
    ($res, $warn) = catch_warning( \&mkmanifest );
    for (qw(albatross foo foobar mymanifest.skip mydefault.skip)) {
        like( $warn, qr/Removed from MANIFEST: \b$_\b/,
              "Removed $_ from MANIFEST" );
    }
    for my $funky_key (@funky_keys) {
        SKIP: {
            my $funky_file = $funky_files{$funky_key};
	    skip "'$funky_key' not created", 1 unless $funky_file;
	    like( $warn, qr/Removed from MANIFEST: \b\Q$funky_file\E\b/,
	      "Removed $funky_file from MANIFEST");
	}
    }
    my $files = maniread;
    ok( ! exists $files->{albatross}, 'albatross excluded via MANIFEST.SKIP' );
    ok( exists $files->{yarrow},      'yarrow included in MANIFEST' );
    ok( exists $files->{bar},         'bar included in MANIFEST' );
    ok( ! exists $files->{foobar},    'foobar excluded via mymanifest.skip' );
    ok( ! exists $files->{foo},       'foo excluded via mymanifest.skip' );
    ok( ! exists $files->{'mymanifest.skip'},
        'mymanifest.skip excluded via mydefault.skip' );
    ok( ! exists $files->{'mydefault.skip'},
        'mydefault.skip excluded via mydefault.skip' );

    # test exclusion of funky files
    for my $funky_key (@funky_keys) {
        SKIP: {
            my $funky_file = $funky_files{$funky_key};
	    skip "'$funky_key' not created", 1 unless $funky_file;
	    ok( ! exists $files->{$funky_file},
		  "'$funky_file' excluded via mymanifest.skip" );
	}
    }

    # tests for maniskip
    my $skipchk = maniskip();
    is ( $skipchk->('albatross'), 1,
	'albatross excluded via MANIFEST.SKIP' );
    is( $skipchk->('yarrow'), '',
	'yarrow included in MANIFEST' );
    is( $skipchk->('bar'), '',
	'bar included in MANIFEST' );
    $skipchk = maniskip('mymanifest.skip');
    is( $skipchk->('foobar'), 1,
	'foobar excluded via mymanifest.skip' );
    is( $skipchk->('foo'), 1,
	'foo excluded via mymanifest.skip' );
    is( $skipchk->('mymanifest.skip'), '',
        'mymanifest.skip included via mydefault.skip' );
    is( $skipchk->('mydefault.skip'), '',
        'mydefault.skip included via mydefault.skip' );
    $skipchk = maniskip('mydefault.skip');
    is( $skipchk->('foobar'), '',
	'foobar included via mydefault.skip' );
    is( $skipchk->('foo'), '',
	'foo included via mydefault.skip' );
    is( $skipchk->('mymanifest.skip'), 1,
        'mymanifest.skip excluded via mydefault.skip' );
    is( $skipchk->('mydefault.skip'), 1,
        'mydefault.skip excluded via mydefault.skip' );

    my $extsep = $Is_VMS_noefs ? '_' : '.';
    $Files{"$_.bak"}++ for ('MANIFEST', "MANIFEST${extsep}SKIP");
}

add_file('MANIFEST'   => 'Makefile.PL');
maniadd({ foo  => 'bar' });
$files = maniread;
# VMS downcases the MANIFEST.  We normalize it here to match.
%$files = map { (lc $_ => $files->{$_}) } keys %$files;
my %expect = ( 'makefile.pl' => '',
               'foo'    => 'bar'
             );
is_deeply( $files, \%expect, 'maniadd() vs MANIFEST without trailing newline');

#add_file('MANIFEST'   => 'Makefile.PL');
#maniadd({ foo => 'bar' });

SKIP: {
    chmod( 0400, 'MANIFEST' );
    skip "Can't make MANIFEST read-only", 2 if -w 'MANIFEST';

    eval {
        maniadd({ 'foo' => 'bar' });
    };
    is( $@, '',  "maniadd() won't open MANIFEST if it doesn't need to" );

    eval {
        maniadd({ 'grrrwoof' => 'yippie' });
    };
    like( $@, qr/^\Qmaniadd() could not open MANIFEST:\E/,
                 "maniadd() dies if it can't open the MANIFEST" );

    chmod( 0600, 'MANIFEST' );
}


END {
	is( unlink( keys %Files ), keys %Files, 'remove all added files' );
	remove_dir( 'moretest', 'copy' );

	# now get rid of the parent directory
	ok( chdir( $cwd ), 'return to parent directory' );
	remove_dir( 'mantest' );
}