Upgrade to Archive::Tar 1.29
[p5sagit/p5-mst-13.2.git] / lib / Archive / Tar / t / 02_methods.t
1 BEGIN {
2     if( $ENV{PERL_CORE} ) {
3         chdir '../lib/Archive/Tar' if -d '../lib/Archive/Tar';
4     }       
5     use lib '../../..';
6 }
7
8 BEGIN { chdir 't' if -d 't' }
9
10 use Test::More 'no_plan';
11 use strict;
12 use lib '../lib';
13
14 use Cwd;
15 use Config;
16 use IO::File;
17 use File::Copy;
18 use File::Path;
19 use File::Spec          ();
20 use File::Spec::Unix    ();
21 use File::Basename      ();
22 use Data::Dumper;
23
24 use Archive::Tar;
25 use Archive::Tar::Constant;
26
27 ### XXX TODO:
28 ### * change to fullname
29 ### * add tests for global variables
30
31 ### set up the environment ###
32 my @EXPECT_NORMAL = (
33     ### dirs        filename    contents
34     [   [],         'c',        qr/^iiiiiiiiiiii\s*$/ ],
35     [   [],         'd',        qr/^uuuuuuuu\s*$/ ],
36 );
37
38 ### includes binary data
39 my $ALL_CHARS = join '', "\r\n", map( chr, 1..255 ), "zzz\n\r";
40
41 ### @EXPECTBIN is used to ensure that $tarbin is written in the right
42 ### order and that the contents and order match exactly when extracted
43 my @EXPECTBIN = (
44     ###  dirs   filename      contents       ###
45     [    [],    'bIn11',      $ALL_CHARS x 11 ],
46     [    [],    'bIn3',       $ALL_CHARS x  3 ],
47     [    [],    'bIn4',       $ALL_CHARS x  4 ],
48     [    [],    'bIn1',       $ALL_CHARS      ],
49     [    [],    'bIn2',       $ALL_CHARS x  2 ],
50 );
51
52 ### @EXPECTX is used to ensure that $tarx is written in the right
53 ### order and that the contents and order match exactly when extracted
54 ### the 'x/x' extraction used to fail before A::T 1.08
55 my @EXPECTX = (
56     ###  dirs       filename    contents
57     [    [ 'x' ],   'k',        '',     ],
58     [    [ 'x' ],   'x',        'j',    ],   # failed before A::T 1.08
59 );
60
61 my $LONG_FILE = qq[directory/really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-long-directory-name/myfile];
62
63 ### wintendo can't deal with too long paths, so we might have to skip tests ###
64 my $TOO_LONG    =   ($^O eq 'MSWin32' or $^O eq 'cygwin' or $^O eq 'VMS')
65                     && length( cwd(). $LONG_FILE ) > 247;
66
67 ### warn if we are going to skip long file names
68 $TOO_LONG ? diag("No long filename support - long filename extraction disabled")
69           : ( push @EXPECT_NORMAL, [ [], $LONG_FILE, qr/^hello\s*$/] ) ;
70
71 my @ROOT        = grep { length }   'src', $TOO_LONG ? 'short' : 'long';
72
73 my $ZLIB        = eval { require IO::Zlib; 1 } ? 1 : 0;
74 my $NO_UNLINK   = $ARGV[0] ? 1 : 0;
75
76 ### enable debugging?
77 $Archive::Tar::DEBUG = 1 if $ARGV[1];
78
79 ### tests for binary and x/x files
80 my $TARBIN      = Archive::Tar->new;
81 my $TARX        = Archive::Tar->new;
82
83 ### paths to a .tar and .tgz file to use for tests
84 my $TAR_FILE        = File::Spec->catfile( @ROOT, 'bar.tar' );
85 my $TGZ_FILE        = File::Spec->catfile( @ROOT, 'foo.tgz' );
86 my $OUT_TAR_FILE    = File::Spec->catfile( @ROOT, 'out.tar' );
87 my $OUT_TGZ_FILE    = File::Spec->catfile( @ROOT, 'out.tgz' );
88
89 my $COMPRESS_FILE = 'copy';
90 $^O eq 'VMS' and $COMPRESS_FILE .= '.';
91 copy( File::Basename::basename($0), $COMPRESS_FILE );
92 chmod 0644, $COMPRESS_FILE;
93
94 ### done setting up environment ###
95
96
97 ### did we probe IO::Zlib support ok? ###
98 {   is( Archive::Tar->can_handle_compressed_files, $ZLIB,
99                                     "Proper IO::Zlib support detected" );
100 }
101
102
103 ### tar error tests
104 {   my $tar     = Archive::Tar->new;
105
106     ok( $tar,                       "Object created" );
107     isa_ok( $tar,                   'Archive::Tar');
108
109     local $Archive::Tar::WARN  = 0;
110
111     ### should be empty to begin with
112     is( $tar->error, '',            "The error string is empty" );
113
114     ### try a read on nothing
115     my @list = $tar->read();
116
117     ok(!(scalar @list),             "Function read returns 0 files on error" );
118     ok( $tar->error,                "   error string is non empty" );
119     like( $tar->error, qr/No file to read from/,
120                                     "   error string from create()" );
121     unlike( $tar->error, qr/add/,   "   error string does not contain add" );
122
123     ### now, add empty data
124     my $obj = $tar->add_data( '' );
125
126     ok( !$obj,                      "'add_data' returns undef on error" );
127     ok( $tar->error,                "   error string is non empty" );
128     like( $tar->error, qr/add/,     "   error string contains add" );
129     unlike( $tar->error, qr/create/,"   error string does not contain create" );
130
131     ### check if ->error eq $error
132     is( $tar->error, $Archive::Tar::error,
133                                     '$error matches error() method' );
134 }
135
136 ### read tests ###
137 {   ### normal tar + gz compressed file
138     my $archive         = $TAR_FILE;
139     my $compressed      = $TGZ_FILE;
140     my $tar             = Archive::Tar->new;
141     my $gzip            = 0;
142
143     ### check we got the object
144     ok( $tar,                       "Object created" );
145     isa_ok( $tar,                   'Archive::Tar');
146
147     for my $type( $archive, $compressed ) {
148         my $state = $gzip ? 'compressed' : 'uncompressed';
149
150         SKIP: {
151
152             ### skip gz compressed archives wihtout IO::Zlib
153             skip(   "No IO::Zlib - cannot read compressed archives",
154                     4 + 2 * (scalar @EXPECT_NORMAL)
155             ) if( $gzip and !$ZLIB);
156
157             ### ->read test
158             {   my @list    = $tar->read( $type );
159                 my $cnt     = scalar @list;
160                 my $expect  = scalar __PACKAGE__->get_expect();
161
162                 ok( $cnt,           "Reading $state file using 'read()'" );
163                 is( $cnt, $expect,  "   All files accounted for" );
164
165                 for my $file ( @list ) {
166                     ok( $file,      "Got File object" );
167                     isa_ok( $file,  "Archive::Tar::File" );
168
169                     ### whitebox test -- make sure find_entry gets the
170                     ### right files
171                     for my $test ( $file->full_path, $file ) {
172                         is( $tar->_find_entry( $test ), $file,
173                                     "   Found proper object" );
174                     }
175                     
176                     next unless $file->is_file;
177
178                     my $name = $file->full_path;
179                     my($expect_name, $expect_content) =
180                         get_expect_name_and_contents( $name, \@EXPECT_NORMAL );
181
182                     ### ->fullname!
183                     ok($expect_name,"   Found expected file '$name'" );
184
185                     like($tar->get_content($name), $expect_content,
186                                     "   Content OK" );
187                 }
188             }
189
190
191             ### list_archive test
192             {   my @list    = Archive::Tar->list_archive( $archive );
193                 my $cnt     = scalar @list;
194                 my $expect  = scalar __PACKAGE__->get_expect();
195
196                 ok( $cnt,           "Reading $state file using 'list_archive'");
197                 is( $cnt, $expect,  "   All files accounted for" );
198
199                 for my $file ( @list ) {
200                     next if __PACKAGE__->is_dir( $file ); # directories
201
202                     my($expect_name, $expect_content) =
203                         get_expect_name_and_contents( $file, \@EXPECT_NORMAL );
204
205                     ok( $expect_name,
206                                     "   Found expected file '$file'" );
207                 }
208             }
209         }
210
211         ### now we try gz compressed archives
212         $gzip++;
213     }
214 }
215
216 ### add files tests ###
217 {   my @add     = map { File::Spec->catfile( @ROOT, @$_ ) } ['b'];
218     my @addunix = map { File::Spec::Unix->catfile( @ROOT, @$_ ) } ['b'];
219     my $tar     = Archive::Tar->new;
220
221     ### check we got the object
222     ok( $tar,                       "Object created" );
223     isa_ok( $tar,                   'Archive::Tar');
224
225     ### add the files
226     {   my @files = $tar->add_files( @add );
227
228         is( scalar @files, scalar @add,
229                                     "Adding files");
230         is( $files[0]->name, 'b',   "   Proper name" );
231
232         SKIP: {
233             skip( "You are building perl using symlinks", 1)
234                 if ($ENV{PERL_CORE} and $Config{config_args} =~/Dmksymlinks/);
235
236             is( $files[0]->is_file, 1,  
237                                     "   Proper type" );
238         }
239
240         like( $files[0]->get_content, qr/^bbbbbbbbbbb\s*$/,
241                                     "   Content OK" );
242
243         ### check if we have then in our tar object
244         for my $file ( @addunix ) {
245             ok( $tar->contains_file($file),
246                                     "   File found in archive" );
247         }
248     }
249
250     ### check adding files doesn't conflict with a secondary archive
251     ### old A::T bug, we should keep testing for it
252     {   my $tar2    = Archive::Tar->new;
253         my @added   = $tar2->add_files( $COMPRESS_FILE );
254         my @count   = $tar2->list_files;
255
256         is( scalar @added, 1,       "Added files to secondary archive" );
257         is( scalar @added, scalar @count,
258                                     "   Does not conflict with first archive" );
259
260         ### check the adding of directories
261         my @add_dirs  = File::Spec->catfile( @ROOT );
262         my @dirs      = $tar2->add_files( @add_dirs );
263         is( scalar @dirs, scalar @add_dirs,
264                                     "Adding dirs");
265         ok( $dirs[0]->is_dir,       "   Proper type" );
266     }
267 }
268
269 ### add data tests ###
270 {
271     {   ### standard data ###
272         my @to_add  = ( 'a', 'aaaaa' );
273         my $tar     = Archive::Tar->new;
274
275         ### check we got the object
276         ok( $tar,                   "Object created" );
277         isa_ok( $tar,               'Archive::Tar');
278
279         ### add a new file item as data
280         my $obj = $tar->add_data( @to_add );
281
282         ok( $obj,                   "Adding data" );
283         is( $obj->name, $to_add[0], "   Proper name" );
284         is( $obj->is_file, 1,       "   Proper type" );
285         like( $obj->get_content, qr/^$to_add[1]\s*$/,
286                                     "   Content OK" );
287     }
288
289     {   ### binary data +
290         ### dir/file structure -- x/y always went ok, x/x used to extract
291         ### in the wrong way -- this test catches that
292         for my $list (  [$TARBIN,   \@EXPECTBIN],
293                         [$TARX,     \@EXPECTX],
294         ) {
295             ### XXX GLOBAL! changes may affect other tests!
296             my($tar,$struct) = @$list;
297
298             for my $aref ( @$struct ) {
299                 my ($dirs,$file,$data) = @$aref;
300
301                 my $path = File::Spec::Unix->catfile(
302                                 grep { length } @$dirs, $file );
303
304                 my $obj = $tar->add_data( $path, $data );
305
306                 ok( $obj,               "Adding data '$file'" );
307                 is( $obj->full_path, $path,
308                                         "   Proper name" );
309                 ok( $obj->is_file,      "   Proper type" );
310                 is( $obj->get_content, $data,
311                                         "   Content OK" );
312             }
313         }
314     }
315 }
316
317 ### rename/replace_content tests ###
318 {   my $tar     = Archive::Tar->new;
319     my $from    = 'c';
320     my $to      = 'e';
321
322     ### read in the file, check the proper files are there
323     ok( $tar->read( $TAR_FILE ),    "Read in '$TAR_FILE'" );
324     ok( $tar->get_files($from),     "   Found file '$from'" );
325     {   local $Archive::Tar::WARN = 0;
326         ok(!$tar->get_files($to),   "   File '$to' not yet found" );
327     }
328
329     ### rename an entry, check the rename has happened
330     ok( $tar->rename( $from, $to ), "   Renamed '$from' to '$to'" );
331     ok( $tar->get_files($to),       "   File '$to' now found" );
332     {   local $Archive::Tar::WARN = 0;
333         ok(!$tar->get_files($from), "   File '$from' no longer found'");
334     }
335
336     ### now, replace the content
337     my($expect_name, $expect_content) =
338                         get_expect_name_and_contents( $from, \@EXPECT_NORMAL );
339
340     like( $tar->get_content($to), $expect_content,
341                                     "Original content of '$from' in '$to'" );
342     ok( $tar->replace_content( $to, $from ),
343                                     "   Set content for '$to' to '$from'" );
344     is( $tar->get_content($to), $from,
345                                     "   Content for '$to' is indeed '$from'" );
346 }
347
348 ### remove tests ###
349 {   my $remove  = 'c';
350     my $tar     = Archive::Tar->new;
351
352     ok( $tar->read( $TAR_FILE ),    "Read in '$TAR_FILE'" );
353
354     ### remove returns the files left, which should be equal to list_files
355     is( scalar($tar->remove($remove)), scalar($tar->list_files),
356                                     "Removing file '$remove'" );
357
358     ### so what's left should be all expected files minus 1
359     is( scalar($tar->list_files), scalar(__PACKAGE__->get_expect) - 1,
360                                     "   Proper files remaining" );
361 }
362
363 ### write + read + extract tests ###
364 SKIP: {
365     skip('no IO::String', 326) if   !$Archive::Tar::HAS_PERLIO && 
366                                     !$Archive::Tar::HAS_IO_STRING;
367                                     
368     my $tar = Archive::Tar->new;
369     my $new = Archive::Tar->new;
370     ok( $tar->read( $TAR_FILE ),    "Read in '$TAR_FILE'" );
371
372     for my $aref (  [$tar,    \@EXPECT_NORMAL],
373                     [$TARBIN, \@EXPECTBIN],
374                     [$TARX,   \@EXPECTX]
375     ) {
376         my($obj,$struct) = @$aref;
377
378         ### check if we stringify it ok
379         {   my $string = $obj->write;
380             ok( $string,           "Stringified tar file has size" );
381             cmp_ok( length($string) % BLOCK, '==', 0,
382                                     "Tar archive stringified" );
383         }
384
385         ### write tar tests
386         {   my $out = $OUT_TAR_FILE;
387
388             {   ### write()
389                 ok( $obj->write($out),
390                                     "Wrote tarfile using 'write'" );
391                 check_tar_file( $out );
392                 check_tar_object( $obj, $struct );
393
394                 ### now read it in again
395                 ok( $new->read( $out ),
396                                     "Read '$out' in again" );
397
398                 check_tar_object( $new, $struct );
399
400                 ### now extract it again
401                 ok( $new->extract,  "Extracted '$out' with 'extract'" );
402                 check_tar_extract( $new, $struct );
403
404                 rm( $out ) unless $NO_UNLINK;
405             }
406
407
408             {   ### create_archive()
409                 ok( Archive::Tar->create_archive( $out, 0, $COMPRESS_FILE ),
410                                     "Wrote tarfile using 'create_archive'" );
411                 check_tar_file( $out );
412
413                 ### now extract it again
414                 ok( Archive::Tar->extract_archive( $out ),
415                                     "Extracted file using 'extract_archive'");
416                 rm( $out ) unless $NO_UNLINK;
417             }
418         }
419
420         ## write tgz tests
421         {   my $out = $OUT_TGZ_FILE;
422
423             SKIP: {
424
425                 ### weird errors from scalar(@x,@y,@z), dot it this way...
426                 my $file_cnt;
427                 map { $file_cnt += scalar @$_ } \@EXPECT_NORMAL, \@EXPECTBIN,
428                                                 \@EXPECTX;
429
430                 my $cnt =   5 +                 # the tests below
431                             (5*3*2) +           # check_tgz_file
432                                                 # check_tar_object fixed tests
433                             (3 * 2 * (2 + $file_cnt)) +
434                             ((4*$file_cnt) + 1);# check_tar_extract tests
435
436                 skip( "No IO::Zlib - cannot write compressed archives", $cnt )
437                     unless $ZLIB;
438
439                 {   ### write()
440                     ok($obj->write($out, 1),
441                                     "Writing compressed file using 'write'" );
442                     check_tgz_file( $out );
443                     check_tar_object( $obj, $struct );
444
445                     ### now read it in again
446                     ok( $new->read( $out ),
447                                     "Read '$out' in again" );
448                     check_tar_object( $new, $struct );
449
450                     ### now extract it again
451                     ok( $new->extract,
452                                     "Extracted '$out' again" );
453                     check_tar_extract( $new, $struct );
454
455                     rm( $out ) unless $NO_UNLINK;
456                 }
457
458                 {   ### create_archive()
459                     ok( Archive::Tar->create_archive( $out, 1, $COMPRESS_FILE ),
460                                     "Wrote gzip file using 'create_archive'" );
461                     check_tgz_file( $out );
462
463                     ### now extract it again
464                     ok( Archive::Tar->extract_archive( $out, 1 ),
465                                     "Extracted file using 'extract_archive'");
466                     rm( $out ) unless $NO_UNLINK;
467                 }
468             }
469         }
470     }
471 }
472
473
474 ### limited read + extract tests ###
475 {   my $tar     = Archive::Tar->new;
476     my @files   = $tar->read( $TAR_FILE, 0, { limit => 1 } );
477     my $obj     = $files[0];
478
479     is( scalar @files, 1,           "Limited read" );
480
481     my ($name,$content) = get_expect_name_and_contents(
482                                 $obj->full_path, \@EXPECT_NORMAL );
483
484     is( $obj->name, $name,          "   Expected file found" );
485
486
487     ### extract this single file to cwd()
488     for my $meth (qw[extract extract_file]) {
489
490         ### extract it by full path and object
491         for my $arg ( $obj, $obj->full_path ) {
492
493             ok( $tar->$meth( $arg ),
494                                     "Extracted '$name' to cwd() with $meth" );
495             ok( -e $obj->full_path, "   Extracted file exists" );
496             rm( $obj->full_path ) unless $NO_UNLINK;
497         }
498     }
499
500     ### extract this file to @ROOT
501     ### can only do that with 'extract_file', not with 'extract'
502     for my $meth (qw[extract_file]) {
503         my $outpath = File::Spec->catdir( @ROOT );
504         my $outfile = File::Spec->catfile( $outpath, $$ ); #$obj->full_path );
505
506         ok( $tar->$meth( $obj->full_path, $outfile ),
507                                     "Extracted file '$name' to $outpath with $meth" );
508         ok( -e $outfile,            "   Extracted file '$outfile' exists" );
509         rm( $outfile ) unless $NO_UNLINK;
510     }
511
512 }
513
514
515 ### clear tests ###
516 {   my $tar     = Archive::Tar->new;
517     my @files   = $tar->read( $TAR_FILE );
518
519     my $cnt = $tar->list_files();
520     ok( $cnt,                       "Found old data" );
521     ok( $tar->clear,                "   Clearing old data" );
522
523     my $new_cnt = $tar->list_files;
524     ok( !$new_cnt,                  "   Old data cleared" );
525 }
526
527 ### $DO_NOT_USE_PREFIX tests
528 {   my $tar     = Archive::Tar->new;
529
530
531     ### first write a tar file without prefix
532     {   my ($obj)   = $tar->add_files( $COMPRESS_FILE );
533         my $dir     = '';   # dir is empty!
534         my $file    = File::Basename::basename( $COMPRESS_FILE );
535
536         ok( $obj,                   "File added" );
537         isa_ok( $obj,               "Archive::Tar::File" );
538
539         ### internal storage ###
540         is( $obj->name, $file,      "   Name set to '$file'" );
541         is( $obj->prefix, $dir,     "   Prefix set to '$dir'" );
542
543         ### write the tar file without a prefix in it
544         local $Archive::Tar::DO_NOT_USE_PREFIX = 1;
545         ok( $tar->write( $OUT_TAR_FILE ),
546                                     "   Tar file written" );
547
548         ### and forget all about it...
549         $tar->clear;
550     }
551
552     ### now read it back in, there should be no prefix
553     {   ok( $tar->read( $OUT_TAR_FILE ),
554                                     "Tar file read in again" );
555
556         my ($obj) = $tar->get_files;
557         ok( $obj,                   "   File retrieved" );
558         isa_ok( $obj,               "Archive::Tar::File" );
559
560         is( $obj->name, $COMPRESS_FILE,
561                                     "   Name now set to '$COMPRESS_FILE'" );
562         is( $obj->prefix, '',       "   Prefix now empty" );
563
564         my $re = quotemeta $COMPRESS_FILE;
565         like( $obj->raw, qr/^$re/,  "   Prefix + name in name slot of header" );
566     }
567
568     rm( $OUT_TAR_FILE ) unless $NO_UNLINK;
569 }
570
571 ### clean up stuff
572 END {
573     for my $struct ( \@EXPECT_NORMAL, \@EXPECTBIN, \@EXPECTX ) {
574         for my $aref (@$struct) {
575
576             my $dir = $aref->[0]->[0];
577             rmtree $dir if $dir && -d $dir && not $NO_UNLINK;
578         }
579     }
580
581     my ($dir) = File::Spec::Unix->splitdir( $LONG_FILE );
582     rmtree $dir if $dir && -d $dir && not $NO_UNLINK;
583     1 while unlink $COMPRESS_FILE;
584 }
585
586 ###########################
587 ###     helper subs     ###
588 ###########################
589 sub get_expect {
590     return  map {
591                 split '/', $_
592             } map {
593                 File::Spec::Unix->catfile(
594                     grep { defined } @{$_->[0]}, $_->[1]
595                 )
596             } @EXPECT_NORMAL;
597 }
598
599 sub is_dir {
600     my $file = pop();
601     return $file =~ m|/$| ? 1 : 0;
602 }
603
604 sub rm {
605     my $x = shift;
606     if  ( is_dir($x) ) {
607          rmtree($x);
608     } else {
609          1 while unlink $x;
610     }
611 }
612
613 sub check_tar_file {
614     my $file        = shift;
615     my $filesize    = -s $file;
616     my $contents    = slurp_binfile( $file );
617
618     ok( defined( $contents ),   "   File read" );
619     ok( $filesize,              "   File written size=$filesize" );
620
621     cmp_ok( $filesize % BLOCK,     '==', 0,
622                         "   File size is a multiple of 512" );
623
624     cmp_ok( length($contents), '==', $filesize,
625                         "   File contents match size" );
626
627     is( TAR_END x 2, substr( $contents, -(BLOCK*2) ),
628                         "   Ends with 1024 null bytes" );
629
630     return $contents;
631 }
632
633 sub check_tgz_file {
634     my $file                = shift;
635     my $filesize            = -s $file;
636     my $contents            = slurp_gzfile( $file );
637     my $uncompressedsize    = length $contents;
638
639     ok( defined( $contents ),   "   File read and uncompressed" );
640     ok( $filesize,              "   File written size=$filesize uncompressed size=$uncompressedsize" );
641
642     cmp_ok( $uncompressedsize % BLOCK, '==', 0,
643                                 "   Uncompressed size is a multiple of 512" );
644
645     is( TAR_END x 2, substr($contents, -(BLOCK*2)),
646                                 "   Ends with 1024 null bytes" );
647
648     cmp_ok( $filesize, '<',  $uncompressedsize,
649                                 "   Compressed size < uncompressed size" );
650
651     return $contents;
652 }
653
654 sub check_tar_object {
655     my $obj     = shift;
656     my $struct  = shift or return;
657
658     ### amount of files (not dirs!) there should be in the object
659     my $expect  = scalar @$struct;
660     my @files   = grep { $_->is_file } $obj->get_files;
661
662     ### count how many files there are in the object
663     ok( scalar @files,          "   Found some files in the archive" );
664     is( scalar @files, $expect, "   Found expected number of files" );
665
666     for my $file (@files) {
667
668         ### XXX ->fullname
669         #my $path = File::Spec::Unix->catfile(
670         #            grep { length } $file->prefix, $file->name );
671         my($ename,$econtent) =
672             get_expect_name_and_contents( $file->full_path, $struct );
673
674         ok( $file->is_file,     "   It is a file" );
675         is( $file->full_path, $ename,
676                                 "   Name matches expected name" );
677         like( $file->get_content, $econtent,
678                                 "   Content as expected" );
679     }
680 }
681
682 sub check_tar_extract {
683     my $tar     = shift;
684     my $struct  = shift;
685
686     my @dirs;
687     for my $file ($tar->get_files) {
688         push @dirs, $file && next if $file->is_dir;
689
690
691         my $path = $file->full_path;
692         my($ename,$econtent) =
693             get_expect_name_and_contents( $path, $struct );
694
695
696         is( $ename, $path,          "   Expected file found" );
697         ok( -e $path,               "   File '$path' exists" );
698
699         my $fh;
700         open $fh, "$path" or warn "Error opening file '$path': $!\n";
701         binmode $fh;
702
703         ok( $fh,                    "   Opening file" );
704
705         my $content = do{local $/;<$fh>}; chomp $content;
706         like( $content, qr/$econtent/,
707                                     "   Contents OK" );
708
709         $NO_UNLINK or 1 while unlink $path;
710
711         ### alternate extract path tests 
712         ### to abs and rel paths
713         {   for my $outpath (   File::Spec->catdir( @ROOT ),
714                                 File::Spec->rel2abs( 
715                                     File::Spec->catdir( @ROOT )
716                                 )
717             ) {
718
719                 my $outfile = File::Spec->catfile( $outpath, $$ );
720     
721                 ok( $tar->extract_file( $file->full_path, $outfile ),
722                                 "   Extracted file '$path' to $outfile" );
723                 ok( -e $outfile,"   Extracted file '$outfile' exists" );
724     
725                 rm( $outfile ) unless $NO_UNLINK;
726             }            
727         }
728     }
729
730     ### now check if list_files is returning the same info as get_files
731     is_deeply( [$tar->list_files], [ map { $_->full_path } $tar->get_files],
732                                     "   Verified via list_files as well" );
733
734     #do { rmtree $_->full_path if -d $_->full_path && not $NO_UNLINK }
735     #    for @dirs;
736 }
737
738 sub slurp_binfile {
739     my $file    = shift;
740     my $fh      = IO::File->new;
741
742     $fh->open( $file ) or warn( "Error opening '$file': $!" ), return undef;
743
744     binmode $fh;
745     local $/;
746     return <$fh>;
747 }
748
749 sub slurp_gzfile {
750     my $file = shift;
751     my $str;
752     my $buff;
753
754     require IO::Zlib;
755     my $fh = new IO::Zlib;
756     $fh->open( $file, READ_ONLY->(1) )
757         or warn( "Error opening '$file' with IO::Zlib" ), return undef;
758
759     $str .= $buff while $fh->read( $buff, 4096 ) > 0;
760     $fh->close();
761     return $str;
762 }
763
764 sub get_expect_name_and_contents {
765     my $find    = shift;
766     my $struct  = shift or return;
767
768     ### find the proper name + contents for this file from
769     ### the expect structure
770     my ($name, $content) =
771         map {
772             @$_;
773         } grep {
774             $_->[0] eq $find
775         } map {
776             [   ### full path ###
777                 File::Spec::Unix->catfile(
778                     grep { length } @{$_->[0]}, $_->[1]
779                 ),
780                 ### regex
781                 $_->[2],
782             ]
783         } @$struct;
784
785     ### not a qr// yet?
786     unless( ref $content ) {
787         my $x     = quotemeta ($content || '');
788         $content = qr/$x/;
789     }
790
791     unless( $name ) {
792         warn "Could not find '$find' in " . Dumper $struct;
793     }
794
795     return ($name, $content);
796 }
797
798 __END__