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