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 = '2017-07-10'; # DATE
4             our $VERSION = '0.57'; # VERSION
5              
6 3     3   412448 use 5.010;
  3         18  
7 3     3   28 use strict;
  3         9  
  3         123  
8 3     3   25 use warnings;
  3         9  
  3         125  
9 3     3   14904 use Log::ger;
  3         425  
  3         24  
10              
11 3     3   7478 use File::Remove qw(remove);
  3         8020  
  3         280  
12 3     3   2313 use Perinci::Access::Schemeless;
  3         122104  
  3         147  
13 3     3   2584 use Perinci::Tx::Manager;
  3         16  
  3         166  
14 3     3   29 use Scalar::Util qw(blessed);
  3         10  
  3         300  
15 3     3   26 use Test::More 0.98;
  3         95  
  3         35  
16 3     3   1121 use UUID::Random;
  3         8  
  3         871  
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 18126 my %targs = @_;
28              
29 7 50       51 my $tmpdir =$targs{tmpdir} or die "BUG: please supply tmpdir";
30 7 50       42 my $reset_state=$targs{reset_state} or die "BUG: please supply reset_state";
31              
32 7         20 my $tm;
33 7 50       39 if ($targs{reset_db_dir}) {
34 0         0 remove "$tmpdir/.tx";
35             }
36              
37 7         38 $reset_state->();
38              
39             my $pa = Perinci::Access::Schemeless->new(
40             use_tx=>1,
41             custom_tx_manager => sub {
42 202     202   91582 my $self = shift;
43 202   66     1545 $tm //= Perinci::Tx::Manager->new(
44             data_dir => "$tmpdir/.tx", pa => $self);
45 202 50       2157 die $tm unless blessed($tm);
46 202         1463 $tm;
47 7         165 });
48              
49 7         4203 my $f = $targs{f};
50 7   50     44 my $fargs = $targs{args} // {};
51             my $tname = $targs{name} //
52 7   33     50 "call $f => {".join(",", map{"$_=>$fargs->{$_}"} sort keys %$fargs)."}";
  0         0  
