use strict; use warnings; use FindBin; use lib "$FindBin::Bin/lib"; use Helpers; # Local helper routines used by the test suite. use Test::More tests => 113; use CDB_File; { my ( $db, undef ) = get_db_file_pair(); close($db); my $file = $db->filename; unlink $file; my %h; ok( !( tie( %h, "CDB_File", $file ) ), "Tie non-existant file" ); } { my ( $db, undef ) = get_db_file_pair(); print {$db} ""; close $db; my $db_name = $db->filename; my %h; ok( -e $db_name && -z _, "$db_name is an empty file" ); ok( ( tie( %h, "CDB_File", $db_name ) ), "Load blank cdb file (invalid file, but loading it works)" ); eval { print $h{'one'} }; like( $@, qr/^Read of CDB_File failed:/, "Test that attempt to read incorrect file fails" ); untie %h; } { my ( $db, $db_tmp ) = get_db_file_pair(1); my %a = qw(one Hello two Goodbye); eval { CDB_File::create( %a, $db->filename, $db_tmp->filename ) or die "Failed to create cdb: $!" }; is( "$@", '', "Create cdb" ); # Test that good file works. my %h; tie( %h, "CDB_File", $db->filename ) and pass("Test that good file works"); my $t = tied %h; isa_ok( $t, "CDB_File" ); is( $t->FETCH('one'), 'Hello', "Test that good file FETCHes right results" ); is( $h{'one'}, 'Hello', "Test that good file hash access gets right results" ); ok( !defined( $h{'1'} ), "Check defined() non-existant entry works" ); ok( exists( $h{'two'} ), "Check exists() on a real entry works" ); ok( !exists( $h{'three'} ), "Check exists() on non-existant entry works" ); # Test low level access. my $fh = $t->handle; my $x; exists( $h{'one'} ); # go to this entry note( "Datapos: ", $t->datapos, ", Datalen: ", $t->datalen ); sysseek( $fh, $t->datapos, 0 ); sysread( $fh, $x, $t->datalen ); is( $x, 'Hello', "Check low level access read worked" ); exists( $h{'two'} ); note( "Datapos: ", $t->datapos, ", Datalen: ", $t->datalen ); sysseek( $fh, $t->datapos, 0 ); sysread( $fh, $x, $t->datalen ); is( $x, 'Goodbye', "Check low level access read worked" ); exists( $h{'three'} ); note( "Datapos: ", $t->datapos, ", Datalen: ", $t->datalen ); is( $t->datapos, 0, "Low level access on no-exist entry" ); is( $t->datalen, 0, "Low level access on no-exist entry" ); my @h = sort keys %h; is( scalar @h, 2, "keys length == 2" ); is( $h[0], 'one', "first key right" ); is( $h[1], 'two', "second key right" ); eval { $h{'four'} = 'foo' }; like( $@, qr/Modification of a CDB_File attempted/, "Check modifying throws exception" ); eval { delete $h{'five'} }; like( $@, qr/Modification of a CDB_File attempted/, "Check modifying throws exception" ); close $fh; # Duped file handle must be closed. undef $t; untie %h; # Release the tie so the file closes and we can remove it. } { my ( $db, $db_tmp ) = get_db_file_pair(1); my ( %a, %h ); eval { CDB_File::create( %a, $db->filename, $db_tmp->filename ) || die "CDB create failed" }; is( !$@, 1, "No errors creating cdb" ); ok( ( tie( %h, "CDB_File", $db->filename ) ), "Tie new empty cdb" ); my @h = keys %h; is( scalar @h, 0, "Empty cdb has no keys" ); untie %h; } { # Test failing new. ok( !CDB_File->new( '..', '.' ), "Creating cdb with dirs fails" ); } { # Test file with repeated keys. my ( $db, $db_tmp ) = get_db_file_pair(1); my $cdbm = CDB_File->new( $db->filename, $db_tmp->filename ); isa_ok( $cdbm, 'CDB_File::Maker' ); $cdbm->insert( 'dog', 'perro' ); $cdbm->insert( 'cat', 'gato' ); $cdbm->insert( 'cat', 'chat' ); $cdbm->insert( 'dog', 'chien' ); $cdbm->insert( 'rabbit', 'conejo' ); $cdbm->finish; undef $cdbm; my %h; my $t = tie %h, "CDB_File", $db->filename; isa_ok( $t, 'CDB_File' ); eval { $t->NEXTKEY('dog') }; # ok($@, qr/^Use CDB_File::FIRSTKEY before CDB_File::NEXTKEY/, "Test that NEXTKEY can't be used immediately after TIEHASH"); is( $@, '', "Test that NEXTKEY can be used immediately after TIEHASH" ); # Check keys/values works my @k = keys %h; my @v = values %h; is( $k[0], 'dog' ); is( $v[0], 'perro' ); is( $k[1], 'cat' ); is( $v[1], 'gato' ); is( $k[2], 'cat' ); is( $v[2], 'chat' ); is( $k[3], 'dog' ); is( $v[3], 'chien' ); is( $k[4], 'rabbit' ); is( $v[4], 'conejo' ); @k = (); @v = (); # Check each works while ( my ( $k, $v ) = each %h ) { push @k, $k; push @v, $v; } is( $k[0], 'dog' ); is( $v[0], 'perro' ); is( $k[1], 'cat' ); is( $v[1], 'gato' ); is( $k[2], 'cat' ); is( $v[2], 'chat' ); is( $k[3], 'dog' ); is( $v[3], 'chien' ); is( $k[4], 'rabbit' ); is( $v[4], 'conejo' ); my $v = $t->multi_get('cat'); is( @$v, 2, "multi_get returned 2 entries" ); is( $v->[0], 'gato' ); is( $v->[1], 'chat' ); $v = $t->multi_get('dog'); is( @$v, 2, "multi_get returned 2 entries" ); is( $v->[0], 'perro' ); is( $v->[1], 'chien' ); $v = $t->multi_get('rabbit'); is( @$v, 1, "multi_get returned 1 entry" ); is( $v->[0], 'conejo' ); $v = $t->multi_get('foo'); is( ref($v), 'ARRAY', "multi_get on non-existant entry works" ); is( @$v, 0 ); while ( my ( $k, $v ) = each %h ) { $v = $t->multi_get($k); ok( $v->[0] eq 'gato' and $v->[1] eq 'chat' ) if $k eq 'cat'; ok( $v->[0] eq 'perro' and $v->[1] eq 'chien' ) if $k eq 'dog'; ok( $v->[0] eq 'conejo' ) if $k eq 'rabbit'; } # Test undefined keys. { my $warned = 0; local $SIG{__WARN__} = sub { $warned = 1 if $_[0] =~ /^Use of uninitialized value/ }; local $^W = 1; my $x; ok( !defined $h{$x} ); SKIP: { skip 'Perl 5.8.2 and below do not warn about $x{undef}', 1 unless $] > 5.008003; ok($warned); } $warned = 0; ok( !exists $h{$x} ); SKIP: { skip 'Perl 5.6 does not warn about $x{undef}', 1 unless $] > 5.007; ok($warned); } $warned = 0; my $v = $t->multi_get('rabbit'); ok($v); ok( !$warned ); } # Check that object is readonly. eval { $$t = 'foo' }; like( $@, qr/^Modification of a read-only value/, "Check object (\$t) is read only" ); is( $h{'cat'}, 'gato' ); undef $t; untie %h; } { my ( $db, $db_tmp ) = get_db_file_pair(1); # Regression test - dumps core in 0.6. my %a = ( 'one', '' ); ok( ( CDB_File::create( %a, $db->filename, $db_tmp->filename ) ), "Create good.cdb" ); my %h; ok( ( tie( %h, "CDB_File", $db->filename ) ), "Tie good.cdb" ); ok( !exists $h{'zero'}, "missing key test" ); ok( defined( $h{'one'} ), "one is found and defined" ); is( $h{'one'}, '', "one is empty" ); untie %h; # Release the tie so the file closes and we can remove it. } { my ( $db, $db_tmp ) = get_db_file_pair(1); # Test numeric data (broken before 0.8) my $h = CDB_File->new( $db->filename, $db_tmp->filename ); isa_ok( $h, 'CDB_File::Maker' ); $h->insert( 1, 1 * 23 ); ok( $h->finish ); my %h; ok( tie( %h, "CDB_File", $db->filename ) ); is( $h{1}, 23, "Numeric comparison works" ); untie %h; } { my ( $db, $db_tmp ) = get_db_file_pair(1); # Test zero value with multi_get (broken before 0.85) my $h = CDB_File->new( $db->filename, $db_tmp->filename ); isa_ok( $h, 'CDB_File::Maker' ); $h->insert( 'x', 0 ); $h->insert( 'x', 1 ); ok( $h->finish ); my %h; my $t = tie( %h, "CDB_File", $db->filename ); isa_ok( $t, 'CDB_File' ); my $x = $t->multi_get('x'); is( @$x, 2 ); is( $x->[0], 0 ); is( $x->[1], 1 ); undef $t; untie %h; } { my ( $db, $db_tmp ) = get_db_file_pair(1); my $h = CDB_File->new( $db->filename, $db_tmp->filename ); isa_ok( $h, 'CDB_File::Maker' ); for ( my $i = 0; $i < 10; ++$i ) { $h->insert( $i, $i ); } ok( $h->finish ); undef $h; my %h; my $t = tie( %h, "CDB_File", $db->filename ); isa_ok( $t, 'CDB_File' ); for ( my $i = 0; $i < 10; ++$i ) { my ( $k, $v ) = each %h; if ( $k == 2 ) { ok( exists( $h{4} ) ); } if ( $k == 5 ) { ok( !exists( $h{23} ) ); } if ( $k == 7 ) { my $m = $t->multi_get(3); is( @$m, 1 ); is( $m->[0], 3 ); } is( $k, $i, "$k eq $i" ); is( $v, $i, "$v eq $i" ); } undef $t; untie %h; undef $h; } exit;