package tests::UtilsTest; use strict; use base qw/ Lire::Test::TestCase /; use File::Spec; use Lire::Utils qw/xml_encode latex_encode diff_lists tilde_expand tmpdir tempfile tempdir min max ratio ratio100 shell_quote file_content create_file sql_quote_name tree_apply item_index deep_copy check_param check_object_param indent period_range text_for_width is_url parse_url unique /; use Time::Local; #our @TESTS = qw//; sub set_up { my $self = $_[0]; $self->SUPER::set_up(); return; } sub tear_down { my $self = $_[0]; $self->SUPER::tear_down(); return; } sub test_xml_encode { my $self = $_[0]; my $v = xml_encode( " & Joe \"Such\" a Friend's birtday" ); $self->assert_equals( "<XML> & </XML> Joe "Such" a Friend's birtday", $v ); } sub test_latex_encode { my $self = $_[0]; $self->assert_str_equals( '\\# \\$ \\& \\~{} \\_ \\^{} \\% \\{ \\} $\\backslash$ Other $[$ $]$ $<$ $>$', latex_encode( '# $ & ~ _ ^ % { } \\ Other [ ] < >' ) ); } sub test_diff_lists { my $self = $_[0]; my $diff = diff_lists( [qw/field1 field2 field3 field4/], [qw/field4 field3 field2 field1/] ); $self->assert_null( $diff, "should have found no diffs" ); $diff = diff_lists( [qw/field1 field2 field3 field4/], [qw/field3 field2 field4 field5/], ); $self->assert_not_null( $diff, "should have found diffs" ); $self->assert_deep_equals( [ "field1" ], $diff->{'remove'} ); $self->assert_deep_equals( [ "field5" ], $diff->{'new'} ); $diff = diff_lists( [qw/field1 field2 field3 field4/], [qw/field6 field3 field2 field4 field5/], ); $self->assert_not_null( $diff, "should have found diffs" ); $self->assert_deep_equals( [ "field1" ], $diff->{'remove'} ); $self->assert_deep_equals( [ "field5", "field6" ], $diff->{'new'} ); } sub test_tilde_expansion { my $self = $_[0]; my $home = $ENV{'HOME'}; $self->assert_equals($home, tilde_expand('~')); $self->assert_equals("$home/", tilde_expand('~/')); $self->assert_equals("$home/~", tilde_expand('~/~')); $self->assert_equals("$home/~/", tilde_expand('~/~/')); $self->assert_equals('/~', tilde_expand('/~')); $self->assert_equals('/~/', tilde_expand('/~/')); my $user = 'uioxzcyo'; $self->assert_equals("~$user", tilde_expand("~$user")); $self->assert_equals("~$user/", tilde_expand("~$user/")); $self->assert_equals("/$user~", tilde_expand("/$user~")); $self->assert_equals("/$user~/", tilde_expand("/$user~/")); $user = getpwuid($<); my ($name,$pass,$uid,$gid,$quota,$comment,$gecos,$dir) = getpwnam($user) or return; $self->assert_equals("$dir", tilde_expand("~$user")); $self->assert_equals("$dir/", tilde_expand("~$user/")); $self->assert_equals("/$user~", tilde_expand("/$user~")); $self->assert_equals("/$user~/", tilde_expand("/$user~/")); } sub test_min { my $self = $_[0]; $self->assert_null( min() ); $self->assert_null( min( undef, undef ) ); $self->assert_num_equals( -1, min( -1, undef) ); $self->assert_num_equals( 2, min( 2, undef) ); $self->assert_num_equals( -2, min( -1,-2, 0 ) ); $self->assert_num_equals( 0, min(0,0,0)); $self->assert_num_equals( 0, min(1,0,0)); } sub test_max { my $self = $_[0]; $self->assert_null( max() ); $self->assert_null( max( undef, undef ) ); $self->assert_num_equals( 1, max( -1, 0, 1, undef ) ); $self->assert_num_equals( -1, max( -1, -2) ); $self->assert_num_equals( 0, max( 0, 0, 0) ); $self->assert_num_equals( -1, max( -1, undef ) ); } sub test_tmpdir { my $self = $_[0]; $self->assert_equals( File::Spec->tmpdir, tmpdir() ); { local $ENV{'TMPDIR'} = $ENV{'HOME'}; $self->assert_equals( $ENV{'HOME'}, Lire::Utils::_tmpdir() ); } } sub test_tempfile { my $self = $_[0]; my $fh = Lire::Utils::_tempfile(); $self->assert_not_null( $fh, "_tempfile() returned undef" ); $self->assert( -r $fh, "file handle isn't readable" ); $self->assert( -w $fh, "file handle isn't writeable" ); my ( $nfh, $filename ) = Lire::Utils::_tempfile( "lireXXXXXX" ); $self->assert( qr/lire[a-zA-Z0-9]{6}$/, $filename ); $self->assert( -r $nfh, "file handle isn't readable" ); $self->assert( -w $nfh, "file handle isn't writeable" ); unlink $filename; # Try with perl's standard ones. (PerlUnit only works with perl 5.6.1 and # later my $pfh = tempfile(); $self->assert_not_null( $pfh, "tempfile() returned undef" ); $self->assert( -r $pfh, "file handle isn't readable" ); $self->assert( -w $pfh, "file handle isn't writeable" ); my ( $pnfh, $pfilename ) = tempfile( "lireXXXXXX" ); $self->assert( qr/lire[a-zA-Z0-9]{6}$/, $pfilename ); $self->assert( -r $pnfh, "file handle isn't readable" ); $self->assert( -w $pnfh, "file handle isn't writeable" ); unlink $pfilename; } sub test_tempdir { my $self = $_[0]; my $tmpdir = Lire::Utils::_tempdir(); $self->assert_not_null( $tmpdir, "_tempdir() returned undef" ); $self->assert( -r $tmpdir, "tempory dir isn't readable" ); $self->assert( -w $tmpdir, "tempory dir isn't writeable" ); $self->assert( -x $tmpdir, "tempory dir isn't searchable" ); rmdir $tmpdir; $tmpdir = Lire::Utils::_tempdir( "lireXXXXXX" );; $self->assert_not_null( $tmpdir, "_tempdir() returned undef" ); $self->assert( qr/lire[a-zA-Z0-9]{6}$/, $tmpdir ); $self->assert( -r $tmpdir, "tempory dir isn't readable" ); $self->assert( -w $tmpdir, "tempory dir isn't writeable" ); $self->assert( -x $tmpdir, "tempory dir isn't searchable" ); rmdir $tmpdir; # Try with perl's standard ones. (PerlUnit only works with perl 5.6.1 and # later my $ptmpdir = tempdir(); $self->assert_not_null( $ptmpdir, "tempdir() returned undef" ); $self->assert( -r $ptmpdir, "tempory dir isn't readable" ); $self->assert( -w $ptmpdir, "tempory dir isn't writeable" ); $self->assert( -x $ptmpdir, "tempory dir isn't searchable" ); rmdir $ptmpdir; $ptmpdir = tempdir( "lireXXXXXX" );; $self->assert_not_null( $ptmpdir, "_tmpdir() returned undef" ); $self->assert( qr/lire[a-zA-Z0-9]{6}$/, $ptmpdir ); $self->assert( -r $ptmpdir, "tempory dir isn't readable" ); $self->assert( -w $ptmpdir, "tempory dir isn't writeable" ); $self->assert( -x $ptmpdir, "tempory dir isn't searchable" ); rmdir $ptmpdir; } sub test_ratio { my $self = $_[0]; $self->assert_equals( 2, ratio( 10, 5 ) ); # Force string comparison since NaN == NaN is false $self->assert_str_equals( "NaN", ratio( 10, 0 ) ); $self->assert_equals( 0, ratio( 0, 10 ) ); $self->assert_equals( "3.33", ratio( 10, 3 ) ); } sub test_ratio100 { my $self = $_[0]; $self->assert_equals( 50, ratio100( 5, 10 ) ); # Force string comparison since NaN == NaN is false $self->assert_str_equals( "NaN", ratio100( 0, 0 ) ); $self->assert_equals( 0, ratio100( 0, 10 ) ); $self->assert_equals( "33.3", ratio100( 3333, 10000 ) ); } sub test_shell_quote { my $self = $_[0]; $self->assert_equals( "''", shell_quote( undef ) ); $self->assert_equals( "'Test'", shell_quote( 'Test' ) ); $self->assert_equals( q{'Test'\'''}, shell_quote( "Test'" ) ); $self->assert_equals( q{'`echo shell'\''s test`'}, shell_quote( "`echo shell's test`" ) ); } sub test_file_content { my $self = $_[0]; my $content = <assert_dies( qr/missing 'filename' parameter/, sub { file_content() } ); $self->assert_dies( qr{could not open '/no/such/file' for reading}, sub { file_content( "/no/such/file" ) } ); $self->assert_equals( $content, file_content( $name ) ); unlink $name; } sub test_create_file { my $self = $_[0]; no warnings 'redefine'; my $encoding = undef; my $set_fh_encoding = sub { $encoding = 1; }; local *Lire::I18N::set_fh_encoding = $set_fh_encoding; $self->assert_died( sub { create_file()}, qr/missing 'filename' parameter/ ); $self->assert_died( sub { create_file( "/no/such/file" ) }, qr/error creating.*: No such file/ ); my $content = < 1 ); my $filename = "$tmpdir/test_file"; create_file( $filename ); $self->assert( -f $filename, "$filename wasn't created" ); $self->assert( ! -s $filename, "$filename should be empty" ); create_file( $filename, $content ); my $fh; open $fh, $filename or $self->error( "open failed: $!" ); local $/; my $new_content = <$fh>; close $fh; $self->assert_equals( $content, $new_content ); $self->assert_null( $encoding ); create_file( $filename, $content, 1 ); $self->assert_num_equals( 1, $encoding ); return; } sub test_sql_quote_name { my $self = $_[0]; $self->assert_equals( 'field_name', sql_quote_name( 'field_name' ) ); $self->assert_equals( '"field-name"', sql_quote_name( 'field-name' ) ); $self->assert_equals( '"field.name"', sql_quote_name( 'field.name' ) ); $self->assert_equals( '"field:name"', sql_quote_name( 'field:name' ) ); } sub set_up_tree { my $self = $_[0]; my $root = new tests::node(); my @nodes = ( $root ); for ( 1..3) { push @nodes, new tests::node( $root ); } for ( 1..3 ) { push @nodes, new tests::node( $nodes[$_] ); push @nodes, new tests::node( $nodes[$_] ); } $self->assert_equals( 10, scalar @nodes ); return ( $root, \@nodes ); } sub test_tree_apply_bad_params { my $self = $_[0]; $self->assert_died( sub { tree_apply() }, qr/missing 'root' parameter/ ); $self->assert_died( sub { tree_apply( {}, ) }, qr/missing 'children_func' parameter/ ); $self->assert_died( sub { tree_apply( {}, sub {} ) }, qr/missing 'apply_func' parameter/ ); } sub test_tree_apply_subref { my $self = $_[0]; my ( $root, $nodes ) = $self->set_up_tree(); my $preorder_nodes = [ $nodes->[0], $nodes->[0]{'children'}[0], $nodes->[0]{'children'}[0]{'children'}[0], $nodes->[0]{'children'}[0]{'children'}[1], $nodes->[0]{'children'}[1], $nodes->[0]{'children'}[1]{'children'}[0], $nodes->[0]{'children'}[1]{'children'}[1], $nodes->[0]{'children'}[2], $nodes->[0]{'children'}[2]{'children'}[0], $nodes->[0]{'children'}[2]{'children'}[1], ]; my $idx = 1; my $result = tree_apply( $root, sub { $_[0]->{'children'} }, sub { $_[0]{'flag'} = $idx++ } ); my $e_result = [ 1 .. 10 ]; $self->assert_deep_equals( $e_result, $result ); for ( my $i=0; $i < @$preorder_nodes; $i++ ) { $self->assert_equals( $e_result->[$i], $preorder_nodes->[$i]{'flag'} ); } } sub test_tree_apply_method { my $self = $_[0]; my ( $root, $nodes ) = $self->set_up_tree(); my $result = tree_apply( $root, 'children', 'set_flag' ); $self->assert_deep_equals( [ ( 1 ) x 10 ], $result ); foreach my $n ( @$nodes ) { $self->assert_equals( 1, $n->{'flag'} ); } } sub test_item_index { my $self = $_[0]; my $ref = {}; my $array = [ 'wawa', 2, $ref, 4.0 ]; $self->assert_died( sub { item_index() }, qr/missing 'array' parameter/ ); $self->assert_died( sub { item_index( {} ) }, qr/'array' should be an array ref, not 'HASH/ ); #' $self->assert_died( sub { item_index( [] ) }, qr/missing 'item' parameter/ ); $self->assert_num_equals( 0, item_index( $array, 'wawa' ) ); $self->assert_num_equals( 1, item_index( $array, '2' ) ); $self->assert_num_equals( 2, item_index( $array, $ref ) ); $self->assert_num_equals( 3, item_index( $array, 4 ) ); $self->assert_null( undef, item_index( $array, 'cocotte' ) ); } sub test_deep_copy { my $self = $_[0]; $self->assert_died( sub { deep_copy() }, qr/missing 'object' parameter/ ); $self->assert_str_equals( 'Test', deep_copy( 'Test' ) ); $self->assert_died( sub { deep_copy( 'Test', {} ) }, qr/'exclusions' should be an array reference, not 'HASH/ ); my $array = [ 1, 'string', [ 2, undef, 4 ] ]; $self->assert_deep_equals( $array, deep_copy( $array ) ); $self->assert_str_not_equals( $array, deep_copy( $array ) ); my $rx = qr/.*/; $self->assert_str_equals( $rx, deep_copy( $rx ) ); my $hash = { 'key1' => $array, 'key2' => 1, 'key3' => $array, }; $hash->{'key4'} = $hash; # cyclic reference my $copy = deep_copy( $hash ); $self->assert_deep_equals( $hash, $copy ); $self->assert_str_not_equals( $hash, $copy ); $self->assert_str_not_equals( $array, $copy->{'key1'} ); $self->assert_str_equals( $copy->{'key1'}, $copy->{'key3'} ); $self->assert_str_equals( $copy, $copy->{'key4'} ); my $object = bless { 'key' => 'string1' }, 'object'; my $object_copy = deep_copy( $object ); $self->assert_str_equals( ref $object, ref $object_copy ); $self->assert_str_not_equals( $object, $object_copy ); $self->assert_deep_equals( $object, $object_copy ); } sub test_clone_scalar_ref { my $self = $_[0]; my $string = 'wawa'; my $ref1 = \$string; my $ref2 = \$ref1; my $excl = []; no warnings 'redefine'; local *Lire::Utils::_deep_copy = sub { my ( $object, $seen, $exclusions ) = @_; $self->assert_str_equals( $excl, $exclusions ); return $object unless ref $object; return $seen->{$object} if exists $seen->{$object}; if ( ref $object eq 'REF' || ref $object eq 'SCALAR') { return Lire::Utils::_clone_scalar_ref( $object, $seen,$exclusions); } else { $self->error( "ref type unsupported by deep_copy(): $object" ); } }; my $seen = {}; my $ref1_copy = Lire::Utils::_clone_scalar_ref( $ref1, $seen, $excl ); $self->assert_str_equals( $$ref1, $$ref1_copy ); $self->assert_str_not_equals( $ref1, $ref1_copy ); $self->assert_str_equals( $seen->{$ref1}, $ref1_copy ); my $ref2_copy = Lire::Utils::_clone_scalar_ref( $ref2, $seen, $excl ); $self->assert_str_equals( $$ref2_copy, $ref1_copy ); $self->assert_str_equals( $seen->{$ref2}, $ref2_copy ); } sub test_clone_array_ref { my $self = $_[0]; my $array1 = [ 1, 2, 3 ]; my $array2 = [ $array1 ]; my $excl = []; no warnings 'redefine'; local *Lire::Utils::_deep_copy = sub { my ( $object, $seen, $exclusions ) = @_; $self->assert_str_equals( $excl, $exclusions ); return $object unless ref $object; return $seen->{$object} if exists $seen->{$object}; if ( ref $object eq 'ARRAY' ) { return Lire::Utils::_clone_array_ref( $object, $seen, $exclusions); } else { $self->error( "ref type unsupported by deep_copy(): $object" ); } }; my $seen = {}; my $array1_copy = Lire::Utils::_clone_array_ref( $array1, $seen, $excl ); $self->assert_str_not_equals( $array1, $array1_copy ); $self->assert_deep_equals( $array1_copy, $array1 ); $self->assert_str_equals( $array1_copy, $seen->{$array1} ); my $array2_copy = Lire::Utils::_clone_array_ref( $array2, $seen, $excl ); $self->assert_str_equals( $array2_copy, $seen->{$array2} ); $self->assert_deep_equals( $array2_copy, $array2 ); } sub test_clone_hash_ref { my $self = $_[0]; my $hash1 = { 'key1' => 'value1', 'key2' => 'value2' }; my $hash2 = { 'key1' => 1, 'key2' => $hash1 }; my $excl = []; no warnings 'redefine'; local *Lire::Utils::_deep_copy = sub { my ( $object, $seen, $exclusions ) = @_; $self->assert_str_equals( $excl, $exclusions ); return $object unless ref $object; return $seen->{$object} if exists $seen->{$object}; if ( ref $object eq 'HASH' ) { return Lire::Utils::_clone_hash_ref( $object, $seen, $exclusions ); } else { $self->error( "ref type unsupported by deep_copy(): $object" ); } }; my $seen = {}; my $hash1_copy = Lire::Utils::_clone_hash_ref( $hash1, $seen, $excl ); $self->assert_str_not_equals( $hash1, $hash1_copy ); $self->assert_deep_equals( $hash1_copy, $hash1 ); $self->assert_str_equals( $hash1_copy, $seen->{$hash1} ); my $hash2_copy = Lire::Utils::_clone_hash_ref( $hash2, $seen, $excl ); $self->assert_str_equals( $hash2_copy, $seen->{$hash2} ); $self->assert_deep_equals( $hash2_copy, $hash2 ); } sub test_clone_object { my $self = $_[0]; my $scalar = 'Test'; my $scalar_object = bless \$scalar, 'scalar'; my $array_object = bless [], 'array'; my $glob_object = bless \*SYMBOL, 'symbol'; my $object1 = bless { 'key1' => 1 }, "package1"; my $object2 = bless { 'key1' => $object1 }, 'package2'; $self->assert_died( sub { Lire::Utils::_clone_object( $glob_object, {} ) }, qr/unsupported object storage: 'symbol=GLOB/ ); my $seen = {}; my $ob1_copy = Lire::Utils::_clone_object( $object1, $seen ); $self->assert_str_not_equals( $object1, $ob1_copy ); $self->assert_str_equals( ref $object1, ref $ob1_copy ); $self->assert_deep_equals( $object1, $ob1_copy ); $self->assert_str_equals( $ob1_copy, $seen->{$object1} ); my $ob2_copy = Lire::Utils::_clone_object( $object2, $seen ); $self->assert_str_equals( $ob2_copy, $seen->{$object2} ); $self->assert_deep_equals( $ob2_copy, $object2 ); $self->assert_str_equals( $object2, Lire::Utils::_clone_object( $object2, $seen, [ 'package2' ] ) ); } sub test_check_param { my $self = $_[0]; $self->assert_died( sub { check_param() }, qr/check_param needs at least 2 arguments: a parameter, the parameter\'s name/ ); $self->assert_died( sub { check_param( undef, 'wawa&' ) }, qr/parameter 'name' is not a valid name: 'wawa&'/ ); $self->assert_died( sub { check_param( undef, 'wawa' ) }, qr/missing \'wawa\' parameter/ ); $self->assert_died( sub { check_param( '123', 'wawa', qr/^[a-z]+$/ ) }, qr/\'wawa\' parameter doesn\'t match \'\(.*\)': \'123\'/ ); $self->assert_died( sub { check_param( 'abc', 'wawa', qr/^[0-9]+$/, "'wawa' parameter is not a valid integer" )}, qr/\'wawa\' parameter is not a valid integer: \'abc\'/ ); $self->assert_died( sub { check_param( 0, 'wawa', sub { return $_[0] }, "'wawa' parameter is false" )}, qr/\'wawa\' parameter is false: \'0\'/ ); check_param( 1, 'wawa', sub { return $_[0] }, "This test of check_param should have passed" ); return; } sub test_check_object_param { my $self = $_[0]; my $test_obj = bless {}, 'TestClass'; $self->assert_died( sub { check_object_param() }, qr/check_object_param needs 3 arguments: an object, the object parameter\'s name, the object's class name/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawaobj&', [ 'WawaClass' ] ) }, qr/parameter 'name' is not a valid name: 'wawaobj&'/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawaobj', 'WawaClass&' ) }, qr/parameter 'class' contains an invalid class name: 'WawaClass&'/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawaobj', [ 'WawaClass&' ] ) }, qr/parameter 'class' contains an invalid class name: 'WawaClass&'/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawaobj', [ 'Wawa:Class' ] ) }, qr/parameter 'class' contains an invalid class name: 'Wawa:Class'/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawaobj', [ 'Wawa:::Class' ] ) }, qr/parameter 'class' contains an invalid class name: 'Wawa:::Class'/ ); $self->assert_died( sub { check_object_param( 'wawa', 'wawaname', [ 'wawaclass' ] ) }, qr/'wawaname' parameter should be a 'wawaclass' instance, not \'wawa\'/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawa', 'WawaClass' ) }, qr/'wawa' parameter should be a 'WawaClass' instance, not 'TestClass/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawa', [ 'WawaClass' ] ) }, qr/'wawa' parameter should be a 'WawaClass' instance, not 'TestClass/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawa', [ 'WawaClass', 'Chill::Class', 'Mega::Class::Name' ] ) }, qr/'wawa' parameter should be a 'WawaClass', 'Chill::Class' or 'Mega::Class::Name' instance, not 'TestClass/ ); $self->assert_died( sub { check_object_param( $test_obj, 'wawa', [] )}, qr/parameter \'class\' should contain at least one class name/ ); my $rc = check_object_param( $test_obj, 'wawaobj', [ 'TestClass' ] ); $self->assert_null( $rc, "there should be no return code" ); check_object_param( bless ({}, 'Test::Class') , 'wawaobj', [ 'Test::Class' ] ); return; } sub _check_object_param_fail_method { my ( $self, $object ); check_object_param( $object, 'test_object', [ 'tests::UtilsTest' ] ); } sub test_check_object_param_caller { my $self = $_[0]; # check_object_param() invocations with a wrong object instance should # return the method's caller informations (1st check) while bad parameters # given to check_object_param() itself should refer to the # check_object_param's caller (2nd check). my $this_file = 'nofile'; my $this_line = 'noline'; eval { $this_file = __FILE__; $this_line = __LINE__; _check_object_param_fail_method( undef ); }; my $err = $@; $self->assert_not_null( $err, "no error message??" ); $self->assert_matches( qr/^missing 'test_object' parameter at $this_file:$this_line$/, $err ); eval { $this_file = __FILE__; $this_line = __LINE__; check_object_param();}; $err = $@; $self->assert_not_null( $err, "no error message??" ); $self->assert_matches( qr/^check_object_param needs 3 arguments: an object, the object parameter's name, the object's class name at $this_file:$this_line$/, $err ); return; } sub test_indent { my $self = $_[0]; $self->assert_str_equals( '', indent() ); $self->assert_str_equals( '', indent( '' ) ); my $start_text = 'simple line'; $self->assert_str_equals( ' simple line', indent( $start_text ) ); $start_text = <<_EOT; text for indentation indented line end of indentation _EOT my $e_text = <<_EOT; text for indentation indented line end of indentation _EOT $self->assert_str_equals( $e_text, indent( $start_text ) ); $e_text = <<_EOT; text for indentation indented line end of indentation _EOT $self->assert_str_equals( $e_text, indent( $start_text, 4 ) ); return; } sub test_period_range { my $self = $_[0]; $self->{'cfg'}{'lr_week_numbering'} = 'U'; $self->set_up_tz( 'UTC' ); my $dec31_2004 = timelocal( 0, 5, 23, 31, 11, 2004 ); $self->assert_dies( qr/'period' parameter should be one of 'hourly', 'daily', 'weekly', 'monthly' or 'yearly'/, sub { period_range( 'unique', $dec31_2004 ) } ); $self->assert_deep_equals( [ timelocal( 0, 0, 23, 31, 11, 2004 ), timelocal( 0, 0, 0, 1, 0, 2005 ) ], period_range( 'hourly', $dec31_2004 ) ); $self->assert_deep_equals( [ timelocal( 0, 0, 0, 31, 11, 2004 ), timelocal( 0, 0, 0, 1, 0, 2005 ) ], period_range( 'daily', $dec31_2004 ) ); $self->assert_deep_equals( [ timelocal( 0, 0, 0, 1, 11, 2004 ), timelocal( 0, 0, 0, 1, 0, 2005 ) ], period_range( 'monthly', $dec31_2004 ) ); my $jan1_2004 = timelocal( 0, 0, 0, 1, 0, 2004 ); $self->assert_deep_equals( [ timelocal( 0, 0, 0, 1, 0, 2004 ), timelocal( 0, 0, 0, 1, 1, 2004 ) ], period_range( 'monthly', $jan1_2004 ) ); $self->assert_deep_equals( [ timelocal( 0, 0, 0, 1, 0, 2004 ), timelocal( 0, 0, 0, 1, 0, 2005 ) ], period_range( 'yearly', $dec31_2004 ) ); $self->assert_deep_equals( [ timelocal( 0, 0, 0, 26, 11, 2004 ), timelocal( 0, 0, 0, 2, 0, 2005 ) ], period_range( 'weekly', $dec31_2004 ) ); } sub test_text_for_width { my $self = $_[0]; $self->assert_dies( qr/missing \'text\' parameter/, sub { text_for_width() } ); $self->assert_dies( qr/missing \'width\' parameter/, sub { text_for_width( 'wawa' ) } ); $self->assert_dies( qr/\'width' should be a positive, greater than or equal to 5, integer: \'-3\'/, sub { text_for_width( 'wawa', -3 ) } ); $self->assert_dies( qr/\'width' should be a positive, greater than or equal to 5, integer: \'wawa\'/, sub { text_for_width( 'wawa', 'wawa' ) } ); $self->assert_str_equals( 'wawa', text_for_width( 'wawa', 10 ) ); $self->assert_str_equals( 'b...s', text_for_width( 'bigwawatestormorethan9chars', 5 ) ); $self->assert_str_equals( 'big...ars', text_for_width( 'big wawa test or more than 9 chars', 9 ) ); $self->assert_str_equals( 'big...hars', text_for_width( 'big wawa test or more than 10 chars', 10 ) ); $self->assert_str_equals( 'big...test', text_for_width( 'big test', 10 ) ); } sub test_is_url { my $self = $_[0]; my $nourl_string1 = "/etc/lire"; my $nourl_string2 = "/etc/lire/file://tmp"; my $nourl_string3 = "file:/etc/lire/wawa"; my $nourl_string4 = "fi le://etc/lire/wawa"; my $url_string1 = "http://www.wawa.org"; my $url_string2 = "file:///tmp"; my $url_string3 = "file://c:/temp"; $self->assert_num_equals( 0, is_url( $nourl_string1 ) ); $self->assert_num_equals( 0, is_url( $nourl_string2 ) ); $self->assert_num_equals( 0, is_url( $nourl_string3 ) ); $self->assert_num_equals( 0, is_url( $nourl_string4 ) ); $self->assert_num_equals( 1, is_url( $url_string1 ) ); $self->assert_num_equals( 1, is_url( $url_string2 ) ); $self->assert_num_equals( 1, is_url( $url_string3 ) ); return; } sub test_parse_url { my $self = $_[0]; $self->assert_deep_equals( { 'scheme' => 'http', 'host' => 'www.logreport.org', 'port' => 80, 'path' => '/index.html', 'query' => 'search=test', 'fragment' => 'anchor' }, parse_url( 'http://www.logreport.org:80/index.html?search=test#anchor' ) ); $self->assert_deep_equals( { 'scheme' => 'http', 'host' => 'www.logreport.org', 'port' => undef, 'path' => '/index.html', 'query' => undef, 'fragment' => undef }, parse_url( 'http://www.logreport.org/index.html' ) ); $self->assert_deep_equals( { 'scheme' => undef, 'host' => undef, 'port' => undef, 'path' => '/index.html', 'query' => undef, 'fragment' => 'anchor' }, parse_url( '/index.html#anchor' ) ); } sub test_unique { my $self = $_[0]; $self->assert_deep_equals( [ 1, 2, 3 ], unique( [ 1, 2, 3, 1, 2, 3] ) ); $self->assert_deep_equals( [ 'aa', 'b', 'c' ], unique( [ 'aa', 'aa', 'aa', 'b', 'c' ] ) ); } package tests::node; sub new { my ( $pkg, $parent ) = @_; my $self = bless { 'flag' => 0, 'children' => [] }, $pkg; push @{$parent->{'children'}}, $self if defined $parent; return $self; } sub children { return $_[0]{'children'}; } sub set_flag { return $_[0]{'flag'} = 1; } 1;