1 # Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
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.
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
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
17 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20 use Test::More tests => 130;
22 use lib "@amperldir@";
26 use Amanda::Util qw(slurp burp safe_overwrite_file);
30 # Data::Dumper is used to output strings with control characters
32 $Data::Dumper::Useqq = 1; # quote strings
33 $Data::Dumper::Terse = 1; # no $VAR1 = ..
34 $Data::Dumper::Indent = 0; # no newlines
36 # most of Amanda::Util is tested via running applications that use it
38 # Test hexencode/hexdecode lightly (they have a "make check" test)
39 is(Amanda::Util::hexencode("hi"), "hi", "no encoding needed");
40 is(Amanda::Util::hexencode("hi!"), "hi%21", "encoding");
41 is(Amanda::Util::hexdecode("hi%21"), "hi!", "decoding");
42 ok(!eval {Amanda::Util::hexdecode("%"); 1}, "decoding error throws exception");
44 # Tests for quote_string and unquote string. First, some fuzzing of the
45 # quote + unquote round-trip.
52 "\t", "\r", "\n", "\f",
53 '\\\\\\\\', # memory overflow?
57 "line\nanother", # real newline
64 for my $fuzzstr (@fuzzstrs) {
65 is(Amanda::Util::unquote_string(Amanda::Util::quote_string($fuzzstr)), $fuzzstr,
66 "fuzz " . Dumper($fuzzstr));
69 # since users often provide quoted strings (e.g., in config files), test that chosen
70 # quoted strings are correctly unquoted. The need to quote the quoted strings for perl
71 # makes this a little hard to read..
72 my %unquote_checks = (
81 '"\\\\n"' => '\n', # literal \
86 while (my ($qstr, $uqstr) = each %unquote_checks) {
87 is(Amanda::Util::unquote_string($qstr), $uqstr,
88 "unquote " . Dumper($qstr));
91 for my $a (keys %unquote_checks) {
92 for my $b ("unquoted", "\"quoted str\"") {
93 my ($a_out, $b_out) = Amanda::Util::skip_quoted_string("$a $b");
94 is_deeply([$a_out, $b_out], [$a, $b],
95 "skip_quoted string over " . Dumper("$a $b"));
99 is_deeply([ Amanda::Util::skip_quoted_string("foobar") ],
101 "skip_quoted_string with one quoted string");
103 is_deeply([ Amanda::Util::skip_quoted_string("foo bar") ],
105 "skip_quoted_string with two spaces keeps second space");
107 is_deeply([ Amanda::Util::skip_quoted_string("foo\tbar") ],
109 "skip_quoted_string with a tab still splits");
111 is_deeply([ Amanda::Util::split_quoted_string_friendly("a b c d") ],
113 "split_quoted_string_friendly with a basic split");
115 is_deeply([ Amanda::Util::split_quoted_string_friendly("\ta b\nc \t \td ") ],
117 "split_quoted_string_friendly with extra whitespace");
119 is_deeply([ Amanda::Util::split_quoted_string_friendly("") ],
121 "split_quoted_string_friendly with empty string");
123 is_deeply([ Amanda::Util::split_quoted_string_friendly("\n\t ") ],
125 "split_quoted_string_friendly with just whitespace");
127 is_deeply([ Amanda::Util::split_quoted_string_friendly("\n\"hi there\"\t ") ],
129 "split_quoted_string_friendly with one string (containing whitespace)");
134 [ 'abc', 'def', 'ghi' ],
144 [ 'a\\,b', 'c\\{d', 'e\\}f' ],
147 for my $strs (@try_bracing) {
148 my $rt = [ Amanda::Util::expand_braced_alternates(
149 Amanda::Util::collapse_braced_alternates($strs)) ];
150 is_deeply($rt, $strs,
151 "round-trip of " . Dumper($strs));
155 [ Amanda::Util::expand_braced_alternates("t{0..3,5}") ],
156 [ qw(t0 t1 t2 t3 t5) ],
157 "expand_braced_alternates('t{0..3,5}')");
160 [ Amanda::Util::expand_braced_alternates("t{13..12}") ],
162 "expand_braced_alternates('t{13..12}') (sequence not parsed)");
165 [ Amanda::Util::expand_braced_alternates("t{999..999}") ],
167 "expand_braced_alternates('t{999..999}')");
170 [ Amanda::Util::expand_braced_alternates("t{0..3}") ],
172 "expand_braced_alternates('t{0..3}')");
175 [ Amanda::Util::expand_braced_alternates("t{10..13}") ],
176 [ qw(t10 t11 t12 t13) ],
177 "expand_braced_alternates('t{10..13}')");
180 [ Amanda::Util::expand_braced_alternates("t{9..13}") ],
181 [ qw(t9 t10 t11 t12 t13) ],
182 "expand_braced_alternates('t{9..13}')");
185 [ Amanda::Util::expand_braced_alternates("t{09..13}") ],
186 [ qw(t09 t10 t11 t12 t13) ],
187 "expand_braced_alternates('t{09..13}')");
190 [ Amanda::Util::expand_braced_alternates("t{009..13}") ],
191 [ qw(t009 t010 t011 t012 t013) ],
192 "expand_braced_alternates('t{009..13}') (ldigits > rdigits)");
195 [ sort(+Amanda::Util::expand_braced_alternates("x{001..004}y{1..2}z")) ],
196 [ sort(qw( x001y1z x002y1z x003y1z x004y1z x001y2z x002y2z x003y2z x004y2z )) ],
197 "expand_braced_alternates('x{001..004}y{1..2}z')");
200 [ Amanda::Util::expand_braced_alternates("t{1..100}e") ],
201 [ map { "t$_"."e" } (1 .. 100) ],
202 "expand_braced_alternates('t{1..100}e')");
210 [ 'foo/bar:baz', 'foo_bar_baz' ],
213 for my $strs (@try_sanitise) {
214 my ($in, $exp) = @{$strs};
215 is(Amanda::Util::sanitise_filename($in), $exp, "sanitise " . $in);
218 ## test full_read and full_write
220 my $testfile = "$Installcheck::TMP/Amanda_Util";
224 # set up a 1K test file
226 open (my $fh, ">", $testfile) or die("Opening $testfile: $!");
227 print $fh 'abcd' x 256;
232 my $rv = Amanda::Util::full_read(-1, 13);
233 isnt($!, '', "bad full_read gives a nonzero errno ($!)");
236 $rv = Amanda::Util::full_write(-1, "hello", 5);
237 isnt($!, '', "bad full_write gives a nonzero errno ($!)");
239 $fd = POSIX::open($testfile, POSIX::O_RDONLY);
240 die "Could not open '$testfile'" unless defined $fd;
243 $buf = Amanda::Util::full_read($fd, 1000);
244 is(length($buf), 1000, "a valid read gets the right number of bytes");
245 is(substr($buf, 0, 8), "abcdabcd", "..and what looks like the right data");
246 is($!, '', "..and no error");
249 $buf = Amanda::Util::full_read($fd, 1000);
250 is(length($buf), 24, "a second read, to EOF, gets the right number of bytes");
251 is(substr($buf, 0, 8), "abcdabcd", "..and what looks like the right data");
252 is($!, '', "..and no error");
256 $fd = POSIX::open($testfile, POSIX::O_WRONLY);
257 die "Could not open '$testfile'" unless defined $fd;
260 $rv = Amanda::Util::full_write($fd, "swank!", 6);
261 is($rv, 6, "full_write returns number of bytes written");
262 is($!, '', "..and no error");
268 # just a quick check for split_quoted_strings - thorough checks are done in
269 # common-src/quoting-test.c.
270 is_deeply([ Amanda::Util::split_quoted_strings('one "T W O" thr\ ee'), ],
271 [ "one", "T W O", "thr ee" ],
272 "split_quoted_strings seems to work");
274 ## tests for slurp and burp
278 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean id
279 neque interdum ligula euismod cursus at vel tortor. Praesent interdum
280 molestie felis, nec vehicula lorem luctus quis. Suspendisse in laoreet
281 diam. Maecenas fringilla lectus vel libero vehicula
282 condimentum. Aenean ac luctus nulla. Nullam sagittis lacinia orci, et
283 consectetur nunc malesuada sed. Nulla eu felis ipsum. Duis feugiat
284 risus a lectus blandit lobortis. Fusce quis neque neque. Class aptent
285 taciti sociosqu ad litora torquent per conubia nostra, per inceptos
288 Nulla at auctor mi. Mauris vestibulum ante vel metus auctor at iaculis
289 neque semper. Nullam ipsum lorem, convallis ullamcorper ornare in,
290 lacinia eu magna. Vivamus vulputate fermentum quam, quis pulvinar eros
291 varius at. Phasellus ac diam nec erat elementum facilisis et ac
292 est. Nunc nec nulla nec quam tristique dignissim at ut arcu. Integer
293 accumsan tincidunt nisi non consectetur. Donec nec massa sed dui
294 auctor sodales eget ac elit. Aliquam luctus sollicitudin nibh, eu
295 volutpat augue tempor sed. Mauris ac est et neque mollis iaculis vel
296 in libero. Duis molestie felis ultrices elit fringilla varius. In eget
297 turpis dignissim sem varius sagittis eget vel neque.
301 my $burp_corpus_fname = "$Installcheck::TMP/burp_corpus";
303 ok( burp( $burp_corpus_fname, $corpus ), "burp round-trip test" );
304 is( slurp($burp_corpus_fname), $corpus, "slurp round-trip test" );
306 # test safe_overwrite_file
308 my $sof_data = <<EOF;
309 DISK planner somebox /lib
310 START planner date 20080111
311 START driver date 20080111
312 STATS driver hostname somebox
313 STATS driver startup time 0.051
314 FINISH planner date 20080111 time 82.721
315 START taper datestamp 20080111 label Conf-001 tape 1
316 SUCCESS dumper somebox /lib 20080111 0 [sec 0.209 kb 1970 kps 9382.2 orig-kb 1970]
317 SUCCESS chunker somebox /lib 20080111 0 [sec 0.305 kb 420 kps 1478.7]
318 STATS driver estimate somebox /lib 20080111 0 [sec 1 nkb 2002 ckb 480 kps 385]
319 PART taper Conf-001 1 somebox /lib 20080111 1/1 0 [sec 4.813543 kb 419 kps 87.133307]
320 DONE taper somebox /lib 20080111 1 0 [sec 4.813543 kb 419 kps 87.133307]
321 FINISH driver date 20080111 time 2167.581
324 ok(safe_overwrite_file($burp_corpus_fname, $sof_data),
325 "safe_overwrite_file success");
326 is(slurp($burp_corpus_fname), $sof_data,
327 "safe_overwrite_file round-trip check");
329 # check out get_fs_usage
330 my $fs_usage = Amanda::Util::get_fs_usage(POSIX::getcwd);
332 ok($fs_usage->{'blocks'}, "get_fs_usage returns something");
334 fail("get_fs_usage fails: $!");
337 # check file_lock -- again, full checks are in common-src/amflock-test.c
338 my $filename = "$Installcheck::TMP/testlock";
340 my $fl = Amanda::Util::file_lock->new($filename);
341 is($fl->data, undef, "data is initially undefined");
343 is($fl->data, undef, "data is undefined even after lock");
344 $fl->write("THIS IS MY DATA");
345 is($fl->data, "THIS IS MY DATA", "data is set correctly after write()");
349 $fl = Amanda::Util::file_lock->new($filename);
350 is($fl->data, undef, "data is initially undefined");
352 is($fl->data, "THIS IS MY DATA", "data is set correctly after lock");
354 ## check (un)marshal_tapespec
357 "FOO:1,2;BAR:3" => [ FOO => [ 1, 2 ], BAR => [ 3 ] ],
358 "SE\\;MI:0;COL\\:ON:3" => [ 'SE;MI' => [0], 'COL:ON' => [3] ],
359 "CO\\,MMA:88,99;BACK\\\\SLASH:3" => [ 'CO,MMA' => [88,99], 'BACK\\SLASH' => [3] ],
360 "FUNNY\\;:1;CHARS\\::2;AT\\,:3;END\\\\:4" =>
361 [ 'FUNNY;' => [ 1 ], 'CHARS:' => [ 2 ], 'AT,' => [ 3 ], 'END\\' => [ 4 ], ],
362 "\\;FUNNY:1;\\:CHARS:2;\\,AT:3;\\\\BEG:4" =>
363 [ ';FUNNY' => [ 1 ], ':CHARS' => [ 2 ], ',AT' => [ 3 ], '\\BEG' => [ 4 ], ],
367 my $tapespec = shift @tapespecs;
368 my $filelist = shift @tapespecs;
369 is(Amanda::Util::marshal_tapespec($filelist), $tapespec,
370 "marshal '$tapespec'");
371 is_deeply(Amanda::Util::unmarshal_tapespec($tapespec), $filelist,
372 "unmarshal '$tapespec'");
375 is_deeply(Amanda::Util::unmarshal_tapespec("x:100,99"), [ x => [99,100] ],
376 "filenums are sorted when unmarshalled");
378 is_deeply(Amanda::Util::marshal_tapespec([ x => [100, 99] ]), "x:100,99",
379 "un-sorted filenums are NOT sorted when marshalled");
381 is_deeply(Amanda::Util::unmarshal_tapespec("x:34,34"), [ x => [34, 34] ],
382 "duplicate filenums are NOT collapsed when unmarshalled");
384 is_deeply(Amanda::Util::marshal_tapespec([ x => [34, 34] ]), "x:34,34",
385 "duplicate filenums are NOT collapsed when marshalled");
387 is_deeply(Amanda::Util::unmarshal_tapespec("sim\\\\ple\\:quoted\\;file\\,name"),
388 [ "sim\\ple:quoted;file,name" => [0] ],
389 "simple non-tapespec string translated like string:0");
391 is_deeply(Amanda::Util::unmarshal_tapespec("tricky\\,tricky\\:1,2,3"),
392 [ "tricky,tricky:1,2,3" => [0] ],
393 "tricky non-tapespec string also translated to string:0");
395 is_deeply(Amanda::Util::unmarshal_tapespec("\\:3"), # one slash
397 "one slash escapes the colon");
399 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\:3"), # two slashes
401 "two slashes escape to one");
403 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\\\:3"), # three slashes
405 "three slashes escape to a slash and a colon");
407 is_deeply(Amanda::Util::unmarshal_tapespec("\\\\\\\\:3"), # four slashes
409 "four slashes escape to two");