File Coverage

blib/lib/JIP/Mock/Control.pm
Criterion Covered Total %
statement 168 176 95.4
branch 44 46 95.6
condition 5 7 71.4
subroutine 36 38 94.7
pod 7 7 100.0
total 260 274 94.8


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