File Coverage

blib/lib/Test/Perinci/Tx/Manager.pm
Criterion Covered Total %
statement 326 356 91.5
branch 115 164 70.1
condition 25 56 44.6
subroutine 14 14 100.0
pod 1 1 100.0
total 481 591 81.3


line stmt bran cond sub pod time code
1             package Test::Perinci::Tx::Manager;
2              
3             our $DATE = '2016-06-10'; # DATE
4             our $VERSION = '0.56'; # VERSION
5              
6 3     3   220569 use 5.010;
  3         11  
7 3     3   14 use strict;
  3         7  
  3         67  
8 3     3   13 use warnings;
  3         6  
  3         86  
9 3     3   2053 use Log::Any::IfLOG '$log';
  3         49  
  3         16  
10              
11 3     3   1228 use File::Remove qw(remove);
  3         4634  
  3         165  
12 3     3   1536 use Perinci::Access::Schemeless;
  3         62569  
  3         112  
13 3     3   1674 use Perinci::Tx::Manager;
  3         10  
  3         153  
14 3     3   28 use Scalar::Util qw(blessed);
  3         7  
  3         171  
15 3     3   16 use Test::More 0.98;
  3         50  
  3         23  
16 3     3   772 use UUID::Random;
  3         6  
  3         709  
17              
18             require Exporter;
19             our @ISA = qw(Exporter);
20             our @EXPORT_OK = qw(test_tx_action);
21              
22             # note: performing transaction actions is done via riap, just for convenience as
23             # well as testing riap. unless when testing lower-level stuffs, where we access
24             # $tm and the transactional function directly.
25              
26             sub test_tx_action {
27 7     7 1 10349 my %targs = @_;
28              
29 7 50       34 my $tmpdir =$targs{tmpdir} or die "BUG: please supply tmpdir";
30 7 50       28 my $reset_state=$targs{reset_state} or die "BUG: please supply reset_state";
31              
32 7         14 my $tm;
33 7 50       28 if ($targs{reset_db_dir}) {
34 0         0 remove "$tmpdir/.tx";
35             }
36              
37 7         26 $reset_state->();
38              
39             my $pa = Perinci::Access::Schemeless->new(
40             use_tx=>1,
41             custom_tx_manager => sub {
42 202     202   29536 my $self = shift;
43 202   66     732 $tm //= Perinci::Tx::Manager->new(
44             data_dir => "$tmpdir/.tx", pa => $self);
45 202 50       995 die $tm unless blessed($tm);
46 202         660 $tm;
47 7         102 });
48              
49 7         2571 my $f = $targs{f};
50 7   50     35 my $fargs = $targs{args} // {};
51             my $tname = $targs{name} //
52 7   33     25 "call $f => {".join(",", map{"$_=>$fargs->{$_}"} sort keys %$fargs)."}";
  0         0  
