Imported Upstream version 3.3.3
[debian/amanda] / installcheck / Amanda_Archive.pl
1 # Copyright (c) 2008-2012 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
20 use Test::More tests => 20;
21 use strict;
22 use warnings;
23
24 # This test only puts the perl wrappers through their paces -- the underlying
25 # library is well-covered by amar-test.
26
27 use lib "@amperldir@";
28 use Installcheck;
29 use Amanda::Archive;
30 use Amanda::Paths;
31 use Data::Dumper;
32
33 my $arch_filename = "$Installcheck::TMP/amanda_archive.bin";
34 my $data_filename = "$Installcheck::TMP/some_data.bin";
35 my ($fh, $dfh, $ar, $f1, $f2, $a1, $a2, @res, $posn);
36
37 # some versions of Test::More will fail tests if the identity
38 # relationships of the two objects passed to is_deeply do not
39 # match, so we use the same object for $user_data throughout.
40 my $user_data = [ "x", "y", "z" ];
41
42 # set up a large file full of data
43
44 open($dfh, ">", $data_filename);
45 my $onek = "abcd" x 256;
46 my $onemeg = $onek x 1024;
47 for (my $i = 0; $i < 5; $i++) {
48     print $dfh $onemeg;
49 }
50 $onek = $onemeg = undef;
51 close($dfh);
52
53 # utility functions for creating a "fake" archive file
54
55 sub make_header {
56     my ($fh, $version) = @_;
57     my $hdr = "AMANDA ARCHIVE FORMAT $version";
58     $hdr .= "\0" x (28 - length $hdr);
59     print $fh $hdr;
60 }
61
62 sub make_record {
63     my ($fh, $filenum, $attrid, $data, $eoa) = @_;
64     my $size = length($data);
65     if ($eoa) {
66         $size |= 0x80000000;
67     }
68     print $fh pack("nnN", $filenum, $attrid, $size);
69     print $fh $data;
70 }
71
72 ####
73 ## TEST WRITING
74
75 open($fh, ">", $arch_filename) or die("opening $arch_filename: $!");
76 $ar = Amanda::Archive->new(fileno($fh), ">");
77 pass("Create a new archive");
78
79 $f1 = $ar->new_file("filename1");
80 pass("Start an archive file");
81
82 $a1 = $f1->new_attr(18);
83 $a1->add_data("foo!", 0);
84 $a2 = $f1->new_attr(19);
85 $a2->add_data("BAR!", 0);
86 $a1->add_data("FOO.", 1);
87 $a2->add_data("bar.", 0);
88 pass("Write some interleaved data");
89
90 $a1->close();
91 pass("Close an attribute with the close() method");
92
93 $a1 = Amanda::Archive::Attr->new($f1, 99);
94 pass("Create an attribute with its constructor");
95
96 open($dfh, "<", $data_filename);
97 $a1->add_data_fd(fileno($dfh), 1);
98 close($dfh);
99 pass("Add data from a file descriptor");
100
101 $a1 = undef;
102 pass("Close attribute when its refcount hits zero");
103
104 $f2 = Amanda::Archive::File->new($ar, "filename2");
105 pass("Add a new file (filename2)");
106
107 $a1 = $f2->new_attr(82);
108 $a1->add_data("word", 1);
109 pass("Add data to it");
110
111 $a2->add_data("barrrrr?", 0);   # note no EOA
112 pass("Add more data to first attribute");
113
114 ($f1, $posn) = $ar->new_file("posititioned file", 1);
115 ok($posn > 0, "new_file returns a positive position");
116
117 $ar = undef;
118 pass("unref archive early");
119
120 ($ar, $f1, $f2, $a1, $a2) = ();
121 pass("Close remaining objects");
122
123 close($fh);
124
125 ####
126 ## TEST READING
127
128 open($fh, ">", $arch_filename);
129 make_header($fh, 1);
130 make_record($fh, 16, 0, "/etc/passwd", 1);
131 make_record($fh, 16, 20, "root:foo", 1);
132 make_record($fh, 16, 21, "boot:foot", 0);
133 make_record($fh, 16, 22, "dustin:snazzy", 1);
134 make_record($fh, 16, 21, "..more-boot:foot", 1);
135 make_record($fh, 16, 1, "", 1);
136 close($fh);
137
138 open($fh, "<", $arch_filename);
139 $ar = Amanda::Archive->new(fileno($fh), "<");
140 pass("Create a new archive for reading");
141
142 @res = ();
143 $ar->read(
144     file_start => sub {
145         push @res, [ "file_start", @_ ];
146         return "cows";
147     },
148     file_finish => sub {
149         push @res, [ "file_finish", @_ ];
150     },
151     0 => sub {
152         push @res, [ "frag", @_ ];
153         return "ants";
154     },
155     user_data => $user_data,
156 );
157 is_deeply([@res], [
158         [ 'file_start', $user_data, 16, '/etc/passwd' ],
159         [ 'frag', $user_data, 16, "cows", 20, undef, 'root:foo', 1, 0 ],
160         [ 'frag', $user_data, 16, "cows", 21, undef, 'boot:foot', 0, 0 ],
161         [ 'frag', $user_data, 16, "cows", 22, undef, 'dustin:snazzy', 1, 0 ],
162         [ 'frag', $user_data, 16, "cows", 21, "ants", '..more-boot:foot', 1, 0 ],
163         [ 'file_finish', $user_data, "cows", 16, 0 ]
164 ], "simple read callbacks called in the right order")
165     or diag(Dumper(\@res));
166 $ar->close();
167 close($fh);
168
169
170 open($fh, "<", $arch_filename);
171 $ar = Amanda::Archive->new(fileno($fh), "<");
172 pass("Create a new archive for reading");
173
174 @res = ();
175 $ar->read(
176     file_start => sub {
177         push @res, [ "file_start", @_ ];
178         return "IGNORE";
179     },
180     file_finish => sub {
181         push @res, [ "file_finish", @_ ];
182     },
183     0 => sub {
184         push @res, [ "frag", @_ ];
185         return "ants";
186     },
187     user_data => $user_data,
188 );
189 is_deeply([@res], [
190         [ 'file_start', $user_data, 16, '/etc/passwd' ],
191 ], "'IGNORE' handled correctly")
192     or diag(Dumper(\@res));
193 # TODO: check that file data gets dumped appropriately?
194
195
196 open($fh, "<", $arch_filename);
197 $ar = Amanda::Archive->new(fileno($fh), "<");
198
199 @res = ();
200 $ar->read(
201     file_start => sub {
202         push @res, [ "file_start", @_ ];
203         return "dogs";
204     },
205     file_finish => sub {
206         push @res, [ "file_finish", @_ ];
207     },
208     21 => [ 100, sub {
209         push @res, [ "fragbuf", @_ ];
210         return "pants";
211     } ],
212     0 => sub {
213         push @res, [ "frag", @_ ];
214         return "ants";
215     },
216     user_data => $user_data,
217 );
218 is_deeply([@res], [
219         [ 'file_start', $user_data, 16, '/etc/passwd' ],
220         [ 'frag', $user_data, 16, "dogs", 20, undef, 'root:foo', 1, 0 ],
221         [ 'frag', $user_data, 16, "dogs", 22, undef, 'dustin:snazzy', 1, 0 ],
222         [ 'fragbuf', $user_data, 16, "dogs", 21, undef, 'boot:foot..more-boot:foot', 1, 0 ],
223         [ 'file_finish', $user_data, "dogs", 16, 0 ]
224 ], "buffering parameters parsed correctly")
225     or diag(Dumper(\@res));
226
227
228 open($fh, "<", $arch_filename);
229 $ar = Amanda::Archive->new(fileno($fh), "<");
230
231 @res = ();
232 eval {
233     $ar->read(
234         file_start => sub {
235             push @res, [ "file_start", @_ ];
236             die "uh oh";
237         },
238         user_data => $user_data,
239     );
240 };
241 like($@, qr/uh oh at .*/, "exception propagated correctly");
242 is_deeply([@res], [
243         [ 'file_start', $user_data, 16, '/etc/passwd' ],
244 ], "file_start called before exception was rasied")
245     or diag(Dumper(\@res));
246 $ar->close();
247
248 unlink($data_filename);
249 unlink($arch_filename);