File Coverage

blib/lib/Test/Mocha/Util.pm
Criterion Covered Total %
statement 86 86 100.0
branch 35 36 100.0
condition 10 10 100.0
subroutine 18 18 100.0
pod 0 7 100.0
total 149 157 100.0


line stmt bran cond sub pod time code
1             package Test::Mocha::Util;
2             # ABSTRACT: Internal utility functions
3             $Test::Mocha::Util::VERSION = '0.61';
4 21     21   74 use strict;
  21         20  
  21         630  
5 21     21   98 use warnings;
  21         26  
  21         519  
6              
7             # smartmatch dependencies
8 21     21   456 use 5.010001;
  21         69  
  21         678  
9 21     21   8871 use experimental qw( smartmatch );
  21         12770  
  21         85  
10              
11 21     21   1081 use Carp qw( confess croak );
  21         27  
  21         859  
12 21     21   83 use Exporter qw( import );
  21         23  
  21         497  
13 21     21   77 use Scalar::Util qw( blessed looks_like_number refaddr );
  21         21  
  21         851  
14 21     21   80 use Test::Builder;
  21         29  
  21         307  
15 21     21   3516 use Try::Tiny;
  21         11516  
  21         13362  
16              
17             our @EXPORT_OK = qw(
18             extract_method_name
19             find_caller
20             find_stub
21             getattr
22             get_method_call
23             has_caller_package
24             is_called
25             match
26             );
27              
28             my $TB = Test::Builder->new;
29              
30             sub extract_method_name {
31             # """Extracts the method name from its fully qualified name."""
32             # uncoverable pod
33 569     569 0 631 my ($method_name) = @_;
34 569         2377 $method_name =~ s/.*:://sm;
35 569         1238 return $method_name;
36             }
37              
38             sub find_caller {
39             # """Search the call stack to find an external caller"""
40             # uncoverable pod
41 499     499 0 425 my ( $package, $file, $line );
42              
43 499         414 my $i = 1;
44 499         415 while () {
45 502         2543 ( $package, $file, $line ) = caller $i++;
46 502 100       1345 last if $package ne 'UNIVERSAL::ref';
47             }
48 499         1929 return ( $file, $line );
49             }
50              
51             sub find_stub {
52             # uncoverable pod
53 503     503 0 481 my ( $mock, $method_call ) = @_;
54              
55 503         571 my $stubs = getattr( $mock, 'stubs' );
56 503 100       1023 return if !defined $stubs->{ $method_call->name };
57              
58 168         154 foreach my $stub ( @{ $stubs->{ $method_call->name } } ) {
  168         265  
59 224 100       412 return $stub if $stub->satisfied_by($method_call);
60             }
61 29         75 return;
62             }
63              
64             sub getattr {
65             # """Safely get the attribute value of an object."""
66             # uncoverable pod
67 2760     2760 0 4183 my ( $object, $attribute ) = @_;
68              
69             # uncoverable branch true
70 2760 50       4019 confess 'getattr() must be given an object'
71             if not ref $object;
72 2760 100       4342 confess "Attribute '$attribute' does not exist for object '$object'"
73             if not exists $object->{$attribute};
74              
75 2758         4263 return $object->{$attribute};
76             }
77              
78             sub get_method_call {
79             # """
80             # Get the last method called on a mock object,
81             # removes it from the invocation history,
82             # and restores the last method stub execution.
83             # """
84             # uncoverable pod
85 267     267 0 327 my ($coderef) = @_;
86              
87             try {
88 267     267   7083 $coderef->();
89             }
90             catch {
91             ## no critic (RequireCarping,RequireExtendedFormatting)
92 16 100 100 16   1120 die $_
93             if ( m{^No arguments allowed after a slurpy type constraint}sm
94             || m{^Slurpy argument must be a type of ArrayRef or HashRef}sm );
95             ## use critic
96 267         1261 };
97              
98 253 100       3361 croak 'Coderef must have a single method invocation on a mock object'
99             if $Test::Mocha::Mock::num_method_calls != 1;
100              
101 249         232 my $method_call = $Test::Mocha::Mock::last_method_call;
102 249         529 my $mock = $method_call->invocant;
103              
104             # restore the last method stub execution
105 249 100       415 if ( defined $Test::Mocha::Mock::last_execution ) {
106 4         11 my $stub = find_stub( $mock, $method_call );
107 4         6 unshift @{ $stub->{executions} }, $Test::Mocha::Mock::last_execution;
  4         9  
108             }
109              
110             # remove the last method call from the invocation history
111 249         213 pop @{ getattr( $mock, 'calls' ) };
  249         303  
112              
113 249         697 return $method_call;
114             }
115              
116             sub has_caller_package {
117             # """
118             # Returns whether the given C<$package> is in the current call stack.
119             # """
120             # uncoverable pod
121 4     4 0 7 my ($package) = @_;
122              
123 4         8 my $level = 1;
124 4         29 while ( my ($caller) = caller $level++ ) {
125 32 100       241 return 1 if $caller eq $package;
126             }
127 1         3 return;
128             }
129              
130             #sub is_called {
131             # # """
132             # # Tests whether the given method call was invoked the correct number of
133             # # times. The test is run as a Test::Builder test.
134             # # """
135             # # uncoverable pod
136             # my ( $method_call, %options ) = @_;
137             #
138             # my $mock = $method_call->invocant;
139             # my $calls = getattr( $mock, 'calls' );
140             #
141             # my $got = grep { $method_call->satisfied_by($_) } @{$calls};
142             # my $exp;
143             # my $test_ok;
144             #
145             # ## no critic (ProhibitCascadingIfElse)
146             # # uncoverable branch false count:4
147             # if ( defined $options{times} ) {
148             # $exp = $options{times};
149             # $test_ok = $got == $options{times};
150             # }
151             # elsif ( defined $options{at_least} ) {
152             # $exp = "at least $options{at_least}";
153             # $test_ok = $got >= $options{at_least};
154             # }
155             # elsif ( defined $options{at_most} ) {
156             # $exp = "at most $options{at_most}";
157             # $test_ok = $got <= $options{at_most};
158             # }
159             # elsif ( defined $options{between} ) {
160             # my ( $lower, $upper ) = @{ $options{between} };
161             # $exp = "between $lower and $upper";
162             # $test_ok = $lower <= $got && $got <= $upper;
163             # }
164             # ## use critic
165             #
166             # my $test_name =
167             # defined $options{test_name}
168             # ? $options{test_name}
169             # : sprintf '%s was called %s time(s)', $method_call, $exp;
170             #
171             # # Test failure report should not trace back to Mocha modules
172             # local $Test::Builder::Level = 2;
173             #
174             # $TB->ok( $test_ok, $test_name );
175             #
176             # # output diagnostics to aid with debugging
177             # unless ( $test_ok || $TB->in_todo ) {
178             # my $diag = <<"END";
179             #Error: unexpected number of calls to '$method_call'
180             # got: $got time(s)
181             # expected: $exp time(s)
182             #Complete method call history (most recent call last):
183             #END
184             # if ( @{$calls} ) {
185             # $diag .= ( (q{ }) . $_->stringify_long . "\n" )
186             # foreach @{$calls};
187             # }
188             # else {
189             # $diag .= " (No methods were called)\n";
190             # }
191             # $TB->diag($diag);
192             # }
193             # return;
194             #}
195              
196             sub match {
197             # """Match 2 values for equality."""
198             # uncoverable pod
199 333     333 0 308 my ( $x, $y ) = @_;
200              
201             # This function uses smart matching, but we need to limit the scenarios
202             # in which it is used because of its quirks.
203              
204             # ref types must match
205 333 100       711 return if ref $x ne ref $y;
206              
207             # objects match only if they are the same object
208 287 100 100     952 if ( blessed($x) || ref($x) eq 'CODE' ) {
209 32         263 return refaddr($x) == refaddr($y);
210             }
211              
212             # don't smartmatch on arrays because it recurses
213             # which leads to the same quirks that we want to avoid
214 255 100       337 if ( ref($x) eq 'ARRAY' ) {
215 14 100       13 return if $#{$x} != $#{$y};
  14         17  
  14         28  
216              
217             # recurse to handle nested structures
218 12         14 foreach ( 0 .. $#{$x} ) {
  12         21  
219 34 100       46 return if !match( $x->[$_], $y->[$_] );
220             }
221 8         30 return 1;
222             }
223              
224 241 100       314 if ( ref($x) eq 'HASH' ) {
225             # smartmatch only matches the hash keys
226 8 100       30 return if not $x ~~ $y;
227              
228             # ... but we want to match the hash values too
229 6         8 foreach ( keys %{$x} ) {
  6         14  
230 11 100       15 return if !match( $x->{$_}, $y->{$_} );
231             }
232 4         16 return 1;
233             }
234              
235             # avoid smartmatch doing number matches on strings
236             # e.g. '5x' ~~ 5 is true
237 233 100 100     966 return if looks_like_number($x) xor looks_like_number($y);
238              
239 211         979 return $x ~~ $y;
240             }
241              
242             # sub print_call_stack {
243             # # """
244             # # Returns whether the given C<$package> is in the current call stack.
245             # # """
246             # # uncoverable pod
247             # my ( $message ) = @_;
248             #
249             # print $message, "\n";
250             # my $level = 1;
251             # while ( my ( $caller, $file, $line, $sub ) = caller $level++ ) {
252             # print "\t[$caller] $sub\n";
253             # }
254             # return;
255             # }
256              
257             1;