53              
54             subtest $tname => sub {
55 7     7   7048 my $res;
56             my $estatus; # expected status
57 7         0 my $tx_id;
58 7         0 my ($tx_id1);
59 7         0 my $done_testing;
60              
61 7         23 my $uri = "/$f"; $uri =~ s!::!/!g;
  7         35  
62              
63 7         14 my $num_actions = 0;
64 7         16 my $num_undo_actions = 0;
65 3     3   19 no strict 'refs';
  3         6  
  3         7924  
66 7         27 $res = *{$f}{CODE}->(%$fargs, -tx_action=>'check_state');
  7         56  
67 7         296 my $has_do_actions;
68 7 100       32 if ($res->[0] == 200) {
69 2 100       10 if ($res->[3]{do_actions}) {
70 1         2 $num_actions = @{ $res->[3]{do_actions} };
  1         2  
71 1         2 $has_do_actions++;
72             } else {
73 1         2 $num_actions = 1;
74             }
75 2         11 note "number of actions: $num_actions";
76 2         698 $num_undo_actions = @{ $res->[3]{undo_actions} };
  2         6  
77 2         10 note "number of undo actions: $num_undo_actions";
78             }
79              
80              
81             subtest "==test_tx_action 01/11: normal action + commit==" => sub {
82 7         5861 $tx_id = UUID::Random::generate();
83 7         381 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
84 7 50       177 unless (is($res->[0], 200, "begin_tx succeeds")) {
85 0         0 note "res = ", explain($res);
86 0         0 goto DONE_TESTING;
87             }
88              
89             $res = $pa->request(call => $uri, {
90 7         4814 args => $fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
91 7   100     423 $estatus = $targs{status} // 200;
92 7 50       63 unless(is($res->[0], $estatus, "status is $estatus")) {
93 0         0 note "res = ", explain($res);
94 0         0 goto DONE_TESTING;
95             }
96 7 100       4665 do { $done_testing++; return } unless $estatus == 200;
  4         53  
  4         94  
97              
98 3         23 $res = $pa->request(commit_tx => "/", {tx_id=>$tx_id});
99 3 50       86 unless(is($res->[0], 200, "commit_tx succeeds")) {
100 0         0 note "res = ", explain($res);
101 0         0 goto DONE_TESTING;
102             }
103 3         1897 $tx_id1 = $tx_id;
104 7         696 };
105 0         0 subtest "after_do" => sub { $targs{after_do}->(); ok 1 }
  0         0  
106 7 50       12096 if $targs{after_do};
107 7 100 66     73 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
108              
109              
110             subtest "==test_tx_action 02/11: repeat action -> noop (idempotent), rollback==" => sub {
111 3         2991 $tx_id = UUID::Random::generate();
112 3         147 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
113             $res = $pa->request(call => $uri, {
114 3         67 args => $fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
115 3 50       113 unless(is($res->[0], 304, "status is 304")) {
116 0         0 note "res = ", explain($res);
117 0         0 goto DONE_TESTING;
118             }
119              
120 3         1522 $res = $pa->request(rollback_tx => "/", {tx_id=>$tx_id});
121 3 50       53 unless(is($res->[0], 200, "rollback_tx succeeds")) {
122 0         0 note "res = ", explain($res);
123 0         0 goto DONE_TESTING;
124             }
125 3         553 };
126 3 50 33     5607 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
127              
128              
129 0         0 subtest "before_undo" => sub { $targs{before_undo}->(); ok 1 }
  0         0  
130 3 50       427 if $targs{before_undo};
131             subtest "==test_tx_action 03/11: undo==" => sub {
132             $res = $pa->request(undo => "/", {
133 3         2636 tx_id=>$tx_id1, confirm=>$targs{confirm}});
134 3   50     81 $estatus = $targs{undo_status} // 200;
135 3 50       30 unless(is($res->[0], $estatus, "status is $estatus")) {
136 0         0 note "res = ", explain($res);
137 0         0 goto DONE_TESTING;
138             }
139 3 50       1978 do { $done_testing++; return } unless $estatus == 200;
  0         0  
  0         0  
140 3         25 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
141 3 50       46 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
142             or note "res = ", explain($res);
143 3         26 };
144 0         0 subtest "after_undo" => sub { $targs{after_undo}->(); ok 1 }
  0         0  
145 3 50       6089 if $targs{after_undo};
146 3 50 33     26 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
147              
148              
149             subtest "==test_tx_action 04/11: crash during action -> rollback==" => sub {
150 3         2649 $tx_id = UUID::Random::generate();
151              
152 3         125 for my $i (1..$num_actions) {
153 4         4537 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
154             subtest "crash at action #$i" => sub {
155 4         4985 my $ja = 0;
156             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
157 14         56 my ($self, %args) = @_;
158 14   50     49 my $nl = $self->{_action_nest_level} // 0;
159 14 100       50 return unless $nl <= ($has_do_actions ? 2:1);
    50          
160 14 100       51 return if $args{which} eq 'rollback';
161 7 50       23 $ja++ if $args{which} eq 'action';
162 7 100 66     47 if ($ja == $i && $nl == ($has_do_actions ? 2:1)) {
    100          
163 4         10 for ("CRASH DURING ACTION") {$log->trace($_);die $_}
  4         29  
  4         65  
164             }
165 4         31 };
166 4         12 eval {
167 4         29 $res = $pa->request(call=>$uri,
168             {args=>$fargs,tx_id=>$tx_id});
169             };
170              
171             # doesn't die, trapped by eval{} in _action_loop. there's
172             # also eval{} placed by periwrap
173             #ok($@, "dies") or note "res = ", explain($res);
174              
175             # reinit TM / recover
176 4         203 $tm = Perinci::Tx::Manager->new(
177             data_dir => "$tmpdir/.tx", pa => $pa);
178 4         25 $res = $tm->list(tx_id=>$tx_id, detail=>1);
179 4 50       66 is($res->[2][0]{tx_status}, 'R', "transaction status is R")
180             or note "res = ", explain($res);
181 4         104 };
182              
183             }
184 3 100       4798 ok 1 if !$num_actions;
185 3         449 };
186 3 50 33     4722 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
187              
188              
189             subtest "==test_tx_action 05/11: crash during rollback -> tx status X==" => sub {
190 3         3007 $tx_id = UUID::Random::generate();
191              
192 3         115 my $i = 0;
193 3         8 my $last;
194 3         5 while (1) {
195 10         52 $i++;
196 10 100       38 last if $last;
197 7         42 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
198             subtest "crash at rollback #$i" => sub {
199 7         8357 my $ja = 0; my $jrb = 0; my $crashed;
  7         16  
  7         17  
200             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
201 25         95 my ($self, %args) = @_;
202 25   50     90 my $nl = $self->{_action_nest_level} // 0;
203 25 100       136 return unless $nl <= ($has_do_actions ? 2:1);
    50          
204 25 100       73 if ($args{which} eq 'action') {
205             # we need to trigger the rollback first, after last
206             # action
207 14 100       60 return unless ++$ja >= $num_actions;
208 6         18 for ("CRASH DURING ACTION") {$log->trace($_);die $_}
  6         36  
  6         133  
209             }
210 11 50       35 $jrb++ if $args{which} eq 'rollback';
211 11 100       48 if ($jrb == $i) {
212 4         10 for("CRASH DURING ROLLBACK"){
213 4         8 $crashed++; $log->trace($_); die $_;
  4         19  
  4         49  
214             }
215             }
216 7         43 };
217 7         19 eval {
218 7         43 $res = $pa->request(call=>$uri,
219             {args=>$fargs,tx_id=>$tx_id});
220             };
221 7 100       297 do { ok 1; $last++; return } unless $crashed;
  3         25  
  3         1967  
  3         42  
222              
223             # doesn't die, trapped by eval{} in _action_loop. there's
224             # also eval{} placed by periwrap
225             #ok($@, "dies") or note "res = ", explain($res);
226              
227             # reinit TM / recover
228 4         40 $tm = Perinci::Tx::Manager->new(
229             data_dir => "$tmpdir/.tx", pa => $pa);
230 4         21 $res = $tm->list(tx_id=>$tx_id, detail=>1);
231 4 50       68 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
232             or note "res = ", explain($res);
233 7         221 };
234 7         13419 $reset_state->();
235             }
236 3         477 };
237 3 50 33     4259 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
238              
239              
240             subtest "==test_tx_action 06/11: redo==" => sub {
241             $res = $pa->request(redo => "/", {
242 3         2827 tx_id=>$tx_id1, confirm=>$targs{confirm}});
243 3 50       102 unless (is($res->[0], 200, "redo succeeds")) {
244 0         0 note "res = ", explain($res);
245 0         0 goto DONE_TESTING;
246             }
247 3         2047 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
248 3 50       44 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
249             or note "res = ", explain($res);
250 3         487 };
251 3 50 33     7101 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
252              
253              
254 0         0 subtest "before_undo" => sub { $targs{before_undo}->(); ok 1 }
  0         0  
255 3 50       526 if $targs{before_undo};
256             subtest "==test_tx_action 07/11: undo #2==" => sub {
257             $res = $pa->request(undo => "/", {
258 3         3317 tx_id=>$tx_id1, confirm=>$targs{confirm}});
259 3 50       66 unless (is($res->[0], 200, "undo succeeds")) {
260 0         0 note "res = ", explain($res);
261 0         0 goto DONE_TESTING;
262             }
263 3         1914 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
264 3 50       44 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
265             or note "res = ", explain($res);
266 3         33 };
267 3 50 33     7056 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
268 0         0 subtest "after_undo" => sub { $targs{after_undo}->(); ok 1 }
  0         0  
269 3 50       524 if $targs{after_undo};
270              
271              
272             subtest "==test_tx_action 08/11: crash while undo -> roll forward==" => sub {
273 3         3309 $tx_id = UUID::Random::generate();
274 3         155 for my $i (1..$num_undo_actions) {
275              
276             # first create a committed transaction
277 4         4471 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
278 4         64 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
279             $pa->request(call => $uri, {
280 4         96 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
281 4         198 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
282 4         57 $res = $tm->list(tx_id=>$tx_id, detail=>1);
283 4 50       62 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
284             or note "res = ", explain($res);
285              
286             subtest "crash at undo action #$i" => sub {
287 4         4120 my $ju = 0;
288 4         15 local $Perinci::Tx::Manager::_settings{default_rollback_on_action_failure} = 0;
289             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
290 14         51 my ($self, %args) = @_;
291 14   50     70 my $nl = $self->{_action_nest_level} // 0;
292 14 50       42 return unless $args{which} eq 'undo';
293 14 100       56 if (++$ju == $i) {
294 4         10 for ("CRASH DURING UNDO ACTION") {
295 4         22 $log->trace($_);die $_;
  4         61  
296             }
297             }
298 4         23 };
299 4         9 eval {
300 4         19 $res = $pa->request(undo=>"/", {tx_id=>$tx_id});
301             };
302              
303             # doesn't die, trapped by eval{} in _action_loop. there's
304             # also eval{} placed by periwrap
305             #ok($@, "dies") or note "res = ", explain($res);
306              
307             # reinit TM / recover
308 4         75 $tm = Perinci::Tx::Manager->new(
309             data_dir => "$tmpdir/.tx", pa => $pa);
310 4         22 $res = $tm->list(tx_id=>$tx_id, detail=>1);
311 4 50       67 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
312             or note "res = ", explain($res);
313 4         2652 };
314              
315             }
316 3 100       4512 ok 1 if !$num_undo_actions;
317 3         36 };
318 3 50 33     4573 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
319              
320              
321             subtest "==test_tx_action 09/11: crash while roll forward failed undo -> tx status X==" => sub {
322 3         2691 $tx_id = UUID::Random::generate();
323              
324 3         115 my $i = 0;
325 3         7 my $last;
326 3         6 while (1) {
327 10         14346 $i++;
328 10 100       39 last if $last;
329              
330             # first create a committed transaction
331 7         37 $reset_state->();
332 7         70 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
333 7         99 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
334             $pa->request(call => $uri, {
335 7         144 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
336 7         293 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
337 7         106 $res = $tm->list(tx_id=>$tx_id, detail=>1);
338 7 50       97 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
339             or note "res = ", explain($res);
340              
341             subtest "crash at rollback action #$i" => sub {
342 7         7278 my $ju = 0; my $jrb = 0; my $crashed;
  7         18  
  7         13  
343             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
344 26         93 my ($self, %args) = @_;
345 26 100       91 if ($args{which} eq 'undo') {
    50          
346             # first we trigger a rollback at the last step
347 15 100       80 if (++$ju == $num_undo_actions) {
348 6         15 for ("CRASH DURING UNDO ACTION") {
349 6         34 $log->trace($_);die $_;
  6         99  
350             }
351             }
352             } elsif ($args{which} eq 'rollback') {
353 11 100       47 if (++$jrb == $i) {
354 4         10 for ("CRASH DURING ROLLBACK") {
355 4         8 $crashed++; $log->trace($_);die $_;
  4         19  
  4         49  
356             }
357             }
358             }
359 7         44 };
360 7         18 eval {
361 7         38 $res = $pa->request(undo=>"/", {tx_id=>$tx_id});
362             };
363 7 100       130 do { ok 1; $last++; return } unless $crashed;
  3         23  
  3         1920  
  3         32  
364              
365             # doesn't die, trapped by eval{} in _action_loop. there's
366             # also eval{} placed by periwrap
367             #ok($@, "dies") or note "res = ", explain($res);
368              
369             # reinit TM / recover
370 4         39 $tm = Perinci::Tx::Manager->new(
371             data_dir => "$tmpdir/.tx", pa => $pa);
372 4         20 $res = $tm->list(tx_id=>$tx_id, detail=>1);
373 4 50       68 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
374             or note "res = ", explain($res);
375 7         4513 };
376              
377             }
378 3 100       19 ok 1 if !$num_undo_actions;
379 3         435 };
380 3 50 33     4653 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
381              
382              
383             subtest "==test_tx_action 10/11: crash while redo -> roll forward==" => sub {
384 3         2683 $tx_id = UUID::Random::generate();
385              
386 3         121 my $i = 0;
387 3         7 my $last;
388 3         7 while (1) {
389 11         16189 $i++;
390 11 100       46 last if $last;
391              
392 8         50 $reset_state->();
393             # first create an undone transaction
394 8         82 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
395 8         116 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
396             $pa->request(call => $uri, {
397 8         146 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
398 8         338 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
399 8         124 $pa->request(undo => "/", {tx_id=>$tx_id});
400 8         135 $res = $tm->list(tx_id=>$tx_id, detail=>1);
401 8 50       132 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
402             or note "res = ", explain($res);
403              
404             subtest "crash at redo action #$i" => sub {
405 8         8347 my $jrd = 0; my $crashed;
  8         20  
406 8         28 local $Perinci::Tx::Manager::_settings{default_rollback_on_action_failure} = 0;
407             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
408 21         85 my ($self, %args) = @_;
409 21   50     112 my $nl = $self->{_action_nest_level} // 0;
410 21 50       62 return unless $args{which} eq 'redo';
411 21 100       86 if (++$jrd == $i) {
412 5         15 for ("CRASH DURING REDO ACTION") {
413 5         13 $crashed++; $log->trace($_); die $_;
  5         35  
  5         82  
414             }
415             }
416 8         49 };
417 8         19 eval {
418 8         40 $res = $pa->request(redo=>"/", {tx_id=>$tx_id});
419             };
420 8 100       123 do { ok 1; $last++; return } unless $crashed;
  3         26  
  3         1692  
  3         27  
421              
422             # doesn't die, trapped by eval{} in _action_loop. there's
423             # also eval{} placed by periwrap
424             #ok($@, "dies") or note "res = ", explain($res);
425              
426             # reinit TM / recover
427 5         53 $tm = Perinci::Tx::Manager->new(
428             data_dir => "$tmpdir/.tx", pa => $pa);
429 5         32 $res = $tm->list(tx_id=>$tx_id, detail=>1);
430 5 50       76 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
431             or note "res = ", explain($res);
432 8         5816 };
433              
434             }
435 3 100       24 ok 1 if !$num_actions;
436 3         533 };
437 3 50 33     4746 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
438              
439              
440             subtest "==test_tx_action 11/11: crash while roll forward failed redo -> tx status X==" => sub {
441 3         2723 $tx_id = UUID::Random::generate();
442              
443 3         111 my $i = 0;
444 3         8 my $last;
445 3         6 while (1) {
446 10         16294 $i++;
447 10 100       40 last if $last;
448              
449             # first create an undone transaction
450 7         41 $reset_state->();
451 7         67 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
452 7         121 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
453             $pa->request(call => $uri, {
454 7         162 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
455 7         428 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
456 7         134 $pa->request(undo => "/", {tx_id=>$tx_id});
457 7         193 $res = $tm->list(tx_id=>$tx_id, detail=>1);
458 7 50       125 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
459             or note "res = ", explain($res);
460              
461             subtest "crash at rollback action #$i" => sub {
462 7         8236 my $jrd = 0; my $jrb = 0; my $crashed;
  7         22  
  7         16  
463             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
464 26         121 my ($self, %args) = @_;
465 26 100       123 if ($args{which} eq 'redo') {
    50          
466             # first we trigger a rollback at the last step
467 15 100       76 if (++$jrd == $num_actions) {
468 6         16 for ("CRASH DURING REDO ACTION") {
469 6         37 $log->trace($_);die $_;
  6         121  
470             }
471             }
472             } elsif ($args{which} eq 'rollback') {
473 11 100       52 if (++$jrb == $i) {
474 4         11 for ("CRASH DURING ROLLBACK") {
475 4         12 $crashed++; $log->trace($_); die $_;
  4         24  
  4         75  
476             }
477             }
478             }
479 7         58 };
480 7         24 eval {
481 7         49 $res = $pa->request(redo=>"/", {tx_id=>$tx_id});
482             };
483 7 100       147 do { ok 1; $last++; return } unless $crashed;
  3         24  
  3         2040  
  3         36  
484              
485             # doesn't die, trapped by eval{} in _action_loop. there's
486             # also eval{} placed by periwrap
487             #ok($@, "dies") or note "res = ", explain($res);
488              
489             # reinit TM / recover
490 4         53 $tm = Perinci::Tx::Manager->new(
491             data_dir => "$tmpdir/.tx", pa => $pa);
492 4         23 $res = $tm->list(tx_id=>$tx_id, detail=>1);
493 4 50       77 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
494             or note "res = ", explain($res);
495 7         5526 };
496              
497             }
498 3 100       20 ok 1 if !$num_actions;
499 3         509 };
500 3 50 33     4742 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
501              
502              
503 7         482 DONE_TESTING:
504             done_testing;
505 7         72 };
506             }
507              
508             # TODO: test cleanup: .tmp/XXX and .trash/XXX are cleaned
509              
510             1;
511             # ABSTRACT: Transaction tests
512              
513             __END__
514              
515             =pod
516              
517             =encoding UTF-8
518              
519             =head1 NAME
520              
521             Test::Perinci::Tx::Manager - Transaction tests
522              
523             =head1 VERSION
524              
525             This document describes version 0.56 of Test::Perinci::Tx::Manager (from Perl distribution Perinci-Tx-Manager), released on 2016-06-10.
526              
527             =head1 FUNCTIONS
528              
529             =head2 test_tx_action(%args)
530              
531             Test performing action using transaction.
532              
533             Will initialize transaction manager ($tm) and test action. Will test several
534             times with different scenarios to make sure commit, rollback, undo, redo, and
535             crash recoveries work.
536              
537             Arguments (C<*> denotes required arguments):
538              
539             =over 4
540              
541             =item * tmpdir* => STR
542              
543             Specify temporary directory to store transaction data directory in.
544              
545             =item * name => STR
546              
547             The test name.
548              
549             =item * f* => STR
550              
551             Fully-qualified name of transactional function, e.g. C<Setup::File::setup_file>.
552              
553             =item * args* => HASH (default: {})
554              
555             Arguments to feed to transactional function (via $tm->call()).
556              
557             =item * reset_state* => CODE
558              
559             The code to reset to initial state. This is called at the start of tests, as
560             well as after each rollback crash test, because crash during rollback causes the
561             state to become inconsistent.
562              
563             =item * status => INT (default: 200)
564              
565             Expect $tm->action() to return this status.
566              
567             =item * reset_db_dir => BOOL (default: 0)
568              
569             Whether to reset transaction data directory before running the tests. Note that
570             alternatively, you can also use a different C<tmpdir> for each call to this
571             function.
572              
573             =back
574              
575             =head1 HOMEPAGE
576              
577             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Tx-Manager>.
578              
579             =head1 SOURCE
580              
581             Source repository is at L<https://github.com/perlancar/perl-Perinci-Tx-Manager>.
582              
583             =head1 BUGS
584              
585             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Tx-Manager>
586              
587             When submitting a bug or request, please include a test-file or a
588             patch to an existing test-file that illustrates the bug or desired
589             feature.
590              
591             =head1 AUTHOR
592              
593             perlancar <perlancar@cpan.org>
594              
595             =head1 COPYRIGHT AND LICENSE
596              
597             This software is copyright (c) 2016 by perlancar@cpan.org.
598              
599             This is free software; you can redistribute it and/or modify it under
600             the same terms as the Perl 5 programming language system itself.
601              
602             =cut