53              
54             subtest $tname => sub {
55 7     7   11699 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         44 my $uri = "/$f"; $uri =~ s!::!/!g;
  7         47  
62              
63 7         24 my $num_actions = 0;
64 7         22 my $num_undo_actions = 0;
65 3     3   23 no strict 'refs';
  3         8  
  3         10965  
66 7         45 $res = *{$f}{CODE}->(%$fargs, -tx_action=>'check_state');
  7         85  
67 7         505 my $has_do_actions;
68 7 100       40 if ($res->[0] == 200) {
69 2 100       13 if ($res->[3]{do_actions}) {
70 1         3 $num_actions = @{ $res->[3]{do_actions} };
  1         3  
71 1         3 $has_do_actions++;
72             } else {
73 1         4 $num_actions = 1;
74             }
75 2         21 note "number of actions: $num_actions";
76 2         1672 $num_undo_actions = @{ $res->[3]{undo_actions} };
  2         10  
77 2         15 note "number of undo actions: $num_undo_actions";
78             }
79              
80              
81             subtest "==test_tx_action 01/11: normal action + commit==" => sub {
82 7         9810 $tx_id = UUID::Random::generate();
83 7         552 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
84 7 50       428 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         10080 args => $fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
91 7   100     767 $estatus = $targs{status} // 200;
92 7 50       98 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       11190 do { $done_testing++; return } unless $estatus == 200;
  4         18  
  4         22  
97              
98 3         41 $res = $pa->request(commit_tx => "/", {tx_id=>$tx_id});
99 3 50       147 unless(is($res->[0], 200, "commit_tx succeeds")) {
100 0         0 note "res = ", explain($res);
101 0         0 goto DONE_TESTING;
102             }
103 3         3562 $tx_id1 = $tx_id;
104 7         1428 };
105 0         0 subtest "after_do" => sub { $targs{after_do}->(); ok 1 }
  0         0  
106 7 50       26661 if $targs{after_do};
107 7 100 66     119 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         5136 $tx_id = UUID::Random::generate();
112 3         256 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
113             $res = $pa->request(call => $uri, {
114 3         137 args => $fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
115 3 50       559 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         5399 $res = $pa->request(rollback_tx => "/", {tx_id=>$tx_id});
121 3 50       140 unless(is($res->[0], 200, "rollback_tx succeeds")) {
122 0         0 note "res = ", explain($res);
123 0         0 goto DONE_TESTING;
124             }
125 3         698 };
126 3 50 33     19288 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       849 if $targs{before_undo};
131             subtest "==test_tx_action 03/11: undo==" => sub {
132             $res = $pa->request(undo => "/", {
133 3         6568 tx_id=>$tx_id1, confirm=>$targs{confirm}});
134 3   50     420 $estatus = $targs{undo_status} // 200;
135 3 50       1783 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       6233 do { $done_testing++; return } unless $estatus == 200;
  0         0  
  0         0  
140 3         35 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
141 3 50       62 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
142             or note "res = ", explain($res);
143 3         44 };
144 0         0 subtest "after_undo" => sub { $targs{after_undo}->(); ok 1 }
  0         0  
145 3 50       17236 if $targs{after_undo};
146 3 50 33     51 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         6273 $tx_id = UUID::Random::generate();
151              
152 3         256 for my $i (1..$num_actions) {
153 4         8216 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
154             subtest "crash at action #$i" => sub {
155 4         14272 my $ja = 0;
156             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
157 14         130 my ($self, %args) = @_;
158 14   50     108 my $nl = $self->{_action_nest_level} // 0;
159 14 100       354 return unless $nl <= ($has_do_actions ? 2:1);
    50          
160 14 100       97 return if $args{which} eq 'rollback';
161 7 50       46 $ja++ if $args{which} eq 'action';
162 7 100 66     100 if ($ja == $i && $nl == ($has_do_actions ? 2:1)) {
    100          
163 4         18 for ("CRASH DURING ACTION") {log_trace($_);die $_}
  4         61  
  4         116  
164             }
165 4         95 };
166 4         18 eval {
167 4         55 $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         516 $tm = Perinci::Tx::Manager->new(
177             data_dir => "$tmpdir/.tx", pa => $pa);
178 4         39 $res = $tm->list(tx_id=>$tx_id, detail=>1);
179 4 50       128 is($res->[2][0]{tx_status}, 'R', "transaction status is R")
180             or note "res = ", explain($res);
181 4         263 };
182              
183             }
184 3 100       10979 ok 1 if !$num_actions;
185 3         909 };
186 3 50 33     9354 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         5865 $tx_id = UUID::Random::generate();
191              
192 3         220 my $i = 0;
193 3         103 my $last;
194 3         15 while (1) {
195 10         80 $i++;
196 10 100       86 last if $last;
197 7         161 $res = $pa->request(begin_tx => "/", {tx_id=>$tx_id});
198             subtest "crash at rollback #$i" => sub {
199 7         21743 my $ja = 0; my $jrb = 0; my $crashed;
  7         27  
  7         24  
200             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
201 25         418 my ($self, %args) = @_;
202 25   50     464 my $nl = $self->{_action_nest_level} // 0;
203 25 100       173 return unless $nl <= ($has_do_actions ? 2:1);
    50          
204 25 100       149 if ($args{which} eq 'action') {
205             # we need to trigger the rollback first, after last
206             # action
207 14 100       110 return unless ++$ja >= $num_actions;
208 6         25 for ("CRASH DURING ACTION") {log_trace($_);die $_}
  6         41  
  6         148  
209             }
210 11 50       61 $jrb++ if $args{which} eq 'rollback';
211 11 100       80 if ($jrb == $i) {
212 4         16 for("CRASH DURING ROLLBACK"){
213 4         12 $crashed++; log_trace($_); die $_;
  4         22  
  4         84  
214             }
215             }
216 7         97 };
217 7         30 eval {
218 7         92 $res = $pa->request(call=>$uri,
219             {args=>$fargs,tx_id=>$tx_id});
220             };
221 7 100       592 do { ok 1; $last++; return } unless $crashed;
  3         29  
  3         3165  
  3         53  
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         75 $tm = Perinci::Tx::Manager->new(
229             data_dir => "$tmpdir/.tx", pa => $pa);
230 4         38 $res = $tm->list(tx_id=>$tx_id, detail=>1);
231 4 50       135 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
232             or note "res = ", explain($res);
233 7         492 };
234 7         32329 $reset_state->();
235             }
236 3         847 };
237 3 50 33     7429 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         4832 tx_id=>$tx_id1, confirm=>$targs{confirm}});
243 3 50       167 unless (is($res->[0], 200, "redo succeeds")) {
244 0         0 note "res = ", explain($res);
245 0         0 goto DONE_TESTING;
246             }
247 3         4277 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
248 3 50       53 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
249             or note "res = ", explain($res);
250 3         720 };
251 3 50 33     16334 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       1403 if $targs{before_undo};
256             subtest "==test_tx_action 07/11: undo #2==" => sub {
257             $res = $pa->request(undo => "/", {
258 3         7842 tx_id=>$tx_id1, confirm=>$targs{confirm}});
259 3 50       758 unless (is($res->[0], 200, "undo succeeds")) {
260 0         0 note "res = ", explain($res);
261 0         0 goto DONE_TESTING;
262             }
263 3         8621 $res = $tm->list(tx_id=>$tx_id1, detail=>1);
264 3 50       62 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
265             or note "res = ", explain($res);
266 3         42 };
267 3 50 33     12735 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       1038 if $targs{after_undo};
270              
271              
272             subtest "==test_tx_action 08/11: crash while undo -> roll forward==" => sub {
273 3         5391 $tx_id = UUID::Random::generate();
274 3         213 for my $i (1..$num_undo_actions) {
275              
276             # first create a committed transaction
277 4         13887 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
278 4         156 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
279             $pa->request(call => $uri, {
280 4         271 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
281 4         356 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
282 4         190 $res = $tm->list(tx_id=>$tx_id, detail=>1);
283 4 50       100 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         8470 my $ju = 0;
288 4         28 local $Perinci::Tx::Manager::_settings{default_rollback_on_action_failure} = 0;
289             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
290 14         125 my ($self, %args) = @_;
291 14   50     489 my $nl = $self->{_action_nest_level} // 0;
292 14 50       82 return unless $args{which} eq 'undo';
293 14 100       109 if (++$ju == $i) {
294 4         19 for ("CRASH DURING UNDO ACTION") {
295 4         27 log_trace($_);die $_;
  4         107  
296             }
297             }
298 4         55 };
299 4         16 eval {
300 4         60 $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         200 $tm = Perinci::Tx::Manager->new(
309             data_dir => "$tmpdir/.tx", pa => $pa);
310 4         40 $res = $tm->list(tx_id=>$tx_id, detail=>1);
311 4 50       115 is($res->[2][0]{tx_status}, 'U', "transaction status is U")
312             or note "res = ", explain($res);
313 4         5645 };
314              
315             }
316 3 100       15196 ok 1 if !$num_undo_actions;
317 3         51 };
318 3 50 33     15795 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         6366 $tx_id = UUID::Random::generate();
323              
324 3         257 my $i = 0;
325 3         14 my $last;
326 3         12 while (1) {
327 10         33396 $i++;
328 10 100       67 last if $last;
329              
330             # first create a committed transaction
331 7         68 $reset_state->();
332 7         151 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
333 7         612 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
334             $pa->request(call => $uri, {
335 7         414 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
336 7         976 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
337 7         433 $res = $tm->list(tx_id=>$tx_id, detail=>1);
338 7 50       315 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         17792 my $ju = 0; my $jrb = 0; my $crashed;
  7         26  
  7         26  
343             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
344 26         682 my ($self, %args) = @_;
345 26 100       256 if ($args{which} eq 'undo') {
    50          
346             # first we trigger a rollback at the last step
347 15 100       132 if (++$ju == $num_undo_actions) {
348 6         29 for ("CRASH DURING UNDO ACTION") {
349 6         48 log_trace($_);die $_;
  6         417  
350             }
351             }
352             } elsif ($args{which} eq 'rollback') {
353 11 100       242 if (++$jrb == $i) {
354 4         20 for ("CRASH DURING ROLLBACK") {
355 4         15 $crashed++; log_trace($_);die $_;
  4         27  
  4         107  
356             }
357             }
358             }
359 7         420 };
360 7         35 eval {
361 7         87 $res = $pa->request(undo=>"/", {tx_id=>$tx_id});
362             };
363 7 100       334 do { ok 1; $last++; return } unless $crashed;
  3         36  
  3         4482  
  3         65  
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         81 $tm = Perinci::Tx::Manager->new(
371             data_dir => "$tmpdir/.tx", pa => $pa);
372 4         44 $res = $tm->list(tx_id=>$tx_id, detail=>1);
373 4 50       138 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
374             or note "res = ", explain($res);
375 7         8112 };
376              
377             }
378 3 100       31 ok 1 if !$num_undo_actions;
379 3         1056 };
380 3 50 33     8851 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         4685 $tx_id = UUID::Random::generate();
385              
386 3         184 my $i = 0;
387 3         10 my $last;
388 3         8 while (1) {
389 11         41249 $i++;
390 11 100       70 last if $last;
391              
392 8         66 $reset_state->();
393             # first create an undone transaction
394 8         339 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
395 8         434 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
396             $pa->request(call => $uri, {
397 8         519 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
398 8         637 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
399 8         425 $pa->request(undo => "/", {tx_id=>$tx_id});
400 8         326 $res = $tm->list(tx_id=>$tx_id, detail=>1);
401 8 50       233 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         17537 my $jrd = 0; my $crashed;
  8         26  
406 8         51 local $Perinci::Tx::Manager::_settings{default_rollback_on_action_failure} = 0;
407             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
408 21         209 my ($self, %args) = @_;
409 21   50     233 my $nl = $self->{_action_nest_level} // 0;
410 21 50       137 return unless $args{which} eq 'redo';
411 21 100       249 if (++$jrd == $i) {
412 5         25 for ("CRASH DURING REDO ACTION") {
413 5         24 $crashed++; log_trace($_); die $_;
  5         38  
  5         186  
414             }
415             }
416 8         102 };
417 8         32 eval {
418 8         105 $res = $pa->request(redo=>"/", {tx_id=>$tx_id});
419             };
420 8 100       325 do { ok 1; $last++; return } unless $crashed;
  3         84  
  3         4323  
  3         58  
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         549 $tm = Perinci::Tx::Manager->new(
428             data_dir => "$tmpdir/.tx", pa => $pa);
429 5         56 $res = $tm->list(tx_id=>$tx_id, detail=>1);
430 5 50       154 is($res->[2][0]{tx_status}, 'C', "transaction status is C")
431             or note "res = ", explain($res);
432 8         16548 };
433              
434             }
435 3 100       33 ok 1 if !$num_actions;
436 3         833 };
437 3 50 33     13022 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         10920 $tx_id = UUID::Random::generate();
442              
443 3         364 my $i = 0;
444 3         127 my $last;
445 3         12 while (1) {
446 10         30209 $i++;
447 10 100       65 last if $last;
448              
449             # first create an undone transaction
450 7         66 $reset_state->();
451 7         148 $pa->request(discard_tx=>"/", {tx_id=>$tx_id});
452 7         572 $pa->request(begin_tx => "/", {tx_id=>$tx_id});
453             $pa->request(call => $uri, {
454 7         334 args=>$fargs, tx_id=>$tx_id, confirm=>$targs{confirm}});
455 7         6030 $pa->request(commit_tx => "/", {tx_id=>$tx_id});
456 7         611 $pa->request(undo => "/", {tx_id=>$tx_id});
457 7         279 $res = $tm->list(tx_id=>$tx_id, detail=>1);
458 7 50       158 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         16843 my $jrd = 0; my $jrb = 0; my $crashed;
  7         32  
  7         24  
463             local $Perinci::Tx::Manager::_hooks{after_fix_state} = sub {
464 26         645 my ($self, %args) = @_;
465 26 100       211 if ($args{which} eq 'redo') {
    50          
466             # first we trigger a rollback at the last step
467 15 100       124 if (++$jrd == $num_actions) {
468 6         29 for ("CRASH DURING REDO ACTION") {
469 6         79 log_trace($_);die $_;
  6         189  
470             }
471             }
472             } elsif ($args{which} eq 'rollback') {
473 11 100       75 if (++$jrb == $i) {
474 4         21 for ("CRASH DURING ROLLBACK") {
475 4         14 $crashed++; log_trace($_); die $_;
  4         31  
  4         101  
476             }
477             }
478             }
479 7         93 };
480 7         30 eval {
481 7         92 $res = $pa->request(redo=>"/", {tx_id=>$tx_id});
482             };
483 7 100       1816 do { ok 1; $last++; return } unless $crashed;
  3         40  
  3         14141  
  3         68  
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         174 $tm = Perinci::Tx::Manager->new(
491             data_dir => "$tmpdir/.tx", pa => $pa);
492 4         35 $res = $tm->list(tx_id=>$tx_id, detail=>1);
493 4 50       120 is($res->[2][0]{tx_status}, 'X', "transaction status is X")
494             or note "res = ", explain($res);
495 7         8936 };
496              
497             }
498 3 100       30 ok 1 if !$num_actions;
499 3         2144 };
500 3 50 33     9107 goto DONE_TESTING if $done_testing || !Test::More->builder->is_passing;
501              
502              
503 7         1181 DONE_TESTING:
504             done_testing;
505 7         119 };
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.57 of Test::Perinci::Tx::Manager (from Perl distribution Perinci-Tx-Manager), released on 2017-07-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) 2017, 2016, 2015, 2014, 2013, 2012 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