File Coverage

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


line stmt bran cond sub pod time code
1             package Test::Starch;
2 1     1   213996 use 5.008001;
  1         4  
3 1     1   7 use strictures 2;
  1         8  
  1         40  
4             our $VERSION = '0.12';
5              
6             =head1 NAME
7              
8             Test::Starch - Test core features of starch.
9              
10             =head1 SYNOPSIS
11              
12             use Test2::V0;
13             use Test::Starch;
14            
15             my $tester = Test::Starch->new(
16             plugins => [ ... ],
17             store => ...,
18             ...,
19             );
20             $tester->test();
21            
22             done_testing;
23              
24             =head1 DESCRIPTION
25              
26             This class runs the core L test suite by testing public
27             interfaces of L, L, and
28             L. These are the same tests that Starch runs
29             when you install it from CPAN.
30              
31             This module is used by stores and plugins to ensure that they have
32             not broken any of the core features of Starch. All store and plugin
33             authors are highly encouraged to run these tests as part of their
34             test suite.
35              
36             Along the same lines, it is recommended that if you use Starch that
37             you make a test in your in-house test-suite which runs these tests
38             against your configuration.
39              
40             This class takes all the same arguments as L and saves them
41             to be used when L is called by the tests. Unlike L,
42             if the C argument is not passed it will defailt to a Memory store.
43              
44             =cut
45              
46 1     1   797 use Types::Standard -types;
  1         75219  
  1         11  
47 1     1   5095 use Types::Common::String -types;
  1         22968  
  1         11  
48              
49 1     1   1498 use Test2::V0;
  1         2  
  1         8  
50              
51 1     1   1691 use Test2::Require::Module 'Starch';
  1         1292  
  1         11  
52 1     1   202433 BEGIN { require Starch }
53              
54 1     1   6 use Moo;
  1         4  
  1         5  
55 1     1   784 use namespace::clean;
  1         8  
  1         5  
