File Coverage

blib/lib/Test/Starch.pm
Criterion Covered Total %
statement 265 265 100.0
branch n/a
condition n/a
subroutine 17 17 100.0
pod 5 5 100.0
total 287 287 100.0


line stmt bran cond sub pod time code
1             package Test::Starch;
2 10     10   2135877 use 5.008001;
  10         84  
3 10     10   54 use strictures 2;
  10         78  
  10         395  
4             our $VERSION = '0.11';
5              
6             =head1 NAME
7              
8             Test::Starch - Test core features of starch.
9              
10             =head1 SYNOPSIS
11              
12             use Test::More;
13            
14             my $tester = Test::Starch->new(
15             plugins => [ ... ],
16             store => ...,
17             ...,
18             );
19             $tester->test();
20            
21             done_testing;
22              
23             =head1 DESCRIPTION
24              
25             This class runs the core L test suite by testing public
26             interfaces of L, L, and
27             L. These are the same tests that Starch runs
28             when you install it from CPAN.
29              
30             This module is used by stores and plugins to ensure that they have
31             not broken any of the core features of Starch. All store and plugin
32             authors are highly encouraged to run these tests as part of their
33             test suite.
34              
35             Along the same lines, it is recommended that if you use Starch that
36             you make a test in your in-house test-suite which runs these tests
37             against your configuration.
38              
39             This class takes all the same arguments as L and saves them
40             to be used when L is called by the tests. Unlike L,
41             if the C argument is not passed it will defailt to a Memory store.
42              
43             =cut
44              
45 10     10   7860 use Types::Standard -types;
  10         727224  
  10         115  
46 10     10   50989 use Types::Common::String -types;
  10         222239  
  10         134  
47              
48 10     10   19728 use Starch;
  10         33  
  10         349  
49 10     10   69 use Starch::Manager;
  10         17  
  10         206  
50 10     10   50 use Test2::V0;
  10         20  
  10         89  
51              
52 10     10   12006 use Moo;
  10         23  
  10         53  
53 10     10   6305 use namespace::clean;
  10         24  
  10         44  
