File Coverage

blib/lib/Cache/Tester.pm
Criterion Covered Total %
statement 286 292 97.9
branch 7 14 50.0
condition 1 3 33.3
subroutine 29 32 90.6
pod 0 17 0.0
total 323 358 90.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Cache::Tester - test utility for Cache implementations
4              
5             =head1 SYNOPSIS
6              
7             use Cache::Tester;
8              
9             BEGIN { plan tests => 2 + $CACHE_TESTS }
10              
11             use_ok('Cache::Memory');
12              
13             my $cache = Cache::Memory->new();
14             ok($cache, 'Cache created');
15              
16             run_cache_tests($cache);
17              
18             =head1 DESCRIPTION
19              
20             This module is used to run tests against an instance of a Cache implementation
21             to ensure that it operates as required by the Cache specification.
22              
23             =cut
24             package Cache::Tester;
25              
26             require 5.006;
27 3     3   2635 use strict;
  3         7  
  3         120  
28 3     3   17 use warnings;
  3         6  
  3         82  
29 3     3   10123 use Test::More;
  3         53685  
  3         36  
30 3     3   1077 use Exporter;
  3         8  
  3         145  
31 3     3   17 use vars qw(@ISA @EXPORT $VERSION $CACHE_TESTS);
  3         7  
  3         254  
32 3     3   24 use Carp;
  3         7  
  3         6158  
