xref: /openbsd-src/gnu/usr.bin/perl/cpan/DB_File/t/db-recno.t (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1#!./perl -w
2
3use strict;
4use Config;
5
6BEGIN {
7    if(-d "lib" && -f "TEST") {
8        if ($Config{'extensions'} !~ /\bDB_File\b/ ) {
9            print "1..0 # Skip: DB_File was not built\n";
10            exit 0;
11        }
12    }
13}
14
15use DB_File;
16use Fcntl;
17our ($dbh, $Dfile, $bad_ones, $FA);
18
19# full tied array support started in Perl 5.004_57
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
31
32sub ok
33{
34    my $no = shift ;
35    my $result = shift ;
36
37    print "not " unless $result ;
38    print "ok $no\n" ;
39
40    return $result ;
41}
42
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    normalise($result) ;
73    return $result;
74}
75
76sub docat_del
77{
78    my $file = shift;
79    my $result = docat($file);
80    unlink $file ;
81    return $result;
82}
83
84sub safeUntie
85{
86    my $hashref = shift ;
87    my $no_inner = 1;
88    local $SIG{__WARN__} = sub {-- $no_inner } ;
89    untie @$hashref;
90    return $no_inner;
91}
92
93sub bad_one
94{
95    unless ($bad_ones++) {
96	print STDERR <<EOM ;
97#
98# Some older versions of Berkeley DB version 1 will fail db-recno
99# tests 61, 63, 64 and 65.
100EOM
101        if ($^O eq 'darwin'
102	    && $Config{db_version_major} == 1
103	    && $Config{db_version_minor} == 0
104	    && $Config{db_version_patch} == 0) {
105	    print STDERR <<EOM ;
106#
107# For example Mac OS X 10.2 (or earlier) has such an old
108# version of Berkeley DB.
109EOM
110	}
111
112	print STDERR <<EOM ;
113#
114# You can safely ignore the errors if you're never going to use the
115# broken functionality (recno databases with a modified bval). 
116# Otherwise you'll have to upgrade your DB library.
117#
118# If you want to use Berkeley DB version 1, then 1.85 and 1.86 are the
119# last versions that were released. Berkeley DB version 2 is continually
120# being updated -- Check out http://www.sleepycat.com/ for more details.
121#
122EOM
123    }
124}
125
126sub normalise
127{
128    return unless $^O eq 'cygwin' ;
129    foreach (@_)
130      { s#\r\n#\n#g }
131}
132
133BEGIN
134{
135    {
136        local $SIG{__DIE__} ;
137        eval { require Data::Dumper ; import Data::Dumper } ;
138    }
139
140    if ($@) {
141        *Dumper = sub { my $a = shift; return "[ @{ $a } ]" } ;
142    }
143}
144
145my $splice_tests = 10 + 12 + 1; # ten regressions, plus the randoms
146my $total_tests = 181 ;
147$total_tests += $splice_tests if $FA ;
148print "1..$total_tests\n";
149
150$Dfile = "recno.tmp";
151unlink $Dfile ;
152
153umask(0);
154
155# Check the interface to RECNOINFO
156
157$dbh = new DB_File::RECNOINFO ;
158ok(1, ! defined $dbh->{bval}) ;
159ok(2, ! defined $dbh->{cachesize}) ;
160ok(3, ! defined $dbh->{psize}) ;
161ok(4, ! defined $dbh->{flags}) ;
162ok(5, ! defined $dbh->{lorder}) ;
163ok(6, ! defined $dbh->{reclen}) ;
164ok(7, ! defined $dbh->{bfname}) ;
165
166$dbh->{bval} = 3000 ;
167ok(8, $dbh->{bval} == 3000 );
168
169$dbh->{cachesize} = 9000 ;
170ok(9, $dbh->{cachesize} == 9000 );
171
172$dbh->{psize} = 400 ;
173ok(10, $dbh->{psize} == 400 );
174
175$dbh->{flags} = 65 ;
176ok(11, $dbh->{flags} == 65 );
177
178$dbh->{lorder} = 123 ;
179ok(12, $dbh->{lorder} == 123 );
180
181$dbh->{reclen} = 1234 ;
182ok(13, $dbh->{reclen} == 1234 );
183
184$dbh->{bfname} = 1234 ;
185ok(14, $dbh->{bfname} == 1234 );
186
187
188# Check that an invalid entry is caught both for store & fetch
189eval '$dbh->{fred} = 1234' ;
190ok(15, $@ =~ /^DB_File::RECNOINFO::STORE - Unknown element 'fred' at/ );
191eval 'my $q = $dbh->{fred}' ;
192ok(16, $@ =~ /^DB_File::RECNOINFO::FETCH - Unknown element 'fred' at/ );
193
194# Now check the interface to RECNOINFO
195
196my $X  ;
197my @h ;
198ok(17, $X = tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) ;
199
200my %noMode = map { $_, 1} qw( amigaos MSWin32 NetWare cygwin ) ;
201
202ok(18, ((stat($Dfile))[2] & 0777) == (($^O eq 'os2' || $^O eq 'MacOS') ? 0666 : 0640)
203	||  $noMode{$^O} );
204
205#my $l = @h ;
206my $l = $X->length ;
207ok(19, ($FA ? @h == 0 : !$l) );
208
209my @data = qw( a b c d ever f g h  i j k longername m n o p) ;
210
211$h[0] = shift @data ;
212ok(20, $h[0] eq 'a' );
213
214my $ i;
215foreach (@data)
216  { $h[++$i] = $_ }
217
218unshift (@data, 'a') ;
219
220ok(21, defined $h[1] );
221ok(22, ! defined $h[16] );
222ok(23, $FA ? @h == @data : $X->length == @data );
223
224
225# Overwrite an entry & check fetch it
226$h[3] = 'replaced' ;
227$data[3] = 'replaced' ;
228ok(24, $h[3] eq 'replaced' );
229
230#PUSH
231my @push_data = qw(added to the end) ;
232($FA ? push(@h, @push_data) : $X->push(@push_data)) ;
233push (@data, @push_data) ;
234ok(25, $h[++$i] eq 'added' );
235ok(26, $h[++$i] eq 'to' );
236ok(27, $h[++$i] eq 'the' );
237ok(28, $h[++$i] eq 'end' );
238
239# POP
240my $popped = pop (@data) ;
241my $value = ($FA ? pop @h : $X->pop) ;
242ok(29, $value eq $popped) ;
243
244# SHIFT
245$value = ($FA ? shift @h : $X->shift) ;
246my $shifted = shift @data ;
247ok(30, $value eq $shifted );
248
249# UNSHIFT
250
251# empty list
252($FA ? unshift @h,() : $X->unshift) ;
253ok(31, ($FA ? @h == @data : $X->length == @data ));
254
255my @new_data = qw(add this to the start of the array) ;
256$FA ? unshift (@h, @new_data) : $X->unshift (@new_data) ;
257unshift (@data, @new_data) ;
258ok(32, $FA ? @h == @data : $X->length == @data );
259ok(33, $h[0] eq "add") ;
260ok(34, $h[1] eq "this") ;
261ok(35, $h[2] eq "to") ;
262ok(36, $h[3] eq "the") ;
263ok(37, $h[4] eq "start") ;
264ok(38, $h[5] eq "of") ;
265ok(39, $h[6] eq "the") ;
266ok(40, $h[7] eq "array") ;
267ok(41, $h[8] eq $data[8]) ;
268
269# Brief test for SPLICE - more thorough 'soak test' is later.
270my @old;
271if ($FA) {
272    @old = splice(@h, 1, 2, qw(bananas just before));
273}
274else {
275    @old = $X->splice(1, 2, qw(bananas just before));
276}
277ok(42, $h[0] eq "add") ;
278ok(43, $h[1] eq "bananas") ;
279ok(44, $h[2] eq "just") ;
280ok(45, $h[3] eq "before") ;
281ok(46, $h[4] eq "the") ;
282ok(47, $h[5] eq "start") ;
283ok(48, $h[6] eq "of") ;
284ok(49, $h[7] eq "the") ;
285ok(50, $h[8] eq "array") ;
286ok(51, $h[9] eq $data[8]) ;
287$FA ? splice(@h, 1, 3, @old) : $X->splice(1, 3, @old);
288
289# Now both arrays should be identical
290
291my $ok = 1 ;
292my $j = 0 ;
293foreach (@data)
294{
295   $ok = 0, last if $_ ne $h[$j ++] ;
296}
297ok(52, $ok );
298
299# Neagtive subscripts
300
301# get the last element of the array
302ok(53, $h[-1] eq $data[-1] );
303ok(54, $h[-1] eq $h[ ($FA ? @h : $X->length) -1] );
304
305# get the first element using a negative subscript
306eval '$h[ - ( $FA ? @h : $X->length)] = "abcd"' ;
307ok(55, $@ eq "" );
308ok(56, $h[0] eq "abcd" );
309
310# now try to read before the start of the array
311eval '$h[ - (1 + ($FA ? @h : $X->length))] = 1234' ;
312ok(57, $@ =~ '^Modification of non-creatable array value attempted' );
313
314# IMPORTANT - $X must be undefined before the untie otherwise the
315#             underlying DB close routine will not get called.
316undef $X ;
317ok(58, safeUntie \@h);
318
319unlink $Dfile;
320
321
322{
323    # Check bval defaults to \n
324
325    my @h = () ;
326    my $dbh = new DB_File::RECNOINFO ;
327    ok(59, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
328    $h[0] = "abc" ;
329    $h[1] = "def" ;
330    $h[3] = "ghi" ;
331    ok(60, safeUntie \@h);
332    my $x = docat($Dfile) ;
333    unlink $Dfile;
334    ok(61, $x eq "abc\ndef\n\nghi\n") ;
335}
336
337{
338    # Change bval
339
340    my @h = () ;
341    my $dbh = new DB_File::RECNOINFO ;
342    $dbh->{bval} = "-" ;
343    ok(62, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
344    $h[0] = "abc" ;
345    $h[1] = "def" ;
346    $h[3] = "ghi" ;
347    ok(63, safeUntie \@h);
348    my $x = docat($Dfile) ;
349    unlink $Dfile;
350    my $ok = ($x eq "abc-def--ghi-") ;
351    bad_one() unless $ok ;
352    ok(64, $ok) ;
353}
354
355{
356    # Check R_FIXEDLEN with default bval (space)
357
358    my @h = () ;
359    my $dbh = new DB_File::RECNOINFO ;
360    $dbh->{flags} = R_FIXEDLEN ;
361    $dbh->{reclen} = 5 ;
362    ok(65, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
363    $h[0] = "abc" ;
364    $h[1] = "def" ;
365    $h[3] = "ghi" ;
366    ok(66, safeUntie \@h);
367    my $x = docat($Dfile) ;
368    unlink $Dfile;
369    my $ok = ($x eq "abc  def       ghi  ") ;
370    bad_one() unless $ok ;
371    ok(67, $ok) ;
372}
373
374{
375    # Check R_FIXEDLEN with user-defined bval
376
377    my @h = () ;
378    my $dbh = new DB_File::RECNOINFO ;
379    $dbh->{flags} = R_FIXEDLEN ;
380    $dbh->{bval} = "-" ;
381    $dbh->{reclen} = 5 ;
382    ok(68, tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $dbh ) ;
383    $h[0] = "abc" ;
384    $h[1] = "def" ;
385    $h[3] = "ghi" ;
386    ok(69, safeUntie \@h);
387    my $x = docat($Dfile) ;
388    unlink $Dfile;
389    my $ok = ($x eq "abc--def-------ghi--") ;
390    bad_one() unless $ok ;
391    ok(70, $ok) ;
392}
393
394{
395    # check that attempting to tie an associative array to a DB_RECNO will fail
396
397    my $filename = "xyz" ;
398    my %x ;
399    eval { tie %x, 'DB_File', $filename, O_RDWR|O_CREAT, 0640, $DB_RECNO ; } ;
400    ok(71, $@ =~ /^DB_File can only tie an array to a DB_RECNO database/) ;
401    unlink $filename ;
402}
403
404{
405   # sub-class test
406
407   package Another ;
408
409   use warnings ;
410   use strict ;
411
412   open(FILE, ">SubDB.pm") or die "Cannot open SubDB.pm: $!\n" ;
413   print FILE <<'EOM' ;
414
415   package SubDB ;
416
417   use warnings ;
418   use strict ;
419   our (@ISA, @EXPORT);
420
421   require Exporter ;
422   use DB_File;
423   @ISA=qw(DB_File);
424   @EXPORT = @DB_File::EXPORT ;
425
426   sub STORE {
427	my $self = shift ;
428        my $key = shift ;
429        my $value = shift ;
430        $self->SUPER::STORE($key, $value * 2) ;
431   }
432
433   sub FETCH {
434	my $self = shift ;
435        my $key = shift ;
436        $self->SUPER::FETCH($key) - 1 ;
437   }
438
439   sub put {
440	my $self = shift ;
441        my $key = shift ;
442        my $value = shift ;
443        $self->SUPER::put($key, $value * 3) ;
444   }
445
446   sub get {
447	my $self = shift ;
448        $self->SUPER::get($_[0], $_[1]) ;
449	$_[1] -= 2 ;
450   }
451
452   sub A_new_method
453   {
454	my $self = shift ;
455        my $key = shift ;
456        my $value = $self->FETCH($key) ;
457	return "[[$value]]" ;
458   }
459
460   1 ;
461EOM
462
463    close FILE  or die "Could not close: $!";
464
465    BEGIN { push @INC, '.'; }
466    eval 'use SubDB ; ';
467    main::ok(72, $@ eq "") ;
468    my @h ;
469    my $X ;
470    eval '
471	$X = tie(@h, "SubDB","recno.tmp", O_RDWR|O_CREAT, 0640, $DB_RECNO );
472	' ;
473    die "Could not tie: $!" unless $X;
474
475    main::ok(73, $@ eq "") ;
476
477    my $ret = eval '$h[3] = 3 ; return $h[3] ' ;
478    main::ok(74, $@ eq "") ;
479    main::ok(75, $ret == 5) ;
480
481    my $value = 0;
482    $ret = eval '$X->put(1, 4) ; $X->get(1, $value) ; return $value' ;
483    main::ok(76, $@ eq "") ;
484    main::ok(77, $ret == 10) ;
485
486    $ret = eval ' R_NEXT eq main::R_NEXT ' ;
487    main::ok(78, $@ eq "" ) ;
488    main::ok(79, $ret == 1) ;
489
490    $ret = eval '$X->A_new_method(1) ' ;
491    main::ok(80, $@ eq "") ;
492    main::ok(81, $ret eq "[[11]]") ;
493
494    undef $X;
495    main::ok(82, main::safeUntie \@h);
496    unlink "SubDB.pm", "recno.tmp" ;
497
498}
499
500{
501
502    # test $#
503    my $self ;
504    unlink $Dfile;
505    ok(83, $self = tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) ;
506    $h[0] = "abc" ;
507    $h[1] = "def" ;
508    $h[2] = "ghi" ;
509    $h[3] = "jkl" ;
510    ok(84, $FA ? $#h == 3 : $self->length() == 4) ;
511    undef $self ;
512    ok(85, safeUntie \@h);
513    my $x = docat($Dfile) ;
514    ok(86, $x eq "abc\ndef\nghi\njkl\n") ;
515
516    # $# sets array to same length
517    $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ;
518    ok(87, $self)
519        or warn "# $DB_File::Error\n";
520    if ($FA)
521      { $#h = 3 }
522    else
523      { $self->STORESIZE(4) }
524    ok(88, $FA ? $#h == 3 : $self->length() == 4) ;
525    undef $self ;
526    ok(89, safeUntie \@h);
527    $x = docat($Dfile) ;
528    ok(90, $x eq "abc\ndef\nghi\njkl\n") ;
529
530    # $# sets array to bigger
531    ok(91, $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ) ;
532    if ($FA)
533      { $#h = 6 }
534    else
535      { $self->STORESIZE(7) }
536    ok(92, $FA ? $#h == 6 : $self->length() == 7) ;
537    undef $self ;
538    ok(93, safeUntie \@h);
539    $x = docat($Dfile) ;
540    ok(94, $x eq "abc\ndef\nghi\njkl\n\n\n\n") ;
541
542    # $# sets array smaller
543    ok(95, $self = tie @h, 'DB_File', $Dfile, O_RDWR, 0640, $DB_RECNO ) ;
544    if ($FA)
545      { $#h = 2 }
546    else
547      { $self->STORESIZE(3) }
548    ok(96, $FA ? $#h == 2 : $self->length() == 3) ;
549    undef $self ;
550    ok(97, safeUntie \@h);
551    $x = docat($Dfile) ;
552    ok(98, $x eq "abc\ndef\nghi\n") ;
553
554    unlink $Dfile;
555
556
557}
558
559{
560   # DBM Filter tests
561   use warnings ;
562   use strict ;
563   my (@h, $db) ;
564   my ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
565   unlink $Dfile;
566
567   sub checkOutput
568   {
569       my($fk, $sk, $fv, $sv) = @_ ;
570
571       print "# Fetch Key   : expected '$fk' got '$fetch_key'\n"
572           if $fetch_key ne $fk ;
573       print "# Fetch Value : expected '$fv' got '$fetch_value'\n"
574           if $fetch_value ne $fv ;
575       print "# Store Key   : expected '$sk' got '$store_key'\n"
576           if $store_key ne $sk ;
577       print "# Store Value : expected '$sv' got '$store_value'\n"
578           if $store_value ne $sv ;
579       print "# \$_          : expected 'original' got '$_'\n"
580           if $_ ne 'original' ;
581
582       return
583           $fetch_key   eq $fk && $store_key   eq $sk &&
584	   $fetch_value eq $fv && $store_value eq $sv &&
585	   $_ eq 'original' ;
586   }
587
588   ok(99, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
589
590   $db->filter_fetch_key   (sub { $fetch_key = $_ }) ;
591   $db->filter_store_key   (sub { $store_key = $_ }) ;
592   $db->filter_fetch_value (sub { $fetch_value = $_}) ;
593   $db->filter_store_value (sub { $store_value = $_ }) ;
594
595   $_ = "original" ;
596
597   $h[0] = "joe" ;
598   #                   fk   sk     fv   sv
599   ok(100, checkOutput( "", 0, "", "joe")) ;
600
601   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
602   ok(101, $h[0] eq "joe");
603   #                   fk  sk  fv    sv
604   ok(102, checkOutput( "", 0, "joe", "")) ;
605
606   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
607   ok(103, $db->FIRSTKEY() == 0) ;
608   #                    fk     sk  fv  sv
609   ok(104, checkOutput( 0, "", "", "")) ;
610
611   # replace the filters, but remember the previous set
612   my ($old_fk) = $db->filter_fetch_key
613   			(sub { ++ $_ ; $fetch_key = $_ }) ;
614   my ($old_sk) = $db->filter_store_key
615   			(sub { $_ *= 2 ; $store_key = $_ }) ;
616   my ($old_fv) = $db->filter_fetch_value
617   			(sub { $_ = "[$_]"; $fetch_value = $_ }) ;
618   my ($old_sv) = $db->filter_store_value
619   			(sub { s/o/x/g; $store_value = $_ }) ;
620
621   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
622   $h[1] = "Joe" ;
623   #                   fk   sk     fv    sv
624   ok(105, checkOutput( "", 2, "", "Jxe")) ;
625
626   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
627   ok(106, $h[1] eq "[Jxe]");
628   #                   fk   sk     fv    sv
629   ok(107, checkOutput( "", 2, "[Jxe]", "")) ;
630
631   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
632   ok(108, $db->FIRSTKEY() == 1) ;
633   #                   fk   sk     fv    sv
634   ok(109, checkOutput( 1, "", "", "")) ;
635
636   # put the original filters back
637   $db->filter_fetch_key   ($old_fk);
638   $db->filter_store_key   ($old_sk);
639   $db->filter_fetch_value ($old_fv);
640   $db->filter_store_value ($old_sv);
641
642   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
643   $h[0] = "joe" ;
644   ok(110, checkOutput( "", 0, "", "joe")) ;
645
646   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
647   ok(111, $h[0] eq "joe");
648   ok(112, checkOutput( "", 0, "joe", "")) ;
649
650   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
651   ok(113, $db->FIRSTKEY() == 0) ;
652   ok(114, checkOutput( 0, "", "", "")) ;
653
654   # delete the filters
655   $db->filter_fetch_key   (undef);
656   $db->filter_store_key   (undef);
657   $db->filter_fetch_value (undef);
658   $db->filter_store_value (undef);
659
660   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
661   $h[0] = "joe" ;
662   ok(115, checkOutput( "", "", "", "")) ;
663
664   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
665   ok(116, $h[0] eq "joe");
666   ok(117, checkOutput( "", "", "", "")) ;
667
668   ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4 ;
669   ok(118, $db->FIRSTKEY() == 0) ;
670   ok(119, checkOutput( "", "", "", "")) ;
671
672   undef $db ;
673   ok(120, safeUntie \@h);
674   unlink $Dfile;
675}
676
677{
678    # DBM Filter with a closure
679
680    use warnings ;
681    use strict ;
682    my (@h, $db) ;
683
684    unlink $Dfile;
685    ok(121, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
686
687    my %result = () ;
688
689    sub Closure
690    {
691        my ($name) = @_ ;
692	my $count = 0 ;
693	my @kept = () ;
694
695	return sub { ++$count ;
696		     push @kept, $_ ;
697		     $result{$name} = "$name - $count: [@kept]" ;
698		   }
699    }
700
701    $db->filter_store_key(Closure("store key")) ;
702    $db->filter_store_value(Closure("store value")) ;
703    $db->filter_fetch_key(Closure("fetch key")) ;
704    $db->filter_fetch_value(Closure("fetch value")) ;
705
706    $_ = "original" ;
707
708    $h[0] = "joe" ;
709    ok(122, $result{"store key"} eq "store key - 1: [0]");
710    ok(123, $result{"store value"} eq "store value - 1: [joe]");
711    ok(124, ! defined $result{"fetch key"} );
712    ok(125, ! defined $result{"fetch value"} );
713    ok(126, $_ eq "original") ;
714
715    ok(127, $db->FIRSTKEY() == 0 ) ;
716    ok(128, $result{"store key"} eq "store key - 1: [0]");
717    ok(129, $result{"store value"} eq "store value - 1: [joe]");
718    ok(130, $result{"fetch key"} eq "fetch key - 1: [0]");
719    ok(131, ! defined $result{"fetch value"} );
720    ok(132, $_ eq "original") ;
721
722    $h[7]  = "john" ;
723    ok(133, $result{"store key"} eq "store key - 2: [0 7]");
724    ok(134, $result{"store value"} eq "store value - 2: [joe john]");
725    ok(135, $result{"fetch key"} eq "fetch key - 1: [0]");
726    ok(136, ! defined $result{"fetch value"} );
727    ok(137, $_ eq "original") ;
728
729    ok(138, $h[0] eq "joe");
730    ok(139, $result{"store key"} eq "store key - 3: [0 7 0]");
731    ok(140, $result{"store value"} eq "store value - 2: [joe john]");
732    ok(141, $result{"fetch key"} eq "fetch key - 1: [0]");
733    ok(142, $result{"fetch value"} eq "fetch value - 1: [joe]");
734    ok(143, $_ eq "original") ;
735
736    undef $db ;
737    ok(144, safeUntie \@h);
738    unlink $Dfile;
739}
740
741{
742   # DBM Filter recursion detection
743   use warnings ;
744   use strict ;
745   my (@h, $db) ;
746   unlink $Dfile;
747
748   ok(145, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
749
750   $db->filter_store_key (sub { $_ = $h[0] }) ;
751
752   eval '$h[1] = 1234' ;
753   ok(146, $@ =~ /^recursion detected in filter_store_key at/ );
754
755   undef $db ;
756   ok(147, safeUntie \@h);
757   unlink $Dfile;
758}
759
760
761{
762   # Examples from the POD
763
764  my $file = "xyzt" ;
765  {
766    my $redirect = new Redirect $file ;
767
768    use warnings FATAL => qw(all);
769    use strict ;
770    use DB_File ;
771
772    my $filename = "text" ;
773    unlink $filename ;
774
775    my @h ;
776    my $x = tie @h, "DB_File", $filename, O_RDWR|O_CREAT, 0640, $DB_RECNO
777        or die "Cannot open file 'text': $!\n" ;
778
779    # Add a few key/value pairs to the file
780    $h[0] = "orange" ;
781    $h[1] = "blue" ;
782    $h[2] = "yellow" ;
783
784    $FA ? push @h, "green", "black"
785        : $x->push("green", "black") ;
786
787    my $elements = $FA ? scalar @h : $x->length ;
788    print "The array contains $elements entries\n" ;
789
790    my $last = $FA ? pop @h : $x->pop ;
791    print "popped $last\n" ;
792
793    $FA ? unshift @h, "white"
794        : $x->unshift("white") ;
795    my $first = $FA ? shift @h : $x->shift ;
796    print "shifted $first\n" ;
797
798    # Check for existence of a key
799    print "Element 1 Exists with value $h[1]\n" if $h[1] ;
800
801    # use a negative index
802    print "The last element is $h[-1]\n" ;
803    print "The 2nd last element is $h[-2]\n" ;
804
805    undef $x ;
806    untie @h ;
807
808    unlink $filename ;
809  }
810
811  ok(148, docat_del($file) eq <<'EOM') ;
812The array contains 5 entries
813popped black
814shifted white
815Element 1 Exists with value blue
816The last element is green
817The 2nd last element is yellow
818EOM
819
820  my $save_output = "xyzt" ;
821  {
822    my $redirect = new Redirect $save_output ;
823
824    use warnings FATAL => qw(all);
825    use strict ;
826    our (@h, $H, $file, $i);
827    use DB_File ;
828    use Fcntl ;
829
830    $file = "text" ;
831
832    unlink $file ;
833
834    $H = tie @h, "DB_File", $file, O_RDWR|O_CREAT, 0640, $DB_RECNO
835        or die "Cannot open file $file: $!\n" ;
836
837    # first create a text file to play with
838    $h[0] = "zero" ;
839    $h[1] = "one" ;
840    $h[2] = "two" ;
841    $h[3] = "three" ;
842    $h[4] = "four" ;
843
844
845    # Print the records in order.
846    #
847    # The length method is needed here because evaluating a tied
848    # array in a scalar context does not return the number of
849    # elements in the array.
850
851    print "\nORIGINAL\n" ;
852    foreach $i (0 .. $H->length - 1) {
853        print "$i: $h[$i]\n" ;
854    }
855
856    # use the push & pop methods
857    $a = $H->pop ;
858    $H->push("last") ;
859    print "\nThe last record was [$a]\n" ;
860
861    # and the shift & unshift methods
862    $a = $H->shift ;
863    $H->unshift("first") ;
864    print "The first record was [$a]\n" ;
865
866    # Use the API to add a new record after record 2.
867    $i = 2 ;
868    $H->put($i, "Newbie", R_IAFTER) ;
869
870    # and a new record before record 1.
871    $i = 1 ;
872    $H->put($i, "New One", R_IBEFORE) ;
873
874    # delete record 3
875    $H->del(3) ;
876
877    # now print the records in reverse order
878    print "\nREVERSE\n" ;
879    for ($i = $H->length - 1 ; $i >= 0 ; -- $i)
880      { print "$i: $h[$i]\n" }
881
882    # same again, but use the API functions instead
883    print "\nREVERSE again\n" ;
884    my ($s, $k, $v)  = (0, 0, 0) ;
885    for ($s = $H->seq($k, $v, R_LAST) ;
886             $s == 0 ;
887             $s = $H->seq($k, $v, R_PREV))
888      { print "$k: $v\n" }
889
890    undef $H ;
891    untie @h ;
892
893    unlink $file ;
894  }
895
896  ok(149, docat_del($save_output) eq <<'EOM') ;
897
898ORIGINAL
8990: zero
9001: one
9012: two
9023: three
9034: four
904
905The last record was [four]
906The first record was [zero]
907
908REVERSE
9095: last
9104: three
9113: Newbie
9122: one
9131: New One
9140: first
915
916REVERSE again
9175: last
9184: three
9193: Newbie
9202: one
9211: New One
9220: first
923EOM
924
925}
926
927{
928    # Bug ID 20001013.009
929    #
930    # test that $hash{KEY} = undef doesn't produce the warning
931    #     Use of uninitialized value in null operation
932    use warnings ;
933    use strict ;
934    use DB_File ;
935
936    unlink $Dfile;
937    my @h ;
938    my $a = "";
939    local $SIG{__WARN__} = sub {$a = $_[0]} ;
940
941    tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_RECNO
942	or die "Can't open file: $!\n" ;
943    $h[0] = undef;
944    ok(150, $a eq "") ;
945    ok(151, safeUntie \@h);
946    unlink $Dfile;
947}
948
949{
950    # test that %hash = () doesn't produce the warning
951    #     Argument "" isn't numeric in entersub
952    use warnings ;
953    use strict ;
954    use DB_File ;
955    my $a = "";
956    local $SIG{__WARN__} = sub {$a = $_[0]} ;
957
958    unlink $Dfile;
959    my @h ;
960
961    tie @h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_RECNO
962	or die "Can't open file: $!\n" ;
963    @h = (); ;
964    ok(152, $a eq "") ;
965    ok(153, safeUntie \@h);
966    unlink $Dfile;
967}
968
969{
970   # Check that DBM Filter can cope with read-only $_
971
972   use warnings ;
973   use strict ;
974   my (@h, $db) ;
975   unlink $Dfile;
976
977   ok(154, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
978
979   $db->filter_fetch_key   (sub { }) ;
980   $db->filter_store_key   (sub { }) ;
981   $db->filter_fetch_value (sub { }) ;
982   $db->filter_store_value (sub { }) ;
983
984   $_ = "original" ;
985
986   $h[0] = "joe" ;
987   ok(155, $h[0] eq "joe");
988
989   eval { my @r= grep { $h[$_] } (1, 2, 3) };
990   ok (156, ! $@);
991
992
993   # delete the filters
994   $db->filter_fetch_key   (undef);
995   $db->filter_store_key   (undef);
996   $db->filter_fetch_value (undef);
997   $db->filter_store_value (undef);
998
999   $h[1] = "joe" ;
1000
1001   ok(157, $h[1] eq "joe");
1002
1003   eval { my @r= grep { $h[$_] } (1, 2, 3) };
1004   ok (158, ! $@);
1005
1006   undef $db ;
1007   untie @h;
1008   unlink $Dfile;
1009}
1010
1011{
1012   # Check low-level API works with filter
1013
1014   use warnings ;
1015   use strict ;
1016   my (@h, $db) ;
1017   my $Dfile = "xxy.db";
1018   unlink $Dfile;
1019
1020   ok(159, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO ) );
1021
1022
1023   $db->filter_fetch_key   (sub { ++ $_ } );
1024   $db->filter_store_key   (sub { -- $_ } );
1025   $db->filter_fetch_value (sub { $_ = unpack("i", $_) } );
1026   $db->filter_store_value (sub { $_ = pack("i", $_) } );
1027
1028   $_ = 'fred';
1029
1030   my $key = 22 ;
1031   my $value = 34 ;
1032
1033   $db->put($key, $value) ;
1034   ok 160, $key == 22;
1035   ok 161, $value == 34 ;
1036   ok 162, $_ eq 'fred';
1037   #print "k [$key][$value]\n" ;
1038
1039   my $val ;
1040   $db->get($key, $val) ;
1041   ok 163, $key == 22;
1042   ok 164, $val == 34 ;
1043   ok 165, $_ eq 'fred';
1044
1045   $key = 51 ;
1046   $value = 454;
1047   $h[$key] = $value ;
1048   ok 166, $key == 51;
1049   ok 167, $value == 454 ;
1050   ok 168, $_ eq 'fred';
1051
1052   undef $db ;
1053   untie @h;
1054   unlink $Dfile;
1055}
1056
1057
1058{
1059    # Regression Test for bug 30237
1060    # Check that substr can be used in the key to db_put
1061    # and that db_put does not trigger the warning
1062    #
1063    #     Use of uninitialized value in subroutine entry
1064
1065
1066    use warnings ;
1067    use strict ;
1068    my (@h, $db) ;
1069    my $status ;
1070    my $Dfile = "xxy.db";
1071    unlink $Dfile;
1072
1073    ok(169, $db = tie(@h, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0640, $DB_RECNO) );
1074
1075    my $warned = '';
1076    local $SIG{__WARN__} = sub {$warned = $_[0]} ;
1077
1078    # db-put with substr of key
1079    my %remember = () ;
1080    for my $ix ( 0 .. 2 )
1081    {
1082        my $key = $ix . "data" ;
1083        my $value = "value$ix" ;
1084        $remember{substr($key,0, 1)} = $value ;
1085        $db->put(substr($key,0, 1), $value) ;
1086    }
1087
1088    ok 170, $warned eq ''
1089      or print "# Caught warning [$warned]\n" ;
1090
1091    # db-put with substr of value
1092    $warned = '';
1093    for my $ix ( 3 .. 5 )
1094    {
1095        my $key = $ix . "data" ;
1096        my $value = "value$ix" ;
1097        $remember{$ix} = $value ;
1098        $db->put($ix, substr($value,0)) ;
1099    }
1100
1101    ok 171, $warned eq ''
1102      or print "# Caught warning [$warned]\n" ;
1103
1104    # via the tied array is not a problem, but check anyway
1105    # substr of key
1106    $warned = '';
1107    for my $ix ( 6 .. 8 )
1108    {
1109        my $key = $ix . "data" ;
1110        my $value = "value$ix" ;
1111        $remember{substr($key,0,1)} = $value ;
1112        $h[substr($key,0,1)] = $value ;
1113    }
1114
1115    ok 172, $warned eq ''
1116      or print "# Caught warning [$warned]\n" ;
1117
1118    # via the tied array is not a problem, but check anyway
1119    # substr of value
1120    $warned = '';
1121    for my $ix ( 9 .. 10 )
1122    {
1123        my $key = $ix . "data" ;
1124        my $value = "value$ix" ;
1125        $remember{$ix} = $value ;
1126        $h[$ix] = substr($value,0) ;
1127    }
1128
1129    ok 173, $warned eq ''
1130      or print "# Caught warning [$warned]\n" ;
1131
1132    my %bad = () ;
1133    my $key = '';
1134    for (my $status = $db->seq($key, $value, R_FIRST ) ;
1135         $status == 0 ;
1136         $status = $db->seq($key, $value, R_NEXT ) ) {
1137
1138        #print "# key [$key] value [$value]\n" ;
1139        if (defined $remember{$key} && defined $value &&
1140             $remember{$key} eq $value) {
1141            delete $remember{$key} ;
1142        }
1143        else {
1144            $bad{$key} = $value ;
1145        }
1146    }
1147
1148    ok 174, keys %bad == 0 ;
1149    ok 175, keys %remember == 0 ;
1150
1151    print "# missing -- $key $value\n" while ($key, $value) = each %remember;
1152    print "# bad     -- $key $value\n" while ($key, $value) = each %bad;
1153
1154    # Make sure this fix does not break code to handle an undef key
1155    my $value = 'fred';
1156    $warned = '';
1157    $status = $db->put(undef, $value) ;
1158    ok 176, $status == 0
1159      or print "# put failed - status $status\n";
1160    ok 177, $warned eq ''
1161      or print "# Caught warning [$warned]\n" ;
1162    $warned = '';
1163
1164    print "# db_ver $DB_File::db_ver\n";
1165    $value = '' ;
1166    $status = $db->get(undef, $value) ;
1167    ok 178, $status == 0
1168	or print "# get failed - status $status\n" ;
1169    ok(179, $db->get(undef, $value) == 0) or print "# get failed\n" ;
1170    ok 180, $value eq 'fred' or print "# got [$value]\n" ;
1171    ok 181, $warned eq ''
1172      or print "# Caught warning [$warned]\n" ;
1173    $warned = '';
1174
1175    undef $db ;
1176    untie @h;
1177    unlink $Dfile;
1178}
1179
1180# Only test splice if this is a newish version of Perl
1181exit unless $FA ;
1182
1183# Test SPLICE
1184
1185{
1186    # check that the splice warnings are under the same lexical control
1187    # as their non-tied counterparts.
1188
1189    use warnings;
1190    use strict;
1191
1192    my $a = '';
1193    my @a = (1);
1194    local $SIG{__WARN__} = sub {$a = $_[0]} ;
1195
1196    unlink $Dfile;
1197    my @tied ;
1198
1199    tie @tied, 'DB_File', $Dfile, O_RDWR|O_CREAT, 0664, $DB_RECNO
1200	or die "Can't open file: $!\n" ;
1201
1202    # uninitialized offset
1203    use warnings;
1204    my $offset ;
1205    $a = '';
1206    splice(@a, $offset);
1207    ok(182, $a =~ /^Use of uninitialized value /);
1208    $a = '';
1209    splice(@tied, $offset);
1210    ok(183, $a =~ /^Use of uninitialized value in splice/);
1211
1212    no warnings 'uninitialized';
1213    $a = '';
1214    splice(@a, $offset);
1215    ok(184, $a eq '');
1216    $a = '';
1217    splice(@tied, $offset);
1218    ok(185, $a eq '');
1219
1220    # uninitialized length
1221    use warnings;
1222    my $length ;
1223    $a = '';
1224    splice(@a, 0, $length);
1225    ok(186, $a =~ /^Use of uninitialized value /);
1226    $a = '';
1227    splice(@tied, 0, $length);
1228    ok(187, $a =~ /^Use of uninitialized value in splice/);
1229
1230    no warnings 'uninitialized';
1231    $a = '';
1232    splice(@a, 0, $length);
1233    ok(188, $a eq '');
1234    $a = '';
1235    splice(@tied, 0, $length);
1236    ok(189, $a eq '');
1237
1238    # offset past end of array
1239    use warnings;
1240    $a = '';
1241    splice(@a, 3);
1242    my $splice_end_array = ($a =~ /^splice\(\) offset past end of array/);
1243    $a = '';
1244    splice(@tied, 3);
1245    ok(190, !$splice_end_array || $a =~ /^splice\(\) offset past end of array/);
1246
1247    no warnings 'misc';
1248    $a = '';
1249    splice(@a, 3);
1250    ok(191, $a eq '');
1251    $a = '';
1252    splice(@tied, 3);
1253    ok(192, $a eq '');
1254
1255    ok(193, safeUntie \@tied);
1256    unlink $Dfile;
1257}
1258
1259#
1260# These are a few regression tests: bundles of five arguments to pass
1261# to test_splice().  The first four arguments correspond to those
1262# given to splice(), and the last says which context to call it in
1263# (scalar, list or void).
1264#
1265# The expected result is not needed because we get that by running
1266# Perl's built-in splice().
1267#
1268my @tests = ([ [ 'falsely', 'dinosaur', 'remedy', 'commotion',
1269		 'rarely', 'paleness' ],
1270	       -4, -2,
1271	       [ 'redoubled', 'Taylorize', 'Zoe', 'halogen' ],
1272	       'void' ],
1273
1274	     [ [ 'a' ], -2, 1, [ 'B' ], 'void' ],
1275
1276	     [ [ 'Hartley', 'Islandia', 'assents', 'wishful' ],
1277	       0, -4,
1278	       [ 'maids' ],
1279	       'void' ],
1280
1281	     [ [ 'visibility', 'pocketful', 'rectangles' ],
1282	       -10, 0,
1283	       [ 'garbages' ],
1284	       'void' ],
1285
1286	     [ [ 'sleeplessly' ],
1287	       8, -4,
1288	       [ 'Margery', 'clearing', 'repercussion', 'clubs',
1289		 'arise' ],
1290	       'void' ],
1291
1292	     [ [ 'chastises', 'recalculates' ],
1293	       0, 0,
1294	       [ 'momentariness', 'mediates', 'accents', 'toils',
1295		 'regaled' ],
1296	       'void' ],
1297
1298	     [ [ 'b', '' ],
1299	       9, 8,
1300	       [ 'otrb', 'stje', 'ixrpw', 'vxfx', 'lhhf' ],
1301	       'scalar' ],
1302
1303	     [ [ 'b', '' ],
1304	       undef, undef,
1305	       [ 'otrb', 'stje', 'ixrpw', 'vxfx', 'lhhf' ],
1306	       'scalar' ],
1307
1308	     [ [ 'riheb' ], -8, undef, [], 'void' ],
1309
1310	     [ [ 'uft', 'qnxs', '' ],
1311	       6, -2,
1312	       [ 'znp', 'mhnkh', 'bn' ],
1313	       'void' ],
1314	    );
1315
1316my $testnum = 194;
1317my $failed = 0;
1318my $tmp = "dbr$$";
1319foreach my $test (@tests) {
1320    my $err = test_splice(@$test);
1321    if (defined $err) {
1322	print STDERR "# failed: ", Dumper($test);
1323	print STDERR "# error: $err\n";
1324	$failed = 1;
1325	ok($testnum++, 0);
1326    }
1327    else { ok($testnum++, 1) }
1328}
1329
1330if ($failed) {
1331    # Not worth running the random ones
1332    print STDERR '# skipping ', $testnum++, "\n";
1333}
1334else {
1335    # A thousand randomly-generated tests
1336    $failed = 0;
1337    srand(0);
1338    foreach (0 .. 1000 - 1) {
1339	my $test = rand_test();
1340	my $err = test_splice(@$test);
1341	if (defined $err) {
1342	    print STDERR "# failed: ", Dumper($test);
1343	    print STDERR "# error: $err\n";
1344	    $failed = 1;
1345	    print STDERR "# skipping any remaining random tests\n";
1346	    last;
1347	}
1348    }
1349
1350    ok($testnum++, not $failed);
1351}
1352
1353die "testnum ($testnum) != total_tests ($total_tests) + 1"
1354    if $testnum != $total_tests + 1;
1355
1356exit ;
1357
1358# Subroutines for SPLICE testing
1359
1360# test_splice()
1361#
1362# Test the new splice() against Perl's built-in one.  The first four
1363# parameters are those passed to splice(), except that the lists must
1364# be (explicitly) passed by reference, and are not actually modified.
1365# (It's just a test!)  The last argument specifies the context in
1366# which to call the functions: 'list', 'scalar', or 'void'.
1367#
1368# Returns:
1369#   undef, if the two splices give the same results for the given
1370#     arguments and context;
1371#
1372#   an error message showing the difference, otherwise.
1373#
1374# Reads global variable $tmp.
1375#
1376sub test_splice {
1377    die 'usage: test_splice(array, offset, length, list, context)' if @_ != 5;
1378    my ($array, $offset, $length, $list, $context) = @_;
1379    my @array = @$array;
1380    my @list = @$list;
1381
1382    unlink $tmp;
1383
1384    my @h;
1385    my $H = tie @h, 'DB_File', $tmp, O_CREAT|O_RDWR, 0644, $DB_RECNO
1386      or die "cannot open $tmp: $!";
1387
1388    my $i = 0;
1389    foreach ( @array ) { $h[$i++] = $_ }
1390
1391    return "basic DB_File sanity check failed"
1392      if list_diff(\@array, \@h);
1393
1394    # Output from splice():
1395    # Returned value (munged a bit), error msg, warnings
1396    #
1397    my ($s_r, $s_error, @s_warnings);
1398
1399    my $gather_warning = sub { push @s_warnings, $_[0] };
1400    if ($context eq 'list') {
1401	my @r;
1402	eval {
1403	    local $SIG{__WARN__} = $gather_warning;
1404	    @r = splice @array, $offset, $length, @list;
1405	};
1406	$s_error = $@;
1407	$s_r = \@r;
1408    }
1409    elsif ($context eq 'scalar') {
1410	my $r;
1411	eval {
1412	    local $SIG{__WARN__} = $gather_warning;
1413	    $r = splice @array, $offset, $length, @list;
1414	};
1415	$s_error = $@;
1416	$s_r = [ $r ];
1417    }
1418    elsif ($context eq 'void') {
1419	eval {
1420	    local $SIG{__WARN__} = $gather_warning;
1421	    splice @array, $offset, $length, @list;
1422	};
1423	$s_error = $@;
1424	$s_r = [];
1425    }
1426    else {
1427	die "bad context $context";
1428    }
1429
1430    foreach ($s_error, @s_warnings) {
1431	chomp;
1432	s/ at \S+ line \d+\.$//;
1433	# only built-in splice identifies name of uninit value
1434	s/(uninitialized value) \$\w+/$1/;
1435    }
1436
1437    # Now do the same for DB_File's version of splice
1438    my ($ms_r, $ms_error, @ms_warnings);
1439    $gather_warning = sub { push @ms_warnings, $_[0] };
1440    if ($context eq 'list') {
1441	my @r;
1442	eval {
1443	    local $SIG{__WARN__} = $gather_warning;
1444	    @r = splice @h, $offset, $length, @list;
1445	};
1446	$ms_error = $@;
1447	$ms_r = \@r;
1448    }
1449    elsif ($context eq 'scalar') {
1450	my $r;
1451	eval {
1452	    local $SIG{__WARN__} = $gather_warning;
1453	    $r = splice @h, $offset, $length, @list;
1454	};
1455	$ms_error = $@;
1456	$ms_r = [ $r ];
1457    }
1458    elsif ($context eq 'void') {
1459	eval {
1460	    local $SIG{__WARN__} = $gather_warning;
1461	    splice @h, $offset, $length, @list;
1462	};
1463	$ms_error = $@;
1464	$ms_r = [];
1465    }
1466    else {
1467	die "bad context $context";
1468    }
1469
1470    foreach ($ms_error, @ms_warnings) {
1471	chomp;
1472    s/ at \S+(\s+\S+)*? line \d+\.?.*//s;
1473    }
1474
1475    return "different errors: '$s_error' vs '$ms_error'"
1476      if $s_error ne $ms_error;
1477    return('different return values: ' . Dumper($s_r) . ' vs ' . Dumper($ms_r))
1478      if list_diff($s_r, $ms_r);
1479    return('different changed list: ' . Dumper(\@array) . ' vs ' . Dumper(\@h))
1480      if list_diff(\@array, \@h);
1481
1482    if ((scalar @s_warnings) != (scalar @ms_warnings)) {
1483	return 'different number of warnings';
1484    }
1485
1486    while (@s_warnings) {
1487	my $sw  = shift @s_warnings;
1488	my $msw = shift @ms_warnings;
1489
1490	if (defined $sw and defined $msw) {
1491	    $msw =~ s/ \(.+\)$//;
1492	    $msw =~ s/ in splice$// if $] < 5.006;
1493	    if ($sw ne $msw) {
1494		return "different warning: '$sw' vs '$msw'";
1495	    }
1496	}
1497	elsif (not defined $sw and not defined $msw) {
1498	    # Okay.
1499	}
1500	else {
1501	    return "one warning defined, another undef";
1502	}
1503    }
1504
1505    undef $H;
1506    untie @h;
1507
1508    open(TEXT, $tmp) or die "cannot open $tmp: $!";
1509    @h = <TEXT>; normalise @h; chomp @h;
1510    close TEXT or die "cannot close $tmp: $!";
1511    return('list is different when re-read from disk: '
1512	   . Dumper(\@array) . ' vs ' . Dumper(\@h))
1513      if list_diff(\@array, \@h);
1514
1515    unlink $tmp;
1516
1517    return undef; # success
1518}
1519
1520
1521# list_diff()
1522#
1523# Do two lists differ?
1524#
1525# Parameters:
1526#   reference to first list
1527#   reference to second list
1528#
1529# Returns true iff they differ.  Only works for lists of (string or
1530# undef).
1531#
1532# Surely there is a better way to do this?
1533#
1534sub list_diff {
1535    die 'usage: list_diff(ref to first list, ref to second list)'
1536      if @_ != 2;
1537    my ($a, $b) = @_;
1538    my @a = @$a; my @b = @$b;
1539    return 1 if (scalar @a) != (scalar @b);
1540    for (my $i = 0; $i < @a; $i++) {
1541	my ($ae, $be) = ($a[$i], $b[$i]);
1542	if (defined $ae and defined $be) {
1543	    return 1 if $ae ne $be;
1544	}
1545	elsif (not defined $ae and not defined $be) {
1546	    # Two undefined values are 'equal'
1547	}
1548	else {
1549	    return 1;
1550	}
1551    }
1552    return 0;
1553}
1554
1555
1556# rand_test()
1557#
1558# Think up a random ARRAY, OFFSET, LENGTH, LIST, and context.
1559# ARRAY or LIST might be empty, and OFFSET or LENGTH might be
1560# undefined.  Return a 'test' - a listref of these five things.
1561#
1562sub rand_test {
1563    die 'usage: rand_test()' if @_;
1564    my @contexts = qw<list scalar void>;
1565    my $context = $contexts[int(rand @contexts)];
1566    return [ rand_list(),
1567	     (rand() < 0.5) ? (int(rand(20)) - 10) : undef,
1568	     (rand() < 0.5) ? (int(rand(20)) - 10) : undef,
1569	     rand_list(),
1570	     $context ];
1571}
1572
1573
1574sub rand_list {
1575    die 'usage: rand_list()' if @_;
1576    my @r;
1577
1578    while (rand() > 0.1 * (scalar @r + 1)) {
1579	push @r, rand_word();
1580    }
1581    return \@r;
1582}
1583
1584
1585sub rand_word {
1586    die 'usage: rand_word()' if @_;
1587    my $r = '';
1588    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>;
1589    while (rand() > 0.1 * (length($r) + 1)) {
1590	$r .= $chars[int(rand(scalar @chars))];
1591    }
1592    return $r;
1593}
1594
1595
1596