56              
57             around BUILDARGS => sub{
58             my $orig = shift;
59             my $class = shift;
60              
61             my $args = $class->$orig( @_ );
62              
63             return {
64             _manager_args => {
65             store => { class=>'::Memory' },
66             %$args,
67             },
68             };
69             };
70              
71             has _manager_args => (
72             is => 'ro',
73             isa => HashRef,
74             required => 1,
75             );
76              
77             =head1 METHODS
78              
79             =head2 new_manager
80              
81             Creates a new L object and returns it. Any arguments
82             you specify to this method will override those specified when creating
83             the L object.
84              
85             =cut
86              
87             sub new_manager {
88 7     7 1 16 my $self = shift;
89              
90 7         204 my $extra_args = Starch::Manager->BUILDARGS( @_ );
91              
92             return Starch->new(
93 7         30 %{ $self->_manager_args() },
94 7         1527 %{ $extra_args },
  7         42  
95             );
96             }
97              
98             =head2 test
99              
100             Calls L, L, and L.
101              
102             =cut
103              
104             sub test {
105 1     1 1 40 my ($self) = @_;
106 1         14 $self->test_manager();
107 1         6 $self->test_state();
108 1         11 $self->test_store();
109 1         5 return;
110             }
111              
112             =head2 test_manager
113              
114             Tests L.
115              
116             =cut
117              
118             sub test_manager {
119 1     1 1 3 my ($self) = @_;
120              
121 1         4 my $starch = $self->new_manager();
122              
123             subtest 'core tests for ' . ref($starch) => sub{
124             subtest state_id_seed => sub{
125 1         289 isnt( $starch->state_id_seed(), $starch->state_id_seed(), 'two seeds are not the same' );
126 1     1   5997 };
127              
128             subtest generate_state_id => sub{
129 1         293 isnt(
130             $starch->generate_state_id(),
131             $starch->generate_state_id(),
132             'two generated ids are not the same',
133             );
134 1         1783 };
135              
136             subtest clone_data => sub{
137 1         289 my $old_data = { foo=>32, bar=>[1,2,3] };
138 1         5 my $new_data = $starch->clone_data( $old_data );
139              
140 1         121 is( $new_data, $old_data, 'cloned data matches source data' );
141              
142 1         1271 isnt( "$old_data->{bar}", "$new_data->{bar}", 'clone data structure has different reference' );
143 1         1335 };
144              
145             subtest is_data_diff => sub{
146 1         283 my $old_data = { foo=>1 };
147 1         4 my $new_data = { foo=>2 };
148              
149 1         6 ok( $starch->is_data_diff($old_data, $new_data), 'is diff' );
150 1         377 ok( (!$starch->is_data_diff($old_data, $old_data)), 'is not diff' );
151 1         1332 };
152 1         47966 };
153              
154 1         3921 return;
155             }
156              
157             =head2 test_state
158              
159             Test L.
160              
161             =cut
162              
163             sub test_state {
164 1     1 1 3 my ($self) = @_;
165              
166 1         4 my $starch = $self->new_manager();
167              
168             subtest 'core tests for ' . ref($starch->state()) => sub{
169             subtest id => sub{
170 1         270 my $state1 = $starch->state();
171 1         209 my $state2 = $starch->state();
172 1         188 my $id = $starch->generate_state_id();
173 1         21 my $state3 = $starch->state( $id );
174              
175 1         251 ok( $starch->state_id_type->check($id), 'ID looks good' );
176 1         297 isnt( $state1->id(), $state2->id(), 'two generated state IDs are not the same' );
177 1         495 is( $state3->id(), $id, 'custom ID was used' );
178 1     1   2896 };
179              
180             subtest expires => sub{
181 1         294 my $state = $starch->state();
182 1         229 is( $state->expires(), $starch->expires(), 'state expires inherited the global expires' );
183 1         1405 };
184              
185             subtest modified => sub{
186 1         271 my $state = $starch->state();
187 1         242 is( $state->modified(), $state->created(), 'modfied is same as created in new state' );
188 1         2000828 sleep 2;
189 1         43 $state->mark_dirty();
190 1         378 $state->save();
191 1         906 $state = $starch->state( $state->id() );
192 1         442 cmp_ok( $state->modified(), '>', $state->created(), 'modified was updated with save' );
193 1         1290 };
194              
195             subtest created => sub{
196 1         372 my $start_time = time();
197 1         7 my $state = $starch->state();
198 1         229 my $created_time = $state->created();
199 1         120 cmp_ok( $created_time, '>=', $start_time, 'state created on or after test start' );
200 1         187 cmp_ok( $created_time, '<=', $start_time+1, 'state created is on or just after test start' );
201 1         2000353 sleep 2;
202 1         31 $state->mark_dirty();
203 1         240 $state->save();
204 1         658 $state = $starch->state( $state->id() );
205 1         326 is( $state->created(), $created_time, 'created was updated with save' );
206 1         1653 };
207              
208             subtest in_store => sub{
209 1         351 my $state1 = $starch->state();
210 1         224 my $state2 = $starch->state( $state1->id() );
211              
212 1         258 is( $state1->in_store(), 0, 'new state is not in_store' );
213 1         433 is( $state2->in_store(), 1, 'existing state is in_store' );
214              
215 1         385 my $id = $starch->generate_state_id();
216 1         22 my $state3 = $starch->state( $id );
217 1         242 is( $state3->in_store(), 1, 'existing state is in_store' );
218 1         417 $state3->data();
219 1         223 is( $state3->in_store(), 0, 'state is no longer in_store when data was not found' );
220 1         1933 };
221              
222             subtest is_deleted => sub{
223 1         291 my $state = $starch->state();
224 1         214 is( $state->is_deleted(), 0, 'new state is not deleted' );
225 1         377 $state->mark_dirty();
226 1         176 $state->save();
227 1         487 $state->delete();
228 1         117 is( $state->is_deleted(), 1, 'deleted state is deleted' );
229 1         1486 };
230              
231             subtest is_dirty => sub{
232 1         275 my $state = $starch->state();
233 1         208 is( $state->is_dirty(), 0, 'new state is not is_dirty' );
234 1         402 $state->data->{foo} = 543;
235 1         175 is( $state->is_dirty(), 1, 'modified state is_dirty' );
236 1         1318 };
237              
238             subtest is_loaded => sub{
239 1         271 my $state = $starch->state();
240 1         216 ok( (!$state->is_loaded()), 'state is not loaded' );
241 1         272 $state->data();
242 1         122 ok( $state->is_loaded(), 'state is loaded' );
243 1         1393 };
244              
245             subtest is_saved => sub{
246 1         272 my $state = $starch->state();
247 1         234 ok( (!$state->is_saved()), 'state is not saved' );
248 1         295 $state->mark_dirty();
249 1         171 $state->save();
250 1         551 ok( $state->is_saved(), 'state is saved' );
251 1         1161 };
252              
253             subtest save => sub{
254 1         273 my $state1 = $starch->state();
255              
256 1         267 $state1->data->{foo} = 789;
257 1         144 my $state2 = $starch->state( $state1->id() );
258 1         271 is( $state2->data->{foo}, undef, 'new state did not receive data from old' );
259              
260 1         618 is( $state1->is_dirty(), 1, 'is dirty before save' );
261 1         548 $state1->save();
262 1         466 is( $state1->is_dirty(), 0, 'is not dirty after save' );
263 1         510 $state2 = $starch->state( $state1->id() );
264 1         247 is( $state2->data->{foo}, 789, 'new state did receive data from old' );
265              
266 1         564 my $state = $starch->state();
267 1         226 $state->data->{foo} = 931;
268 1         114 $state->save();
269              
270 1         549 $state = $starch->state( $state->id() );
271 1         239 $state->data();
272              
273 1         190 $starch->state( $state->id() )->delete();
274              
275 1         324 $state->save();
276             is(
277             $starch->state( $state->id() )->data->{foo},
278             undef,
279 1         128 'save did not save',
280             );
281              
282 1         790 $state->mark_dirty();
283 1         58 $state->save();
284             is(
285             $starch->state( $state->id() )->data->{foo},
286 1         491 931,
287             'save did save',
288             );
289 1         1274 };
290              
291             subtest reload => sub{
292 1         296 my $state = $starch->state();
293 1         213 is( dies { $state->reload() }, undef, 'reloading a non-dirty state did not fail' );
  1         14  
294              
295 1         424 my $state1 = $starch->state();
296 1         216 $state1->data->{foo} = 91;
297 1         127 $state1->save();
298 1         505 my $state2 = $starch->state( $state1->id() );
299 1         237 $state2->data->{foo} = 19;
300 1         175 $state2->save();
301 1         446 $state1->reload();
302 1         56 is( $state1->data->{foo}, 19, 'reload worked' );
303 1         2071 };
304              
305             subtest mark_clean => sub{
306 1         277 my $state = $starch->state();
307 1         243 $state->data->{foo} = 6934;
308 1         104 is( $state->is_dirty(), 1, 'is dirty' );
309 1         498 $state->mark_clean();
310 1         88 is( $state->is_dirty(), 0, 'is clean' );
311 1         524 is( $state->data->{foo}, 6934, 'data is intact' );
312 1         1484 };
313              
314             subtest mark_dirty => sub{
315 1         279 my $state = $starch->state();
316 1         214 is( $state->is_dirty(), 0, 'is not dirty' );
317 1         402 $state->mark_dirty();
318 1         203 is( $state->is_dirty(), 1, 'is dirty' );
319 1         1380 };
320              
321             subtest rollback => sub{
322 1         272 my $state = $starch->state();
323 1         224 $state->data->{foo} = 6934;
324 1         115 is( $state->is_dirty(), 1, 'is dirty' );
325 1         496 $state->rollback();
326 1         52 is( $state->is_dirty(), 0, 'is clean' );
327 1         543 is( $state->data->{foo}, undef, 'data is rolled back' );
328              
329 1         369 $state->data->{foo} = 23;
330 1         14 $state->mark_clean();
331 1         115 $state->data->{foo} = 95;
332 1         10 $state->rollback();
333 1         54 is( $state->data->{foo}, 23, 'rollback to previous mark_clean' );
334 1         1404 };
335              
336             subtest delete => sub{
337 1         275 my $state = $starch->state();
338 1         223 $state->data->{foo} = 39;
339 1         125 $state->save();
340              
341 1         517 $state = $starch->state( $state->id() );
342 1         242 is( $state->data->{foo}, 39, 'state persists' );
343              
344 1         931 $state->delete();
345 1         123 $state = $starch->state( $state->id() );
346 1         240 is( $state->data->{foo}, undef, 'state was deleted' );
347 1         1466 };
348              
349             subtest set_expires => sub{
350 1         274 my $state = $starch->state();
351 1         223 is( $state->expires(), $starch->expires(), 'double check a new state gets the global expires' );
352 1         484 $state->set_expires( 111 );
353 1         97 $state->save();
354 1         495 $state = $starch->state( $state->id() );
355 1         283 is( $state->expires(), 111, 'custom expires was saved' );
356 1         1431 };
357              
358             subtest reset_expires => sub{
359 1         281 my $starch = $self->new_manager( expires=>111 );
360 1         2175 my $state = $starch->state();
361 1         470 is( $state->expires(), 111, 'state got default expires' );
362 1         528 $state->set_expires( 666 );
363 1         97 $state->save();
364 1         557 $state = $starch->state( $state->id() );
365 1         243 is( $state->expires(), 666, 'expires persisted' );
366 1         557 $state->reset_expires();
367 1         82 $state->save();
368 1         441 $state = $starch->state( $state->id() );
369 1         239 is( $state->expires(), 111, 'state expires was reset' );
370 1         1461 };
371              
372             subtest reset_id => sub{
373 1         354 my $state = $starch->state();
374              
375 1         229 $state->data->{foo} = 54;
376 1         135 ok( $state->is_dirty(), 'state is dirty before save' );
377 1         401 $state->save();
378 1         504 ok( (!$state->is_dirty()), 'state is not dirty after save' );
379 1         360 ok( $state->is_saved(), 'state is marked saved after save' );
380              
381 1         405 my $old_id = $state->id();
382 1         12 $state->reset_id();
383 1         535 ok( (!$state->is_saved()), 'state is not marked saved after reset_id' );
384 1         278 ok( $state->is_dirty(), 'state is marked dirty after reset_id' );
385 1         393 isnt( $state->id(), $old_id, 'state has new id after reset_id' );
386 1         473 $state->save();
387              
388 1         462 my $old_state = $starch->state( $old_id );
389 1         242 is( $old_state->data->{foo}, undef, 'old state data was deleted' );
390 1         1622 };
391 1         2057 };
392              
393 1         14710 return;
394             }
395              
396             =head2 test_store
397              
398             Tests the L.
399              
400             =cut
401              
402             sub test_store {
403 1     1 1 3 my ($self) = @_;
404              
405 1         4 my $starch = $self->new_manager();
406 1         2094 my $store = $starch->store();
407              
408             subtest 'core tests for ' . ref($store) => sub{
409              
410             subtest 'set, get, and remove' => sub{
411 1         267 my $key = 'starch-test-key';
412 1         5 $store->remove( $key, [] );
413              
414 1         85 is( $store->get( $key, [] ), undef, 'no data before set' );
415              
416 1         471 $store->set( $key, [], {foo=>6}, 10 );
417 1         72 is( $store->get( $key, [] )->{foo}, 6, 'has data after set' );
418              
419 1         416 $store->remove( $key, [] );
420              
421 1         40 is( $store->get( $key, [] ), undef, 'no data after remove' );
422 1     1   326 };
423              
424             subtest max_expires => sub{
425 1         275 my $starch = $self->new_manager(
426             expires => 89,
427             );
428 1         1922 is( $starch->store->max_expires(), undef, 'store max_expires left at undef' );
429              
430 1         366 $starch = $self->new_manager(
431             store=>{ class=>'::Memory', max_expires=>67 },
432             expires => 89,
433             );
434 1         1933 is( $starch->store->max_expires(), 67, 'store max_expires explicitly set' );
435 1         1379 };
436              
437             subtest class_name => sub{
438             # Add a random plugin so the real class name has the __WITH__ bit.
439 1         303 my $starch = $self->new_manager( store=>{class=>'::Memory'}, plugins=>['::LogStoreExceptions'] );
440 1         12850 is( $starch->store->base_class_name(), 'Starch::Store::Memory', 'base_class_name' );
441 1         485 is( $starch->store->short_class_name(), 'Store::Memory', 'short_class_name' );
442 1         439 is( $starch->store->short_store_class_name(), 'Memory', 'short_store_class_name' );
443 1         1337 };
444              
445             subtest new_sub_store => sub{
446 1         291 my $sub_store1 = $store->new_sub_store( class=>'::Memory', max_expires=>12 );
447 1         896 isa_ok( $sub_store1, 'Starch::Store::Memory' );
448 1         192 is( ''.$sub_store1->manager(), ''.$store->manager(), 'sub store has same manager as parent store' );
449 1         383 my $sub_store2 = $sub_store1->new_sub_store( class=>'::Memory' );
450 1         824 is( $sub_store2->max_expires(), 12, 'sub store has max_expires from parent store' );
451 1         1492 };
452              
453             subtest calculate_expires => sub{
454 1         279 my $store = $store->new_sub_store( class=>'::Memory', max_expires => 10 );
455 1         864 is( $store->calculate_expires( 5 ), 5, 'expires less than max_expires' );
456 1         400 is( $store->calculate_expires( 15 ), 10, 'expires more than max_expires' );
457 1         1405 };
458              
459             subtest stringify_key => sub{
460 1         280 is( $store->stringify_key( '1234', ['foo'] ), 'foo:1234', 'basic' );
461 1         425 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo:bar:1234', 'deep' );
462 1         384 is( $store->stringify_key( '1234', [] ), '1234', 'empty' );
463              
464 1         397 my $store = $store->new_sub_store( class=>'::Memory', key_separator=>'-' );
465 1         859 is( $store->stringify_key( '1234', ['foo', 'bar'] ), 'foo-bar-1234', 'custom key_separator' );
466 1         1271 };
467              
468             subtest reap_expired => sub{
469 1         293 my $store = $store->new_sub_store( class=>'::Memory' );
470 1         874 ok( (!$store->can_reap_expired()), 'expiration reaping is disabled' );
471             like(
472 1         14 dies { $store->reap_expired() },
473 1         264 qr{does not support expired state reaping},
474             'reap_expired failed',
475             );
476 1         1448 };
477 1         18 };
478              
479 1         12337 return;
480             }
481              
482             1;
483             __END__