1# Copyright (c) 2008-2013 Zmanda, Inc. All Rights Reserved. 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License 5# as published by the Free Software Foundation; either version 2 6# of the License, or (at your option) any later version. 7# 8# This program is distributed in the hope that it will be useful, but 9# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 10# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11# for more details. 12# 13# You should have received a copy of the GNU General Public License along 14# with this program; if not, write to the Free Software Foundation, Inc., 15# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16# 17# Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300 18# Sunnyvale, CA 94086, USA, or: http://www.zmanda.com 19 20use Test::More tests => 31; 21use strict; 22use warnings; 23 24# This test only puts the perl wrappers through their paces -- the underlying 25# library is well-covered by amar-test. 26 27use lib "@amperldir@"; 28use Installcheck; 29use Amanda::Archive; 30use Amanda::Paths; 31use Amanda::MainLoop; 32use Amanda::Debug; 33use Data::Dumper; 34 35Amanda::Debug::dbopen("installcheck"); 36 37my $arch_filename = "$Installcheck::TMP/amanda_archive.bin"; 38my $data_filename = "$Installcheck::TMP/some_data.bin"; 39my ($fh, $dfh, $ar, $f1, $f2, $a1, $a2, @res, $posn); 40 41# some versions of Test::More will fail tests if the identity 42# relationships of the two objects passed to is_deeply do not 43# match, so we use the same object for $user_data throughout. 44my $user_data = [ "x", "y", "z" ]; 45 46# set up a large file full of data 47 48open($dfh, ">", $data_filename); 49my $onek = "abcd" x 256; 50my $onemeg = $onek x 1024; 51for (my $i = 0; $i < 5; $i++) { 52 print $dfh $onemeg; 53} 54$onek = $onemeg = undef; 55close($dfh); 56 57# utility functions for creating a "fake" archive file 58 59sub make_header { 60 my ($fh, $version) = @_; 61 my $hdr = "AMANDA ARCHIVE FORMAT $version"; 62 $hdr .= "\0" x (28 - length $hdr); 63 print $fh $hdr; 64} 65 66sub make_record { 67 my ($fh, $filenum, $attrid, $data, $eoa) = @_; 68 my $size = length($data); 69 if ($eoa) { 70 $size |= 0x80000000; 71 } 72 print $fh pack("nnN", $filenum, $attrid, $size); 73 print $fh $data; 74} 75 76#### 77## TEST WRITING 78 79open($fh, ">", $arch_filename) or die("opening $arch_filename: $!"); 80$ar = Amanda::Archive->new(fileno($fh), ">"); 81pass("Create a new archive"); 82 83$f1 = $ar->new_file("filename1"); 84pass("Start an archive file"); 85 86$a1 = $f1->new_attr($Amanda::Archive::AMAR_ATTR_GENERIC_DATA); 87$a1->add_data("foo!", 0); 88$a2 = $f1->new_attr(19); 89$a2->add_data("BAR!", 0); 90$a1->add_data("FOO.", 1); 91$a2->add_data("bar.", 0); 92pass("Write some interleaved data"); 93 94$a1->close(); 95pass("Close an attribute with the close() method"); 96 97$a1 = Amanda::Archive::Attr->new($f1, 99); 98pass("Create an attribute with its constructor"); 99 100open($dfh, "<", $data_filename); 101$a1->add_data_fd(fileno($dfh), 1); 102close($dfh); 103pass("Add data from a file descriptor"); 104ok($a1->size() == 5242880, "size attribute A is " . $a1->size()); 105ok($f1->size() == 5242961, "size file A is " . $f1->size()); 106ok($ar->size() == 5242989, "Size A is " . $ar->size); 107 108$a1 = undef; 109pass("Close attribute when its refcount hits zero"); 110ok($ar->size() == 5242989, "Size B is " . $ar->size); 111 112$f2 = Amanda::Archive::File->new($ar, "filename2"); 113pass("Add a new file (filename2)"); 114 115$a1 = $f2->new_attr(82); 116$a1->add_data("word", 1); 117pass("Add data to it"); 118ok($a1->size() == 4, "size attribute A1 is " . $a1->size()); 119ok($f2->size() == 29, "size file F2 is " . $f2->size()); 120ok($ar->size() == 5243018, "Size C is " . $ar->size); 121 122$a2->add_data("barrrrr?", 0); # note no EOA 123pass("Add more data to first attribute"); 124ok($a2->size() == 16, "size attribute A2 is " . $a2->size()); 125ok($f1->size() == 5242977, "size file F1 is " . $f1->size()); 126ok($ar->size() == 5243034, "Size D is " . $ar->size); 127 128($f1, $posn) = $ar->new_file("posititioned file", 1); 129ok($posn > 0, "new_file returns a positive position"); 130 131$ar = undef; 132pass("unref archive early"); 133 134($ar, $f1, $f2, $a1, $a2) = (); 135pass("Close remaining objects"); 136 137close($fh); 138 139#### 140## TEST READING 141 142open($fh, ">", $arch_filename); 143make_header($fh, 1); 144make_record($fh, 16, 0, "/etc/passwd", 1); 145make_record($fh, 16, 20, "root:foo", 1); 146make_record($fh, 16, 21, "boot:foot", 0); 147make_record($fh, 16, 22, "dustin:snazzy", 1); 148make_record($fh, 16, 21, "..more-boot:foot", 1); 149make_record($fh, 16, 1, "", 1); 150close($fh); 151 152open($fh, "<", $arch_filename); 153$ar = Amanda::Archive->new(fileno($fh), "<"); 154pass("Create a new archive for reading"); 155 156@res = (); 157$ar->read( 158 file_start => sub { 159 push @res, [ "file_start", @_ ]; 160 return "cows"; 161 }, 162 file_finish => sub { 163 push @res, [ "file_finish", @_ ]; 164 }, 165 0 => sub { 166 push @res, [ "frag", @_ ]; 167 return "ants"; 168 }, 169 user_data => $user_data, 170); 171is_deeply([@res], [ 172 [ 'file_start', $user_data, 16, '/etc/passwd' ], 173 [ 'frag', $user_data, 16, "cows", 20, undef, 'root:foo', 1, 0 ], 174 [ 'frag', $user_data, 16, "cows", 21, undef, 'boot:foot', 0, 0 ], 175 [ 'frag', $user_data, 16, "cows", 22, undef, 'dustin:snazzy', 1, 0 ], 176 [ 'frag', $user_data, 16, "cows", 21, "ants", '..more-boot:foot', 1, 0 ], 177 [ 'file_finish', $user_data, "cows", 16, 0 ] 178], "simple read callbacks called in the right order") 179 or diag(Dumper(\@res)); 180$ar->close(); 181close($fh); 182 183 184open($fh, "<", $arch_filename); 185$ar = Amanda::Archive->new(fileno($fh), "<"); 186pass("Create a new archive for reading"); 187 188@res = (); 189$ar->read( 190 file_start => sub { 191 push @res, [ "file_start", @_ ]; 192 return "IGNORE"; 193 }, 194 file_finish => sub { 195 push @res, [ "file_finish", @_ ]; 196 }, 197 0 => sub { 198 push @res, [ "frag", @_ ]; 199 return "ants"; 200 }, 201 user_data => $user_data, 202); 203is_deeply([@res], [ 204 [ 'file_start', $user_data, 16, '/etc/passwd' ], 205], "'IGNORE' handled correctly") 206 or diag(Dumper(\@res)); 207# TODO: check that file data gets dumped appropriately? 208 209 210open($fh, "<", $arch_filename); 211$ar = Amanda::Archive->new(fileno($fh), "<"); 212 213@res = (); 214$ar->read( 215 file_start => sub { 216 push @res, [ "file_start", @_ ]; 217 return "dogs"; 218 }, 219 file_finish => sub { 220 push @res, [ "file_finish", @_ ]; 221 }, 222 21 => [ 100, sub { 223 push @res, [ "fragbuf", @_ ]; 224 return "pants"; 225 } ], 226 0 => sub { 227 push @res, [ "frag", @_ ]; 228 return "ants"; 229 }, 230 user_data => $user_data, 231); 232is_deeply([@res], [ 233 [ 'file_start', $user_data, 16, '/etc/passwd' ], 234 [ 'frag', $user_data, 16, "dogs", 20, undef, 'root:foo', 1, 0 ], 235 [ 'frag', $user_data, 16, "dogs", 22, undef, 'dustin:snazzy', 1, 0 ], 236 [ 'fragbuf', $user_data, 16, "dogs", 21, undef, 'boot:foot..more-boot:foot', 1, 0 ], 237 [ 'file_finish', $user_data, "dogs", 16, 0 ] 238], "buffering parameters parsed correctly") 239 or diag(Dumper(\@res)); 240 241 242open($fh, "<", $arch_filename); 243$ar = Amanda::Archive->new(fileno($fh), "<"); 244 245@res = (); 246eval { 247 $ar->read( 248 file_start => sub { 249 push @res, [ "file_start", @_ ]; 250 die "uh oh"; 251 }, 252 user_data => $user_data, 253 ); 254}; 255like($@, qr/uh oh at .*/, "exception propagated correctly"); 256is_deeply([@res], [ 257 [ 'file_start', $user_data, 16, '/etc/passwd' ], 258], "file_start called before exception was rasied") 259 or diag(Dumper(\@res)); 260$ar->close(); 261 262unlink($arch_filename); 263 264open($fh, ">", $arch_filename); 265$ar = Amanda::Archive->new(fileno($fh), ">"); 266$f1 = $ar->new_file("filename1"); 267$a1 = $f1->new_attr($Amanda::Archive::AMAR_ATTR_GENERIC_DATA); 268 269open($dfh, "<", $data_filename); 270$a1->add_data_fd(fileno($dfh), 1); 271close($dfh); 272 273$a1->close(); 274$f1->close(); 275 276$f1 = $ar->new_file("filename2"); 277$a1 = $f1->new_attr($Amanda::Archive::AMAR_ATTR_GENERIC_DATA); 278$a1->add_data("abcdefgh" x 16384); 279$a1->close(); 280$f1->close(); 281 282$f1 = $ar->new_file("filename3"); 283$a1 = $f1->new_attr($Amanda::Archive::AMAR_ATTR_GENERIC_DATA); 284$a1->add_data("abcdefgh" x 16384); 285$a1->close(); 286$f1->close(); 287 288$ar->close(); 289close($fh); 290 291open($fh, "<", $arch_filename); 292$ar = Amanda::Archive->new(fileno($fh), "<"); 293@res = (); 294my $fh1; 295open $fh1, ">/dev/null" || die("/dev/null"); 296$ar->set_read_cb( 297 file_start => sub { 298 my ($user_data, $filenum, $filename) = @_; 299 push @res, ["file_start", @_ ]; 300 if ($filename eq "filename1") { 301 my $time_str = Amanda::MainLoop::timeout_source(500); 302 $time_str->set_callback(sub { 303 $ar->read_to($filenum, $Amanda::Archive::AMAR_ATTR_GENERIC_DATA, fileno($fh1)); 304 $ar->start_read(); 305 $time_str->remove(); 306 }); 307 $ar->stop_read(); 308 } 309 return "dog $filenum $filename"; 310 }, 311 file_finish => sub { 312 my ($user_data, $filenum, $filename) = @_; 313 push @res, [ "file_finish", @_ ]; 314 }, 315 16 => sub { 316 my ($user_data, $filenum, $file_data, $attrid, 317 $attr_data, $data, $eoa, $truncated) = @_; 318 push @res, [ "frag", $user_data, $filenum, $file_data, $attrid, $attr_data, $eoa, $truncated ]; 319 }, 320 0 => sub { 321 my ($user_data, $filenum, $file_data, $attrid, 322 $attr_data, $data, $eoa, $truncated) = @_; 323 push @res, [ "16", $user_data, $filenum, $file_data, $attrid, $attr_data, $eoa, $truncated ]; 324 }, 325 user_data => $user_data, 326 done => sub { 327 my ($error) = @_; 328 push @res, [ "done" , @_ ]; 329 Amanda::MainLoop::quit(); 330 } 331); 332Amanda::MainLoop::run(); 333close $fh1; 334$ar->close(); 335 336is_deeply([@res], [ 337 [ 'file_start', $user_data, 1, 'filename1' ], 338 [ 'file_finish', $user_data, 'dog 1 filename1', 1, 0 ], 339 [ 'file_start', $user_data, 2, 'filename2' ], 340 [ 'frag', $user_data, 2, "dog 2 filename2", 16, undef, 0, 0 ], 341 [ 'frag', $user_data, 2, "dog 2 filename2", 16, 4, 1, 0 ], 342 [ 'file_finish', $user_data, 'dog 2 filename2', 2, 0 ], 343 [ 'file_start', $user_data, 3, 'filename3' ], 344 [ 'frag', $user_data, 3, "dog 3 filename3", 16, undef, 0, 0 ], 345 [ 'frag', $user_data, 3, "dog 3 filename3", 16, 8, 1, 0 ], 346 [ 'file_finish', $user_data, "dog 3 filename3", 3, 0 ], 347 [ 'done' ] 348], "buffering parameters parsed correctly") 349 or diag(Dumper(\@res)); 350unlink($data_filename); 351unlink($arch_filename); 352 353 354