1 # Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
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.
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
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
16 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19 use Test::More tests => 130;
21 use lib "@amperldir@";
25 use Amanda::Util qw(slurp burp safe_overwrite_file);
29 # Data::Dumper is used to output strings with control characters
31 $Data::Dumper::Useqq = 1; # quote strings
32 $Data::Dumper::Terse = 1; # no $VAR1 = ..
33 $Data::Dumper::Indent = 0; # no newlines
35 # most of Amanda::Util is tested via running applications that use it
37 # Test hexencode/hexdecode lightly (they have a "make check" test)
38 is(Amanda::Util::hexencode("hi"), "hi", "no encoding needed");
39 is(Amanda::Util::hexencode("hi!"), "hi%21", "encoding");
40 is(Amanda::Util::hexdecode("hi%21"), "hi!", "decoding");
41 ok(!eval {Amanda::Util::hexdecode("%"); 1}, "decoding error throws exception");
43 # Tests for quote_string and unquote string. First, some fuzzing of the
44 # quote + unquote round-trip.
51 "\t", "\r", "\n", "\f",
52 '\\\\\\\\', # memory overflow?
56 "line\nanother", # real newline
63 for my $fuzzstr (@fuzzstrs) {
64 is(Amanda::Util::unquote_string(Amanda::Util::quote_string($fuzzstr)), $fuzzstr,
65 "fuzz " . Dumper($fuzzstr));
68 # since users often provide quoted strings (e.g., in config files), test that chosen
69 # quoted strings are correctly unquoted. The need to quote the quoted strings for perl
70 # makes this a little hard to read..
71 my %unquote_checks = (
80 '"\\\\n"' => '\n', # literal \
85 while (my ($qstr, $uqstr) = each %unquote_checks) {
86 is(Amanda::Util::unquote_string($qstr), $uqstr,
87 "unquote " . Dumper($qstr));
90 for my $a (keys %unquote_checks) {
91 for my $b ("unquoted", "\"quoted str\"") {
92 my ($a_out, $b_out) = Amanda::Util::skip_quoted_string("$a $b");
93 is_deeply([$a_out, $b_out], [$a, $b],
94 "skip_quoted string over " . Dumper("$a $b"));
98 is_deeply([ Amanda::Util::skip_quoted_string("foobar") ],
100 "skip_quoted_string with one quoted string");
102 is_deeply([ Amanda::Util::skip_quoted_string("foo bar") ],
104 "skip_quoted_string with two spaces keeps second space");
106 is_deeply([ Amanda::Util::skip_quoted_string("foo\tbar") ],
108 "skip_quoted_string with a tab still splits");
110 is_deeply([ Amanda::Util::split_quoted_string_friendly("a b c d") ],
112 "split_quoted_string_friendly with a basic split");
114 is_deeply([ Amanda::Util::split_quoted_string_friendly("\ta b\nc \t \td ") ],
116 "split_quoted_string_friendly with extra whitespace");
118 is_deeply([ Amanda::Util::split_quoted_string_friendly("") ],
120 "split_quoted_string_friendly with empty string");
122 is_deeply([ Amanda::Util::split_quoted_string_friendly("\n\t ") ],
124 "split_quoted_string_friendly with just whitespace");
126 is_deeply([ Amanda::Util::split_quoted_string_friendly("\n\"hi there\"\t ") ],
128 "split_quoted_string_friendly with one string (containing whitespace)");
133 [ 'abc', 'def', 'ghi' ],
143 [ 'a\\,b', 'c\\{d', 'e\\}f' ],
146 for my $strs (@try_bracing) {
147 my $rt = [ Amanda::Util::expand_braced_alternates(
148 Amanda::Util::collapse_braced_alternates($strs)) ];
149 is_deeply($rt, $strs,
150 "round-trip of " . Dumper($strs));
154 [ Amanda::Util::expand_braced_alternates("t{0..3,5}") ],
155 [ qw(t0 t1 t2 t3 t5) ],
156 "expand_braced_alternates('t{0..3,5}')");
159 [ Amanda::Util::expand_braced_alternates("t{13..12}") ],
161 "expand_braced_alternates('t{13..12}') (sequence not parsed)");
164 [ Amanda::Util::expand_braced_alternates("t{999..999}") ],
166 "expand_braced_alternates('t{999..999}')");
169 [ Amanda::Util::expand_braced_alternates("t{0..3}") ],
171 "expand_braced_alternates('t{0..3}')");
174 [ Amanda::Util::expand_braced_alternates("t{10..13}") ],
175 [ qw(t10 t11 t12 t13) ],
176 "expand_braced_alternates('t{10..13}')");
179 [ Amanda::Util::expand_braced_alternates("t{9..13}") ],
180 [ qw(t9 t10 t11 t12 t13) ],
181 "expand_braced_alternates('t{9..13}')");
184 [ Amanda::Util::expand_braced_alternates("t{09..13}") ],
185 [ qw(t09 t10 t11 t12 t13) ],
186 "expand_braced_alternates('t{09..13}')");
189 [ Amanda::Util::expand_braced_alternates("t{009..13}") ],
190 [ qw(t009 t010 t011 t012 t013) ],
191 "expand_braced_alternates('t{009..13}') (ldigits > rdigits)");
194 [ sort(+Amanda::Util::expand_braced_alternates("x{001..004}y{1..2}z")) ],
195 [ sort(qw( x001y1z x002y1z x003y1z x004y1z x001y2z x002y2z x003y2z x004y2z )) ],
196 "expand_braced_alternates('x{001..004}y{1..2}z')");
199 [ Amanda::Util::expand_braced_alternates("t{1..100}e") ],
200 [ map { "t$_"."e" } (1 .. 100) ],
201 "expand_braced_alternates('t{1..100}e')");
209 [ 'foo/bar:baz', 'foo_bar_baz' ],
212 for my $strs (@try_sanitise) {
213 my ($in, $exp) = @{$strs};
214 is(Amanda::Util::sanitise_filename($in), $exp, "sanitise " . $in);
217 ## test full_read and full_write
219 my $testfile = "$Installcheck::TMP/Amanda_Util";
223 # set up a 1K test file
225 open (my $fh, ">", $testfile) or die("Opening $testfile: $!");
226 print $fh 'abcd' x 256;
231 my $rv = Amanda::Util::full_read(-1, 13);
232 isnt($!, '', "bad full_read gives a nonzero errno ($!)");
235 $rv = Amanda::Util::full_write(-1, "hello", 5);
236 isnt($!, '', "bad full_write gives a nonzero errno ($!)");
238 $fd = POSIX::open($testfile, POSIX::O_RDONLY);
239 die "Could not open '$testfile'" unless defined $fd;
242 $buf = Amanda::Util::full_read($fd, 1000);
243 is(length($buf), 1000, "a valid read gets the right number of bytes");
244 is(substr($buf, 0, 8), "abcdabcd", "..and what looks like the right data");
245 is($!, '', "..and no error");
248 $buf = Amanda::Util::full_read($fd, 1000);
249 is(length($buf), 24, "a second read, to EOF, gets the right number of bytes");
250 is(substr($buf, 0, 8), "abcdabcd", "..and what looks like the right data");
251 is($!, '', "..and no error");
255 $fd = POSIX::open($testfile, POSIX::O_WRONLY);
256 die "Could not open '$testfile'" unless defined $fd;
259 $rv = Amanda::Util::full_write($fd, "swank!", 6);
260 is($rv, 6, "full_write returns number of bytes written");
261 is($!, '', "..and no error");
267 # just a quick check for split_quoted_strings - thorough checks are done in
268 # common-src/quoting-test.c.
269 is_deeply([ Amanda::Util::split_quoted_strings('one "T W O" thr\ ee'), ],
270 [ "one", "T W O", "thr ee" ],
271 "split_quoted_strings seems to work");
273 ## tests for slurp and burp
277 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
278 neque interdum ligula euismod cursus at vel tortor. Praesent interdum
279 molestie felis, nec vehicula lorem luctus quis. Suspendisse in laoreet
280 diam. Maecenas fringilla lectus vel libero vehicula
281 condimentum. Aenean ac luctus nulla. Nullam sagittis lacinia orci, et
282 consectetur nunc malesuada sed. Nulla eu felis ipsum. Duis feugiat
283 risus a lectus blandit lobortis. Fusce quis neque neque. Class aptent
284 taciti sociosqu ad litora torquent per conubia nostra, per inceptos
287 Nulla at auctor mi. Mauris vestibulum ante vel metus auctor at iaculis
288 neque semper. Nullam ipsum lorem, convallis ullamcorper ornare in,
289 lacinia eu magna. Vivamus vulputate fermentum quam, quis pulvinar eros
290 varius at. Phasellus ac diam nec erat elementum facilisis et ac
291 est. Nunc nec nulla nec quam tristique dignissim at ut arcu. Integer
292 accumsan tincidunt nisi non consectetur. Donec nec massa sed dui
293 auctor sodales eget ac elit. Aliquam luctus sollicitudin nibh, eu
294 volutpat augue tempor sed. Mauris ac est et neque mollis iaculis vel
295 in libero. Duis molestie felis ultrices elit fringilla varius. In eget
296 turpis dignissim sem varius sagittis eget vel neque.
300 my $burp_corpus_fname = "$Installcheck::TMP/burp_corpus";
302 ok( burp( $burp_corpus_fname, $corpus ), "burp round-trip test" );
303 is( slurp($burp_corpus_fname), $corpus, "slurp round-trip test" );
305 # test safe_overwrite_file
307 my $sof_data = <<EOF;
308 DISK planner somebox /lib
309 START planner date 20080111
310 START driver date 20080111
311 STATS driver hostname somebox
312 STATS driver startup time 0.051
313 FINISH planner date 20080111 time 82.721
314 START taper datestamp 20080111 label Conf-001 tape 1
315 SUCCESS dumper somebox /lib 20080111 0 [sec 0.209 kb 1970 kps 9382.2 orig-kb 1970]
316 SUCCESS chunker somebox /lib 20080111 0 [sec 0.305 kb 420 kps 1478.7]
317 STATS driver estimate somebox /lib 20080111 0 [sec 1 nkb 2002 ckb 480 kps 385]
318 PART taper Conf-001 1 somebox /lib 20080111 1/1 0 [sec 4.813543 kb 419 kps 87.133307]
319 DONE taper somebox /lib 20080111 1 0 [sec 4.813543 kb 419 kps 87.133307]
320 FINISH driver date 20080111 time 2167.581
323 ok(safe_overwrite_file($burp_corpus_fname, $sof_data),
324 "safe_overwrite_file success");
325 is(slurp($burp_corpus_fname), $sof_data,
326 "safe_overwrite_file round-trip check");
328 # check out get_fs_usage
329 my $fs_usage = Amanda::Util::get_fs_usage(POSIX::getcwd);
331 ok($fs_usage->{'blocks'}, "get_fs_usage returns something");
333 fail("get_fs_usage fails: $!");
336 # check file_lock -- again, full checks are in common-src/amflock-test.c
337 my $filename = "$Installcheck::TMP/testlock";
339 my $fl = Amanda::Util::file_lock->new($filename);
340 is($fl->data, undef, "data is initially undefined");
342 is($fl->data, undef, "data is undefined even after lock");
343 $fl->write("THIS IS MY DATA");
344 is($fl->data, "THIS IS MY DATA", "data is set correctly after write()");
348 $fl = Amanda::Util::file_lock->new($filename);
349 is($fl->data, undef, "data is initially undefined");
351 is($fl->data, "THIS IS MY DATA", "data is set correctly after lock");
353 ## check (un)marshal_tapespec
356 "FOO:1,2;BAR:3" => [ FOO => [ 1, 2 ], BAR => [ 3 ] ],
357 "SE\\;MI:0;COL\\:ON:3" => [ 'SE;MI' => [0], 'COL:ON' => [3] ],
358 "CO\\,MMA:88,99;BACK\\\\SLASH:3" => [ 'CO,MMA' => [88,99], 'BACK\\SLASH' => [3] ],
359 "FUNNY\\;:1;CHARS\\::2;AT\\,:3;END\\\\:4" =>
360 [ 'FUNNY;' => [ 1 ], 'CHARS:' => [ 2 ], 'AT,' => [ 3 ], 'END\\' => [ 4 ], ],
361 "\\;FUNNY:1;\\:CHARS:2;\\,AT:3;\\\\BEG:4" =>
362 [ ';FUNNY' => [ 1 ], ':CHARS' => [ 2 ], ',AT' => [ 3 ], '\\BEG' => [ 4 ], ],
366 my $tapespec = shift @tapespecs;
367 my $filelist = shift @tapespecs;
368 is(Amanda::Util::marshal_tapespec($filelist), $tapespec,
369 "marshal '$tapespec'");
370 is_deeply(Amanda::Util::unmarshal_tapespec($tapespec), $filelist,
371 "unmarshal '$tapespec'");
374 is_deeply(Amanda::Util::unmarshal_tapespec("x:100,99"), [ x => [99,100] ],
375 "filenums are sorted when unmarshalled");
377 is_deeply(Amanda::Util::marshal_tapespec([ x => [100, 99] ]), "x:100,99",
378 "un-sorted filenums are NOT sorted when marshalled");
380 is_deeply(Amanda::Util::unmarshal_tapespec("x:34,34"), [ x => [34, 34] ],
381 "duplicate filenums are NOT collapsed when unmarshalled");
383 is_deeply(Amanda::Util::marshal_tapespec([ x => [34, 34] ]), "x:34,34",
384 "duplicate filenums are NOT collapsed when marshalled");
386 is_deeply(Amanda::Util::unmarshal_tapespec("sim\\\\ple\\:quoted\\;file\\,name"),
387 [ "sim\\ple:quoted;file,name" => [0] ],
388 "simple non-tapespec string translated like string:0");
390 is_deeply(Amanda::Util::unmarshal_tapespec("tricky\\,tricky\\:1,2,3"),
391 [ "tricky,tricky:1,2,3" => [0] ],
392 "tricky non-tapespec string also translated to string:0");
394 is_deeply(Amanda::Util::unmarshal_tapespec("\\:3"), # one slash
396 "one slash escapes the colon");
398 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\:3"), # two slashes
400 "two slashes escape to one");
402 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\\\:3"), # three slashes
404 "three slashes escape to a slash and a colon");
406 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\\\\\:3"), # four slashes
408 "four slashes escape to two");