1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6#
7# Unit tests for abstract cache implementation
8#
9# Test the following methods:
10# * new()
11# * is_empty()
12# * empty()
13# * lookup(key)
14# * remove(key)
15# * insert(key,val)
16# * update(key,val)
17# * rekey(okeys,nkeys)
18# * expire()
19# * keys()
20# * bytes()
21# DESTROY()
22#
23# 20020327 You somehow managed to miss:
24# * reduce_size_to(bytes)
25#
26
27# print "1..0\n"; exit;
28print "1..42\n";
29
30my ($N, @R, $Q, $ar) = (1);
31
32use Tie::File;
33print "ok $N\n";
34$N++;
35
36my $h = Tie::File::Cache->new(10000) or die;
37print "ok $N\n";
38$N++;
39
40# (3) Are all the methods there?
41{
42  my $good = 1;
43  for my $meth (qw(new is_empty empty lookup remove
44                 insert update rekey expire ckeys bytes
45                   set_limit adj_limit flush  reduce_size_to
46                   _produce _produce_lru )) {
47    unless ($h->can($meth)) {
48      print STDERR "# Method '$meth' is missing.\n";
49      $good = 0;
50    }
51  }
52  print $good ? "ok $N\n" : "not ok $N\n";
53  $N++;
54}
55
56# (4-5) Straight insert and removal FIFO test
57$ar = 'a0';
58for (1..10) {
59  $h->insert($_, $ar++);
60}
611;
62for (1..10) {
63  push @R, $h->expire;
64}
65my $iota = iota('a',9);
66print "@R" eq $iota
67  ? "ok $N\n" : "not ok $N \# expected ($iota), got (@R)\n";
68$N++;
69check($h);
70
71# (6-7) Remove from empty heap
72my $n = $h->expire;
73print ! defined $n ? "ok $N\n" : "not ok $N \# expected UNDEF, got $n";
74$N++;
75check($h);
76
77# (8-9) Interleaved insert and removal
78$Q = 0;
79@R = ();
80for my $i (1..4) {
81  for my $j (1..$i) {
82    $h->insert($Q, "b$Q");
83    $Q++;
84  }
85  for my $j (1..$i) {
86    push @R, $h->expire;
87  }
88}
89$iota = iota('b', 9);
90print "@R" eq $iota ? "ok $N\n" : "not ok $N \# expected ($iota), got (@R)\n";
91$N++;
92check($h);
93
94# (10) It should be empty now
95print $h->is_empty ? "ok $N\n" : "not ok $N\n";
96$N++;
97
98# (11-12) Insert and delete
99$Q = 1;
100for (1..10) {
101  $h->insert($_, "c$Q");
102  $Q++;
103}
104for (2, 4, 6, 8, 10) {
105  $h->remove($_);
106}
107@R = ();
108push @R, $n while defined ($n = $h->expire);
109print "@R" eq "c1 c3 c5 c7 c9" ? 
110  "ok $N\n" : "not ok $N \# expected (c1 c3 c5 c7 c9), got (@R)\n";
111$N++;
112check($h);
113
114# (13-14) Interleaved insert and delete
115$Q = 1; my $QQ = 1;
116@R = ();
117for my $i (1..4) {
118  for my $j (1..$i) {
119    $h->insert($Q, "d$Q");
120    $Q++;
121  }
122  for my $j (1..$i) {
123    $h->remove($QQ) if $QQ % 2 == 0;
124    $QQ++;
125  }
126}
127push @R, $n while defined ($n = $h->expire);
128print "@R" eq "d1 d3 d5 d7 d9" ? 
129  "ok $N\n" : "not ok $N \# expected (d1 d3 d5 d7 d9), got (@R)\n";
130$N++;
131check($h);
132
133# (15-16) Promote
134$h->empty;
135$Q = 1;
136for (1..10) {
137  $h->insert($_, "e$Q");
138  unless ($h->_check_integrity) {
139    die "Integrity failed after inserting ($_, e$Q)\n";
140  }
141  $Q++;
142}
1431;
144for (2, 4, 6, 8, 10) {
145  $h->_promote($_);
146}
147@R = ();
148push @R, $n while defined ($n = $h->expire);
149print "@R" eq "e1 e3 e5 e7 e9 e2 e4 e6 e8 e10" ? 
150    "ok $N\n" : 
151    "not ok $N \# expected (e1 e3 e5 e7 e9 e2 e4 e6 e8 e10), got (@R)\n";
152$N++;
153check($h);
154
155# (17-22) Lookup
156$Q = 1;
157for (1..10) {
158  $h->insert($_, "f$Q");
159  $Q++;
160}
1611;
162for (2, 4, 6, 4, 8) {
163  my $r = $h->lookup($_);
164  print $r eq "f$_" ? "ok $N\n" : "not ok $N \# expected f$_, got $r\n";
165  $N++;
166}
167check($h);
168
169# (23) It shouldn't be empty
170print ! $h->is_empty ? "ok $N\n" : "not ok $N\n";
171$N++;
172
173# (24-25) Lookup should have promoted the looked-up records
174@R = ();
175push @R, $n while defined ($n = $h->expire);
176print "@R" eq "f1 f3 f5 f7 f9 f10 f2 f6 f4 f8" ?
177  "ok $N\n" : 
178  "not ok $N \# expected (f1 f3 f5 f7 f9 f10 f2 f6 f4 f8), got (@R)\n";
179$N++;
180check($h);
181
182# (26-29) Typical 'rekey' operation
183$Q = 1;
184for (1..10) {
185  $h->insert($_, "g$Q");
186  $Q++;
187}
188$h->rekey([6,7,8,9,10], [8,9,10,11,12]);
189my %x = qw(1 g1 2 g2  3 g3  4 g4  5 g5
190           8 g6 9 g7 10 g8 11 g9 12 g10);
191{
192  my $good = 1;
193  for my $k (keys %x) {
194    my $v = $h->lookup($k);
195    $v = "UNDEF" unless defined $v;
196    unless ($v eq $x{$k}) {
197      print "# looked up $k, got $v, expected $x{$k}\n";
198      $good = 0;
199    }
200  }
201  print $good ? "ok $N\n" : "not ok $N\n";
202  $N++;
203}
204check($h);
205{
206  my $good = 1;
207  for my $k (6, 7) {
208    my $v = $h->lookup($k);
209    if (defined $v) {
210      print "# looked up $k, got $v, should have been undef\n";
211      $good = 0;
212    }
213  }
214  print $good ? "ok $N\n" : "not ok $N\n";
215  $N++;
216}
217check($h);
218
219# (30-31) ckeys
220@R = sort { $a <=> $b } $h->ckeys;
221print "@R" eq "1 2 3 4 5 8 9 10 11 12" ?
222  "ok $N\n" : 
223  "not ok $N \# expected (1 2 3 4 5 8 9 10 11 12) got (@R)\n";
224$N++;
225check($h);
2261;
227# (32-33) update
228for (1..5, 8..12) {
229  $h->update($_, "h$_");
230}
231@R = ();
232for (sort { $a <=> $b } $h->ckeys) {
233  push @R, $h->lookup($_);
234}
235print "@R" eq "h1 h2 h3 h4 h5 h8 h9 h10 h11 h12" ?
236  "ok $N\n" : 
237  "not ok $N \# expected (h1 h2 h3 h4 h5 h8 h9 h10 h11 h12) got (@R)\n";
238$N++;
239check($h);
240
241# (34-37) bytes
242my $B;
243$B = $h->bytes;
244print $B == 23 ? "ok $N\n" : "not ok $N \# expected 23, got $B\n";
245$N++;
246check($h);
247$h->update('12', "yobgorgle");
248$B = $h->bytes;
249print $B == 29 ? "ok $N\n" : "not ok $N \# expected 29, got $B\n";
250$N++;
251check($h);
252
253# (38-41) empty
254$h->empty;
255print $h->is_empty ? "ok $N\n" : "not ok $N\n";
256$N++;
257check($h);
258$n = $h->expire;
259print ! defined $n ? "ok $N\n" : "not ok $N \# expected UNDEF, got $n";
260$N++;
261check($h);
262
263# (42) very weak testing of DESTROY
264undef $h;
265# are we still alive?
266print "ok $N\n";
267$N++;
268
269sub check {
270  my $h = shift;
271  print $h->_check_integrity ? "ok $N\n" : "not ok $N\n";
272  $N++;
273}
274
275sub iota {
276  my ($p, $n) = @_;
277  my $r;
278  my $i = 0;
279  while ($i <= $n) {
280    $r .= "$p$i ";
281    $i++;
282  }
283  chop $r;
284  $r;
285}
286