root/feedmelinks/lib/Cache/Tester.pm

Revision 1448, 13.2 kB (checked in by jm3, 2 years ago)

new spammer blacklist, perl libs, and better splinker hunting tools

Line 
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 use strict;
28 use warnings;
29 use Test::More;
30 use Exporter;
31 use vars qw(@ISA @EXPORT $VERSION $CACHE_TESTS);
32 use Carp;
33
34 @ISA = qw(Exporter Test::More);
35 $VERSION = "2.04";
36 @EXPORT = (qw(run_cache_tests $CACHE_TESTS), @Test::More::EXPORT);
37
38 $CACHE_TESTS = 79;
39
40 sub run_cache_tests {
41     my ($cache) = @_;
42
43     $cache or croak "Cache required";
44
45     test_store_scalar($cache);
46     test_entry_size($cache);
47     test_store_complex($cache);
48     test_cache_size($cache);
49     test_cache_count($cache);
50     test_expiry($cache);
51     test_read_handle($cache);
52     test_write_handle($cache);
53     test_append_handle($cache);
54     test_handle_async_read($cache);
55     test_handle_async_remove($cache);
56     test_handle_async_replace($cache);
57     test_validity($cache);
58     test_load_callback($cache);
59     test_validate_callback($cache);
60 }
61
62 # Test storing, retrieving and removing simple scalars
63 sub test_store_scalar {
64     my ($cache) = @_;
65
66     my $key = 'testkey';
67     my $entry = $cache->entry($key);
68     _ok($entry, 'entry returned');
69     _is($entry->key(), $key, 'entry key correct');
70     _ok(!$entry->exists(), 'entry doesn\'t exist initially');
71     _is($entry->get(), undef, '$entry->get() returns undef');
72
73     $entry->set('test data');
74     _ok($entry->exists(), 'entry exists');
75     _is($entry->get(), 'test data', 'set/get worked');
76
77     $entry->remove();
78     _ok(!$entry->exists(), 'entry removed');
79
80     $cache->set($key, 'more test data');
81     _ok($cache->exists($key), 'key exists');
82     _is($cache->get($key), 'more test data', 'cache set/get worked');
83
84     $cache->remove($key);
85     _ok(!$entry->exists(), 'entry removed via cache');
86 }
87
88 # Test size reporting of entries
89 sub test_entry_size {
90     my ($cache) = @_;
91
92     my $entry = $cache->entry('testsize');
93     $entry->set('A'x1234);
94     _ok($entry->exists(), 'entry created');
95     _is($entry->size(), 1234, 'entry size is correct');
96
97     $entry->remove();
98 }
99
100 # Test storing of complex entities
101 sub test_store_complex {
102     my ($cache) = @_;
103
104     my @array = (1, 2, { hi => 'there' });
105
106     my $entry = $cache->entry('testcomplex');
107     $entry->freeze(\@array);
108     _ok($entry->exists(), 'frozen entry created');
109     my $arrayref = $entry->thaw();
110     _ok($array[0] == $$arrayref[0] &&
111         $array[1] == $$arrayref[1] &&
112         $array[2]->{hi} eq $$arrayref[2]->{hi}, 'entry thawed');
113
114     $entry->remove();
115 }
116
117 # Test size tracking of cache
118 sub test_cache_size {
119     my ($cache) = @_;
120
121     $cache->clear();
122     _is($cache->size(), 0, 'cache is empty after clear');
123     $cache->set('testkey', 'A'x4000);
124     _is($cache->size(), 4000, 'cache size is correct after set');
125     $cache->set('testkey2', 'B'x200);
126     _is($cache->size(), 4200, 'cache size is correct after 2 sets');
127     $cache->set('testkey', 'C'x2800);
128     _is($cache->size(), 3000, 'cache size is correct after replace');
129     $cache->remove('testkey2');
130     _is($cache->size(), 2800, 'cache size is correct after remove');
131
132     $cache->clear();
133     _is($cache->size(), 0, 'cache is empty after clear');
134
135     # Add 100 entries of various lengths
136     my $size = 0;
137     my @keys = (1..100);
138     foreach (@keys) {
139         $cache->set("key$_", "D"x$_);
140         $size += $_;
141     }
142     _is($cache->size(), $size, 'cache size is ok after multiple sets');
143
144     shuffle(\@keys);
145     foreach (@keys) {
146         $cache->remove("key$_");
147     }
148     _is($cache->size(), 0, 'cache is empty after multiple removes');
149 }
150
151 # Test count tracking of cache
152 sub test_cache_count {
153     my ($cache) = @_;
154
155     $cache->clear();
156     _is($cache->count(), 0, 'cache is empty after clear');
157     $cache->set('testkey', 'test');
158     _is($cache->count(), 1, 'cache count correct after set');
159     $cache->set('testkey2', 'test2');
160     _is($cache->count(), 2, 'cache count correct after 2 sets');
161     $cache->set('testkey', 'test3');
162     _is($cache->count(), 2, 'cache count correct after replace');
163     $cache->remove('testkey2');
164     _is($cache->count(), 1, 'cache count correct after remove');
165
166     $cache->clear();
167     _is($cache->count(), 0, 'cache is empty after clear');
168
169     # Add 100 entries
170     my @keys = (1..100);
171     foreach (@keys) {
172         $cache->set("key$_", "test");
173     }
174     _is($cache->count(), 100, 'cache count correct after multiple sets');
175    
176     shuffle(\@keys);
177     foreach(@keys) {
178         $cache->remove("key$_");
179     }
180     _is($cache->size(), 0, 'cache empty after multiple removes');
181 }
182
183 # Test expiry
184 sub test_expiry {
185     my ($cache) = @_;
186
187     my $entry = $cache->entry('testexp');
188
189     $entry->set('test data');
190     $entry->set_expiry('100 minutes');
191     _cmp_ok($entry->expiry(), '>', time(), 'expiry set correctly');
192     _cmp_ok($entry->expiry(), '<=', time() + 100*60, 'expiry set correctly');
193     $entry->remove();
194
195     my $size = $cache->size();
196
197     $entry->set('test data', 'now');
198     _ok(!$entry->exists(), 'entry set with instant expiry not added');
199     _is($cache->size(), $size, 'size is unchanged');
200
201     $entry->set('test data', '1 sec');
202     _ok($entry->exists(), 'entry with 1 sec timeout added');
203     sleep(2);
204     _ok(!$entry->exists(), 'entry expired');
205     _is($cache->size(), $size, 'size is unchanged');
206
207     $entry->set('test data', '1 minute');
208     _ok($entry->exists(), 'entry with 1 min timeout added');
209     sleep(2);
210     _ok($entry->exists(), 'entry with 1 min timeout remains');
211     $entry->set_expiry('now');
212     _ok(!$entry->exists(), 'entry expired after change to instant timeout');
213     _is($cache->size(), $size, 'size is unchanged');
214 }
215
216 # Test reading via a handle
217 sub test_read_handle {
218     my ($cache) = @_;
219
220     my $entry = $cache->entry('readhandle');
221     $entry->remove();
222     my $handle = $entry->handle('<');
223     _ok(!$handle, 'read handle not available for empty entry');
224
225     $entry->set('some test data');
226
227     $handle = $entry->handle('<');
228     _ok($handle, 'read handle created');
229     $handle or diag("handle not created: $!");
230
231     local $/;
232     _is(<$handle>, 'some test data', 'read via <$handle> successful');
233
234     {
235         no warnings;
236         print $handle 'this wont work';
237     }
238     $handle->close();
239     _is($entry->get(), 'some test data', 'write to read only handle failed');
240
241     $entry->remove();
242 }
243
244 # Test writing via a handle
245 sub test_write_handle {
246     my ($cache) = @_;
247
248     my $entry = $cache->entry('writehandle');
249     $entry->remove();
250
251     my $size = $cache->size();
252
253     my $handle = $entry->handle('>');
254     _ok($handle, 'write handle created');
255     $handle or diag("handle not created: $!");
256
257     print $handle 'A'x100;
258     $handle->close();
259
260     _is($entry->get(), 'A'x100, 'write to write only handle ok');
261     _is($entry->size(), 100, 'entry size is correct');
262     _is($cache->size(), $size + 100, 'cache size is correct');
263
264     $entry->remove();
265 }
266
267 # Test append via a handle
268 sub test_append_handle {
269     my ($cache) = @_;
270
271     my $entry = $cache->entry('appendhandle');
272     $entry->remove();
273     $entry->set('hello ');
274
275     my $size = $cache->size();
276
277     my $handle = $entry->handle('>>');
278     _ok($handle, 'append handle created');
279     $handle or diag("handle not created: $!");
280
281     $handle->print('world');
282     $handle->close();
283
284     _is($entry->get(), 'hello world', 'write to append handle ok');
285     _is($entry->size(), 11, 'entry size is correct');
286     _is($entry->size(), $size + 5, 'cache size is correct');
287
288     $entry->remove();
289 }
290
291 # Test that a entry can be read while a handle is open for read
292 sub test_handle_async_read {
293     my ($cache) = @_;
294
295     my $entry = $cache->entry('readhandle');
296     $entry->remove();
297
298     my $size = $cache->size();
299
300     my $data = 'test data';
301     $entry->set($data);
302
303     my $handle = $entry->handle('<') or diag("handle not created: $!");
304
305     _ok($entry->exists(), 'entry exists after handle opened');
306     _is(<$handle>, $data, 'handle returns correct data');
307     _is($entry->get(), $data, '$entry->get() returns correct data');
308     $handle->close();
309     _ok($entry->exists(), 'entry exists after handle closed');
310     _is($entry->get(), $data, '$entry->get() returns correct data');
311 }
312
313 # Test that a handle can be removed asynchronously with it being open
314 sub test_handle_async_remove {
315     my ($cache) = @_;
316
317     my $entry = $cache->entry('removehandle');
318     $entry->remove();
319
320     my $size = $cache->size();
321
322     $entry->set('test data');
323
324     my $handle = $entry->handle() or diag("handle not created: $!");
325
326     # extend data by 5 bytes before removing the entry
327     $handle->print('some more data');
328     $handle->seek(0,0);
329
330     $entry->remove();
331     _ok(!$entry->exists(), 'entry removed whilst handle active');
332
333     local $/;
334     _is(<$handle>, 'some more data', 'read via <$handle> successful');
335
336     # ensure we can still write to the handle
337     $handle->seek(0,0);
338     $handle->print('hello wide wide world');
339     $handle->seek(0,0);
340     _is(<$handle>, 'hello wide wide world', 'write via <$handle> successful');
341
342     $handle->close();
343     _ok(!$entry->exists(), 'entry still removed after handle closed');
344     _is($entry->size(), undef, 'entry size is undefined');
345     _is($cache->size(), $size, 'cache size is correct');
346 }
347
348 sub test_handle_async_replace {
349     my ($cache) = @_;
350
351     my $entry = $cache->entry('replacehandle');
352     $entry->remove();
353
354     my $size = $cache->size();
355
356     $entry->set('test data');
357
358     my $handle = $entry->handle();
359
360     $entry->set('A'x20);
361     _is($entry->get(), 'A'x20, 'entry replaced whilst handle active');
362
363     local $/;
364     _is(<$handle>, 'test data', 'read via <$handle> successful');
365     $handle->seek(0,0);
366     $handle->print('hello world');
367     $handle->seek(0,0);
368     _is(<$handle>, 'hello world', 'write via <$handle> successful');
369
370     $handle->close();
371     _ok($entry->exists(), 'entry still exists after handle closed');
372     _is($entry->get(), 'A'x20, 'entry still correct after handle closed');
373     _is($entry->size(), 20, 'entry size is correct');
374     _is($cache->size(), $size+20, 'cache size is correct');
375 }
376
377 sub test_validity {
378     my ($cache) = @_;
379
380     my $entry = $cache->entry('validityentry');
381     $entry->remove();
382
383     # create an entry with validity
384     $entry->set('test data');
385     $entry->set_validity({ tester => 'test string' });
386
387     undef $entry;
388     $entry = $cache->entry('validityentry');
389     my $validity = $entry->validity();
390     _ok($validity, 'validity retrieved');
391     _is($validity->{tester}, 'test string', 'validity correct');
392
393     $entry->remove();
394
395     # create an entry with only validity
396     $entry->set_validity({ tester => 'test string' });
397
398     undef $entry;
399     $entry = $cache->entry('validityentry');
400     $validity = $entry->validity();
401     _ok($validity, 'validity retrieved');
402     _is($validity->{tester}, 'test string', 'validity correct');
403
404     $entry->remove();
405
406     # create an entry with scalar validity
407     $entry->set('test data');
408     $entry->set_validity('test string');
409
410     undef $entry;
411     $entry = $cache->entry('validityentry');
412     $validity = $entry->validity();
413     _ok($validity, 'validity retrieved');
414     _is($validity, 'test string', 'validity correct');
415 }
416
417 sub test_load_callback {
418     my ($cache) = @_;
419
420     my $key = 'testloadcallback';
421     $cache->remove($key);
422
423     my $old_callback = $cache->load_callback();
424     $cache->set_load_callback(sub { return "result ".$_[0]->key() });
425
426     _ok($cache->get($key), "result $key");
427     $cache->set_load_callback($old_callback);
428 }
429
430 sub test_validate_callback {
431     my ($cache) = @_;
432
433     my $key = 'testvalidatecallback';
434     my $result;
435     my $old_callback = $cache->validate_callback();
436     $cache->set_validate_callback(sub { $result = "result ".$_[0]->key() });
437
438     $cache->set($key, 'somedata');
439     $cache->get($key);
440     _is($result, "result $key", "validate_callback ok");
441     $cache->set_validate_callback($old_callback);
442 }
443
444
445 ### Wrappers for test methods to add function name
446
447 sub _ok ($$) {
448     my($test, $name) = @_;
449     ok($test, (caller(1))[3].': '.$name);
450 }
451
452 sub _is ($$$) {
453     my($x, $y, $name) = @_;
454     is($x, $y, (caller(1))[3].': '.$name);
455 }
456
457 sub _isnt ($$$) {
458     my($x, $y, $name) = @_;
459     isnt($x, $y, (caller(1))[3].': '.$name);
460 }
461
462 sub _like ($$$) {
463     my($x, $y, $name) = @_;
464     like($x, $y, (caller(1))[3].': '.$name);
465 }
466
467 sub _unlike ($$$) {
468     my($x, $y, $name) = @_;
469     unlike($x, $y, (caller(1))[3].': '.$name);
470 }
471
472 sub _cmp_ok ($$$$) {
473     my ($x, $c, $y, $name) = @_;
474     cmp_ok($x, $c, $y, (caller(1))[3].': '.$name);
475 }
476
477
478 # Taken from perlfaq4
479 sub shuffle {
480     my $deck = shift;  # $deck is a reference to an array
481     my $i = @$deck;
482     while ($i--) {
483         my $j = int rand ($i+1);
484         @$deck[$i,$j] = @$deck[$j,$i];
485     }
486 }
487
488
489 1;
490 __END__
491
492 =head1 SEE ALSO
493
494 Cache
495
496 =head1 AUTHOR
497
498  Chris Leishman <chris@leishman.org>
499  Based on work by DeWitt Clinton <dewitt@unto.net>
500
501 =head1 COPYRIGHT
502
503  Copyright (C) 2003-2006 Chris Leishman.  All Rights Reserved.
504
505 This module is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
506 either expressed or implied. This program is free software; you can
507 redistribute or modify it under the same terms as Perl itself.
508
509 $Id: Tester.pm,v 1.8 2006/01/31 15:23:58 caleishm Exp $
510
511 =cut
512
Note: See TracBrowser for help on using the browser.