HiRes.t tweaks
[p5sagit/p5-mst-13.2.git] / ext / DB_File / t / db-recno.t
CommitLineData
f6b705ef 1#!./perl -w
a0d0e21e 2
3BEGIN {
88587957 4 chdir 't' if -d 't';
20822f61 5 @INC = '../lib';
a0d0e21e 6 require Config; import Config;
7 if ($Config{'extensions'} !~ /\bDB_File\b/) {
45c0de28 8 print "1..0 # Skip: DB_File was not built\n";
a0d0e21e 9 exit 0;
10 }
11}
12
13use DB_File;
14use Fcntl;
55d68b4a 15use strict ;
3245f058 16use warnings;
045291aa 17use vars qw($dbh $Dfile $bad_ones $FA) ;
18
19# full tied array support started in Perl 5.004_57
a9fd575d 20# Double check to see if it is available.
21
22{
23 sub try::TIEARRAY { bless [], "try" }
24 sub try::FETCHSIZE { $FA = 1 }
25 $FA = 0 ;
26 my @a ;
27 tie @a, 'try' ;
28 my $a = @a ;
29}
30
a0d0e21e 31
55d68b4a 32sub ok
33{
34 my $no = shift ;
35 my $result = shift ;
a0d0e21e 36
55d68b4a 37 print "not " unless $result ;
38 print "ok $no\n" ;
6250ba0a 39
40 return $result ;
41}
42
9b761c68 43{
44 package Redirect ;
45 use Symbol ;
46
47 sub new
48 {
49 my $class = shift ;
50 my $filename = shift ;
51 my $fh = gensym ;
52 open ($fh, ">$filename") || die "Cannot open $filename: $!" ;
53 my $real_stdout = select($fh) ;
54 return bless [$fh, $real_stdout ] ;
55
56 }
57 sub DESTROY
58 {
59 my $self = shift ;
60 close $self->[0] ;
61 select($self->[1]) ;
62 }
63}
64
65sub docat
66{
67 my $file = shift;
68 local $/ = undef;
69 open(CAT,$file) || die "Cannot open $file:$!";
70 my $result = <CAT>;
71 close(CAT);
72 return $result;
73}
74
75sub docat_del
76{
77 my $file = shift;
78 local $/ = undef;
79 open(CAT,$file) || die "Cannot open $file: $!";
80 my $result = <CAT>;
81 close(CAT);
82 unlink $file ;
83 return $result;
84}
85
6250ba0a 86sub bad_one
87{
25268f15 88 print STDERR <<EOM unless $bad_ones++ ;
89#
20896112 90# Some older versions of Berkeley DB version 1 will fail tests 51,
91# 53 and 55.
6250ba0a 92#
93# You can safely ignore the errors if you're never going to use the
94# broken functionality (recno databases with a modified bval).
95# Otherwise you'll have to upgrade your DB library.
96#
20896112 97# If you want to use Berkeley DB version 1, then 1.85 and 1.86 are the
98# last versions that were released. Berkeley DB version 2 is continually
99# being updated -- Check out http://www.sleepycat.com/ for more details.
6250ba0a 100#
101EOM
55d68b4a 102}
103
c6c92ad9 104my $splice_tests = 10 + 1; # ten regressions, plus the randoms
105my $total_tests = 138 ;
106$total_tests += $splice_tests if $FA ;
107print "1..$total_tests\n";
55d68b4a 108
109my $Dfile = "recno.tmp";
110unlink $Dfile ;
a0d0e21e 111
112umask(0);
113
114# Check the interface to RECNOINFO
115
55d68b4a 116my $dbh = new DB_File::RECNOINFO ;
3fe9a6f1 117ok(1, ! defined $dbh->{bval}) ;
118ok(2, ! defined $dbh->{cachesize}) ;
119ok(3, ! defined $dbh->{psize}) ;
120ok(4, ! defined $dbh->{flags}) ;
121ok(5, ! defined $dbh->{lorder}) ;
122ok(6, ! defined $dbh->{reclen}) ;
123ok(7, ! defined $dbh->{bfname}) ;
a0d0e21e 124
125$dbh->{bval} = 3000 ;
f6b705ef 126ok(8, $dbh->{bval} == 3000 );
a0d0e21e 127
128$dbh->{cachesize} = 9000 ;
f6b705ef 129ok(9, $dbh->{cachesize} == 9000 );
a0d0e21e 130
131$dbh->{psize} = 400 ;
f6b705ef 132ok(10, $dbh->{psize} == 400 );
a0d0e21e 133
134$dbh->{flags} = 65 ;
f6b705ef 135ok(11, $dbh->{flags} == 65 );
a0d0e21e 136
137$dbh->{lorder} = 123 ;
f6b705ef 138ok(12, $dbh->{lorder} == 123 );
a0d0e21e 139
140$dbh->{reclen} = 1234 ;
f6b705ef 141ok(13, $dbh->{reclen} == 1234 );
a0d0e21e 142
143$dbh->{bfname} = 1234 ;
f6b705ef 144ok(14, $dbh->{bfname} == 1234 );
a0d0e21e 145
146
147# Check that an invalid entry is caught both for store & fetch
148eval '$dbh->{fred} = 1234' ;
f6b705ef 149ok(15, $@ =~ /^DB_File::RECNOINFO::STORE - Unknown element 'fred' at/ );
55d68b4a 150eval 'my $q = $dbh->{fred}' ;
f6b705ef 151ok(16, $@ =~ /^DB_File::RECNOINFO::FETCH - Unknown element 'fred' at/ );
a0d0e21e 152
153# Now check the interface to RECNOINFO
154
55d68b4a 155my $X ;
156my @h ;
157ok(17, $X = tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) ;
a0d0e21e 158
d536870a 159ok(18, ((stat($Dfile))[2] & 0777) == (($^O eq 'os2' || $^O eq 'MacOS') ? 0666 : 0640)
13d11c6e 160 || $^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'cygwin' || $^O eq 'amigaos') ;
a0d0e21e 161
55d68b4a 162#my $l = @h ;
163my $l = $X->length ;
045291aa 164ok(19, ($FA ? @h == 0 : !$l) );
a0d0e21e 165
55d68b4a 166my @data = qw( a b c d ever f g h i j k longername m n o p) ;
a0d0e21e 167
168$h[0] = shift @data ;
f6b705ef 169ok(20, $h[0] eq 'a' );
a0d0e21e 170
55d68b4a 171my $ i;
a0d0e21e 172foreach (@data)
173 { $h[++$i] = $_ }
174
175unshift (@data, 'a') ;
176
f6b705ef 177ok(21, defined $h[1] );
178ok(22, ! defined $h[16] );
045291aa 179ok(23, $FA ? @h == @data : $X->length == @data );
a0d0e21e 180
181
182# Overwrite an entry & check fetch it
183$h[3] = 'replaced' ;
184$data[3] = 'replaced' ;
f6b705ef 185ok(24, $h[3] eq 'replaced' );
a0d0e21e 186
187#PUSH
55d68b4a 188my @push_data = qw(added to the end) ;
045291aa 189($FA ? push(@h, @push_data) : $X->push(@push_data)) ;
a0d0e21e 190push (@data, @push_data) ;
f6b705ef 191ok(25, $h[++$i] eq 'added' );
192ok(26, $h[++$i] eq 'to' );
193ok(27, $h[++$i] eq 'the' );
194ok(28, $h[++$i] eq 'end' );
a0d0e21e 195
196# POP
f6b705ef 197my $popped = pop (@data) ;
045291aa 198my $value = ($FA ? pop @h : $X->pop) ;
f6b705ef 199ok(29, $value eq $popped) ;
a0d0e21e 200
201# SHIFT
045291aa 202$value = ($FA ? shift @h : $X->shift) ;
f6b705ef 203my $shifted = shift @data ;
204ok(30, $value eq $shifted );
a0d0e21e 205
206# UNSHIFT
207
208# empty list
936edb8b 209($FA ? unshift @h,() : $X->unshift) ;
045291aa 210ok(31, ($FA ? @h == @data : $X->length == @data ));
a0d0e21e 211
55d68b4a 212my @new_data = qw(add this to the start of the array) ;
045291aa 213$FA ? unshift (@h, @new_data) : $X->unshift (@new_data) ;
a0d0e21e 214unshift (@data, @new_data) ;
045291aa 215ok(32, $FA ? @h == @data : $X->length == @data );
f6b705ef 216ok(33, $h[0] eq "add") ;
217ok(34, $h[1] eq "this") ;
218ok(35, $h[2] eq "to") ;
219ok(36, $h[3] eq "the") ;
220ok(37, $h[4] eq "start") ;
221ok(38, $h[5] eq "of") ;
222ok(39, $h[6] eq "the") ;
223ok(40, $h[7] eq "array") ;
224ok(41, $h[8] eq $data[8]) ;
a0d0e21e 225
c6c92ad9 226# Brief test for SPLICE - more thorough 'soak test' is later.
227my @old;
228if ($FA) {
229 @old = splice(@h, 1, 2, qw(bananas just before));
230}
231else {
232 @old = $X->splice(1, 2, qw(bananas just before));
233}
234ok(42, $h[0] eq "add") ;
235ok(43, $h[1] eq "bananas") ;
236ok(44, $h[2] eq "just") ;
237ok(45, $h[3] eq "before") ;
238ok(46, $h[4] eq "the") ;
239ok(47, $h[5] eq "start") ;
240ok(48, $h[6] eq "of") ;
241ok(49, $h[7] eq "the") ;
242ok(50, $h[8] eq "array") ;
243ok(51, $h[9] eq $data[8]) ;
244$FA ? splice(@h, 1, 3, @old) : $X->splice(1, 3, @old);
a0d0e21e 245
246# Now both arrays should be identical
247
55d68b4a 248my $ok = 1 ;
249my $j = 0 ;
a0d0e21e 250foreach (@data)
251{
252 $ok = 0, last if $_ ne $h[$j ++] ;
253}
c6c92ad9 254ok(52, $ok );
a0d0e21e 255
55d68b4a 256# Neagtive subscripts
257
258# get the last element of the array
c6c92ad9 259ok(53, $h[-1] eq $data[-1] );
260ok(54, $h[-1] eq $h[ ($FA ? @h : $X->length) -1] );
55d68b4a 261
262# get the first element using a negative subscript
045291aa 263eval '$h[ - ( $FA ? @h : $X->length)] = "abcd"' ;
c6c92ad9 264ok(55, $@ eq "" );
265ok(56, $h[0] eq "abcd" );
55d68b4a 266
267# now try to read before the start of the array
045291aa 268eval '$h[ - (1 + ($FA ? @h : $X->length))] = 1234' ;
c6c92ad9 269ok(57, $@ =~ '^Modification of non-creatable array value attempted' );
55d68b4a 270
a0d0e21e 271# IMPORTANT - $X must be undefined before the untie otherwise the
272# underlying DB close routine will not get called.
273undef $X ;
274untie(@h);
275
276unlink $Dfile;
277
a6ed719b 278
36477c24 279{
280 # Check bval defaults to \n
281
282 my @h = () ;
283 my $dbh = new DB_File::RECNOINFO ;
c6c92ad9 284 ok(58, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
36477c24 285 $h[0] = "abc" ;
286 $h[1] = "def" ;
287 $h[3] = "ghi" ;
288 untie @h ;
a6ed719b 289 my $x = docat($Dfile) ;
36477c24 290 unlink $Dfile;
13d11c6e 291 ok(59, $x eq "abc\ndef\n\nghi\n" ||
292 $x eq "abc\r\ndef\r\n\r\nghi\r\n") ;
36477c24 293}
294
295{
296 # Change bval
297
298 my @h = () ;
299 my $dbh = new DB_File::RECNOINFO ;
300 $dbh->{bval} = "-" ;
c6c92ad9 301 ok(60, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
36477c24 302 $h[0] = "abc" ;
303 $h[1] = "def" ;
304 $h[3] = "ghi" ;
305 untie @h ;
a6ed719b 306 my $x = docat($Dfile) ;
36477c24 307 unlink $Dfile;
6250ba0a 308 my $ok = ($x eq "abc-def--ghi-") ;
309 bad_one() unless $ok ;
c6c92ad9 310 ok(61, $ok) ;
36477c24 311}
312
313{
314 # Check R_FIXEDLEN with default bval (space)
315
316 my @h = () ;
317 my $dbh = new DB_File::RECNOINFO ;
318 $dbh->{flags} = R_FIXEDLEN ;
319 $dbh->{reclen} = 5 ;
c6c92ad9 320 ok(62, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
36477c24 321 $h[0] = "abc" ;
322 $h[1] = "def" ;
323 $h[3] = "ghi" ;
324 untie @h ;
a6ed719b 325 my $x = docat($Dfile) ;
36477c24 326 unlink $Dfile;
6250ba0a 327 my $ok = ($x eq "abc def ghi ") ;
328 bad_one() unless $ok ;
c6c92ad9 329 ok(63, $ok) ;
36477c24 330}
331
332{
333 # Check R_FIXEDLEN with user-defined bval
334
335 my @h = () ;
336 my $dbh = new DB_File::RECNOINFO ;
337 $dbh->{flags} = R_FIXEDLEN ;
338 $dbh->{bval} = "-" ;
339 $dbh->{reclen} = 5 ;
c6c92ad9 340 ok(64, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
36477c24 341 $h[0] = "abc" ;
342 $h[1] = "def" ;
343 $h[3] = "ghi" ;
344 untie @h ;
a6ed719b 345 my $x = docat($Dfile) ;
36477c24 346 unlink $Dfile;
6250ba0a 347 my $ok = ($x eq "abc--def-------ghi--") ;
348 bad_one() unless $ok ;
c6c92ad9 349 ok(65, $ok) ;
36477c24 350}
351
05475680 352{
353 # check that attempting to tie an associative array to a DB_RECNO will fail
354
355 my $filename = "xyz" ;
356 my %x ;
357 eval { tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $DB_RECNO ; } ;
c6c92ad9 358 ok(66, $@ =~ /^DB_File can only tie an array to a DB_RECNO database/) ;
05475680 359 unlink $filename ;
360}
361
a6ed719b 362{
363 # sub-class test
364
365 package Another ;
366
3245f058 367 use warnings ;
a6ed719b 368 use strict ;
369
370 open(FILE, ">SubDB.pm") or die "Cannot open SubDB.pm: $!\n" ;
371 print FILE <<'EOM' ;
372
373 package SubDB ;
374
3245f058 375 use warnings ;
a6ed719b 376 use strict ;
377 use vars qw( @ISA @EXPORT) ;
378
379 require Exporter ;
380 use DB_File;
381 @ISA=qw(DB_File);
382 @EXPORT = @DB_File::EXPORT ;
383
384 sub STORE {
385 my $self = shift ;
386 my $key = shift ;
387 my $value = shift ;
388 $self->SUPER::STORE($key, $value * 2) ;
389 }
390
391 sub FETCH {
392 my $self = shift ;
393 my $key = shift ;
394 $self->SUPER::FETCH($key) - 1 ;
395 }
396
397 sub put {
398 my $self = shift ;
399 my $key = shift ;
400 my $value = shift ;
401 $self->SUPER::put($key, $value * 3) ;
402 }
403
404 sub get {
405 my $self = shift ;
406 $self->SUPER::get($_[0], $_[1]) ;
407 $_[1] -= 2 ;
408 }
409
410 sub A_new_method
411 {
412 my $self = shift ;
413 my $key = shift ;
414 my $value = $self->FETCH($key) ;
415 return "[[$value]]" ;
416 }
417
418 1 ;
419EOM
420
421 close FILE ;
422
045291aa 423 BEGIN { push @INC, '.'; }
a6ed719b 424 eval 'use SubDB ; ';
c6c92ad9 425 main::ok(67, $@ eq "") ;
a6ed719b 426 my @h ;
427 my $X ;
428 eval '
429 $X = tie(@h, "SubDB","recno.tmp", O_RDWR|O_CREAT, 0640, $DB_RECNO );
430 ' ;
431
c6c92ad9 432 main::ok(68, $@ eq "") ;
a6ed719b 433
434 my $ret = eval '$h[3] = 3 ; return $h[3] ' ;
c6c92ad9 435 main::ok(69, $@ eq "") ;
436 main::ok(70, $ret == 5) ;
a6ed719b 437
438 my $value = 0;
439 $ret = eval '$X->put(1, 4) ; $X->get(1, $value) ; return $value' ;
c6c92ad9 440 main::ok(71, $@ eq "") ;
441 main::ok(72, $ret == 10) ;
a6ed719b 442
443 $ret = eval ' R_NEXT eq main::R_NEXT ' ;
c6c92ad9 444 main::ok(73, $@ eq "" ) ;
445 main::ok(74, $ret == 1) ;
a6ed719b 446
447 $ret = eval '$X->A_new_method(1) ' ;
c6c92ad9 448 main::ok(75, $@ eq "") ;
449 main::ok(76, $ret eq "[[11]]") ;
a6ed719b 450
fac76ed7 451 undef $X;
452 untie(@h);
a6ed719b 453 unlink "SubDB.pm", "recno.tmp" ;
454
455}
456
045291aa 457{
458
459 # test $#
460 my $self ;
461 unlink $Dfile;
c6c92ad9 462 ok(77, $self = tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) ;
045291aa 463 $h[0] = "abc" ;
464 $h[1] = "def" ;
465 $h[2] = "ghi" ;
466 $h[3] = "jkl" ;
c6c92ad9 467 ok(78, $FA ? $#h == 3 : $self->length() == 4) ;
045291aa 468 undef $self ;
469 untie @h ;
470 my $x = docat($Dfile) ;
c6c92ad9 471 ok(79, $x eq "abc\ndef\nghi\njkl\n") ;
045291aa 472
473 # $# sets array to same length
c6c92ad9 474 ok(80, $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ) ;
045291aa 475 if ($FA)
476 { $#h = 3 }
477 else
478 { $self->STORESIZE(4) }
c6c92ad9 479 ok(81, $FA ? $#h == 3 : $self->length() == 4) ;
045291aa 480 undef $self ;
481 untie @h ;
482 $x = docat($Dfile) ;
c6c92ad9 483 ok(82, $x eq "abc\ndef\nghi\njkl\n") ;
045291aa 484
485 # $# sets array to bigger
c6c92ad9 486 ok(83, $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ) ;
045291aa 487 if ($FA)
488 { $#h = 6 }
489 else
490 { $self->STORESIZE(7) }
c6c92ad9 491 ok(84, $FA ? $#h == 6 : $self->length() == 7) ;
045291aa 492 undef $self ;
493 untie @h ;
494 $x = docat($Dfile) ;
c6c92ad9 495 ok(85, $x eq "abc\ndef\nghi\njkl\n\n\n\n") ;
045291aa 496
497 # $# sets array smaller
c6c92ad9 498 ok(86, $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ) ;
045291aa 499 if ($FA)
500 { $#h = 2 }
501 else
502 { $self->STORESIZE(3) }
c6c92ad9 503 ok(87, $FA ? $#h == 2 : $self->length() == 3) ;
045291aa 504 undef $self ;
505 untie @h ;
506 $x = docat($Dfile) ;
c6c92ad9 507 ok(88, $x eq "abc\ndef\nghi\n") ;
045291aa 508
509 unlink $Dfile;
510
511
512}
513
9fe6733a 514{
515 # DBM Filter tests
3245f058 516 use warnings ;
9fe6733a 517 use strict ;
518 my (@h, $db) ;
519 my ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
520 unlink $Dfile;
521
522 sub checkOutput
523 {
524 my($fk, $sk, $fv, $sv) = @_ ;
525 return
526 $fetch_key eq $fk && $store_key eq $sk &&
527 $fetch_value eq $fv && $store_value eq $sv &&
528 $_ eq 'original' ;
529 }
530
c6c92ad9 531 ok(89, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
9fe6733a 532
533 $db->filter_fetch_key (sub { $fetch_key = $_ }) ;
534 $db->filter_store_key (sub { $store_key = $_ }) ;
535 $db->filter_fetch_value (sub { $fetch_value = $_}) ;
536 $db->filter_store_value (sub { $store_value = $_ }) ;
537
538 $_ = "original" ;
539
540 $h[0] = "joe" ;
541 # fk sk fv sv
c6c92ad9 542 ok(90, checkOutput( "", 0, "", "joe")) ;
9fe6733a 543
544 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 545 ok(91, $h[0] eq "joe");
9fe6733a 546 # fk sk fv sv
c6c92ad9 547 ok(92, checkOutput( "", 0, "joe", "")) ;
9fe6733a 548
549 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 550 ok(93, $db->FIRSTKEY() == 0) ;
9fe6733a 551 # fk sk fv sv
c6c92ad9 552 ok(94, checkOutput( 0, "", "", "")) ;
9fe6733a 553
554 # replace the filters, but remember the previous set
555 my ($old_fk) = $db->filter_fetch_key
556 (sub { ++ $_ ; $fetch_key = $_ }) ;
557 my ($old_sk) = $db->filter_store_key
558 (sub { $_ *= 2 ; $store_key = $_ }) ;
559 my ($old_fv) = $db->filter_fetch_value
560 (sub { $_ = "[$_]"; $fetch_value = $_ }) ;
561 my ($old_sv) = $db->filter_store_value
562 (sub { s/o/x/g; $store_value = $_ }) ;
563
564 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
565 $h[1] = "Joe" ;
566 # fk sk fv sv
c6c92ad9 567 ok(95, checkOutput( "", 2, "", "Jxe")) ;
9fe6733a 568
569 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 570 ok(96, $h[1] eq "[Jxe]");
9fe6733a 571 # fk sk fv sv
c6c92ad9 572 ok(97, checkOutput( "", 2, "[Jxe]", "")) ;
9fe6733a 573
574 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 575 ok(98, $db->FIRSTKEY() == 1) ;
9fe6733a 576 # fk sk fv sv
c6c92ad9 577 ok(99, checkOutput( 1, "", "", "")) ;
9fe6733a 578
579 # put the original filters back
580 $db->filter_fetch_key ($old_fk);
581 $db->filter_store_key ($old_sk);
582 $db->filter_fetch_value ($old_fv);
583 $db->filter_store_value ($old_sv);
584
585 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
586 $h[0] = "joe" ;
c6c92ad9 587 ok(100, checkOutput( "", 0, "", "joe")) ;
9fe6733a 588
589 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 590 ok(101, $h[0] eq "joe");
591 ok(102, checkOutput( "", 0, "joe", "")) ;
9fe6733a 592
593 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 594 ok(103, $db->FIRSTKEY() == 0) ;
595 ok(104, checkOutput( 0, "", "", "")) ;
9fe6733a 596
597 # delete the filters
598 $db->filter_fetch_key (undef);
599 $db->filter_store_key (undef);
600 $db->filter_fetch_value (undef);
601 $db->filter_store_value (undef);
602
603 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
604 $h[0] = "joe" ;
c6c92ad9 605 ok(105, checkOutput( "", "", "", "")) ;
9fe6733a 606
607 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 608 ok(106, $h[0] eq "joe");
609 ok(107, checkOutput( "", "", "", "")) ;
9fe6733a 610
611 ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
c6c92ad9 612 ok(108, $db->FIRSTKEY() == 0) ;
613 ok(109, checkOutput( "", "", "", "")) ;
9fe6733a 614
615 undef $db ;
616 untie @h;
617 unlink $Dfile;
618}
619
620{
621 # DBM Filter with a closure
622
3245f058 623 use warnings ;
9fe6733a 624 use strict ;
625 my (@h, $db) ;
626
627 unlink $Dfile;
c6c92ad9 628 ok(110, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
9fe6733a 629
630 my %result = () ;
631
632 sub Closure
633 {
634 my ($name) = @_ ;
635 my $count = 0 ;
636 my @kept = () ;
637
638 return sub { ++$count ;
639 push @kept, $_ ;
640 $result{$name} = "$name - $count: [@kept]" ;
641 }
642 }
643
644 $db->filter_store_key(Closure("store key")) ;
645 $db->filter_store_value(Closure("store value")) ;
646 $db->filter_fetch_key(Closure("fetch key")) ;
647 $db->filter_fetch_value(Closure("fetch value")) ;
648
649 $_ = "original" ;
650
651 $h[0] = "joe" ;
c6c92ad9 652 ok(111, $result{"store key"} eq "store key - 1: [0]");
653 ok(112, $result{"store value"} eq "store value - 1: [joe]");
654 ok(113, ! defined $result{"fetch key"} );
655 ok(114, ! defined $result{"fetch value"} );
656 ok(115, $_ eq "original") ;
657
658 ok(116, $db->FIRSTKEY() == 0 ) ;
659 ok(117, $result{"store key"} eq "store key - 1: [0]");
660 ok(118, $result{"store value"} eq "store value - 1: [joe]");
661 ok(119, $result{"fetch key"} eq "fetch key - 1: [0]");
662 ok(120, ! defined $result{"fetch value"} );
663 ok(121, $_ eq "original") ;
9fe6733a 664
665 $h[7] = "john" ;
c6c92ad9 666 ok(122, $result{"store key"} eq "store key - 2: [0 7]");
667 ok(123, $result{"store value"} eq "store value - 2: [joe john]");
668 ok(124, $result{"fetch key"} eq "fetch key - 1: [0]");
669 ok(125, ! defined $result{"fetch value"} );
670 ok(126, $_ eq "original") ;
671
672 ok(127, $h[0] eq "joe");
673 ok(128, $result{"store key"} eq "store key - 3: [0 7 0]");
674 ok(129, $result{"store value"} eq "store value - 2: [joe john]");
675 ok(130, $result{"fetch key"} eq "fetch key - 1: [0]");
676 ok(131, $result{"fetch value"} eq "fetch value - 1: [joe]");
677 ok(132, $_ eq "original") ;
9fe6733a 678
679 undef $db ;
680 untie @h;
681 unlink $Dfile;
682}
683
684{
685 # DBM Filter recursion detection
3245f058 686 use warnings ;
9fe6733a 687 use strict ;
688 my (@h, $db) ;
689 unlink $Dfile;
690
c6c92ad9 691 ok(133, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
9fe6733a 692
693 $db->filter_store_key (sub { $_ = $h[0] }) ;
694
695 eval '$h[1] = 1234' ;
c6c92ad9 696 ok(134, $@ =~ /^recursion detected in filter_store_key at/ );
9fe6733a 697
698 undef $db ;
699 untie @h;
700 unlink $Dfile;
701}
702
9b761c68 703
704{
705 # Examples from the POD
706
707 my $file = "xyzt" ;
708 {
709 my $redirect = new Redirect $file ;
710
3245f058 711 use warnings FATAL => qw(all);
9b761c68 712 use strict ;
713 use DB_File ;
714
715 my $filename = "text" ;
716 unlink $filename ;
717
718 my @h ;
719 my $x = tie @h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_RECNO
720 or die "Cannot open file 'text': $!\n" ;
721
722 # Add a few key/value pairs to the file
723 $h[0] = "orange" ;
724 $h[1] = "blue" ;
725 $h[2] = "yellow" ;
726
727 $FA ? push @h, "green", "black"
728 : $x->push("green", "black") ;
729
730 my $elements = $FA ? scalar @h : $x->length ;
731 print "The array contains $elements entries\n" ;
732
733 my $last = $FA ? pop @h : $x->pop ;
734 print "popped $last\n" ;
735
736 $FA ? unshift @h, "white"
737 : $x->unshift("white") ;
738 my $first = $FA ? shift @h : $x->shift ;
739 print "shifted $first\n" ;
740
741 # Check for existence of a key
742 print "Element 1 Exists with value $h[1]\n" if $h[1] ;
743
744 # use a negative index
745 print "The last element is $h[-1]\n" ;
746 print "The 2nd last element is $h[-2]\n" ;
747
748 undef $x ;
749 untie @h ;
750
751 unlink $filename ;
752 }
753
c6c92ad9 754 ok(135, docat_del($file) eq <<'EOM') ;
9b761c68 755The array contains 5 entries
756popped black
757shifted white
758Element 1 Exists with value blue
759The last element is green
760The 2nd last element is yellow
761EOM
762
763 my $save_output = "xyzt" ;
764 {
765 my $redirect = new Redirect $save_output ;
766
3245f058 767 use warnings FATAL => qw(all);
9b761c68 768 use strict ;
769 use vars qw(@h $H $file $i) ;
770 use DB_File ;
771 use Fcntl ;
772
773 $file = "text" ;
774
775 unlink $file ;
776
777 $H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0640, $DB_RECNO
778 or die "Cannot open file $file: $!\n" ;
779
780 # first create a text file to play with
781 $h[0] = "zero" ;
782 $h[1] = "one" ;
783 $h[2] = "two" ;
784 $h[3] = "three" ;
785 $h[4] = "four" ;
786
787
788 # Print the records in order.
789 #
790 # The length method is needed here because evaluating a tied
791 # array in a scalar context does not return the number of
792 # elements in the array.
793
794 print "\nORIGINAL\n" ;
795 foreach $i (0 .. $H->length - 1) {
796 print "$i: $h[$i]\n" ;
797 }
798
799 # use the push & pop methods
800 $a = $H->pop ;
801 $H->push("last") ;
802 print "\nThe last record was [$a]\n" ;
803
804 # and the shift & unshift methods
805 $a = $H->shift ;
806 $H->unshift("first") ;
807 print "The first record was [$a]\n" ;
808
809 # Use the API to add a new record after record 2.
810 $i = 2 ;
811 $H->put($i, "Newbie", R_IAFTER) ;
812
813 # and a new record before record 1.
814 $i = 1 ;
815 $H->put($i, "New One", R_IBEFORE) ;
816
817 # delete record 3
818 $H->del(3) ;
819
820 # now print the records in reverse order
821 print "\nREVERSE\n" ;
822 for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
823 { print "$i: $h[$i]\n" }
824
825 # same again, but use the API functions instead
826 print "\nREVERSE again\n" ;
827 my ($s, $k, $v) = (0, 0, 0) ;
828 for ($s = $H->seq($k, $v, R_LAST) ;
829 $s == 0 ;
830 $s = $H->seq($k, $v, R_PREV))
831 { print "$k: $v\n" }
832
833 undef $H ;
834 untie @h ;
835
836 unlink $file ;
837 }
838
c6c92ad9 839 ok(136, docat_del($save_output) eq <<'EOM') ;
9b761c68 840
841ORIGINAL
8420: zero
8431: one
8442: two
8453: three
8464: four
847
848The last record was [four]
849The first record was [zero]
850
851REVERSE
8525: last
8534: three
8543: Newbie
8552: one
8561: New One
8570: first
858
859REVERSE again
8605: last
8614: three
8623: Newbie
8632: one
8641: New One
8650: first
866EOM
867
868}
869
cbc5248d 870{
871 # Bug ID 20001013.009
872 #
873 # test that $hash{KEY} = undef doesn't produce the warning
874 # Use of uninitialized value in null operation
875 use warnings ;
876 use strict ;
877 use DB_File ;
878
879 unlink $Dfile;
880 my @h ;
881 my $a = "";
882 local $SIG{__WARN__} = sub {$a = $_[0]} ;
883
3245f058 884 tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_RECNO
cbc5248d 885 or die "Can't open file: $!\n" ;
886 $h[0] = undef;
c6c92ad9 887 ok(137, $a eq "") ;
3245f058 888 untie @h ;
889 unlink $Dfile;
890}
891
892{
893 # test that %hash = () doesn't produce the warning
894 # Argument "" isn't numeric in entersub
895 use warnings ;
896 use strict ;
897 use DB_File ;
898 my $a = "";
899 local $SIG{__WARN__} = sub {$a = $_[0]} ;
900
901 unlink $Dfile;
902 my @h ;
903
904 tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_RECNO
905 or die "Can't open file: $!\n" ;
906 @h = (); ;
c6c92ad9 907 ok(138, $a eq "") ;
3245f058 908 untie @h ;
cbc5248d 909 unlink $Dfile;
910}
911
c6c92ad9 912# Only test splice if this is a newish version of Perl
913exit unless $FA ;
914
915# Test SPLICE
916#
917# These are a few regression tests: bundles of five arguments to pass
918# to test_splice(). The first four arguments correspond to those
919# given to splice(), and the last says which context to call it in
920# (scalar, list or void).
921#
922# The expected result is not needed because we get that by running
923# Perl's built-in splice().
924#
925my @tests = ([ [ 'falsely', 'dinosaur', 'remedy', 'commotion',
926 'rarely', 'paleness' ],
927 -4, -2,
928 [ 'redoubled', 'Taylorize', 'Zoe', 'halogen' ],
929 'void' ],
930
931 [ [ 'a' ], -2, 1, [ 'B' ], 'void' ],
932
933 [ [ 'Hartley', 'Islandia', 'assents', 'wishful' ],
934 0, -4,
935 [ 'maids' ],
936 'void' ],
937
938 [ [ 'visibility', 'pocketful', 'rectangles' ],
939 -10, 0,
940 [ 'garbages' ],
941 'void' ],
942
943 [ [ 'sleeplessly' ],
944 8, -4,
945 [ 'Margery', 'clearing', 'repercussion', 'clubs',
946 'arise' ],
947 'void' ],
948
949 [ [ 'chastises', 'recalculates' ],
950 0, 0,
951 [ 'momentariness', 'mediates', 'accents', 'toils',
952 'regaled' ],
953 'void' ],
954
955 [ [ 'b', '' ],
956 9, 8,
957 [ 'otrb', 'stje', 'ixrpw', 'vxfx', 'lhhf' ],
958 'scalar' ],
959
960 [ [ 'b', '' ],
961 undef, undef,
962 [ 'otrb', 'stje', 'ixrpw', 'vxfx', 'lhhf' ],
963 'scalar' ],
964
965 [ [ 'riheb' ], -8, undef, [], 'void' ],
966
967 [ [ 'uft', 'qnxs', '' ],
968 6, -2,
969 [ 'znp', 'mhnkh', 'bn' ],
970 'void' ],
971 );
972
973my $testnum = 139;
974my $failed = 0;
975require POSIX; my $tmp = POSIX::tmpnam();
976foreach my $test (@tests) {
977 my $err = test_splice(@$test);
978 if (defined $err) {
979 require Data::Dumper;
980 print STDERR "failed: ", Data::Dumper::Dumper($test);
981 print STDERR "error: $err\n";
982 $failed = 1;
983 ok($testnum++, 0);
984 }
985 else { ok($testnum++, 1) }
986}
987
988if ($failed) {
989 # Not worth running the random ones
990 print STDERR 'skipping ', $testnum++, "\n";
991}
992else {
993 # A thousand randomly-generated tests
994 $failed = 0;
995 srand(0);
996 foreach (0 .. 1000 - 1) {
997 my $test = rand_test();
998 my $err = test_splice(@$test);
999 if (defined $err) {
1000 require Data::Dumper;
1001 print STDERR "failed: ", Data::Dumper::Dumper($test);
1002 print STDERR "error: $err\n";
1003 $failed = 1;
1004 print STDERR "skipping any remaining random tests\n";
1005 last;
1006 }
1007 }
1008
1009 ok($testnum++, not $failed);
1010}
1011
1012die if $testnum != $total_tests + 1;
1013
a0d0e21e 1014exit ;
c6c92ad9 1015
1016# Subroutines for SPLICE testing
1017
1018# test_splice()
1019#
1020# Test the new splice() against Perl's built-in one. The first four
1021# parameters are those passed to splice(), except that the lists must
1022# be (explicitly) passed by reference, and are not actually modified.
1023# (It's just a test!) The last argument specifies the context in
1024# which to call the functions: 'list', 'scalar', or 'void'.
1025#
1026# Returns:
1027# undef, if the two splices give the same results for the given
1028# arguments and context;
1029#
1030# an error message showing the difference, otherwise.
1031#
1032# Reads global variable $tmp.
1033#
1034sub test_splice {
1035 die 'usage: test_splice(array, offset, length, list, context)' if @_ != 5;
1036 my ($array, $offset, $length, $list, $context) = @_;
1037 my @array = @$array;
1038 my @list = @$list;
1039
1040 open(TEXT, ">$tmp") or die "cannot write to $tmp: $!";
1041 foreach (@array) { print TEXT "$_\n" }
1042 close TEXT or die "cannot close $tmp: $!";
1043
1044 my @h;
1045 my $H = tie @h, 'DB_File', $tmp, O_RDWR, 0644, $DB_RECNO
1046 or die "cannot open $tmp: $!";
1047
1048 return "basic DB_File sanity check failed"
1049 if list_diff(\@array, \@h);
1050
1051 # Output from splice():
1052 # Returned value (munged a bit), error msg, warnings
1053 #
1054 my ($s_r, $s_error, @s_warnings);
1055
1056 my $gather_warning = sub { push @s_warnings, $_[0] };
1057 if ($context eq 'list') {
1058 my @r;
1059 eval {
1060 local $SIG{__WARN__} = $gather_warning;
1061 @r = splice @array, $offset, $length, @list;
1062 };
1063 $s_error = $@;
1064 $s_r = \@r;
1065 }
1066 elsif ($context eq 'scalar') {
1067 my $r;
1068 eval {
1069 local $SIG{__WARN__} = $gather_warning;
1070 $r = splice @array, $offset, $length, @list;
1071 };
1072 $s_error = $@;
1073 $s_r = [ $r ];
1074 }
1075 elsif ($context eq 'void') {
1076 eval {
1077 local $SIG{__WARN__} = $gather_warning;
1078 splice @array, $offset, $length, @list;
1079 };
1080 $s_error = $@;
1081 $s_r = [];
1082 }
1083 else {
1084 die "bad context $context";
1085 }
1086
1087 foreach ($s_error, @s_warnings) {
1088 chomp;
1089 s/ at \S+ line \d+\.$//;
1090 }
1091
1092 # Now do the same for DB_File's version of splice
1093 my ($ms_r, $ms_error, @ms_warnings);
1094 $gather_warning = sub { push @ms_warnings, $_[0] };
1095 if ($context eq 'list') {
1096 my @r;
1097 eval {
1098 local $SIG{__WARN__} = $gather_warning;
1099 @r = splice @h, $offset, $length, @list;
1100 };
1101 $ms_error = $@;
1102 $ms_r = \@r;
1103 }
1104 elsif ($context eq 'scalar') {
1105 my $r;
1106 eval {
1107 local $SIG{__WARN__} = $gather_warning;
1108 $r = splice @h, $offset, $length, @list;
1109 };
1110 $ms_error = $@;
1111 $ms_r = [ $r ];
1112 }
1113 elsif ($context eq 'void') {
1114 eval {
1115 local $SIG{__WARN__} = $gather_warning;
1116 splice @h, $offset, $length, @list;
1117 };
1118 $ms_error = $@;
1119 $ms_r = [];
1120 }
1121 else {
1122 die "bad context $context";
1123 }
1124
1125 foreach ($ms_error, @ms_warnings) {
1126 chomp;
1127 s/ at \S+ line \d+\.?$//;
1128 }
1129
1130 return "different errors: '$s_error' vs '$ms_error'"
1131 if $s_error ne $ms_error;
1132 return('different return values: ' . Dumper($s_r) . ' vs ' . Dumper($ms_r))
1133 if list_diff($s_r, $ms_r);
1134 return('different changed list: ' . Dumper(\@array) . ' vs ' . Dumper(\@h))
1135 if list_diff(\@array, \@h);
1136
1137 if ((scalar @s_warnings) != (scalar @ms_warnings)) {
1138 return 'different number of warnings';
1139 }
1140
1141 while (@s_warnings) {
1142 my $sw = shift @s_warnings;
1143 my $msw = shift @ms_warnings;
1144
1145 if (defined $sw and defined $msw) {
1146 $msw =~ s/ \(.+\)$//;
1147 $msw =~ s/ in splice$// if $] < 5.006;
1148 if ($sw ne $msw) {
1149 return "different warning: '$sw' vs '$msw'";
1150 }
1151 }
1152 elsif (not defined $sw and not defined $msw) {
1153 # Okay.
1154 }
1155 else {
1156 return "one warning defined, another undef";
1157 }
1158 }
1159
1160 undef $H;
1161 untie @h;
1162
1163 open(TEXT, $tmp) or die "cannot open $tmp: $!";
1164 @h = <TEXT>; chomp @h;
1165 close TEXT or die "cannot close $tmp: $!";
1166 return('list is different when re-read from disk: '
1167 . Dumper(\@array) . ' vs ' . Dumper(\@h))
1168 if list_diff(\@array, \@h);
1169
1170 return undef; # success
1171}
1172
1173
1174# list_diff()
1175#
1176# Do two lists differ?
1177#
1178# Parameters:
1179# reference to first list
1180# reference to second list
1181#
1182# Returns true iff they differ. Only works for lists of (string or
1183# undef).
1184#
1185# Surely there is a better way to do this?
1186#
1187sub list_diff {
1188 die 'usage: list_diff(ref to first list, ref to second list)'
1189 if @_ != 2;
1190 my ($a, $b) = @_;
1191 my @a = @$a; my @b = @$b;
1192 return 1 if (scalar @a) != (scalar @b);
1193 for (my $i = 0; $i < @a; $i++) {
1194 my ($ae, $be) = ($a[$i], $b[$i]);
1195 if (defined $ae and defined $be) {
1196 return 1 if $ae ne $be;
1197 }
1198 elsif (not defined $ae and not defined $be) {
1199 # Two undefined values are 'equal'
1200 }
1201 else {
1202 return 1;
1203 }
1204 }
1205 return 0;
1206}
1207
1208
1209# rand_test()
1210#
1211# Think up a random ARRAY, OFFSET, LENGTH, LIST, and context.
1212# ARRAY or LIST might be empty, and OFFSET or LENGTH might be
1213# undefined. Return a 'test' - a listref of these five things.
1214#
1215sub rand_test {
1216 die 'usage: rand_test()' if @_;
1217 my @contexts = qw<list scalar void>;
1218 my $context = $contexts[int(rand @contexts)];
1219 return [ rand_list(),
1220 (rand() < 0.5) ? (int(rand(20)) - 10) : undef,
1221 (rand() < 0.5) ? (int(rand(20)) - 10) : undef,
1222 rand_list(),
1223 $context ];
1224}
1225
1226
1227sub rand_list {
1228 die 'usage: rand_list()' if @_;
1229 my @r;
1230
1231 while (rand() > 0.1 * (scalar @r + 1)) {
1232 push @r, rand_word();
1233 }
1234 return \@r;
1235}
1236
1237
1238sub rand_word {
1239 die 'usage: rand_word()' if @_;
1240 my $r = '';
1241 my @chars = qw<a b c d e f g h i j k l m n o p q r s t u v w x y z>;
1242 while (rand() > 0.1 * (length($r) + 1)) {
1243 $r .= $chars[int(rand(scalar @chars))];
1244 }
1245 return $r;
1246}