33              
34             @ISA = qw(Exporter Test::More);
35             $VERSION = '2.10';
36             @EXPORT = (qw(run_cache_tests $CACHE_TESTS), @Test::More::EXPORT);
37              
38             $CACHE_TESTS = 79;
39              
40             sub run_cache_tests {
41 1     1 0 1414 my ($cache) = @_;
42              
43 1 50       5 $cache or croak "Cache required";
44              
45 1         4 test_store_scalar($cache);
46 1         5 test_entry_size($cache);
47 1         6 test_store_complex($cache);
48 1         4 test_cache_size($cache);
49 1         708 test_cache_count($cache);
50 1         648 test_expiry($cache);
51 1         6 test_read_handle($cache);
52 1         5 test_write_handle($cache);
53 1         4 test_append_handle($cache);
54 1         4 test_handle_async_read($cache);
55 1         6 test_handle_async_remove($cache);
56 1         6 test_handle_async_replace($cache);
57 1         6 test_validity($cache);
58 1         5 test_load_callback($cache);
59 1         6 test_validate_callback($cache);
60             }
61              
62             # Test storing, retrieving and removing simple scalars
63             sub test_store_scalar {
64 1     1 0 2 my ($cache) = @_;
65              
66 1         3 my $key = 'testkey';
67 1         4 my $entry = $cache->entry($key);
68 1         4 _ok($entry, 'entry returned');
69 1         836 _is($entry->key(), $key, 'entry key correct');
70 1         802 _ok(!$entry->exists(), 'entry doesn\'t exist initially');
71 1         699 _is($entry->get(), undef, '$entry->get() returns undef');
72              
73 1         518 $entry->set('test data');
74 1         13 _ok($entry->exists(), 'entry exists');
75 1         710 _is($entry->get(), 'test data', 'set/get worked');
76              
77 1         654 $entry->remove();
78 1         4 _ok(!$entry->exists(), 'entry removed');
79              
80 1         832 $cache->set($key, 'more test data');
81 1         12 _ok($cache->exists($key), 'key exists');
82 1         902 _is($cache->get($key), 'more test data', 'cache set/get worked');
83              
84 1         733 $cache->remove($key);
85 1         5 _ok(!$entry->exists(), 'entry removed via cache');
86             }
87              
88             # Test size reporting of entries
89             sub test_entry_size {
90 1     1 0 2 my ($cache) = @_;
91              
92 1         5 my $entry = $cache->entry('testsize');
93 1         8 $entry->set('A'x1234);
94 1         12 _ok($entry->exists(), 'entry created');
95 1         898 _is($entry->size(), 1234, 'entry size is correct');
96              
97 1         849 $entry->remove();
98             }
99              
100             # Test storing of complex entities
101             sub test_store_complex {
102 1     1 0 3 my ($cache) = @_;
103              
104 1         6 my @array = (1, 2, { hi => 'there' });
105              
106 1         5 my $entry = $cache->entry('testcomplex');
107 1         11 $entry->freeze(\@array);
108 1         14 _ok($entry->exists(), 'frozen entry created');
109 1         961 my $arrayref = $entry->thaw();
110 1   33     52 _ok($array[0] == $$arrayref[0] &&
111             $array[1] == $$arrayref[1] &&
112             $array[2]->{hi} eq $$arrayref[2]->{hi}, 'entry thawed');
113              
114 1         863 $entry->remove();
115             }
116              
117             # Test size tracking of cache
118             sub test_cache_size {
119 1     1 0 2 my ($cache) = @_;
120              
121 1         5 $cache->clear();
122 1         4 _is($cache->size(), 0, 'cache is empty after clear');
123 1         637 $cache->set('testkey', 'A'x4000);
124 1         11 _is($cache->size(), 4000, 'cache size is correct after set');
125 1         890 $cache->set('testkey2', 'B'x200);
126 1         15 _is($cache->size(), 4200, 'cache size is correct after 2 sets');
127 1         995 $cache->set('testkey', 'C'x2800);
128 1         9 _is($cache->size(), 3000, 'cache size is correct after replace');
129 1         589 $cache->remove('testkey2');
130 1         65 _is($cache->size(), 2800, 'cache size is correct after remove');
131              
132 1         585 $cache->clear();
133 1         3 _is($cache->size(), 0, 'cache is empty after clear');
134              
135             # Add 100 entries of various lengths
136 1         930 my $size = 0;
137 1         15 my @keys = (1..100);
138 1         3 foreach (@keys) {
139 100         472 $cache->set("key$_", "D"x$_);
140 100         3873 $size += $_;
141             }
142 1         6 _is($cache->size(), $size, 'cache size is ok after multiple sets');
143              
144 1         698 shuffle(\@keys);
145 1         3 foreach (@keys) {
146 100         354 $cache->remove("key$_");
147             }
148 1         5 _is($cache->size(), 0, 'cache is empty after multiple removes');
149             }
150              
151             # Test count tracking of cache
152             sub test_cache_count {
153 1     1 0 4 my ($cache) = @_;
154              
155 1         6 $cache->clear();
156 1         5 _is($cache->count(), 0, 'cache is empty after clear');
157 1         493 $cache->set('testkey', 'test');
158 1         206 _is($cache->count(), 1, 'cache count correct after set');
159 1         453 $cache->set('testkey2', 'test2');
160 1         10 _is($cache->count(), 2, 'cache count correct after 2 sets');
161 1         472 $cache->set('testkey', 'test3');
162 1         10 _is($cache->count(), 2, 'cache count correct after replace');
163 1         609 $cache->remove('testkey2');
164 1         4 _is($cache->count(), 1, 'cache count correct after remove');
165              
166 1         1084 $cache->clear();
167 1         6 _is($cache->count(), 0, 'cache is empty after clear');
168              
169             # Add 100 entries
170 1         1106 my @keys = (1..100);
171 1         4 foreach (@keys) {
172 100         478 $cache->set("key$_", "test");
173             }
174 1         5 _is($cache->count(), 100, 'cache count correct after multiple sets');
175              
176 1         590 shuffle(\@keys);
177 1         3 foreach(@keys) {
178 100         317 $cache->remove("key$_");
179             }
180 1         7 _is($cache->size(), 0, 'cache empty after multiple removes');
181             }
182              
183             # Test expiry
184             sub test_expiry {
185 1     1 0 3 my ($cache) = @_;
186              
187 1         5 my $entry = $cache->entry('testexp');
188              
189 1         5 $entry->set('test data');
190 1         17 $entry->set_expiry('100 minutes');
191 1         5 _cmp_ok($entry->expiry(), '>', time(), 'expiry set correctly');
192 1         405 _cmp_ok($entry->expiry(), '<=', time() + 100*60, 'expiry set correctly');
193 1         492 $entry->remove();
194              
195 1         5 my $size = $cache->size();
196              
197 1         6 $entry->set('test data', 'now');
198 1         4 _ok(!$entry->exists(), 'entry set with instant expiry not added');
199 1         483 _is($cache->size(), $size, 'size is unchanged');
200              
201             # This is to fix/workaround the test failures by high load. See:
202             # https://rt.cpan.org/Public/Bug/Display.html?id=27280
203 1 50       442 my $delay = $ENV{PERL_CACHE_PM_TESTING} ? 1 : 3;
204 1         8 $entry->set('test data', "$delay sec");
205 1         6 _ok($entry->exists(), "entry with $delay sec timeout added");
206 1         4000601 sleep($delay+1);
207 1         22 _ok(!$entry->exists(), 'entry expired');
208 1         751 _is($cache->size(), $size, 'size is unchanged');
209              
210 1         518 $entry->set('test data', '1 minute');
211 1         4 _ok($entry->exists(), 'entry with 1 min timeout added');
212 1         2000482 sleep(2);
213 1         16 _ok($entry->exists(), 'entry with 1 min timeout remains');
214 1         1347 $entry->set_expiry('now');
215 1         12 _ok(!$entry->exists(), 'entry expired after change to instant timeout');
216 1         946 _is($cache->size(), $size, 'size is unchanged');
217             }
218              
219             # Test reading via a handle
220             sub test_read_handle {
221 1     1 0 4 my ($cache) = @_;
222              
223 1         6 my $entry = $cache->entry('readhandle');
224 1         5 $entry->remove();
225 1         39 my $handle = $entry->handle('<');
226 1         8 _ok(!$handle, 'read handle not available for empty entry');
227              
228 1         1059 $entry->set('some test data');
229              
230 1         15 $handle = $entry->handle('<');
231 1         7 _ok($handle, 'read handle created');
232 1 50       675 $handle or diag("handle not created: $!");
233              
234 1         5 local $/;
235 1         12 _is(<$handle>, 'some test data', 'read via <$handle> successful');
236              
237             {
238 3     3   21 no warnings;
  3         7  
  3         6004  
  1         899  
239 1         12 print $handle 'this wont work';
240             }
241 1         7 $handle->close();
242 1         16 _is($entry->get(), 'some test data', 'write to read only handle failed');
243              
244 1         953 $entry->remove();
245             }
246              
247             # Test writing via a handle
248             sub test_write_handle {
249 1     1 0 3 my ($cache) = @_;
250              
251 1         7 my $entry = $cache->entry('writehandle');
252 1         4 $entry->remove();
253              
254 1         6 my $size = $cache->size();
255              
256 1         6 my $handle = $entry->handle('>');
257 1         9 _ok($handle, 'write handle created');
258 1 50       943 $handle or diag("handle not created: $!");
259              
260 1         7 print $handle 'A'x100;
261 1         23 $handle->close();
262              
263 1         14 _is($entry->get(), 'A'x100, 'write to write only handle ok');
264 1         1049 _is($entry->size(), 100, 'entry size is correct');
265 1         997 _is($cache->size(), $size + 100, 'cache size is correct');
266              
267 1         883 $entry->remove();
268             }
269              
270             # Test append via a handle
271             sub test_append_handle {
272 1     1 0 4 my ($cache) = @_;
273              
274 1         5 my $entry = $cache->entry('appendhandle');
275 1         6 $entry->remove();
276 1         6 $entry->set('hello ');
277              
278 1         13 my $size = $cache->size();
279              
280 1         7 my $handle = $entry->handle('>>');
281 1         10 _ok($handle, 'append handle created');
282 1 50       1410 $handle or diag("handle not created: $!");
283              
284 1         15 $handle->print('world');
285 1         25 $handle->close();
286              
287 1         14 _is($entry->get(), 'hello world', 'write to append handle ok');
288 1         967 _is($entry->size(), 11, 'entry size is correct');
289 1         824 _is($entry->size(), $size + 5, 'cache size is correct');
290              
291 1         828 $entry->remove();
292             }
293              
294             # Test that a entry can be read while a handle is open for read
295             sub test_handle_async_read {
296 1     1 0 3 my ($cache) = @_;
297              
298 1         6 my $entry = $cache->entry('readhandle');
299 1         5 $entry->remove();
300              
301 1         4 my $size = $cache->size();
302              
303 1         2 my $data = 'test data';
304 1         5 $entry->set($data);
305              
306 1 50       13 my $handle = $entry->handle('<') or diag("handle not created: $!");
307              
308 1         12 _ok($entry->exists(), 'entry exists after handle opened');
309 1         1066 _is(<$handle>, $data, 'handle returns correct data');
310 1         888 _is($entry->get(), $data, '$entry->get() returns correct data');
311 1         1025 $handle->close();
312 1         14 _ok($entry->exists(), 'entry exists after handle closed');
313 1         818 _is($entry->get(), $data, '$entry->get() returns correct data');
314             }
315              
316             # Test that a handle can be removed asynchronously with it being open
317             sub test_handle_async_remove {
318 1     1 0 3 my ($cache) = @_;
319              
320 1         6 my $entry = $cache->entry('removehandle');
321 1         5 $entry->remove();
322              
323 1         6 my $size = $cache->size();
324              
325 1         6 $entry->set('test data');
326              
327 1 50       14 my $handle = $entry->handle() or diag("handle not created: $!");
328              
329             # extend data by 5 bytes before removing the entry
330 1         13 $handle->print('some more data');
331 1         23 $handle->seek(0,0);
332              
333 1         23 $entry->remove();
334 1         6 _ok(!$entry->exists(), 'entry removed whilst handle active');
335              
336 1         1228 local $/;
337 1         84 _is(<$handle>, 'some more data', 'read via <$handle> successful');
338              
339             # ensure we can still write to the handle
340 1         949 $handle->seek(0,0);
341 1         19 $handle->print('hello wide wide world');
342 1         16 $handle->seek(0,0);
343 1         16 _is(<$handle>, 'hello wide wide world', 'write via <$handle> successful');
344              
345 1         820 $handle->close();
346 1         14 _ok(!$entry->exists(), 'entry still removed after handle closed');
347 1         1007 _is($entry->size(), undef, 'entry size is undefined');
348 1         1054 _is($cache->size(), $size, 'cache size is correct');
349             }
350              
351             sub test_handle_async_replace {
352 1     1 0 4 my ($cache) = @_;
353              
354 1         6 my $entry = $cache->entry('replacehandle');
355 1         6 $entry->remove();
356              
357 1         6 my $size = $cache->size();
358              
359 1         5 $entry->set('test data');
360              
361 1         13 my $handle = $entry->handle();
362              
363 1         9 $entry->set('A'x20);
364 1         12 _is($entry->get(), 'A'x20, 'entry replaced whilst handle active');
365              
366 1         928 local $/;
367 1         7 _is(<$handle>, 'test data', 'read via <$handle> successful');
368 1         809 $handle->seek(0,0);
369 1         25 $handle->print('hello world');
370 1         1125 $handle->seek(0,0);
371 1         21 _is(<$handle>, 'hello world', 'write via <$handle> successful');
372              
373 1         6579 $handle->close();
374 1         14 _ok($entry->exists(), 'entry still exists after handle closed');
375 1         650 _is($entry->get(), 'A'x20, 'entry still correct after handle closed');
376 1         397 _is($entry->size(), 20, 'entry size is correct');
377 1         456 _is($cache->size(), $size+20, 'cache size is correct');
378             }
379              
380             sub test_validity {
381 1     1 0 2 my ($cache) = @_;
382              
383 1         7 my $entry = $cache->entry('validityentry');
384 1         6 $entry->remove();
385              
386             # create an entry with validity
387 1         6 $entry->set('test data');
388 1         17 $entry->set_validity({ tester => 'test string' });
389              
390 1         3 undef $entry;
391 1         5 $entry = $cache->entry('validityentry');
392 1         6 my $validity = $entry->validity();
393 1         6 _ok($validity, 'validity retrieved');
394 1         2262 _is($validity->{tester}, 'test string', 'validity correct');
395              
396 1         656 $entry->remove();
397              
398             # create an entry with only validity
399 1         7 $entry->set_validity({ tester => 'test string' });
400              
401 1         3 undef $entry;
402 1         6 $entry = $cache->entry('validityentry');
403 1         7 $validity = $entry->validity();
404 1         7 _ok($validity, 'validity retrieved');
405 1         404 _is($validity->{tester}, 'test string', 'validity correct');
406              
407 1         374 $entry->remove();
408              
409             # create an entry with scalar validity
410 1         4 $entry->set('test data');
411 1         9 $entry->set_validity('test string');
412              
413 1         2 undef $entry;
414 1         3 $entry = $cache->entry('validityentry');
415 1         5 $validity = $entry->validity();
416 1         10 _ok($validity, 'validity retrieved');
417 1         420 _is($validity, 'test string', 'validity correct');
418             }
419              
420             sub test_load_callback {
421 1     1 0 3 my ($cache) = @_;
422              
423 1         5 my $key = 'testloadcallback';
424 1         5 $cache->remove($key);
425              
426 1         11 my $old_callback = $cache->load_callback();
427 1     1   9 $cache->set_load_callback(sub { return "result ".$_[0]->key() });
  1         4  
428              
429 1         5 _ok($cache->get($key), "result $key");
430 1         623 $cache->set_load_callback($old_callback);
431             }
432              
433             sub test_validate_callback {
434 1     1 0 2 my ($cache) = @_;
435              
436 1         3 my $key = 'testvalidatecallback';
437 1         2 my $result;
438 1         11 my $old_callback = $cache->validate_callback();
439 1     1   17 $cache->set_validate_callback(sub { $result = "result ".$_[0]->key() });
  1         4  
440              
441 1         4 $cache->set($key, 'somedata');
442 1         12 $cache->get($key);
443 1         4 _is($result, "result $key", "validate_callback ok");
444 1         576 $cache->set_validate_callback($old_callback);
445             }
446              
447              
448             ### Wrappers for test methods to add function name
449              
450             sub _ok ($$) {
451 28     28   70 my($test, $name) = @_;
452 28         366 ok($test, (caller(1))[3].': '.$name);
453             }
454              
455             sub _is ($$$) {
456 49     49   163 my($x, $y, $name) = @_;
457 49         545 is($x, $y, (caller(1))[3].': '.$name);
458             }
459              
460             sub _isnt ($$$) {
461 0     0   0 my($x, $y, $name) = @_;
462 0         0 isnt($x, $y, (caller(1))[3].': '.$name);
463             }
464              
465             sub _like ($$$) {
466 0     0   0 my($x, $y, $name) = @_;
467 0         0 like($x, $y, (caller(1))[3].': '.$name);
468             }
469              
470             sub _unlike ($$$) {
471 0     0   0 my($x, $y, $name) = @_;
472 0         0 unlike($x, $y, (caller(1))[3].': '.$name);
473             }
474              
475             sub _cmp_ok ($$$$) {
476 2     2   4 my ($x, $c, $y, $name) = @_;
477 2         21 cmp_ok($x, $c, $y, (caller(1))[3].': '.$name);
478             }
479              
480              
481             # Taken from perlfaq4
482             sub shuffle {
483 2     2 0 4 my $deck = shift; # $deck is a reference to an array
484 2         5 my $i = @$deck;
485 2         8 while ($i--) {
486 200         319 my $j = int rand ($i+1);
487 200         570 @$deck[$i,$j] = @$deck[$j,$i];
488             }
489             }
490              
491              
492             1;
493             __END__