diff options
Diffstat (limited to 't/01_Archive-Extract.t')
-rw-r--r-- | t/01_Archive-Extract.t | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/t/01_Archive-Extract.t b/t/01_Archive-Extract.t new file mode 100644 index 0000000..cb67d27 --- /dev/null +++ b/t/01_Archive-Extract.t @@ -0,0 +1,557 @@ +BEGIN { chdir 't' if -d 't' }; +BEGIN { mkdir 'out' unless -d 'out' }; + +### left behind, at least on Win32. See core patch #31904 +END { rmtree('out') }; + +use strict; +use lib qw[../lib]; + +use constant IS_WIN32 => $^O eq 'MSWin32' ? 1 : 0; +use constant IS_CYGWIN => $^O eq 'cygwin' ? 1 : 0; +use constant IS_VMS => $^O eq 'VMS' ? 1 : 0; + +use Cwd qw[cwd]; +use Test::More qw[no_plan]; +use File::Spec; +use File::Spec::Unix; +use File::Path; +use Data::Dumper; +use File::Basename qw[basename]; +use Module::Load::Conditional qw[check_install]; + +### uninitialized value in File::Spec warnings come from A::Zip: +# 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. +# File::Spec::Unix::catpath('File::Spec','','','undef') called at /opt/lib/perl5/site_perl/5.8.3/Archive/Zip.pm line 473 +# Archive::Zip::_asLocalName('') called at /opt/lib/perl5/site_perl/5.8.3/Archive/Zip.pm line 652 +# Archive::Zip::Archive::extractMember('Archive::Zip::Archive=HASH(0x9679c8)','Archive::Zip::ZipFileMember=HASH(0x9678fc)') called at ../lib/Archive/Extract.pm line 753 +# Archive::Extract::_unzip_az('Archive::Extract=HASH(0x966eac)') called at ../lib/Archive/Extract.pm line 674 +# Archive::Extract::_unzip('Archive::Extract=HASH(0x966eac)') called at ../lib/Archive/Extract.pm line 275 +# 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 +#BEGIN { $SIG{__WARN__} = sub { require Carp; Carp::cluck(@_) } }; + +if ((IS_WIN32 or IS_CYGWIN) && ! $ENV{PERL_CORE}) { + diag( "Older versions of Archive::Zip may cause File::Spec warnings" ); + diag( "See bug #19713 in rt.cpan.org. It is safe to ignore them" ); +} + +my $Me = basename( $0 ); +my $Class = 'Archive::Extract'; + +use_ok($Class); + +### debug will always be enabled on dev versions +my $Debug = (not $ENV{PERL_CORE} and + ($ARGV[0] or $Archive::Extract::VERSION =~ /_/)) + ? 1 + : 0; + +my $Self = File::Spec->rel2abs( + IS_WIN32 ? &Win32::GetShortPathName( cwd() ) : cwd() + ); +my $SrcDir = File::Spec->catdir( $Self,'src' ); +my $OutDir = File::Spec->catdir( $Self,'out' ); + +### stupid stupid silly stupid warnings silly! ### +$Archive::Extract::DEBUG = $Archive::Extract::DEBUG = $Debug; +$Archive::Extract::WARN = $Archive::Extract::WARN = $Debug; + +diag( "\n\n*** DEBUG INFORMATION ENABLED ***\n\n" ) if $Debug; + +# Be as evil as possible to print +$\ = "ORS_FLAG"; +$, = "OFS_FLAG"; +$" = "LISTSEP_FLAG"; + +my $tmpl = { + ### plain files + 'x.bz2' => { programs => [qw[bunzip2]], + modules => [qw[IO::Uncompress::Bunzip2]], + method => 'is_bz2', + outfile => 'a', + }, + 'x.tgz' => { programs => [qw[gzip tar]], + modules => [qw[Archive::Tar IO::Zlib]], + method => 'is_tgz', + outfile => 'a', + }, + 'x.tar.gz' => { programs => [qw[gzip tar]], + modules => [qw[Archive::Tar IO::Zlib]], + method => 'is_tgz', + outfile => 'a', + }, + 'x.tar' => { programs => [qw[tar]], + modules => [qw[Archive::Tar]], + method => 'is_tar', + outfile => 'a', + }, + 'x.gz' => { programs => [qw[gzip]], + modules => [qw[Compress::Zlib]], + method => 'is_gz', + outfile => 'a', + }, + 'x.Z' => { programs => [qw[uncompress]], + modules => [qw[Compress::Zlib]], + method => 'is_Z', + outfile => 'a', + }, + 'x.zip' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'a', + }, + 'x.jar' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'a', + }, + 'x.ear' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'a', + }, + 'x.war' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'a', + }, + 'x.par' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'a', + }, + 'x.lzma' => { programs => [qw[unlzma]], + modules => [qw[Compress::unLZMA]], + method => 'is_lzma', + outfile => 'a', + }, + 'x.xz' => { programs => [qw[unxz]], + modules => [qw[IO::Uncompress::UnXz]], + method => 'is_xz', + outfile => 'a', + }, + 'x.txz' => { programs => [qw[unxz tar]], + modules => [qw[Archive::Tar + IO::Uncompress::UnXz]], + method => 'is_txz', + outfile => 'a', + }, + 'x.tar.xz'=> { programs => [qw[unxz tar]], + modules => [qw[Archive::Tar + IO::Uncompress::UnXz]], + method => 'is_txz', + outfile => 'a', + }, + ### with a directory + 'y.tbz' => { programs => [qw[bunzip2 tar]], + modules => [qw[Archive::Tar + IO::Uncompress::Bunzip2]], + method => 'is_tbz', + outfile => 'z', + outdir => 'y', + }, + 'y.tar.bz2' => { programs => [qw[bunzip2 tar]], + modules => [qw[Archive::Tar + IO::Uncompress::Bunzip2]], + method => 'is_tbz', + outfile => 'z', + outdir => 'y' + }, + 'y.txz' => { programs => [qw[unxz tar]], + modules => [qw[Archive::Tar + IO::Uncompress::UnXz]], + method => 'is_txz', + outfile => 'z', + outdir => 'y', + }, + 'y.tar.xz' => { programs => [qw[unxz tar]], + modules => [qw[Archive::Tar + IO::Uncompress::UnXz]], + method => 'is_txz', + outfile => 'z', + outdir => 'y' + }, + 'y.tgz' => { programs => [qw[gzip tar]], + modules => [qw[Archive::Tar IO::Zlib]], + method => 'is_tgz', + outfile => 'z', + outdir => 'y' + }, + 'y.tar.gz' => { programs => [qw[gzip tar]], + modules => [qw[Archive::Tar IO::Zlib]], + method => 'is_tgz', + outfile => 'z', + outdir => 'y' + }, + 'y.tar' => { programs => [qw[tar]], + modules => [qw[Archive::Tar]], + method => 'is_tar', + outfile => 'z', + outdir => 'y' + }, + 'y.zip' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'z', + outdir => 'y' + }, + 'y.par' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'z', + outdir => 'y' + }, + 'y.jar' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'z', + outdir => 'y' + }, + 'y.ear' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'z', + outdir => 'y' + }, + 'y.war' => { programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'z', + outdir => 'y' + }, + ### with non-same top dir + 'double_dir.zip' => { + programs => [qw[unzip]], + modules => [qw[Archive::Zip]], + method => 'is_zip', + outfile => 'w', + outdir => 'x' + }, +}; + +### XXX special case: on older solaris boxes (8), +### bunzip2 is version 0.9.x. Older versions (pre 1), +### only extract files that end in .bz2, and nothing +### else. So remove that test case if we have an older +### bunzip2 :( +{ if( $Class->have_old_bunzip2 ) { + delete $tmpl->{'y.tbz'}; + diag "Old bunzip2 detected, skipping .tbz test"; + } +} + +### show us the tools IPC::Cmd will use to run binary programs +if( $Debug ) { + diag( "IPC::Run enabled: $IPC::Cmd::USE_IPC_RUN " ); + diag( "IPC::Run available: " . IPC::Cmd->can_use_ipc_run ); + diag( "IPC::Run vesion: $IPC::Run::VERSION" ); + diag( "IPC::Open3 enabled: $IPC::Cmd::USE_IPC_OPEN3 " ); + diag( "IPC::Open3 available: " . IPC::Cmd->can_use_ipc_open3 ); + diag( "IPC::Open3 vesion: $IPC::Open3::VERSION" ); +} + +### test all type specifications to new() +### this tests bug #24578: Wrong check for `type' argument +{ my $meth = 'types'; + + can_ok( $Class, $meth ); + + my @types = $Class->$meth; + ok( scalar(@types), " Got a list of types" ); + + for my $type ( @types ) { + my $obj = $Class->new( archive => $Me, type => $type ); + ok( $obj, " Object created based on '$type'" ); + ok( !$obj->error, " No error logged" ); + } + + ### test unknown type + { ### must turn on warnings to catch error here + local $Archive::Extract::WARN = 1; + + my $warnings; + local $SIG{__WARN__} = sub { $warnings .= "@_" }; + + my $ae = $Class->new( archive => $Me ); + ok( !$ae, " No archive created based on '$Me'" ); + ok( !$Class->error, " Error not captured in class method" ); + ok( $warnings, " Error captured as warning" ); + like( $warnings, qr/Cannot determine file type for/, + " Error is: unknown file type" ); + } +} + +### test multiple errors +### XXX whitebox test +{ ### grab a random file from the template, so we can make an object + my $ae = Archive::Extract->new( + archive => File::Spec->catfile($SrcDir,[keys %$tmpl]->[0]) + ); + ok( $ae, "Archive created" ); + ok( not($ae->error), " No errors yet" ); + + ### log a few errors + { local $Archive::Extract::WARN = 0; + $ae->_error( $_ ) for 1..5; + } + + my $err = $ae->error; + ok( $err, " Errors retrieved" ); + + my $expect = join $/, 1..5; + is( $err, $expect, " As expected" ); + + ### this resets the errors + ### override the 'check' routine to return false, so we bail out of + ### extract() early and just run the error reset code; + { no warnings qw[once redefine]; + local *Archive::Extract::check = sub { return }; + $ae->extract; + } + ok( not($ae->error), " Errors erased after ->extract() call" ); +} + +### XXX whitebox test +### test __get_extract_dir +SKIP: { my $meth = '__get_extract_dir'; + + ### get the right separator -- File::Spec does clean ups for + ### paths, so we need to join ourselves. + my $sep = [ split '', File::Spec->catfile( 'a', 'b' ) ]->[1]; + + ### bug #23999: Attempt to generate Makefile.PL gone awry + ### showed that dirs in the style of './dir/' were reported + ### to be unpacked in '.' rather than in 'dir'. here we test + ### for this. + for my $prefix ( '', '.' ) { + skip "Prepending ./ to a valid path doesn't give you another valid path on VMS", 2 + if IS_VMS && length($prefix); + + my $dir = basename( $SrcDir ); + + ### build a list like [dir, dir/file] and [./dir ./dir/file] + ### where the dir and file actually exist, which is important + ### for the method call + my @files = map { length $prefix + ? join $sep, $prefix, $_ + : $_ + } $dir, File::Spec->catfile( $dir, [keys %$tmpl]->[0] ); + + my $res = $Class->$meth( \@files ); + $res = &Win32::GetShortPathName( $res ) if IS_WIN32; + + ok( $res, "Found extraction dir '$res'" ); + is( $res, $SrcDir, " Is expected dir '$SrcDir'" ); + } +} + +### configuration to run in: allow perl or allow binaries +for my $switch ( [0,1], [1,0] ) { + my $cfg = "PP: $switch->[0] Bin: $switch->[1]"; + + local $Archive::Extract::_ALLOW_PURE_PERL = $switch->[0]; + local $Archive::Extract::_ALLOW_BIN = $switch->[1]; + + diag("Running extract with configuration: $cfg") if $Debug; + + for my $archive (keys %$tmpl) { + diag("Archive : $archive") if $Debug; + + ### check first if we can do the proper + + my $ae = Archive::Extract->new( + archive => File::Spec->catfile($SrcDir,$archive) ); + + ### Do an extra run with _ALLOW_TAR_ITER = 0 if it's a tar file of some + ### sort + my @with_tar_iter = ( 1 ); + push @with_tar_iter, 0 if grep { $ae->$_ } qw[is_tbz is_tgz is_txz is_tar]; + + for my $tar_iter (@with_tar_iter) { SKIP: { + + ### Doesn't matter unless .tar, .tbz, .tgz, .txz + local $Archive::Extract::_ALLOW_TAR_ITER = $tar_iter; + + diag("Archive::Tar->iter: $tar_iter") if $Debug; + + isa_ok( $ae, $Class ); + + my $method = $tmpl->{$archive}->{method}; + ok( $ae->$method(), "Archive type $method recognized properly" ); + + my $file = $tmpl->{$archive}->{outfile}; + my $dir = $tmpl->{$archive}->{outdir}; # can be undef + my $rel_path = File::Spec->catfile( grep { defined } $dir, $file ); + my $abs_path = File::Spec->catfile( $OutDir, $rel_path ); + my $abs_dir = File::Spec->catdir( + grep { defined } $OutDir, $dir ); + my $nix_path = File::Spec::Unix->catfile( + grep { defined } $dir, $file ); + + ### check if we can run this test ### + my $pgm_fail; my $mod_fail; + for my $pgm ( @{$tmpl->{$archive}->{programs}} ) { + ### no binary extract method + $pgm_fail++, next unless $pgm; + + ### we dont have the program + $pgm_fail++ unless $Archive::Extract::PROGRAMS->{$pgm} && + $Archive::Extract::PROGRAMS->{$pgm}; + + } + + for my $mod ( @{$tmpl->{$archive}->{modules}} ) { + ### no module extract method + $mod_fail++, next unless $mod; + + ### we dont have the module + $mod_fail++ unless check_install( module => $mod ); + } + + ### where to extract to -- try both dir and file for gz files + ### XXX test me! + #my @outs = $ae->is_gz ? ($abs_path, $OutDir) : ($OutDir); + my @outs = $ae->is_gz || $ae->is_bz2 || $ae->is_Z || $ae->is_lzma || $ae->is_xz + ? ($abs_path) + : ($OutDir); + + ### 10 tests from here on down ### + if( ($mod_fail && ($pgm_fail || !$Archive::Extract::_ALLOW_BIN)) + || + ($pgm_fail && ($mod_fail || !$Archive::Extract::_ALLOW_PURE_PERL)) + ) { + skip "No binaries or modules to extract ".$archive, + (10 * scalar @outs); + } + + ### we dont warnings spewed about missing modules, that might + ### be a problem... + local $IPC::Cmd::WARN = 0; + local $IPC::Cmd::WARN = 0; + + for my $use_buffer ( IPC::Cmd->can_capture_buffer , 0 ) { + + ### test buffers ### + my $turn_off = !$use_buffer && !$pgm_fail && + $Archive::Extract::_ALLOW_BIN; + + ### whitebox test ### + ### stupid warnings ### + local $IPC::Cmd::USE_IPC_RUN = 0 if $turn_off; + local $IPC::Cmd::USE_IPC_RUN = 0 if $turn_off; + local $IPC::Cmd::USE_IPC_OPEN3 = 0 if $turn_off; + local $IPC::Cmd::USE_IPC_OPEN3 = 0 if $turn_off; + + + ### try extracting ### + for my $to ( @outs ) { + + diag("Extracting to: $to") if $Debug; + diag("Buffers enabled: ".!$turn_off) if $Debug; + + my $rv = $ae->extract( to => $to ); + + SKIP: { + my $re = qr/^No buffer captured/; + my $err = $ae->error || ''; + + ### skip buffer tests if we dont have buffers or + ### explicitly turned them off + skip "No buffers available", 8 + if ( $turn_off || !IPC::Cmd->can_capture_buffer) + && $err =~ $re; + + ### skip tests if we dont have an extractor + skip "No extractor available", 8 + if $err =~ /Extract failed; no extractors available/; + + ### win32 + bin utils is notorious, and none of them are + ### officially supported by strawberry. So if we + ### encounter an error while extracting while running + ### with $PREFER_BIN on win32, just skip the tests. + ### See rt#46948: unable to install install on win32 + ### for details on the pain + skip "Binary tools on Win32 are very unreliable", 8 + if $err and $Archive::Extract::_ALLOW_BIN + and IS_WIN32; + + ok( $rv, "extract() for '$archive' reports success ($cfg)"); + + diag("Extractor was: " . $ae->_extractor) if $Debug; + + ### if we /should/ have buffers, there should be + ### no errors complaining we dont have them... + unlike( $err, $re, + "No errors capturing buffers" ); + + ### might be 1 or 2, depending whether we extracted + ### a dir too + my $files = $ae->files || []; + my $file_cnt = grep { defined } $file, $dir; + is( scalar @$files, $file_cnt, + "Found correct number of output files (@$files)" ); + + ### due to prototypes on is(), if there's no -1 index on + ### the array ref, it'll give a fatal exception: + ### "Modification of non-creatable array value attempted, + ### subscript -1 at -e line 1." So wrap it in do { } + is( do { $files->[-1] }, $nix_path, + "Found correct output file '$nix_path'" ); + + ok( -e $abs_path, + "Output file '$abs_path' exists" ); + ok( $ae->extract_path, + "Extract dir found" ); + ok( -d $ae->extract_path, + "Extract dir exists" ); + is( $ae->extract_path, $abs_dir, + "Extract dir is expected '$abs_dir'" ); + } + + SKIP: { + skip "Unlink tests are unreliable on Win32", 3 if IS_WIN32; + + 1 while unlink $abs_path; + ok( !(-e $abs_path), "Output file successfully removed" ); + + SKIP: { + skip "No extract path captured, can't remove paths", 2 + unless $ae->extract_path; + + ### if something went wrong with determining the out + ### path, don't go deleting stuff.. might be Really Bad + my $out_re = quotemeta( $OutDir ); + + ### VMS directory layout is different. Craig Berry + ### explains: + ### the test is trying to determine if C</disk1/foo/bar> + ### is part of C</disk1/foo/bar/baz>. Except in VMS + ### syntax, that would mean trying to determine whether + ### C<disk1:[foo.bar]> is part of C<disk1:[foo.bar.baz]> + ### Because we have both a directory delimiter + ### (dot) and a directory spec terminator (right + ### bracket), we have to trim the right bracket from + ### the first one to make it successfully match the + ### second one. Since we're asserting the same truth -- + ### that one path spec is the leading part of the other + ### -- it seems to me ok to have this in the test only. + ### + ### so we strip the ']' of the back of the regex + $out_re =~ s/\\\]// if IS_VMS; + + if( $ae->extract_path !~ /^$out_re/ ) { + ok( 0, "Extractpath WRONG (".$ae->extract_path.")"); + skip( "Unsafe operation -- skip cleanup!!!" ), 1; + } + + eval { rmtree( $ae->extract_path ) }; + ok( !$@, " rmtree gave no error" ); + ok( !(-d $ae->extract_path ), + " Extract dir successfully removed" ); + } + } + } + } + } } + } +} |