86a33a27ea4e35086e61d643ded1be492ac11bbc
[debian/amanda] / installcheck / Amanda_Xfer.pl
1 # Copyright (c) 2005-2008 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 Mathlida Ave, Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
18
19 use Test::More tests => 12;
20 use File::Path;
21 use strict;
22
23 use lib "@amperldir@";
24 use Installcheck::Run;
25 use Amanda::Xfer qw( :constants );
26 use Amanda::Device qw( :constants );
27 use Amanda::Types;
28 use Amanda::Debug;
29 use Amanda::MainLoop;
30 use Amanda::Paths;
31 use Amanda::Config;
32
33 # set up debugging so debug output doesn't interfere with test results
34 Amanda::Debug::dbopen("installcheck");
35
36 # and disable Debug's die() and warn() overrides
37 Amanda::Debug::disable_die_override();
38
39 # initialize configuration for the device API
40 Amanda::Config::config_init(0, undef);
41
42 {
43     my $RANDOM_SEED = 0xD00D;
44
45     my $xfer = Amanda::Xfer->new([
46         Amanda::Xfer::Source::Random->new(1024*1024, $RANDOM_SEED),
47         Amanda::Xfer::Filter::Xor->new(0), # key of 0 -> no change, so random seeds match
48         Amanda::Xfer::Dest::Null->new($RANDOM_SEED),
49     ]);
50
51     pass("Creating a transfer doesn't crash"); # hey, it's a start..
52
53     my $got_msg = "(not received)";
54     $xfer->get_source()->set_callback(sub {
55         my ($src, $msg, $xfer) = @_;
56         if ($msg->{type} == $XMSG_ERROR) {
57             die $msg->{elt} . " failed: " . $msg->{message};
58         }
59         if ($msg->{type} == $XMSG_INFO) {
60             $got_msg = $msg->{message};
61         }
62         elsif ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
63             $src->remove();
64             Amanda::MainLoop::quit();
65         }
66     });
67     $xfer->start();
68     Amanda::MainLoop::run();
69     pass("A simple transfer runs to completion");
70     is($got_msg, "Is this thing on?",
71         "XMSG_INFO from Amanda::Xfer::Dest::Null has correct message");
72 }
73
74 {
75     my $RANDOM_SEED = 0xDEADBEEF;
76
77     my $xfer1 = Amanda::Xfer->new([
78         Amanda::Xfer::Source::Random->new(1024*1024, $RANDOM_SEED),
79         Amanda::Xfer::Dest::Null->new($RANDOM_SEED),
80     ]);
81     my $xfer2 = Amanda::Xfer->new([
82         Amanda::Xfer::Source::Random->new(1024*1024*3, $RANDOM_SEED),
83         Amanda::Xfer::Filter::Xor->new(0xf0),
84         Amanda::Xfer::Filter::Xor->new(0xf0),
85         Amanda::Xfer::Dest::Null->new($RANDOM_SEED),
86     ]);
87
88     my $cb = sub {
89         my ($src, $msg, $xfer) = @_;
90         if ($msg->{type} == $XMSG_ERROR) {
91             die $msg->{elt} . " failed: " . $msg->{message};
92         }
93         if  ($xfer1->get_status() == $Amanda::Xfer::XFER_DONE
94          and $xfer2->get_status() == $Amanda::Xfer::XFER_DONE) {
95             $xfer1->get_source()->remove();
96             $xfer2->get_source()->remove();
97             Amanda::MainLoop::quit();
98         }
99     };
100
101     $xfer1->get_source()->set_callback($cb);
102     $xfer2->get_source()->set_callback($cb);
103
104     $xfer1->start();
105     $xfer2->start();
106 }
107 # let the already-started transfers go out of scope before they 
108 # complete, as a memory management test..
109 Amanda::MainLoop::run();
110 pass("Two simultaneous transfers run to completion");
111
112 {
113     my $RANDOM_SEED = 0xD0DEEDAA;
114     my @elts;
115
116     # note that, because the Xor filter is flexible, assembling
117     # long pipelines can take an exponentially long time.  A 10-elt
118     # pipeline exercises the linking algorithm without wasting
119     # too many CPU cycles
120
121     push @elts, Amanda::Xfer::Source::Random->new(1024*1024, $RANDOM_SEED);
122     for my $i (1 .. 4) {
123         push @elts, Amanda::Xfer::Filter::Xor->new($i);
124         push @elts, Amanda::Xfer::Filter::Xor->new($i);
125     }
126     push @elts, Amanda::Xfer::Dest::Null->new($RANDOM_SEED);
127     my $xfer = Amanda::Xfer->new(\@elts);
128
129     my $cb = sub {
130         my ($src, $msg, $xfer) = @_;
131         if ($msg->{type} == $XMSG_ERROR) {
132             die $msg->{elt} . " failed: " . $msg->{message};
133         }
134         if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
135             $xfer->get_source()->remove();
136             Amanda::MainLoop::quit();
137         }
138     };
139
140     $xfer->get_source()->set_callback($cb);
141     $xfer->start();
142
143     Amanda::MainLoop::run();
144     pass("One 10-element transfer runs to completion");
145 }
146
147
148 {
149     my $read_filename = "$Amanda::Paths::AMANDA_TMPDIR/xfer-junk-src.tmp";
150     my $write_filename = "$Amanda::Paths::AMANDA_TMPDIR/xfer-junk-dest.tmp";
151     my ($rfh, $wfh);
152
153     mkdir($Amanda::Paths::AMANDA_TMPDIR) unless (-e $Amanda::Paths::AMANDA_TMPDIR);
154
155     # fill the file with some stuff
156     open($wfh, ">", $read_filename) or die("Could not open '$read_filename' for writing");
157     for my $i (1 .. 100) { print $wfh "line $i\n"; }
158     close($wfh);
159
160     open($rfh, "<", $read_filename) or die("Could not open '$read_filename' for reading");
161     open($wfh, ">", "$write_filename") or die("Could not open '$write_filename' for writing");
162
163     # now run a transfer out of it
164     my $xfer = Amanda::Xfer->new([
165         Amanda::Xfer::Source::Fd->new(fileno($rfh)),
166         Amanda::Xfer::Filter::Xor->new(0xde),
167         Amanda::Xfer::Filter::Xor->new(0xde),
168         Amanda::Xfer::Dest::Fd->new(fileno($wfh)),
169     ]);
170
171     my $cb = sub {
172         my ($src, $msg, $xfer) = @_;
173         if ($msg->{type} == $XMSG_ERROR) {
174             die $msg->{elt} . " failed: " . $msg->{message};
175         }
176         if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
177             $xfer->get_source()->remove();
178             Amanda::MainLoop::quit();
179         }
180     };
181
182     $xfer->get_source()->set_callback($cb);
183     $xfer->start();
184
185     Amanda::MainLoop::run();
186
187     close($wfh);
188     close($rfh);
189
190     # now verify the file contents are identical
191     open($rfh, "<", $read_filename);
192     my $src = do { local $/; <$rfh> };
193
194     open($rfh, "<", $write_filename);
195     my $dest = do { local $/; <$rfh> };
196
197     is($src, $dest, "Source::Fd and Dest::Fd read and write files");
198
199     unlink($read_filename);
200     unlink($write_filename);
201 }
202
203 # exercise device source and destination
204 {
205     my $RANDOM_SEED = 0xFACADE;
206     my $xfer;
207
208     my $quit_cb = sub {
209         my ($src, $msg, $xfer) = @_;
210         if ($msg->{type} == $XMSG_ERROR) {
211             die $msg->{elt} . " failed: " . $msg->{message};
212         }
213         if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
214             $xfer->get_source()->remove();
215             Amanda::MainLoop::quit();
216         }
217     };
218
219     # set up vtapes
220     my $testconf = Installcheck::Run::setup();
221     $testconf->write();
222
223     # set up a device for slot 1
224     my $device = Amanda::Device->new("file:" . Installcheck::Run::load_vtape(1));
225     die("Could not open VFS device: " . $device->error())
226         unless ($device->status() == $DEVICE_STATUS_SUCCESS);
227
228     # write to it
229     my $hdr = Amanda::Types::dumpfile_t->new();
230     $hdr->{type} = $Amanda::Types::F_DUMPFILE;
231     $hdr->{name} = "installcheck";
232     $hdr->{disk} = "/";
233     $hdr->{datestamp} = "20080102030405";
234
235     $device->finish();
236     $device->start($ACCESS_WRITE, "TESTCONF01", "20080102030405");
237     $device->start_file($hdr);
238
239     $xfer = Amanda::Xfer->new([
240         Amanda::Xfer::Source::Random->new(1024*1024, $RANDOM_SEED),
241         Amanda::Xfer::Dest::Device->new($device, $device->block_size() * 10),
242     ]);
243
244     $xfer->get_source()->set_callback($quit_cb);
245     $xfer->start();
246
247     Amanda::MainLoop::run();
248     pass("write to a device (completed succesfully; data may not be correct)");
249
250     # finish up the file and device
251     ok(!$device->in_file(), "not in_file");
252     ok($device->finish(), "finish");
253
254     # now turn around and read from it
255     $device->start($ACCESS_READ, undef, undef);
256     $device->seek_file(1);
257
258     $xfer = Amanda::Xfer->new([
259         Amanda::Xfer::Source::Device->new($device),
260         Amanda::Xfer::Dest::Null->new($RANDOM_SEED),
261     ]);
262
263     $xfer->get_source()->set_callback($quit_cb);
264     $xfer->start();
265
266     Amanda::MainLoop::run();
267     pass("read from a device succeeded, too, and data was correct");
268 }
269
270 {
271     my $RANDOM_SEED = 0x5EAF00D;
272
273     # build a transfer that will keep going forever
274     my $xfer = Amanda::Xfer->new([
275         Amanda::Xfer::Source::Random->new(0, $RANDOM_SEED),
276         Amanda::Xfer::Filter::Xor->new(14),
277         Amanda::Xfer::Filter::Xor->new(14),
278         Amanda::Xfer::Dest::Null->new($RANDOM_SEED),
279     ]);
280
281     my $got_timeout = 0;
282     Amanda::MainLoop::timeout_source(200)->set_callback(sub {
283         my ($src) = @_;
284         $got_timeout = 1;
285         $src->remove();
286         $xfer->cancel();
287     });
288     $xfer->get_source()->set_callback(sub {
289         my ($src, $msg, $xfer) = @_;
290         if ($msg->{type} == $XMSG_ERROR) {
291             die $msg->{elt} . " failed: " . $msg->{message};
292         }
293         if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
294             $src->remove();
295             Amanda::MainLoop::quit();
296         }
297     });
298     $xfer->start();
299     Amanda::MainLoop::run();
300     ok($got_timeout, "A neverending transfer finishes after being cancelled");
301     # (note that this does not test all of the cancellation possibilities)
302 }
303
304 {
305     # build a transfer that will write to a read-only fd
306     my $read_filename = "$Amanda::Paths::AMANDA_TMPDIR/xfer-junk-src.tmp";
307     my $rfh;
308
309     # create the file
310     open($rfh, ">", $read_filename) or die("Could not open '$read_filename' for writing");
311
312     # open it for reading
313     open($rfh, "<", $read_filename) or die("Could not open '$read_filename' for reading");;
314
315     my $xfer = Amanda::Xfer->new([
316         Amanda::Xfer::Source::Random->new(0, 1),
317         Amanda::Xfer::Dest::Fd->new(fileno($rfh)),
318     ]);
319
320     my $got_error = 0;
321     $xfer->get_source()->set_callback(sub {
322         my ($src, $msg, $xfer) = @_;
323         if ($msg->{type} == $XMSG_ERROR) {
324             $got_error = 1;
325         }
326         if ($xfer->get_status() == $Amanda::Xfer::XFER_DONE) {
327             $src->remove();
328             Amanda::MainLoop::quit();
329         }
330     });
331     $xfer->start();
332     Amanda::MainLoop::run();
333     ok($got_error, "A transfer with an error cancels itself after sending an error");
334 }