This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Move common code from ext/[GONS]DBM_File/t/[gons]dbm.t to t/lib/dbmt_common.pl
[perl5.git] / t / lib / dbmt_common.pl
1 #!perl
2 BEGIN {
3 }
4
5 use strict;
6 use warnings;
7
8 use Test::More;
9 use Config;
10
11 our $DBM_Class;
12
13 my ($create, $write);
14 BEGIN {
15     plan(skip_all => "$DBM_Class was not built")
16         unless $Config{extensions} =~ /\b$DBM_Class\b/;
17     plan(skip_all => "$DBM_Class not compatible with C++")
18          if $DBM_Class eq 'ODBM_File' && $Config{d_cplusplus};
19
20     use_ok($DBM_Class);
21
22     if ($::Create_and_Write) {
23         ($create, $write) = eval $::Create_and_Write;
24         isnt($create, undef, "(eval q{$::Create_and_Write})[0]");
25         isnt($write, undef, "(eval q{$::Create_and_Write})[1]");
26     } else {
27         #If Fcntl is not available, try 0x202 or 0x102 for O_RDWR|O_CREAT
28         use_ok('Fcntl');
29         $create = O_RDWR()|O_CREAT();
30         $write = O_RDWR();
31     }
32 }
33
34 unlink <Op_dbmx.*>;
35
36 umask(0);
37 my %h;
38 isa_ok(tie(%h, $DBM_Class, 'Op_dbmx', $create, 0640), $DBM_Class);
39
40 my $Dfile = "Op_dbmx.pag";
41 if (! -e $Dfile) {
42         ($Dfile) = <Op_dbmx*>;
43 }
44 SKIP: {
45     skip "different file permission semantics on $^O", 1
46         if $^O eq 'amigaos' || $^O eq 'os2' || $^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'dos' || $^O eq 'cygwin';
47     my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
48         $blksize,$blocks) = stat($Dfile);
49     is($mode & 0777, 0640);
50 }
51 my $i = 0;
52 while (my ($key,$value) = each(%h)) {
53     $i++;
54 }
55 is($i, 0);
56
57 $h{'goner1'} = 'snork';
58
59 $h{'abc'} = 'ABC';
60 $h{'def'} = 'DEF';
61 $h{'jkl','mno'} = "JKL\034MNO";
62 $h{'a',2,3,4,5} = join("\034",'A',2,3,4,5);
63 $h{'a'} = 'A';
64 $h{'b'} = 'B';
65 $h{'c'} = 'C';
66 $h{'d'} = 'D';
67 $h{'e'} = 'E';
68 $h{'f'} = 'F';
69 $h{'g'} = 'G';
70 $h{'h'} = 'H';
71 $h{'i'} = 'I';
72
73 $h{'goner2'} = 'snork';
74 delete $h{'goner2'};
75
76 untie(%h);
77 isa_ok(tie(%h, $DBM_Class, 'Op_dbmx', $write, 0640), $DBM_Class);
78
79 $h{'j'} = 'J';
80 $h{'k'} = 'K';
81 $h{'l'} = 'L';
82 $h{'m'} = 'M';
83 $h{'n'} = 'N';
84 $h{'o'} = 'O';
85 $h{'p'} = 'P';
86 $h{'q'} = 'Q';
87 $h{'r'} = 'R';
88 $h{'s'} = 'S';
89 $h{'t'} = 'T';
90 $h{'u'} = 'U';
91 $h{'v'} = 'V';
92 $h{'w'} = 'W';
93 $h{'x'} = 'X';
94 $h{'y'} = 'Y';
95 $h{'z'} = 'Z';
96
97 $h{'goner3'} = 'snork';
98
99 delete $h{'goner1'};
100 delete $h{'goner3'};
101
102 my @keys = keys(%h);
103 my @values = values(%h);
104
105 is($#keys, 29);
106 is($#values, 29);
107
108 while (my ($key, $value) = each(%h)) {
109     if ($key eq $keys[$i] && $value eq $values[$i] && $key eq lc($value)) {
110         $key =~ y/a-z/A-Z/;
111         $i++ if $key eq $value;
112     }
113 }
114
115 is($i, 30);
116
117 @keys = ('blurfl', keys(%h), 'dyick');
118 is($#keys, 31);
119
120 $h{'foo'} = '';
121 $h{''} = 'bar';
122
123 my $ok = 1;
124 for ($i = 1; $i < 200; $i++) { $h{$i + 0} = $i + 0; }
125 for ($i = 1; $i < 200; $i++) { $ok = 0 unless $h{$i} == $i; }
126 is($ok, 1, 'check cache overflow and numeric keys and contents');
127
128 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
129    $blksize,$blocks) = stat($Dfile);
130 cmp_ok($size, '>', 0);
131
132 @h{0..200} = 200..400;
133 my @foo = @h{0..200};
134 is(join(':',200..400), join(':',@foo));
135
136 is($h{'foo'}, '');
137 is($h{''}, 'bar');
138
139 if($DBM_Class eq 'SDBM_File') {
140     is(exists $h{goner1}, '');
141     is(exists $h{foo}, 1);
142 }
143
144 untie %h;
145 unlink <Op_dbmx*>, $Dfile;
146
147 {
148    # sub-class test
149
150    package Another;
151
152    open my $file, '>', 'SubDB.pm' or die "Cannot open SubDB.pm: $!\n";
153    printf $file <<'EOM', $DBM_Class, $DBM_Class, $DBM_Class;
154
155    package SubDB;
156
157    use strict;
158    use warnings;
159    use vars qw(@ISA @EXPORT);
160
161    require Exporter;
162    use %s;
163    @ISA=qw(%s);
164    @EXPORT = @%s::EXPORT;
165
166    sub STORE {
167         my $self = shift;
168         my $key = shift;
169         my $value = shift;
170         $self->SUPER::STORE($key, $value * 2);
171    }
172
173    sub FETCH {
174         my $self = shift;
175         my $key = shift;
176         $self->SUPER::FETCH($key) - 1;
177    }
178
179    sub A_new_method
180    {
181         my $self = shift;
182         my $key = shift;
183         my $value = $self->FETCH($key);
184         return "[[$value]]";
185    }
186
187    1;
188 EOM
189
190     close $file or die "Could not close: $!";
191
192     BEGIN { push @INC, '.'; }
193     unlink <dbhash_tmp*>;
194
195     main::use_ok('SubDB');
196     my %h;
197     my $X;
198     eval '
199         $X = tie(%h, "SubDB", "dbhash_tmp", $create, 0640 );
200         ';
201
202     main::is($@, "");
203
204     my $ret = eval '$h{"fred"} = 3; return $h{"fred"} ';
205     main::is($@, "");
206     main::is($ret, 5);
207
208     $ret = eval '$X->A_new_method("fred") ';
209     main::is($@, "");
210     main::is($ret, "[[5]]");
211
212     if ($DBM_Class eq 'GDBM_File') {
213         $ret = eval 'GDBM_WRCREAT eq main::GDBM_WRCREAT';
214         main::is($@, "");
215         main::is($ret, 1);
216     }
217
218     undef $X;
219     untie(%h);
220     unlink "SubDB.pm", <dbhash_tmp.*>;
221
222 }
223
224 untie %h;
225 unlink <Op_dbmx*>, $Dfile;
226
227 {
228    # DBM Filter tests
229    my (%h, $db);
230    my ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
231
232    sub checkOutput
233    {
234        my($fk, $sk, $fv, $sv) = @_;
235        return
236            $fetch_key eq $fk && $store_key eq $sk &&
237            $fetch_value eq $fv && $store_value eq $sv &&
238            $_ eq 'original';
239    }
240
241    unlink <Op_dbmx*>;
242    $db = tie %h, $DBM_Class, 'Op_dbmx', $create, 0640;
243    isa_ok($db, $DBM_Class);
244
245    $db->filter_fetch_key   (sub { $fetch_key = $_ });
246    $db->filter_store_key   (sub { $store_key = $_ });
247    $db->filter_fetch_value (sub { $fetch_value = $_});
248    $db->filter_store_value (sub { $store_value = $_ });
249
250    $_ = "original";
251
252    $h{"fred"} = "joe";
253    #                   fk   sk     fv   sv
254    ok(checkOutput("", "fred", "", "joe"));
255
256    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
257    is($h{"fred"}, "joe");
258    #                   fk    sk     fv    sv
259    ok(checkOutput("", "fred", "joe", ""));
260
261    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
262    is($db->FIRSTKEY(), "fred");
263    #                    fk     sk  fv  sv
264    ok(checkOutput("fred", "", "", ""));
265
266    # replace the filters, but remember the previous set
267    my ($old_fk) = $db->filter_fetch_key
268                         (sub { $_ = uc $_; $fetch_key = $_ });
269    my ($old_sk) = $db->filter_store_key
270                         (sub { $_ = lc $_; $store_key = $_ });
271    my ($old_fv) = $db->filter_fetch_value
272                         (sub { $_ = "[$_]"; $fetch_value = $_ });
273    my ($old_sv) = $db->filter_store_value
274                         (sub { s/o/x/g; $store_value = $_ });
275
276    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
277    $h{"Fred"} = "Joe";
278    #                   fk   sk     fv    sv
279    ok(checkOutput("", "fred", "", "Jxe"));
280
281    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
282    is($h{"Fred"}, "[Jxe]");
283    #                   fk   sk     fv    sv
284    ok(checkOutput("", "fred", "[Jxe]", ""));
285
286    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
287    is($db->FIRSTKEY(), "FRED");
288    #                   fk   sk     fv    sv
289    ok(checkOutput("FRED", "", "", ""));
290
291    # put the original filters back
292    $db->filter_fetch_key   ($old_fk);
293    $db->filter_store_key   ($old_sk);
294    $db->filter_fetch_value ($old_fv);
295    $db->filter_store_value ($old_sv);
296
297    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
298    $h{"fred"} = "joe";
299    ok(checkOutput("", "fred", "", "joe"));
300
301    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
302    is($h{"fred"}, "joe");
303    ok(checkOutput("", "fred", "joe", ""));
304
305    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
306    is($db->FIRSTKEY(), "fred");
307    ok(checkOutput("fred", "", "", ""));
308
309    # delete the filters
310    $db->filter_fetch_key   (undef);
311    $db->filter_store_key   (undef);
312    $db->filter_fetch_value (undef);
313    $db->filter_store_value (undef);
314
315    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
316    $h{"fred"} = "joe";
317    ok(checkOutput("", "", "", ""));
318
319    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
320    is($h{"fred"}, "joe");
321    ok(checkOutput("", "", "", ""));
322
323    ($fetch_key, $store_key, $fetch_value, $store_value) = ("") x 4;
324    is($db->FIRSTKEY(), "fred");
325    ok(checkOutput("", "", "", ""));
326
327    undef $db;
328    untie %h;
329    unlink <Op_dbmx*>;
330 }
331
332 {
333     # DBM Filter with a closure
334
335     my (%h, $db);
336
337     unlink <Op_dbmx*>;
338     $db = tie %h, $DBM_Class, 'Op_dbmx', $create, 0640;
339     isa_ok($db, $DBM_Class);
340
341     my %result = ();
342
343     sub Closure
344     {
345         my ($name) = @_;
346         my $count = 0;
347         my @kept = ();
348
349         return sub { ++$count;
350                      push @kept, $_;
351                      $result{$name} = "$name - $count: [@kept]";
352                    }
353     }
354
355     $db->filter_store_key(Closure("store key"));
356     $db->filter_store_value(Closure("store value"));
357     $db->filter_fetch_key(Closure("fetch key"));
358     $db->filter_fetch_value(Closure("fetch value"));
359
360     $_ = "original";
361
362     $h{"fred"} = "joe";
363     is($result{"store key"}, "store key - 1: [fred]");
364     is($result{"store value"}, "store value - 1: [joe]");
365     is($result{"fetch key"}, undef);
366     is($result{"fetch value"}, undef);
367     is($_, "original");
368
369     is($db->FIRSTKEY(), "fred");
370     is($result{"store key"}, "store key - 1: [fred]");
371     is($result{"store value"}, "store value - 1: [joe]");
372     is($result{"fetch key"}, "fetch key - 1: [fred]");
373     is($result{"fetch value"}, undef);
374     is($_, "original");
375
376     $h{"jim"}  = "john";
377     is($result{"store key"}, "store key - 2: [fred jim]");
378     is($result{"store value"}, "store value - 2: [joe john]");
379     is($result{"fetch key"}, "fetch key - 1: [fred]");
380     is($result{"fetch value"}, undef);
381     is($_, "original");
382
383     is($h{"fred"}, "joe");
384     is($result{"store key"}, "store key - 3: [fred jim fred]");
385     is($result{"store value"}, "store value - 2: [joe john]");
386     is($result{"fetch key"}, "fetch key - 1: [fred]");
387     is($result{"fetch value"}, "fetch value - 1: [joe]");
388     is($_, "original");
389
390     undef $db;
391     untie %h;
392     unlink <Op_dbmx*>;
393 }               
394
395 {
396    # DBM Filter recursion detection
397    my (%h, $db);
398    unlink <Op_dbmx*>;
399
400    $db = tie %h, $DBM_Class, 'Op_dbmx', $create, 0640;
401    isa_ok($db, $DBM_Class);
402
403    $db->filter_store_key (sub { $_ = $h{$_} });
404
405    eval '$h{1} = 1234';
406    like($@, qr/^recursion detected in filter_store_key at/);
407
408    undef $db;
409    untie %h;
410    unlink <Op_dbmx*>;
411 }
412
413 {
414     # Bug ID 20001013.009
415     #
416     # test that $hash{KEY} = undef doesn't produce the warning
417     #     Use of uninitialized value in null operation
418
419     unlink <Op_dbmx*>;
420     my %h;
421     my $a = "";
422     local $SIG{__WARN__} = sub {$a = $_[0]};
423
424     isa_ok(tie(%h, $DBM_Class, 'Op_dbmx', $create, 0640), $DBM_Class);
425     $h{ABC} = undef;
426     is($a, "");
427     untie %h;
428     unlink <Op_dbmx*>;
429 }
430
431 {
432     # When iterating over a tied hash using "each", the key passed to FETCH
433     # will be recycled and passed to NEXTKEY. If a Source Filter modifies the
434     # key in FETCH via a filter_fetch_key method we need to check that the
435     # modified key doesn't get passed to NEXTKEY.
436     # Also Test "keys" & "values" while we are at it.
437
438     unlink <Op_dbmx*>;
439     my $bad_key = 0;
440     my %h = ();
441     my $db = tie %h, $DBM_Class, 'Op_dbmx', $create, 0640;
442     isa_ok($db, $DBM_Class);
443     $db->filter_fetch_key (sub { $_ =~ s/^Beta_/Alpha_/ if defined $_});
444     $db->filter_store_key (sub { $bad_key = 1 if /^Beta_/; $_ =~ s/^Alpha_/Beta_/});
445
446     $h{'Alpha_ABC'} = 2;
447     $h{'Alpha_DEF'} = 5;
448
449     is($h{'Alpha_ABC'}, 2);
450     is($h{'Alpha_DEF'}, 5);
451
452     my ($k, $v) = ("", "");
453     while (($k, $v) = each %h) {}
454     is($bad_key, 0);
455
456     $bad_key = 0;
457     foreach $k (keys %h) {}
458     is($bad_key, 0);
459
460     $bad_key = 0;
461     foreach $v (values %h) {}
462     is($bad_key, 0);
463
464     undef $db;
465     untie %h;
466     unlink <Op_dbmx*>;
467 }
468
469 {
470    # Check that DBM Filter can cope with read-only $_
471
472    my %h;
473    unlink <Op1_dbmx*>;
474
475    my $db = tie %h, $DBM_Class, 'Op1_dbmx', $create, 0640;
476    isa_ok($db, $DBM_Class);
477
478    $db->filter_fetch_key   (sub { });
479    $db->filter_store_key   (sub { });
480    $db->filter_fetch_value (sub { });
481    $db->filter_store_value (sub { });
482
483    $_ = "original";
484
485    $h{"fred"} = "joe";
486    is($h{"fred"}, "joe");
487
488    is_deeply([eval { map { $h{$_} } (1, 2, 3) }], [undef, undef, undef]);
489    is($@, '');
490
491
492    # delete the filters
493    $db->filter_fetch_key   (undef);
494    $db->filter_store_key   (undef);
495    $db->filter_fetch_value (undef);
496    $db->filter_store_value (undef);
497
498    $h{"fred"} = "joe";
499
500    is($h{"fred"}, "joe");
501
502    is($db->FIRSTKEY(), "fred");
503
504    is_deeply([eval { map { $h{$_} } (1, 2, 3) }], [undef, undef, undef]);
505    is($@, '');
506
507    undef $db;
508    untie %h;
509    unlink <Op1_dbmx*>;
510 }
511
512 done_testing();
513 1;