This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade Archive-Extract to 0.60
[perl5.git] / cpan / Archive-Extract / t / 01_Archive-Extract.t
1 BEGIN { chdir 't' if -d 't' };
2 BEGIN { mkdir 'out' unless -d 'out' };
3
4 ### left behind, at least on Win32. See core patch #31904
5 END   { rmtree('out') };
6
7 use strict;
8 use lib qw[../lib];
9
10 use constant IS_WIN32   => $^O eq 'MSWin32' ? 1 : 0;
11 use constant IS_CYGWIN  => $^O eq 'cygwin'  ? 1 : 0;
12 use constant IS_VMS     => $^O eq 'VMS'     ? 1 : 0;
13
14 use Cwd                         qw[cwd];
15 use Test::More                  qw[no_plan];
16 use File::Spec;
17 use File::Spec::Unix;
18 use File::Path;
19 use Data::Dumper;
20 use File::Basename              qw[basename];
21 use Module::Load::Conditional   qw[check_install];
22
23 ### uninitialized value in File::Spec warnings come from A::Zip:
24 # t/01_Archive-Extract....ok 135/0Use of uninitialized value in concatenation (.) or string at /opt/lib/perl5/5.8.3/File/Spec/Unix.pm line 313.
25 #         File::Spec::Unix::catpath('File::Spec','','','undef') called at /opt/lib/perl5/site_perl/5.8.3/Archive/Zip.pm line 473
26 #         Archive::Zip::_asLocalName('') called at /opt/lib/perl5/site_perl/5.8.3/Archive/Zip.pm line 652
27 #         Archive::Zip::Archive::extractMember('Archive::Zip::Archive=HASH(0x9679c8)','Archive::Zip::ZipFileMember=HASH(0x9678fc)') called at ../lib/Archive/Extract.pm line 753
28 #         Archive::Extract::_unzip_az('Archive::Extract=HASH(0x966eac)') called at ../lib/Archive/Extract.pm line 674
29 #         Archive::Extract::_unzip('Archive::Extract=HASH(0x966eac)') called at ../lib/Archive/Extract.pm line 275
30 #         Archive::Extract::extract('Archive::Extract=HASH(0x966eac)','to','/Users/kane/sources/p4/other/archive-extract/t/out') called at t/01_Archive-Extract.t line 180
31 #BEGIN { $SIG{__WARN__} = sub { require Carp; Carp::cluck(@_) } };
32
33 if ((IS_WIN32 or IS_CYGWIN) && ! $ENV{PERL_CORE}) {
34     diag( "Older versions of Archive::Zip may cause File::Spec warnings" );
35     diag( "See bug #19713 in rt.cpan.org. It is safe to ignore them" );
36 }
37
38 my $Me      = basename( $0 );
39 my $Class   = 'Archive::Extract';
40
41 use_ok($Class);
42
43 ### debug will always be enabled on dev versions
44 my $Debug   = (not $ENV{PERL_CORE} and
45               ($ARGV[0] or $Archive::Extract::VERSION =~ /_/))
46                 ? 1
47                 : 0;
48
49 my $Self    = File::Spec->rel2abs(
50                     IS_WIN32 ? &Win32::GetShortPathName( cwd() ) : cwd()
51                 );
52 my $SrcDir  = File::Spec->catdir( $Self,'src' );
53 my $OutDir  = File::Spec->catdir( $Self,'out' );
54
55 ### stupid stupid silly stupid warnings silly! ###
56 $Archive::Extract::DEBUG    = $Archive::Extract::DEBUG  = $Debug;
57 $Archive::Extract::WARN     = $Archive::Extract::WARN   = $Debug;
58
59 diag( "\n\n*** DEBUG INFORMATION ENABLED ***\n\n" ) if $Debug;
60
61 # Be as evil as possible to print
62 $\ = "ORS_FLAG";
63 $, = "OFS_FLAG";
64 $" = "LISTSEP_FLAG";
65
66 my $tmpl = {
67     ### plain files
68     'x.bz2' => {    programs    => [qw[bunzip2]],
69                     modules     => [qw[IO::Uncompress::Bunzip2]],
70                     method      => 'is_bz2',
71                     outfile     => 'a',
72                 },
73     'x.tgz' => {    programs    => [qw[gzip tar]],
74                     modules     => [qw[Archive::Tar IO::Zlib]],
75                     method      => 'is_tgz',
76                     outfile     => 'a',
77                 },
78     'x.tar.gz' => { programs    => [qw[gzip tar]],
79                     modules     => [qw[Archive::Tar IO::Zlib]],
80                     method      => 'is_tgz',
81                     outfile     => 'a',
82                 },
83     'x.tar' => {    programs    => [qw[tar]],
84                     modules     => [qw[Archive::Tar]],
85                     method      => 'is_tar',
86                     outfile     => 'a',
87                 },
88     'x.gz'  => {    programs    => [qw[gzip]],
89                     modules     => [qw[Compress::Zlib]],
90                     method      => 'is_gz',
91                     outfile     => 'a',
92                 },
93     'x.Z'   => {    programs    => [qw[uncompress]],
94                     modules     => [qw[Compress::Zlib]],
95                     method      => 'is_Z',
96                     outfile     => 'a',
97                 },
98     'x.zip' => {    programs    => [qw[unzip]],
99                     modules     => [qw[Archive::Zip]],
100                     method      => 'is_zip',
101                     outfile     => 'a',
102                 },
103     'x.jar' => {    programs    => [qw[unzip]],
104                     modules     => [qw[Archive::Zip]],
105                     method      => 'is_zip',
106                     outfile     => 'a',
107                 },
108     'x.ear' => {    programs    => [qw[unzip]],
109                     modules     => [qw[Archive::Zip]],
110                     method      => 'is_zip',
111                     outfile     => 'a',
112                 },
113     'x.war' => {    programs    => [qw[unzip]],
114                     modules     => [qw[Archive::Zip]],
115                     method      => 'is_zip',
116                     outfile     => 'a',
117                 },
118     'x.par' => {    programs    => [qw[unzip]],
119                     modules     => [qw[Archive::Zip]],
120                     method      => 'is_zip',
121                     outfile     => 'a',
122                 },
123     'x.lzma' => {   programs    => [qw[unlzma]],
124                     modules     => [qw[Compress::unLZMA]],
125                     method      => 'is_lzma',
126                     outfile     => 'a',
127                 },
128     'x.xz'   => {   programs    => [qw[unxz]],
129                     modules     => [qw[IO::Uncompress::UnXz]],
130                     method      => 'is_xz',
131                     outfile     => 'a',
132                 },
133     'x.txz'  => {   programs    => [qw[unxz tar]],
134                     modules     => [qw[Archive::Tar
135                                            IO::Uncompress::UnXz]],
136                     method      => 'is_txz',
137                     outfile     => 'a',
138                 },
139     'x.tar.xz'=> {  programs    => [qw[unxz tar]],
140                     modules     => [qw[Archive::Tar
141                                            IO::Uncompress::UnXz]],
142                     method      => 'is_txz',
143                     outfile     => 'a',
144                 },
145     ### with a directory
146     'y.tbz'     => {    programs    => [qw[bunzip2 tar]],
147                         modules     => [qw[Archive::Tar
148                                            IO::Uncompress::Bunzip2]],
149                         method      => 'is_tbz',
150                         outfile     => 'z',
151                         outdir      => 'y',
152                     },
153     'y.tar.bz2' => {    programs    => [qw[bunzip2 tar]],
154                         modules     => [qw[Archive::Tar
155                                            IO::Uncompress::Bunzip2]],
156                         method      => 'is_tbz',
157                         outfile     => 'z',
158                         outdir      => 'y'
159                     },
160     'y.txz'     => {    programs    => [qw[unxz tar]],
161                         modules     => [qw[Archive::Tar
162                                            IO::Uncompress::UnXz]],
163                         method      => 'is_txz',
164                         outfile     => 'z',
165                         outdir      => 'y',
166                     },
167     'y.tar.xz'  => {    programs    => [qw[unxz tar]],
168                         modules     => [qw[Archive::Tar
169                                            IO::Uncompress::UnXz]],
170                         method      => 'is_txz',
171                         outfile     => 'z',
172                         outdir      => 'y'
173                     },
174     'y.tgz'     => {    programs    => [qw[gzip tar]],
175                         modules     => [qw[Archive::Tar IO::Zlib]],
176                         method      => 'is_tgz',
177                         outfile     => 'z',
178                         outdir      => 'y'
179                     },
180     'y.tar.gz' => {     programs    => [qw[gzip tar]],
181                         modules     => [qw[Archive::Tar IO::Zlib]],
182                         method      => 'is_tgz',
183                         outfile     => 'z',
184                         outdir      => 'y'
185                     },
186     'y.tar' => {    programs    => [qw[tar]],
187                     modules     => [qw[Archive::Tar]],
188                     method      => 'is_tar',
189                     outfile     => 'z',
190                     outdir      => 'y'
191                 },
192     'y.zip' => {    programs    => [qw[unzip]],
193                     modules     => [qw[Archive::Zip]],
194                     method      => 'is_zip',
195                     outfile     => 'z',
196                     outdir      => 'y'
197                 },
198     'y.par' => {    programs    => [qw[unzip]],
199                     modules     => [qw[Archive::Zip]],
200                     method      => 'is_zip',
201                     outfile     => 'z',
202                     outdir      => 'y'
203                 },
204     'y.jar' => {    programs    => [qw[unzip]],
205                     modules     => [qw[Archive::Zip]],
206                     method      => 'is_zip',
207                     outfile     => 'z',
208                     outdir      => 'y'
209                 },
210     'y.ear' => {    programs    => [qw[unzip]],
211                     modules     => [qw[Archive::Zip]],
212                     method      => 'is_zip',
213                     outfile     => 'z',
214                     outdir      => 'y'
215                 },
216     'y.war' => {    programs    => [qw[unzip]],
217                     modules     => [qw[Archive::Zip]],
218                     method      => 'is_zip',
219                     outfile     => 'z',
220                     outdir      => 'y'
221               },
222     ### with non-same top dir
223     'double_dir.zip' => {
224                     programs    => [qw[unzip]],
225                     modules     => [qw[Archive::Zip]],
226                     method      => 'is_zip',
227                     outfile     => 'w',
228                     outdir      => 'x'
229                 },
230 };
231
232 ### XXX special case: on older solaris boxes (8),
233 ### bunzip2 is version 0.9.x. Older versions (pre 1),
234 ### only extract files that end in .bz2, and nothing
235 ### else. So remove that test case if we have an older
236 ### bunzip2 :(
237 {   if( $Class->have_old_bunzip2 ) {
238         delete $tmpl->{'y.tbz'};
239         diag "Old bunzip2 detected, skipping .tbz test";
240     }
241 }
242
243 ### show us the tools IPC::Cmd will use to run binary programs
244 if( $Debug ) {
245     diag( "IPC::Run enabled: $IPC::Cmd::USE_IPC_RUN " );
246     diag( "IPC::Run available: " . IPC::Cmd->can_use_ipc_run );
247     diag( "IPC::Run vesion: $IPC::Run::VERSION" );
248     diag( "IPC::Open3 enabled: $IPC::Cmd::USE_IPC_OPEN3 " );
249     diag( "IPC::Open3 available: " . IPC::Cmd->can_use_ipc_open3 );
250     diag( "IPC::Open3 vesion: $IPC::Open3::VERSION" );
251 }
252
253 ### test all type specifications to new()
254 ### this tests bug #24578: Wrong check for `type' argument
255 {   my $meth = 'types';
256
257     can_ok( $Class, $meth );
258
259     my @types = $Class->$meth;
260     ok( scalar(@types),         "   Got a list of types" );
261
262     for my $type ( @types ) {
263         my $obj = $Class->new( archive => $Me, type => $type );
264         ok( $obj,               "   Object created based on '$type'" );
265         ok( !$obj->error,       "       No error logged" );
266     }
267
268     ### test unknown type
269     {   ### must turn on warnings to catch error here
270         local $Archive::Extract::WARN = 1;
271
272         my $warnings;
273         local $SIG{__WARN__} = sub { $warnings .= "@_" };
274
275         my $ae = $Class->new( archive => $Me );
276         ok( !$ae,               "   No archive created based on '$Me'" );
277         ok( !$Class->error,     "       Error not captured in class method" );
278         ok( $warnings,          "       Error captured as warning" );
279         like( $warnings, qr/Cannot determine file type for/,
280                                 "           Error is: unknown file type" );
281     }
282 }
283
284 ### test multiple errors
285 ### XXX whitebox test
286 {   ### grab a random file from the template, so we can make an object
287     my $ae = Archive::Extract->new(
288                 archive =>  File::Spec->catfile($SrcDir,[keys %$tmpl]->[0])
289              );
290     ok( $ae,                    "Archive created" );
291     ok( not($ae->error),        "   No errors yet" );
292
293     ### log a few errors
294     {   local $Archive::Extract::WARN = 0;
295         $ae->_error( $_ ) for 1..5;
296     }
297
298     my $err = $ae->error;
299     ok( $err,                   "   Errors retrieved" );
300
301     my $expect = join $/, 1..5;
302     is( $err, $expect,          "       As expected" );
303
304     ### this resets the errors
305     ### override the 'check' routine to return false, so we bail out of
306     ### extract() early and just run the error reset code;
307     {   no warnings qw[once redefine];
308         local *Archive::Extract::check = sub { return };
309         $ae->extract;
310     }
311     ok( not($ae->error),        "   Errors erased after ->extract() call" );
312 }
313
314 ### XXX whitebox test
315 ### test __get_extract_dir
316 SKIP: {   my $meth = '__get_extract_dir';
317
318     ### get the right separator -- File::Spec does clean ups for
319     ### paths, so we need to join ourselves.
320     my $sep  = [ split '', File::Spec->catfile( 'a', 'b' ) ]->[1];
321
322     ### bug #23999: Attempt to generate Makefile.PL gone awry
323     ### showed that dirs in the style of './dir/' were reported
324     ### to be unpacked in '.' rather than in 'dir'. here we test
325     ### for this.
326     for my $prefix ( '', '.' ) {
327         skip "Prepending ./ to a valid path doesn't give you another valid path on VMS", 2
328             if IS_VMS && length($prefix);
329
330         my $dir = basename( $SrcDir );
331
332         ### build a list like [dir, dir/file] and [./dir ./dir/file]
333         ### where the dir and file actually exist, which is important
334         ### for the method call
335         my @files = map { length $prefix
336                                 ? join $sep, $prefix, $_
337                                 : $_
338                       } $dir, File::Spec->catfile( $dir, [keys %$tmpl]->[0] );
339
340         my $res = $Class->$meth( \@files );
341         $res = &Win32::GetShortPathName( $res ) if IS_WIN32;
342
343         ok( $res,               "Found extraction dir '$res'" );
344         is( $res, $SrcDir,      "   Is expected dir '$SrcDir'" );
345     }
346 }
347
348 ### configuration to run in: allow perl or allow binaries
349 for my $switch ( [0,1], [1,0] ) {
350     my $cfg = "PP: $switch->[0] Bin: $switch->[1]";
351
352     local $Archive::Extract::_ALLOW_PURE_PERL   = $switch->[0];
353     local $Archive::Extract::_ALLOW_BIN         = $switch->[1];
354
355     diag("Running extract with configuration: $cfg") if $Debug;
356
357     for my $archive (keys %$tmpl) {
358         diag("Archive : $archive") if $Debug;
359
360         ### check first if we can do the proper
361
362         my $ae = Archive::Extract->new(
363                         archive => File::Spec->catfile($SrcDir,$archive) );
364
365         ### Do an extra run with _ALLOW_TAR_ITER = 0 if it's a tar file of some
366         ### sort
367         my @with_tar_iter = ( 1 );
368         push @with_tar_iter, 0 if grep { $ae->$_ } qw[is_tbz is_tgz is_txz is_tar];
369
370         for my $tar_iter (@with_tar_iter) { SKIP: {
371
372             ### Doesn't matter unless .tar, .tbz, .tgz, .txz
373             local $Archive::Extract::_ALLOW_TAR_ITER = $tar_iter;
374
375             diag("Archive::Tar->iter: $tar_iter") if $Debug;
376
377             isa_ok( $ae, $Class );
378
379             my $method = $tmpl->{$archive}->{method};
380             ok( $ae->$method(),         "Archive type $method recognized properly" );
381
382             my $file        = $tmpl->{$archive}->{outfile};
383             my $dir         = $tmpl->{$archive}->{outdir};  # can be undef
384             my $rel_path    = File::Spec->catfile( grep { defined } $dir, $file );
385             my $abs_path    = File::Spec->catfile( $OutDir, $rel_path );
386             my $abs_dir     = File::Spec->catdir(
387                                 grep { defined } $OutDir, $dir );
388             my $nix_path    = File::Spec::Unix->catfile(
389                                 grep { defined } $dir, $file );
390
391             ### check if we can run this test ###
392             my $pgm_fail; my $mod_fail;
393             for my $pgm ( @{$tmpl->{$archive}->{programs}} ) {
394                 ### no binary extract method
395                 $pgm_fail++, next unless $pgm;
396
397                 ### we dont have the program
398                 $pgm_fail++ unless $Archive::Extract::PROGRAMS->{$pgm} &&
399                                    $Archive::Extract::PROGRAMS->{$pgm};
400
401             }
402
403             for my $mod ( @{$tmpl->{$archive}->{modules}} ) {
404                 ### no module extract method
405                 $mod_fail++, next unless $mod;
406
407                 ### we dont have the module
408                 $mod_fail++ unless check_install( module => $mod );
409             }
410
411             ### where to extract to -- try both dir and file for gz files
412             ### XXX test me!
413             #my @outs = $ae->is_gz ? ($abs_path, $OutDir) : ($OutDir);
414             my @outs = $ae->is_gz || $ae->is_bz2 || $ae->is_Z || $ae->is_lzma || $ae->is_xz
415                             ? ($abs_path)
416                             : ($OutDir);
417
418             ### 10 tests from here on down ###
419             if( ($mod_fail && ($pgm_fail || !$Archive::Extract::_ALLOW_BIN))
420                 ||
421                 ($pgm_fail && ($mod_fail || !$Archive::Extract::_ALLOW_PURE_PERL))
422             ) {
423                 skip "No binaries or modules to extract ".$archive,
424                     (10 * scalar @outs);
425             }
426
427             ### we dont warnings spewed about missing modules, that might
428             ### be a problem...
429             local $IPC::Cmd::WARN = 0;
430             local $IPC::Cmd::WARN = 0;
431
432             for my $use_buffer ( IPC::Cmd->can_capture_buffer , 0 ) {
433
434                 ### test buffers ###
435                 my $turn_off = !$use_buffer && !$pgm_fail &&
436                                 $Archive::Extract::_ALLOW_BIN;
437
438                 ### whitebox test ###
439                 ### stupid warnings ###
440                 local $IPC::Cmd::USE_IPC_RUN    = 0 if $turn_off;
441                 local $IPC::Cmd::USE_IPC_RUN    = 0 if $turn_off;
442                 local $IPC::Cmd::USE_IPC_OPEN3  = 0 if $turn_off;
443                 local $IPC::Cmd::USE_IPC_OPEN3  = 0 if $turn_off;
444
445
446                 ### try extracting ###
447                 for my $to ( @outs ) {
448
449                     diag("Extracting to: $to")                  if $Debug;
450                     diag("Buffers enabled: ".!$turn_off)        if $Debug;
451
452                     my $rv = $ae->extract( to => $to );
453
454                     SKIP: {
455                         my $re  = qr/^No buffer captured/;
456                         my $err = $ae->error || '';
457
458                         ### skip buffer tests if we dont have buffers or
459                         ### explicitly turned them off
460                         skip "No buffers available", 8
461                             if ( $turn_off || !IPC::Cmd->can_capture_buffer)
462                                 && $err =~ $re;
463
464                         ### skip tests if we dont have an extractor
465                         skip "No extractor available", 8
466                             if $err =~ /Extract failed; no extractors available/;
467
468                         ### win32 + bin utils is notorious, and none of them are
469                         ### officially supported by strawberry. So if we
470                         ### encounter an error while extracting while running
471                         ### with $PREFER_BIN on win32, just skip the tests.
472                         ### See rt#46948: unable to install install on win32
473                         ### for details on the pain
474                         skip "Binary tools on Win32 are very unreliable", 8
475                             if $err and $Archive::Extract::_ALLOW_BIN
476                                     and IS_WIN32;
477
478                         ok( $rv, "extract() for '$archive' reports success ($cfg)");
479
480                         diag("Extractor was: " . $ae->_extractor)   if $Debug;
481
482                         ### if we /should/ have buffers, there should be
483                         ### no errors complaining we dont have them...
484                         unlike( $err, $re,
485                                         "No errors capturing buffers" );
486
487                         ### might be 1 or 2, depending whether we extracted
488                         ### a dir too
489                         my $files    = $ae->files || [];
490                         my $file_cnt = grep { defined } $file, $dir;
491                         is( scalar @$files, $file_cnt,
492                                         "Found correct number of output files (@$files)" );
493
494                         ### due to prototypes on is(), if there's no -1 index on
495                         ### the array ref, it'll give a fatal exception:
496                         ### "Modification of non-creatable array value attempted,
497                         ### subscript -1 at -e line 1." So wrap it in do { }
498                         is( do { $files->[-1] }, $nix_path,
499                                         "Found correct output file '$nix_path'" );
500
501                         ok( -e $abs_path,
502                                         "Output file '$abs_path' exists" );
503                         ok( $ae->extract_path,
504                                         "Extract dir found" );
505                         ok( -d $ae->extract_path,
506                                         "Extract dir exists" );
507                         is( $ae->extract_path, $abs_dir,
508                                         "Extract dir is expected '$abs_dir'" );
509                     }
510
511                     SKIP: {
512                         skip "Unlink tests are unreliable on Win32", 3 if IS_WIN32;
513
514                         1 while unlink $abs_path;
515                         ok( !(-e $abs_path), "Output file successfully removed" );
516
517                         SKIP: {
518                             skip "No extract path captured, can't remove paths", 2
519                                 unless $ae->extract_path;
520
521                             ### if something went wrong with determining the out
522                             ### path, don't go deleting stuff.. might be Really Bad
523                             my $out_re = quotemeta( $OutDir );
524
525                             ### VMS directory layout is different. Craig Berry
526                             ### explains:
527                             ### the test is trying to determine if C</disk1/foo/bar>
528                             ### is part of C</disk1/foo/bar/baz>.  Except in VMS
529                             ### syntax, that would mean trying to determine whether
530                             ### C<disk1:[foo.bar]> is part of C<disk1:[foo.bar.baz]>
531                             ### Because we have both a directory delimiter
532                             ### (dot) and a directory spec terminator (right
533                             ### bracket), we have to trim the right bracket from
534                             ### the first one to make it successfully match the
535                             ### second one.  Since we're asserting the same truth --
536                             ### that one path spec is the leading part of the other
537                             ### -- it seems to me ok to have this in the test only.
538                             ###
539                             ### so we strip the ']' of the back of the regex
540                             $out_re =~ s/\\\]// if IS_VMS;
541
542                             if( $ae->extract_path !~ /^$out_re/ ) {
543                                 ok( 0, "Extractpath WRONG (".$ae->extract_path.")");
544                                 skip(  "Unsafe operation -- skip cleanup!!!" ), 1;
545                             }
546
547                             eval { rmtree( $ae->extract_path ) };
548                             ok( !$@,        "   rmtree gave no error" );
549                             ok( !(-d $ae->extract_path ),
550                                             "   Extract dir successfully removed" );
551                         }
552                     }
553                 }
554             }
555         } }
556     }
557 }