File Coverage

blib/lib/JIP/Mock/Control.pm
Criterion Covered Total %
statement 175 183 95.6
branch 48 50 96.0
condition 5 7 71.4
subroutine 38 40 95.0
pod 9 9 100.0
total 275 289 95.1


line stmt bran cond sub pod time code
1             package JIP::Mock::Control;
2              
3 2     2   71983 use strict;
  2         14  
  2         55  
4 2     2   11 use warnings;
  2         4  
  2         58  
5              
6 2     2   15 use Carp qw(croak);
  2         3  
  2         122  
7 2     2   13 use English qw(-no_match_vars);
  2         2  
  2         11  
8 2     2   645 use Scalar::Util qw(reftype blessed);
  2         5  
  2         106  
9              
10 2     2   813 use JIP::Mock::Event;
  2         6  
  2         2857  
11              
12             our $VERSION = 'v0.0.4';
13              
14             sub new {
15 35     35 1 57035 my ( $class, %param ) = @ARG;
16              
17 35 100       104 if ( my $error = $class->_validate(%param) ) {
18 4         458 croak 'Cannot instantiate: ' . $error;
19             }
20              
21 31         91 return $class->_instantiate(%param);
22             }
23              
24             sub package { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
25 94     94 1 1002 my ($self) = @ARG;
26              
27 94         300 return $self->{package};
28             }
29              
30             sub want_array {
31 42     42 1 68 my ($self) = @ARG;
32              
33 42         93 return $self->{want_array};
34             }
35              
36             sub times { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
37 43     43 1 2204 my ($self) = @ARG;
38              
39 43         117 return $self->{times};
40             }
41              
42             sub events {
43 13     13 1 1178 my ($self) = @ARG;
44              
45 13         18 my @events = map { $self->_extract_event_state($_) } @{ $self->_events() };
  10         23  
  13         25  
46              
47 13         129 return \@events;
48             }
49              
50             sub override {
51 28     28 1 235 my ( $self, %pair ) = @ARG;
52              
53 28         46 my @pairs;
54 28         80 foreach my $name ( sort keys %pair ) {
55 27         50 my $new_sub = $pair{$name};
56              
57 27 100       56 if ( my $error = $self->_validate_overriding( $name, $new_sub ) ) {
58 5         371 croak 'Cannot override: ' . $error;
59             }
60              
61 22         78 push @pairs, [ $name, $new_sub ];
62             }
63              
64 23         43 foreach my $pair (@pairs) {
65 22         28 my ( $name, $new_sub ) = @{$pair};
  22         45  
66              
67 22         47 $self->_override( $name, $new_sub );
68             }
69              
70 23         108 return;
71             } ## end sub override
72              
73             sub call_original {
74 7     7 1 58 my ( $self, $name, @arguments ) = @ARG;
75              
76 7 100       15 if ( my $error = $self->_validate_call_original($name) ) {
77 4         343 croak 'Cannot call original: ' . $error;
78             }
79              
80 3         7 my $want_array = wantarray;
81 3         7 my $original_sub = $self->_get_original($name);
82              
83             # void context
84 3 100       15 if ( !defined $want_array ) {
    100          
85 1         5 $original_sub->(@arguments);
86              
87 1         9 return;
88             }
89              
90             # is looking for a list value
91             elsif ($want_array) {
92 1         3 my @results = $original_sub->(@arguments);
93              
94 1         16 return @results;
95             }
96              
97             # is looking for a scalar
98             else {
99 1         5 my $result = $original_sub->(@arguments);
100              
101 1         12 return $result;
102             }
103             } ## end sub call_original
104              
105             sub called {
106 6     6 1 31 my ($self) = @ARG;
107              
108 6 100       10 return 1 if keys %{ $self->times() };
  6         12  
109 2         13 return 0;
110             }
111              
112             sub not_called {
113 3     3 1 11 my ($self) = @ARG;
114              
115 3 100       6 return 1 if !$self->called();
116 2         7 return 0;
117             }
118              
119             sub DESTROY {
120 9     9   6049 my ($self) = @ARG;
121              
122 9         25 $self->_restore_all();
123              
124 9         34 return;
125             }
126              
127             sub _validate {
128 35     35   82 my ( undef, %param ) = @ARG;
129              
130 35         58 my $package = $param{package};
131              
132 35 100       99 return 'package name is not present!' if !length $package;
133              
134 32 100       69 return if _is_package_loaded($package);
135              
136 1         6 return sprintf 'package "%s" is not loaded!', $package;
137             }
138              
139             sub _validate_overriding {
140 27     27   58 my ( $self, $name, $new_sub ) = @ARG;
141              
142 27 100       61 return 'name is not present!' if !length $name;
143              
144 26 100       53 if ( !$self->package->can($name) ) {
145 1         7 return sprintf 'cannot override non-existent sub "%s"!', $name;
146             }
147              
148 25 100       56 if ( !$new_sub ) {
149 2         12 return sprintf 'new sub of "%s" is not present!', $name;
150             }
151              
152 23 100       48 return if _is_coderef($new_sub);
153              
154 1         5 return sprintf 'new sub of "%s" is not CODE reference!', $name;
155             }
156              
157             sub _validate_call_original {
158 7     7   15 my ( $self, $name ) = @ARG;
159              
160 7 100       22 return 'name is not present!' if !length $name;
161              
162 4 100       8 return if $self->_get_original($name);
163              
164 1         6 return sprintf 'cannot find sub "%s" by name!', $name;
165             }
166              
167             sub _instantiate {
168 31     31   68 my ( $class, %param ) = @ARG;
169              
170             return bless(
171             {
172             package => $param{package},
173             want_array => $param{want_array},
174 31         212 originals => {},
175             times => {},
176             events => [],
177             },
178             $class,
179             );
180             }
181              
182             sub _events {
183 43     43   64 my ($self) = @ARG;
184              
185 43         94 return $self->{events};
186             }
187              
188             sub _originals {
189 60     60   557 my ($self) = @ARG;
190              
191 60         101 return $self->{originals};
192             }
193              
194             sub _get_original {
195 29     29   45 my ( $self, $name ) = @ARG;
196              
197 29         45 my $originals = $self->_originals();
198              
199 29         43 my $original_sub = $originals->{$name};
200              
201 29         78 return $original_sub;
202             }
203              
204             sub _add_original {
205 22     22   39 my ( $self, $name, $original_sub ) = @ARG;
206              
207 22         39 my $originals = $self->_originals();
208              
209 22 50       50 return if exists $originals->{$name};
210              
211 22         41 $originals->{$name} = $original_sub;
212              
213 22         30 return;
214             }
215              
216             sub _delete_original {
217 0     0   0 my ( $self, $name ) = @ARG;
218              
219 0         0 my $originals = $self->_originals();
220              
221 0         0 return delete $originals->{$name};
222             }
223              
224             sub _override {
225 22     22   39 my ( $self, $name, $new_sub ) = @_;
226              
227 22         51 $self->_collect_original($name);
228              
229 22         42 my $new_sub_wrapper = $self->_init_wrapper( $name, $new_sub );
230              
231 22         60 $self->_monkey_patch( $name, $new_sub_wrapper );
232              
233 22         44 return;
234             }
235              
236             sub _collect_original {
237 22     22   36 my ( $self, $name ) = @ARG;
238              
239 22 50       44 return if $self->_get_original($name);
240              
241 22         43 my $original_sub = $self->package->can($name);
242              
243 22         57 $self->_add_original( $name, $original_sub );
244              
245 22         48 return;
246             }
247              
248             sub _init_wrapper {
249 22     22   42 my ( $self, $name, $new_sub ) = @ARG;
250              
251             return sub {
252 30     30   7359 my @arguments = @ARG;
253 30         49 my $want_array = wantarray;
254              
255 30 100       90 if ( my $first_argument = $arguments[0] ) {
256 22         45 my $package = $self->package();
257              
258             #<<< no perltidy
259 22   66     142 my $is_class_or_object = (
260             ( $first_argument eq $package )
261             || ( ( blessed($first_argument) // q{} ) eq $package )
262             );
263             #>>>
264              
265 22 100       50 if ($is_class_or_object) {
266 20         36 shift @arguments;
267             }
268             }
269              
270 30         71 my %event = (
271             method => $name,
272             arguments => \@arguments,
273             times => $self->_increment_times($name),
274             );
275              
276 30 100       57 if ( $self->want_array() ) {
277 4         9 $event{want_array} = $want_array;
278             }
279              
280 30         124 my $event = JIP::Mock::Event->new(%event);
281              
282 30         78 $self->_collect_event($event);
283              
284             # void context
285 30 100       71 if ( !defined $want_array ) {
    100          
286 16         41 $new_sub->($event);
287              
288 16         5425 return;
289             }
290              
291             # is looking for a list value
292             elsif ($want_array) {
293 8         17 my @results = $new_sub->($event);
294              
295 8         1087 return @results;
296             }
297              
298             # is looking for a scalar
299             else {
300 6         14 my $result = $new_sub->($event);
301              
302 6         2138 return $result;
303             }
304 22         107 };
305             } ## end sub _init_wrapper
306              
307             sub _monkey_patch {
308 22     22   39 my ( $self, $name, $sub ) = @ARG;
309              
310 22         37 my $target = $self->package() . q{::} . $name;
311              
312 2     2   17 no strict 'refs'; ## no critic (ProhibitNoStrict)
  2         4  
  2         69  
313 2     2   11 no warnings 'redefine'; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
  2         4  
  2         844  
314              
315 22         34 *{$target} = $sub;
  22         79  
316              
317 22         37 return;
318             }
319              
320             sub _restore_all {
321 9     9   16 my ($self) = @ARG;
322              
323 9         17 my $originals = $self->_originals();
324              
325 9         14 foreach my $name ( sort keys %{$originals} ) {
  9         35  
326 0         0 $self->_restore($name);
327             }
328              
329 9         14 return;
330             }
331              
332             sub _restore {
333 0     0   0 my ( $self, $name ) = @ARG;
334              
335 0         0 my $original_sub = $self->_delete_original($name);
336              
337 0         0 $self->_monkey_patch( $name, $original_sub );
338              
339 0         0 return;
340             }
341              
342             sub _increment_times {
343 30     30   59 my ( $self, $name ) = @ARG;
344              
345 30         51 my $times = $self->times();
346              
347 30         45 my $count = $times->{$name};
348              
349 30   100     106 $count //= 0;
350              
351 30         46 $count += 1;
352              
353 30         46 $times->{$name} = $count;
354              
355 30         88 return $count;
356             }
357              
358             sub _collect_event {
359 30     30   53 my ( $self, $event ) = @ARG;
360              
361 30         35 push @{ $self->_events() }, $event;
  30         54  
362              
363 30         45 return;
364             }
365              
366             sub _extract_event_state {
367 10     10   16 my ( $self, $event ) = @ARG;
368              
369 10         27 my %state = (
370             method => $event->method(),
371             arguments => $event->arguments(),
372             );
373              
374 10 100       19 if ( $self->want_array() ) {
375 4         10 $state{want_array} = $event->want_array();
376             }
377              
378 10         33 return \%state;
379             }
380              
381             sub _is_coderef {
382 23     23   31 my ($sub) = @ARG;
383              
384 23         94 my $reftype = reftype($sub);
385              
386 23   50     50 $reftype //= q{};
387              
388 23 100       93 return 1 if $reftype eq 'CODE';
389 1         3 return 0;
390             }
391              
392             sub _is_package_loaded {
393 32     32   53 my ($package) = @ARG;
394              
395 32         56 $package .= q{::};
396              
397 2     2   23 no strict 'refs'; ## no critic (ProhibitNoStrict)
  2         4  
  2         130  
398              
399 32 100       41 return 1 if %{$package};
  32         192  
400 1         3 return 0;
401             }
402              
403             1;
404              
405             __END__