File Coverage

blib/lib/Test/Starch.pm
Criterion Covered Total %
statement 261 261 100.0
branch 1 2 50.0
condition n/a
subroutine 15 15 100.0
pod 5 5 100.0
total 282 283 99.6


line stmt bran cond sub pod time code
1             package Test::Starch;
2             our $VERSION = '0.14';
3              
4             =head1 NAME
5              
6             Test::Starch - Test core features of starch.
7              
8             =head1 SYNOPSIS
9              
10             use Test2::V0;
11             use Test::Starch;
12            
13             my $tester = Test::Starch->new(
14             plugins => [ ... ],
15             store => ...,
16             ...,
17             );
18             $tester->test();
19            
20             done_testing;
21              
22             =head1 DESCRIPTION
23              
24             This class runs the core L test suite by testing public
25             interfaces of L, L, and
26             L. These are the same tests that Starch runs
27             when you install it from CPAN.
28              
29             This module is used by stores and plugins to ensure that they have
30             not broken any of the core features of Starch. All store and plugin
31             authors are highly encouraged to run these tests as part of their
32             test suite.
33              
34             Along the same lines, it is recommended that if you use Starch that
35             you make a test in your in-house test-suite which runs these tests
36             against your configuration.
37              
38             This class takes all the same arguments as L and saves them
39             to be used when L is called by the tests. Unlike L,
40             if the C argument is not passed it will defailt to a Memory store.
41              
42             =cut
43              
44 10     10   2073351 use Starch;
  10         36  
  10         359  
45 10     10   62 use Types::Common::String -types;
  10         17  
  10         91  
46 10     10   14580 use Types::Standard -types;
  10         22  
  10         60  
47              
48             # Avoid CPANTS Kwalitee check for Test2::V0.
49             BEGIN {
50 10     10   41736 local $@;
51 10         512 my $ok = eval 'requ' . 'ire Test2::V0; 1';
52 10 50       54 die $@ if !$ok;
53 10         60 Test2::V0->import();
54             };
55              
56 10     10   10501 use Moo;
  10         19  
  10         58  
57 10     10   5588 use strictures 2;
  10         52  
  10         356  
58 10     10   1678 use namespace::clean;
  10         19  
  10         47  