54              
55             around BUILDARGS => sub{
56             my $orig = shift;
57             my $class = shift;
58              
59             my $args = $class->$orig( @_ );
60              
61             return {
62             args => {
63             store => { class=>'::Memory' },
64             %$args,
65             },
66             };
67             };
68              
69             has args => (
70             is => 'ro',
71             isa => HashRef,
72             required => 1,
73             );
74              
75             =head1 METHODS
76              
77             =head2 new_manager
78              
79             Creates a new L object and returns it. Any arguments
80             you specify to this method will override those specified when creating
81             the L object.
82              
83             =cut
84              
85             sub new_manager {
86 53     53 1 136 my $self = shift;
87              
88 53         1632 my $extra_args = Starch::Manager->BUILDARGS( @_ );
89              
90             return Starch->new(
91 53         368 %{ $self->args() },
92 53         11782 %{ $extra_args },
  53         376  
93             );
94             }
95              
96             =head2 test
97              
98             Calls L, L, and L.
99              
100             =cut
101              
102             sub test {
103 6     6 1 244 my ($self) = @_;
104 6         29 $self->test_manager();
105 6         38 $self->test_state();
106 6         40 $self->test_store();
107 6         23 return;
108             }
109              
110             =head2 test_manager
111              
112             Tests L.
113              
114             =cut
115              
116             sub test_manager {
117 7     7 1 64 my ($self) = @_;
118              
119 7         27 my $starch = $self->new_manager();
120              
121             subtest 'core tests for ' . ref($starch) => sub{
122             subtest state_id_seed => sub{
123 7         2209 isnt( $starch->state_id_seed(), $starch->state_id_seed(), 'two seeds are not the same' );
124 7     7   30796 };
125              
126             subtest generate_state_id => sub{
127 7         2147 isnt(
128             $starch->generate_state_id(),
129             $starch->generate_state_id(),
130             'two generated ids are not the same',
131             );
132 7         12800 };
133              
134             subtest clone_data => sub{
135 7         1965 my $old_data = { foo=>32, bar=>[1,2,3] };
136 7         59 my $new_data = $starch->clone_data( $old_data );
137              
138 7         57 is( $new_data, $old_data, 'cloned data matches source data' );
139              
140 7         9086 isnt( "$old_data->{bar}", "$new_data->{bar}", 'clone data structure has different reference' );
141 7         8688 };
142              
143             subtest is_data_diff => sub{
144 7         1940 my $old_data = { foo=>1 };
145 7         23 my $new_data = { foo=>2 };
146              
147 7         67 ok( $starch->is_data_diff($old_data, $new_data), 'is diff' );
148 7         1803 ok( (!$starch->is_data_diff($old_data, $old_data)), 'is not diff' );
149 7         9040 };
150 7         129 };
151              
152 7         27167 return;
153             }
154              
155             =head2 test_state
156              
157             Test L.
158              
159             =cut
160              
161             sub test_state {
162 7     7 1 58 my ($self) = @_;
163              
164 7         30 my $starch = $self->new_manager();
165              
166             subtest 'core tests for ' . ref($starch->state()) => sub{
167             subtest id => sub{
168 7         1959 my $state1 = $starch->state();
169 7         51 my $state2 = $starch->state();
170 7         55 my $id = $starch->generate_state_id();
171 7         50 my $state3 = $starch->state( $id );
172              
173 7         34 ok( $starch->state_id_type->check($id), 'ID looks good' );
174 7         2375 isnt( $state1->id(), $state2->id(), 'two generated state IDs are not the same' );
175 7         3435 is( $state3->id(), $id, 'custom ID was used' );
176 7     7   7963 };
177              
178             subtest expires => sub{
179 7         2001 my $state = $starch->state();
180 7         154 is( $state->expires(), $starch->expires(), 'state expires inherited the global expires' );
181 7         10195 };
182              
183             subtest modified => sub{
184 7         2032 my $state = $starch->state();
185 7         194 is( $state->modified(), $state->created(), 'modfied is same as created in new state' );
186 7         14004178 sleep 2;
187 7         235 $state->mark_dirty();
188 7         68 $state->save();
189 7         128 $state = $starch->state( $state->id() );
190 7         150 cmp_ok( $state->modified(), '>', $state->created(), 'modified was updated with save' );
191 7         8575 };
192              
193             subtest created => sub{
194 7         2412 my $start_time = time();
195 7         58 my $state = $starch->state();
196 7         135 my $created_time = $state->created();
197 7         41 cmp_ok( $created_time, '>=', $start_time, 'state created on or after test start' );
198 7         1277 cmp_ok( $created_time, '<=', $start_time+1, 'state created is on or just after test start' );
199 7         14002409 sleep 2;
200 7         272 $state->mark_dirty();
201 7         64 $state->save();
202 7         129 $state = $starch->state( $state->id() );
203 7         145 is( $state->created(), $created_time, 'created was updated with save' );
204 7         9396 };
205              
206             subtest in_store => sub{
207 7         2423 my $state1 = $starch->state();
208 7         142 my $state2 = $starch->state( $state1->id() );
209              
210 7         141 is( $state1->in_store(), 0, 'new state is not in_store' );
211 7         2893 is( $state2->in_store(), 1, 'existing state is in_store' );
212              
213 7         2708 my $id = $starch->generate_state_id();
214 7         53 my $state3 = $starch->state( $id );
215 7         140 is( $state3->in_store(), 1, 'existing state is in_store' );
216 7         3195 $state3->data();
217 7         177 is( $state3->in_store(), 0, 'state is no longer in_store when data was not found' );
218 7         12499 };
219              
220             subtest is_deleted => sub{
221 7         2118 my $state = $starch->state();
222 7         55 is( $state->is_deleted(), 0, 'new state is not deleted' );
223 7         2860 $state->mark_dirty();
224 7         50 $state->save();
225 7         77 $state->delete();
226 7         42 is( $state->is_deleted(), 1, 'deleted state is deleted' );
227 7         10515 };
228              
229             subtest is_dirty => sub{
230 7         2199 my $state = $starch->state();
231 7         33 is( $state->is_dirty(), 0, 'new state is not is_dirty' );
232 7         3084 $state->data->{foo} = 543;
233 7         44 is( $state->is_dirty(), 1, 'modified state is_dirty' );
234 7         10131 };
235              
236             subtest is_loaded => sub{
237 7         2027 my $state = $starch->state();
238 7         71 ok( (!$state->is_loaded()), 'state is not loaded' );
239 7         1983 $state->data();
240 7         62 ok( $state->is_loaded(), 'state is loaded' );
241 7         9283 };
242              
243             subtest is_saved => sub{
244 7         2033 my $state = $starch->state();
245 7         55 ok( (!$state->is_saved()), 'state is not saved' );
246 7         1846 $state->mark_dirty();
247 7         52 $state->save();
248 7         30 ok( $state->is_saved(), 'state is saved' );
249 7         7996 };
250              
251             subtest save => sub{
252 7         2389 my $state1 = $starch->state();
253              
254 7         138 $state1->data->{foo} = 789;
255 7         188 my $state2 = $starch->state( $state1->id() );
256 7         149 is( $state2->data->{foo}, undef, 'new state did not receive data from old' );
257              
258 7         3191 is( $state1->is_dirty(), 1, 'is dirty before save' );
259 7         2813 $state1->save();
260 7         31 is( $state1->is_dirty(), 0, 'is not dirty after save' );
261 7         2902 $state2 = $starch->state( $state1->id() );
262 7         137 is( $state2->data->{foo}, 789, 'new state did receive data from old' );
263              
264 7         2866 my $state = $starch->state();
265 7         135 $state->data->{foo} = 931;
266 7         60 $state->save();
267              
268 7         125 $state = $starch->state( $state->id() );
269 7         144 $state->data();
270              
271 7         149 $starch->state( $state->id() )->delete();
272              
273 7         54 $state->save();
274             is(
275             $starch->state( $state->id() )->data->{foo},
276             undef,
277 7         183 'save did not save',
278             );
279              
280 7         3003 $state->mark_dirty();
281 7         43 $state->save();
282             is(
283             $starch->state( $state->id() )->data->{foo},
284 7         126 931,
285             'save did save',
286             );
287 7         8363 };
288              
289             subtest reload => sub{
290 7         2193 my $state = $starch->state();
291 7         71 is( dies { $state->reload() }, undef, 'reloading a non-dirty state did not fail' );
  7         134  
292              
293 7         2565 my $state1 = $starch->state();
294 7         134 $state1->data->{foo} = 91;
295 7         72 $state1->save();
296 7         123 my $state2 = $starch->state( $state1->id() );
297 7         152 $state2->data->{foo} = 19;
298 7         55 $state2->save();
299 7         50 $state1->reload();
300 7         115 is( $state1->data->{foo}, 19, 'reload worked' );
301 7         12025 };
302              
303             subtest mark_clean => sub{
304 7         2007 my $state = $starch->state();
305 7         138 $state->data->{foo} = 6934;
306 7         42 is( $state->is_dirty(), 1, 'is dirty' );
307 7         2782 $state->mark_clean();
308 7         30 is( $state->is_dirty(), 0, 'is clean' );
309 7         2912 is( $state->data->{foo}, 6934, 'data is intact' );
310 7         9488 };
311              
312             subtest mark_dirty => sub{
313 7         1972 my $state = $starch->state();
314 7         33 is( $state->is_dirty(), 0, 'is not dirty' );
315 7         2708 $state->mark_dirty();
316 7         30 is( $state->is_dirty(), 1, 'is dirty' );
317 7         9645 };
318              
319             subtest rollback => sub{
320 7         2094 my $state = $starch->state();
321 7         138 $state->data->{foo} = 6934;
322 7         44 is( $state->is_dirty(), 1, 'is dirty' );
323 7         2789 $state->rollback();
324 7         28 is( $state->is_dirty(), 0, 'is clean' );
325 7         2821 is( $state->data->{foo}, undef, 'data is rolled back' );
326              
327 7         2567 $state->data->{foo} = 23;
328 7         91 $state->mark_clean();
329 7         123 $state->data->{foo} = 95;
330 7         120 $state->rollback();
331 7         129 is( $state->data->{foo}, 23, 'rollback to previous mark_clean' );
332 7         9148 };
333              
334             subtest delete => sub{
335 7         2020 my $state = $starch->state();
336 7         139 $state->data->{foo} = 39;
337 7         70 $state->save();
338              
339 7         127 $state = $starch->state( $state->id() );
340 7         137 is( $state->data->{foo}, 39, 'state persists' );
341              
342 7         2887 $state->delete();
343 7         126 $state = $starch->state( $state->id() );
344 7         137 is( $state->data->{foo}, undef, 'state was deleted' );
345 7         10321 };
346              
347             subtest set_expires => sub{
348 7         1996 my $state = $starch->state();
349 7         140 is( $state->expires(), $starch->expires(), 'double check a new state gets the global expires' );
350 7         2829 $state->set_expires( 111 );
351 7         48 $state->save();
352 7         126 $state = $starch->state( $state->id() );
353 7         139 is( $state->expires(), 111, 'custom expires was saved' );
354 7         8924 };
355              
356             subtest reset_expires => sub{
357 7         2058 my $starch = $self->new_manager( expires=>111 );
358 7         63 my $state = $starch->state();
359 7         134 is( $state->expires(), 111, 'state got default expires' );
360 7         2985 $state->set_expires( 666 );
361 7         52 $state->save();
362 7         128 $state = $starch->state( $state->id() );
363 7         144 is( $state->expires(), 666, 'expires persisted' );
364 7         2914 $state->reset_expires();
365 7         46 $state->save();
366 7         140 $state = $starch->state( $state->id() );
367 7         136 is( $state->expires(), 111, 'state expires was reset' );
368 7         8977 };
369              
370             subtest reset_id => sub{
371 7         2168 my $state = $starch->state();
372              
373 7         138 $state->data->{foo} = 54;
374 7         42 ok( $state->is_dirty(), 'state is dirty before save' );
375 7         1873 $state->save();
376 7         32 ok( (!$state->is_dirty()), 'state is not dirty after save' );
377 7         1800 ok( $state->is_saved(), 'state is marked saved after save' );
378              
379 7         1982 my $old_id = $state->id();
380 7         132 $state->reset_id();
381 7         26 ok( (!$state->is_saved()), 'state is not marked saved after reset_id' );
382 7         1831 ok( $state->is_dirty(), 'state is marked dirty after reset_id' );
383 7         1957 isnt( $state->id(), $old_id, 'state has new id after reset_id' );
384 7         3087 $state->save();
385              
386 7         46 my $old_state = $starch->state( $old_id );
387 7         134 is( $old_state->data->{foo}, undef, 'old state data was deleted' );
388 7         10116 };
389 7         81 };
390              
391 7         98607 return;
392             }
393              
394             =head2 test_store
395              
396             Tests the L.
397              
398             =cut
399              
400             sub test_store {
401 8     8 1 117 my ($self) = @_;
402              
403 8         44 my $starch = $self->new_manager();
404 8         182 my $store = $starch->store();
405              
406             subtest 'core tests for ' . ref($store) => sub{
407              
408             subtest 'set, get, and remove' => sub{
409 8         2189 my $key = 'starch-test-key';
410 8         135 $store->remove( $key, [] );
411              
412 8         128 is( $store->get( $key, [] ), undef, 'no data before set' );
413              
414 8         3892 $store->set( $key, [], {foo=>6}, 10 );
415 8         118 is( $store->get( $key, [] )->{foo}, 6, 'has data after set' );
416              
417 8         3572 $store->remove( $key, [] );
418              
419 8         115 is( $store->get( $key, [] ), undef, 'no data after remove' );
420 8     8   14042 };
421              
422             subtest max_expires => sub{
423 8         2410 my $starch = $self->new_manager(
424             expires => 89,
425             );
426 8         167 is( $starch->store->max_expires(), undef, 'store max_expires left at undef' );
427              
428 8         3240 $starch = $self->new_manager(
429             store=>{ class=>'::Memory', max_expires=>67 },
430             expires => 89,
431             );
432 8         190 is( $starch->store->max_expires(), 67, 'store max_expires explicitly set' );
433 8         11598 };
434              
435             subtest class_name => sub{
436             # Add a random plugin so the real class name has the __WITH__ bit.
437 8         2381 my $starch = $self->new_manager( store=>{class=>'::Memory'}, plugins=>['::LogStoreExceptions'] );
438 8         159 is( $starch->store->base_class_name(), 'Starch::Store::Memory', 'base_class_name' );
439 8         4194 is( $starch->store->short_class_name(), 'Store::Memory', 'short_class_name' );
440 8         3233 is( $starch->store->short_store_class_name(), 'Memory', 'short_store_class_name' );
441 8         10917 };
442              
443             subtest new_sub_store => sub{
444 8         2606 my $sub_store1 = $store->new_sub_store( class=>'::Memory', max_expires=>12 );
445 8         73 isa_ok( $sub_store1, 'Starch::Store::Memory' );
446 8         1567 is( ''.$sub_store1->manager(), ''.$store->manager(), 'sub store has same manager as parent store' );
447 8         3053 my $sub_store2 = $sub_store1->new_sub_store( class=>'::Memory' );
448 8         63 is( $sub_store2->max_expires(), 12, 'sub store has max_expires from parent store' );
449 8         11651 };
450              
451             subtest calculate_expires => sub{
452 8         2220 my $store = $store->new_sub_store( class=>'::Memory', max_expires => 10 );
453 8         57 is( $store->calculate_expires( 5 ), 5, 'expires less than max_expires' );
454 8         3112 is( $store->calculate_expires( 15 ), 10, 'expires more than max_expires' );
455 8         11215 };
456              
457             subtest stringify_key => sub{
458 8         2286 is( $store->stringify_key( '1234', ['foo'] ), 'foo:1234', 'basic' );
459 8         3026 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo:bar:1234', 'deep' );
460 8         3052 is( $store->stringify_key( '1234', [] ), '1234', 'empty' );
461              
462 8         3086 my $store = $store->new_sub_store( class=>'::Memory', key_separator=>'-' );
463 8         57 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo-bar-1234', 'custom key_separator' );
464 8         10259 };
465              
466             subtest reap_expired => sub{
467 8         2308 my $store = $store->new_sub_store( class=>'::Memory' );
468 8         80 ok( (!$store->can_reap_expired()), 'expiration reaping is disabled' );
469             like(
470 8         141 dies { $store->reap_expired() },
471 8         2218 qr{does not support expired state reaping},
472             'reap_expired failed',
473             );
474 8         11804 };
475 8         202 };
476              
477 8         55388 return;
478             }
479              
480             1;
481             __END__