59              
60             around BUILDARGS => sub{
61             my $orig = shift;
62             my $class = shift;
63              
64             my $args = $class->$orig( @_ );
65              
66             return {
67             _manager_args => {
68             store => { class=>'::Memory' },
69             %$args,
70             },
71             };
72             };
73              
74             has _manager_args => (
75             is => 'ro',
76             isa => HashRef,
77             required => 1,
78             );
79              
80             =head1 METHODS
81              
82             =head2 new_manager
83              
84             Creates a new L object and returns it. Any arguments
85             you specify to this method will override those specified when creating
86             the L object.
87              
88             =cut
89              
90             sub new_manager {
91 53     53 1 123 my $self = shift;
92              
93 53         1566 my $extra_args = Starch::Manager->BUILDARGS( @_ );
94              
95             return Starch->new(
96 53         301 %{ $self->_manager_args() },
97 53         11850 %{ $extra_args },
  53         354  
98             );
99             }
100              
101             =head2 test
102              
103             Calls L, L, and L.
104              
105             =cut
106              
107             sub test {
108 6     6 1 227 my ($self) = @_;
109 6         24 $self->test_manager();
110 6         35 $self->test_state();
111 6         38 $self->test_store();
112 6         23 return;
113             }
114              
115             =head2 test_manager
116              
117             Tests L.
118              
119             =cut
120              
121             sub test_manager {
122 7     7 1 48 my ($self) = @_;
123              
124 7         23 my $starch = $self->new_manager();
125              
126             subtest 'core tests for ' . ref($starch) => sub{
127             subtest state_id_seed => sub{
128 7         1906 isnt( $starch->state_id_seed(), $starch->state_id_seed(), 'two seeds are not the same' );
129 7     7   27255 };
130              
131             subtest generate_state_id => sub{
132 7         1794 isnt(
133             $starch->generate_state_id(),
134             $starch->generate_state_id(),
135             'two generated ids are not the same',
136             );
137 7         11455 };
138              
139             subtest clone_data => sub{
140 7         1740 my $old_data = { foo=>32, bar=>[1,2,3] };
141 7         62 my $new_data = $starch->clone_data( $old_data );
142              
143 7         49 is( $new_data, $old_data, 'cloned data matches source data' );
144              
145 7         8066 isnt( "$old_data->{bar}", "$new_data->{bar}", 'clone data structure has different reference' );
146 7         7914 };
147              
148             subtest is_data_diff => sub{
149 7         1763 my $old_data = { foo=>1 };
150 7         19 my $new_data = { foo=>2 };
151              
152 7         51 ok( $starch->is_data_diff($old_data, $new_data), 'is diff' );
153 7         1616 ok( (!$starch->is_data_diff($old_data, $old_data)), 'is not diff' );
154 7         8011 };
155 7         152 };
156              
157 7         24392 return;
158             }
159              
160             =head2 test_state
161              
162             Test L.
163              
164             =cut
165              
166             sub test_state {
167 7     7 1 159 my ($self) = @_;
168              
169 7         31 my $starch = $self->new_manager();
170              
171             subtest 'core tests for ' . ref($starch->state()) => sub{
172             subtest id => sub{
173 7         1835 my $state1 = $starch->state();
174 7         40 my $state2 = $starch->state();
175 7         46 my $id = $starch->generate_state_id();
176 7         64 my $state3 = $starch->state( $id );
177              
178 7         28 ok( $starch->state_id_type->check($id), 'ID looks good' );
179 7         2330 isnt( $state1->id(), $state2->id(), 'two generated state IDs are not the same' );
180 7         3206 is( $state3->id(), $id, 'custom ID was used' );
181 7     7   8395 };
182              
183             subtest expires => sub{
184 7         1837 my $state = $starch->state();
185 7         162 is( $state->expires(), $starch->expires(), 'state expires inherited the global expires' );
186 7         9691 };
187              
188             subtest modified => sub{
189 7         1838 my $state = $starch->state();
190 7         157 is( $state->modified(), $state->created(), 'modfied is same as created in new state' );
191 7         14003999 sleep 2;
192 7         373 $state->mark_dirty();
193 7         70 $state->save();
194 7         121 $state = $starch->state( $state->id() );
195 7         155 cmp_ok( $state->modified(), '>', $state->created(), 'modified was updated with save' );
196 7         7818 };
197              
198             subtest created => sub{
199 7         2413 my $start_time = time();
200 7         55 my $state = $starch->state();
201 7         135 my $created_time = $state->created();
202 7         38 cmp_ok( $created_time, '>=', $start_time, 'state created on or after test start' );
203 7         1257 cmp_ok( $created_time, '<=', $start_time+1, 'state created is on or just after test start' );
204 7         14002584 sleep 2;
205 7         368 $state->mark_dirty();
206 7         63 $state->save();
207 7         131 $state = $starch->state( $state->id() );
208 7         161 is( $state->created(), $created_time, 'created was updated with save' );
209 7         9391 };
210              
211             subtest in_store => sub{
212 7         2568 my $state1 = $starch->state();
213 7         142 my $state2 = $starch->state( $state1->id() );
214              
215 7         161 is( $state1->in_store(), 0, 'new state is not in_store' );
216 7         3001 is( $state2->in_store(), 1, 'existing state is in_store' );
217              
218 7         2838 my $id = $starch->generate_state_id();
219 7         53 my $state3 = $starch->state( $id );
220 7         144 is( $state3->in_store(), 1, 'existing state is in_store' );
221 7         2963 $state3->data();
222 7         171 is( $state3->in_store(), 0, 'state is no longer in_store when data was not found' );
223 7         12688 };
224              
225             subtest is_deleted => sub{
226 7         2080 my $state = $starch->state();
227 7         56 is( $state->is_deleted(), 0, 'new state is not deleted' );
228 7         2815 $state->mark_dirty();
229 7         55 $state->save();
230 7         78 $state->delete();
231 7         38 is( $state->is_deleted(), 1, 'deleted state is deleted' );
232 7         10467 };
233              
234             subtest is_dirty => sub{
235 7         2130 my $state = $starch->state();
236 7         36 is( $state->is_dirty(), 0, 'new state is not is_dirty' );
237 7         2906 $state->data->{foo} = 543;
238 7         47 is( $state->is_dirty(), 1, 'modified state is_dirty' );
239 7         9421 };
240              
241             subtest is_loaded => sub{
242 7         2091 my $state = $starch->state();
243 7         72 ok( (!$state->is_loaded()), 'state is not loaded' );
244 7         1976 $state->data();
245 7         60 ok( $state->is_loaded(), 'state is loaded' );
246 7         9348 };
247              
248             subtest is_saved => sub{
249 7         2074 my $state = $starch->state();
250 7         49 ok( (!$state->is_saved()), 'state is not saved' );
251 7         1827 $state->mark_dirty();
252 7         55 $state->save();
253 7         30 ok( $state->is_saved(), 'state is saved' );
254 7         8293 };
255              
256             subtest save => sub{
257 7         2022 my $state1 = $starch->state();
258              
259 7         142 $state1->data->{foo} = 789;
260 7         173 my $state2 = $starch->state( $state1->id() );
261 7         144 is( $state2->data->{foo}, undef, 'new state did not receive data from old' );
262              
263 7         3221 is( $state1->is_dirty(), 1, 'is dirty before save' );
264 7         2929 $state1->save();
265 7         34 is( $state1->is_dirty(), 0, 'is not dirty after save' );
266 7         3042 $state2 = $starch->state( $state1->id() );
267 7         143 is( $state2->data->{foo}, 789, 'new state did receive data from old' );
268              
269 7         2928 my $state = $starch->state();
270 7         140 $state->data->{foo} = 931;
271 7         57 $state->save();
272              
273 7         127 $state = $starch->state( $state->id() );
274 7         146 $state->data();
275              
276 7         147 $starch->state( $state->id() )->delete();
277              
278 7         55 $state->save();
279             is(
280             $starch->state( $state->id() )->data->{foo},
281             undef,
282 7         153 'save did not save',
283             );
284              
285 7         2656 $state->mark_dirty();
286 7         45 $state->save();
287             is(
288             $starch->state( $state->id() )->data->{foo},
289 7         136 931,
290             'save did save',
291             );
292 7         8310 };
293              
294             subtest reload => sub{
295 7         2191 my $state = $starch->state();
296 7         74 is( dies { $state->reload() }, undef, 'reloading a non-dirty state did not fail' );
  7         134  
297              
298 7         2584 my $state1 = $starch->state();
299 7         142 $state1->data->{foo} = 91;
300 7         67 $state1->save();
301 7         127 my $state2 = $starch->state( $state1->id() );
302 7         147 $state2->data->{foo} = 19;
303 7         57 $state2->save();
304 7         82 $state1->reload();
305 7         120 is( $state1->data->{foo}, 19, 'reload worked' );
306 7         12034 };
307              
308             subtest mark_clean => sub{
309 7         2134 my $state = $starch->state();
310 7         140 $state->data->{foo} = 6934;
311 7         38 is( $state->is_dirty(), 1, 'is dirty' );
312 7         2937 $state->mark_clean();
313 7         28 is( $state->is_dirty(), 0, 'is clean' );
314 7         2941 is( $state->data->{foo}, 6934, 'data is intact' );
315 7         9494 };
316              
317             subtest mark_dirty => sub{
318 7         2075 my $state = $starch->state();
319 7         34 is( $state->is_dirty(), 0, 'is not dirty' );
320 7         2830 $state->mark_dirty();
321 7         31 is( $state->is_dirty(), 1, 'is dirty' );
322 7         9780 };
323              
324             subtest rollback => sub{
325 7         2041 my $state = $starch->state();
326 7         139 $state->data->{foo} = 6934;
327 7         46 is( $state->is_dirty(), 1, 'is dirty' );
328 7         2909 $state->rollback();
329 7         28 is( $state->is_dirty(), 0, 'is clean' );
330 7         2925 is( $state->data->{foo}, undef, 'data is rolled back' );
331              
332 7         2633 $state->data->{foo} = 23;
333 7         95 $state->mark_clean();
334 7         161 $state->data->{foo} = 95;
335 7         88 $state->rollback();
336 7         139 is( $state->data->{foo}, 23, 'rollback to previous mark_clean' );
337 7         9338 };
338              
339             subtest delete => sub{
340 7         2038 my $state = $starch->state();
341 7         138 $state->data->{foo} = 39;
342 7         69 $state->save();
343              
344 7         130 $state = $starch->state( $state->id() );
345 7         143 is( $state->data->{foo}, 39, 'state persists' );
346              
347 7         2918 $state->delete();
348 7         131 $state = $starch->state( $state->id() );
349 7         146 is( $state->data->{foo}, undef, 'state was deleted' );
350 7         10473 };
351              
352             subtest set_expires => sub{
353 7         2115 my $state = $starch->state();
354 7         148 is( $state->expires(), $starch->expires(), 'double check a new state gets the global expires' );
355 7         2877 $state->set_expires( 111 );
356 7         49 $state->save();
357 7         131 $state = $starch->state( $state->id() );
358 7         145 is( $state->expires(), 111, 'custom expires was saved' );
359 7         9095 };
360              
361             subtest reset_expires => sub{
362 7         2188 my $starch = $self->new_manager( expires=>111 );
363 7         60 my $state = $starch->state();
364 7         142 is( $state->expires(), 111, 'state got default expires' );
365 7         3094 $state->set_expires( 666 );
366 7         55 $state->save();
367 7         126 $state = $starch->state( $state->id() );
368 7         140 is( $state->expires(), 666, 'expires persisted' );
369 7         2973 $state->reset_expires();
370 7         44 $state->save();
371 7         128 $state = $starch->state( $state->id() );
372 7         145 is( $state->expires(), 111, 'state expires was reset' );
373 7         9570 };
374              
375             subtest reset_id => sub{
376 7         2166 my $state = $starch->state();
377              
378 7         137 $state->data->{foo} = 54;
379 7         37 ok( $state->is_dirty(), 'state is dirty before save' );
380 7         1902 $state->save();
381 7         32 ok( (!$state->is_dirty()), 'state is not dirty after save' );
382 7         2018 ok( $state->is_saved(), 'state is marked saved after save' );
383              
384 7         1984 my $old_id = $state->id();
385 7         149 $state->reset_id();
386 7         24 ok( (!$state->is_saved()), 'state is not marked saved after reset_id' );
387 7         1977 ok( $state->is_dirty(), 'state is marked dirty after reset_id' );
388 7         1986 isnt( $state->id(), $old_id, 'state has new id after reset_id' );
389 7         3100 $state->save();
390              
391 7         56 my $old_state = $starch->state( $old_id );
392 7         144 is( $old_state->data->{foo}, undef, 'old state data was deleted' );
393 7         10291 };
394 7         73 };
395              
396 7         100953 return;
397             }
398              
399             =head2 test_store
400              
401             Tests the L.
402              
403             =cut
404              
405             sub test_store {
406 8     8 1 119 my ($self) = @_;
407              
408 8         43 my $starch = $self->new_manager();
409 8         183 my $store = $starch->store();
410              
411             subtest 'core tests for ' . ref($store) => sub{
412              
413             subtest 'set, get, and remove' => sub{
414 8         2328 my $key = 'starch-test-key';
415 8         136 $store->remove( $key, [] );
416              
417 8         134 is( $store->get( $key, [] ), undef, 'no data before set' );
418              
419 8         3976 $store->set( $key, [], {foo=>6}, 10 );
420 8         119 is( $store->get( $key, [] )->{foo}, 6, 'has data after set' );
421              
422 8         3550 $store->remove( $key, [] );
423              
424 8         115 is( $store->get( $key, [] ), undef, 'no data after remove' );
425 8     8   14625 };
426              
427             subtest max_expires => sub{
428 8         2323 my $starch = $self->new_manager(
429             expires => 89,
430             );
431 8         219 is( $starch->store->max_expires(), undef, 'store max_expires left at undef' );
432              
433 8         3197 $starch = $self->new_manager(
434             store=>{ class=>'::Memory', max_expires=>67 },
435             expires => 89,
436             );
437 8         182 is( $starch->store->max_expires(), 67, 'store max_expires explicitly set' );
438 8         11637 };
439              
440             subtest class_name => sub{
441             # Add a random plugin so the real class name has the __WITH__ bit.
442 8         2450 my $starch = $self->new_manager( store=>{class=>'::Memory'}, plugins=>['::LogStoreExceptions'] );
443 8         208 is( $starch->store->base_class_name(), 'Starch::Store::Memory', 'base_class_name' );
444 8         4241 is( $starch->store->short_class_name(), 'Store::Memory', 'short_class_name' );
445 8         3435 is( $starch->store->short_store_class_name(), 'Memory', 'short_store_class_name' );
446 8         11096 };
447              
448             subtest new_sub_store => sub{
449 8         2596 my $sub_store1 = $store->new_sub_store( class=>'::Memory', max_expires=>12 );
450 8         78 isa_ok( $sub_store1, 'Starch::Store::Memory' );
451 8         1599 is( ''.$sub_store1->manager(), ''.$store->manager(), 'sub store has same manager as parent store' );
452 8         3181 my $sub_store2 = $sub_store1->new_sub_store( class=>'::Memory' );
453 8         67 is( $sub_store2->max_expires(), 12, 'sub store has max_expires from parent store' );
454 8         11959 };
455              
456             subtest calculate_expires => sub{
457 8         2359 my $store = $store->new_sub_store( class=>'::Memory', max_expires => 10 );
458 8         60 is( $store->calculate_expires( 5 ), 5, 'expires less than max_expires' );
459 8         3233 is( $store->calculate_expires( 15 ), 10, 'expires more than max_expires' );
460 8         11551 };
461              
462             subtest stringify_key => sub{
463 8         2413 is( $store->stringify_key( '1234', ['foo'] ), 'foo:1234', 'basic' );
464 8         3136 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo:bar:1234', 'deep' );
465 8         3141 is( $store->stringify_key( '1234', [] ), '1234', 'empty' );
466              
467 8         3091 my $store = $store->new_sub_store( class=>'::Memory', key_separator=>'-' );
468 8         67 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo-bar-1234', 'custom key_separator' );
469 8         10574 };
470              
471             subtest reap_expired => sub{
472 8         2400 my $store = $store->new_sub_store( class=>'::Memory' );
473 8         73 ok( (!$store->can_reap_expired()), 'expiration reaping is disabled' );
474             like(
475 8         156 dies { $store->reap_expired() },
476 8         2200 qr{does not support expired state reaping},
477             'reap_expired failed',
478             );
479 8         12032 };
480 8         157 };
481              
482 8         55588 return;
483             }
484              
485             1;
486             __END__