1*5486feefSafresh1package Test2::Mock;
2*5486feefSafresh1use strict;
3*5486feefSafresh1use warnings;
4*5486feefSafresh1
5*5486feefSafresh1our $VERSION = '0.000162';
6*5486feefSafresh1
7*5486feefSafresh1use Carp qw/croak confess/;
8*5486feefSafresh1our @CARP_NOT = (__PACKAGE__);
9*5486feefSafresh1
10*5486feefSafresh1use Scalar::Util qw/weaken reftype blessed set_prototype/;
11*5486feefSafresh1use Test2::Util qw/pkg_to_file/;
12*5486feefSafresh1use Test2::Util::Stash qw/parse_symbol slot_to_sig get_symbol get_stash purge_symbol/;
13*5486feefSafresh1use Test2::Util::Sub qw/gen_accessor gen_reader gen_writer/;
14*5486feefSafresh1
15*5486feefSafresh1sub new; # Prevent hashbase from giving us 'new';
16*5486feefSafresh1use Test2::Util::HashBase qw/class parent child _purge_on_destroy _blocked_load _symbols _track sub_tracking call_tracking/;
17*5486feefSafresh1
18*5486feefSafresh1sub new {
19*5486feefSafresh1    my $class = shift;
20*5486feefSafresh1
21*5486feefSafresh1    croak "Called new() on a blessed instance, did you mean to call \$control->class->new()?"
22*5486feefSafresh1        if blessed($class);
23*5486feefSafresh1
24*5486feefSafresh1    my $self = bless({}, $class);
25*5486feefSafresh1
26*5486feefSafresh1    $self->{+SUB_TRACKING}  ||= {};
27*5486feefSafresh1    $self->{+CALL_TRACKING} ||= [];
28*5486feefSafresh1
29*5486feefSafresh1    my @sets;
30*5486feefSafresh1    while (my $arg = shift @_) {
31*5486feefSafresh1        my $val = shift @_;
32*5486feefSafresh1
33*5486feefSafresh1        if ($class->can(uc($arg))) {
34*5486feefSafresh1            $self->{$arg} = $val;
35*5486feefSafresh1            next;
36*5486feefSafresh1        }
37*5486feefSafresh1
38*5486feefSafresh1        push @sets => [$arg, $val];
39*5486feefSafresh1    }
40*5486feefSafresh1
41*5486feefSafresh1    croak "The 'class' field is required"
42*5486feefSafresh1        unless $self->{+CLASS};
43*5486feefSafresh1
44*5486feefSafresh1    for my $set (@sets) {
45*5486feefSafresh1        my ($meth, $val) = @$set;
46*5486feefSafresh1        my $type = reftype($val);
47*5486feefSafresh1
48*5486feefSafresh1        confess "'$meth' is not a valid constructor argument for $class"
49*5486feefSafresh1            unless $self->can($meth);
50*5486feefSafresh1
51*5486feefSafresh1        if (!$type) {
52*5486feefSafresh1            $self->$meth($val);
53*5486feefSafresh1        }
54*5486feefSafresh1        elsif($type eq 'HASH') {
55*5486feefSafresh1            $self->$meth(%$val);
56*5486feefSafresh1        }
57*5486feefSafresh1        elsif($type eq 'ARRAY') {
58*5486feefSafresh1            $self->$meth(@$val);
59*5486feefSafresh1        }
60*5486feefSafresh1        else {
61*5486feefSafresh1            croak "'$val' is not a valid argument for '$meth'"
62*5486feefSafresh1        }
63*5486feefSafresh1    }
64*5486feefSafresh1
65*5486feefSafresh1    return $self;
66*5486feefSafresh1}
67*5486feefSafresh1
68*5486feefSafresh1sub _check {
69*5486feefSafresh1    return unless $_[0]->{+CHILD};
70*5486feefSafresh1    croak "There is an active child controller, cannot proceed";
71*5486feefSafresh1}
72*5486feefSafresh1
73*5486feefSafresh1sub purge_on_destroy {
74*5486feefSafresh1    my $self = shift;
75*5486feefSafresh1    ($self->{+_PURGE_ON_DESTROY}) = @_ if @_;
76*5486feefSafresh1    return $self->{+_PURGE_ON_DESTROY};
77*5486feefSafresh1}
78*5486feefSafresh1
79*5486feefSafresh1sub stash {
80*5486feefSafresh1    my $self = shift;
81*5486feefSafresh1    get_stash($self->{+CLASS});
82*5486feefSafresh1}
83*5486feefSafresh1
84*5486feefSafresh1sub file {
85*5486feefSafresh1    my $self = shift;
86*5486feefSafresh1    my $file = $self->class;
87*5486feefSafresh1    return pkg_to_file($self->class);
88*5486feefSafresh1}
89*5486feefSafresh1
90*5486feefSafresh1sub block_load {
91*5486feefSafresh1    my $self = shift;
92*5486feefSafresh1    $self->_check();
93*5486feefSafresh1
94*5486feefSafresh1    my $file = $self->file;
95*5486feefSafresh1
96*5486feefSafresh1    croak "Cannot block the loading of module '" . $self->class . "', already loaded in file $INC{$file}"
97*5486feefSafresh1        if $INC{$file};
98*5486feefSafresh1
99*5486feefSafresh1    $INC{$file} = __FILE__;
100*5486feefSafresh1
101*5486feefSafresh1    $self->{+_BLOCKED_LOAD} = 1;
102*5486feefSafresh1}
103*5486feefSafresh1
104*5486feefSafresh1my %NEW = (
105*5486feefSafresh1    hash => sub {
106*5486feefSafresh1        my ($class, %params) = @_;
107*5486feefSafresh1        return bless \%params, $class;
108*5486feefSafresh1    },
109*5486feefSafresh1    array => sub {
110*5486feefSafresh1        my ($class, @params) = @_;
111*5486feefSafresh1        return bless \@params, $class;
112*5486feefSafresh1    },
113*5486feefSafresh1    ref => sub {
114*5486feefSafresh1        my ($class, $params) = @_;
115*5486feefSafresh1        return bless $params, $class;
116*5486feefSafresh1    },
117*5486feefSafresh1    ref_copy => sub {
118*5486feefSafresh1        my ($class, $params) = @_;
119*5486feefSafresh1        my $type = reftype($params);
120*5486feefSafresh1
121*5486feefSafresh1        return bless {%$params}, $class
122*5486feefSafresh1            if $type eq 'HASH';
123*5486feefSafresh1
124*5486feefSafresh1        return bless [@$params], $class
125*5486feefSafresh1            if $type eq 'ARRAY';
126*5486feefSafresh1
127*5486feefSafresh1        croak "Not sure how to construct an '$class' from '$params'";
128*5486feefSafresh1    },
129*5486feefSafresh1);
130*5486feefSafresh1
131*5486feefSafresh1sub override_constructor {
132*5486feefSafresh1    my $self = shift;
133*5486feefSafresh1    my ($name, $type) = @_;
134*5486feefSafresh1    $self->_check();
135*5486feefSafresh1
136*5486feefSafresh1    my $sub = $NEW{$type}
137*5486feefSafresh1        || croak "'$type' is not a known constructor type";
138*5486feefSafresh1
139*5486feefSafresh1    $self->override($name => $sub);
140*5486feefSafresh1}
141*5486feefSafresh1
142*5486feefSafresh1sub add_constructor {
143*5486feefSafresh1    my $self = shift;
144*5486feefSafresh1    my ($name, $type) = @_;
145*5486feefSafresh1    $self->_check();
146*5486feefSafresh1
147*5486feefSafresh1    my $sub = $NEW{$type}
148*5486feefSafresh1        || croak "'$type' is not a known constructor type";
149*5486feefSafresh1
150*5486feefSafresh1    $self->add($name => $sub);
151*5486feefSafresh1}
152*5486feefSafresh1
153*5486feefSafresh1sub autoload {
154*5486feefSafresh1    my $self = shift;
155*5486feefSafresh1    $self->_check();
156*5486feefSafresh1    my $class = $self->class;
157*5486feefSafresh1    my $stash = $self->stash;
158*5486feefSafresh1
159*5486feefSafresh1    croak "Class '$class' already has an AUTOLOAD"
160*5486feefSafresh1        if $stash->{AUTOLOAD} && *{$stash->{AUTOLOAD}}{CODE};
161*5486feefSafresh1    croak "Class '$class' already has an can"
162*5486feefSafresh1        if $stash->{can} && *{$stash->{can}}{CODE};
163*5486feefSafresh1
164*5486feefSafresh1    # Weaken this reference so that AUTOLOAD does not prevent its own
165*5486feefSafresh1    # destruction.
166*5486feefSafresh1    weaken(my $c = $self);
167*5486feefSafresh1
168*5486feefSafresh1    my ($file, $line) = (__FILE__, __LINE__ + 3);
169*5486feefSafresh1    my $autoload = eval <<EOT || die "Failed generating AUTOLOAD sub: $@";
170*5486feefSafresh1package $class;
171*5486feefSafresh1#line $line "$file (Generated AUTOLOAD)"
172*5486feefSafresh1our \$AUTOLOAD;
173*5486feefSafresh1    sub {
174*5486feefSafresh1        my (\$self) = \@_;
175*5486feefSafresh1        my (\$pkg, \$name) = (\$AUTOLOAD =~ m/^(.*)::([^:]+)\$/g);
176*5486feefSafresh1        \$AUTOLOAD = undef;
177*5486feefSafresh1
178*5486feefSafresh1        return if \$name eq 'DESTROY';
179*5486feefSafresh1        my \$sub = sub {
180*5486feefSafresh1            my \$self = shift;
181*5486feefSafresh1            (\$self->{\$name}) = \@_ if \@_;
182*5486feefSafresh1            return \$self->{\$name};
183*5486feefSafresh1        };
184*5486feefSafresh1
185*5486feefSafresh1        \$c->add(\$name => \$sub);
186*5486feefSafresh1
187*5486feefSafresh1        if (\$c->{_track}) {
188*5486feefSafresh1            my \$call = {sub_name => \$name, sub_ref => \$sub, args => [\@_]};
189*5486feefSafresh1            push \@{\$c->{sub_tracking}->{\$name}} => \$call;
190*5486feefSafresh1            push \@{\$c->{call_tracking}} => \$call;
191*5486feefSafresh1        }
192*5486feefSafresh1
193*5486feefSafresh1        goto &\$sub;
194*5486feefSafresh1    }
195*5486feefSafresh1EOT
196*5486feefSafresh1
197*5486feefSafresh1    $line = __LINE__ + 3;
198*5486feefSafresh1    my $can = eval <<EOT || die "Failed generating can method: $@";
199*5486feefSafresh1package $class;
200*5486feefSafresh1#line $line "$file (Generated can)"
201*5486feefSafresh1use Scalar::Util 'reftype';
202*5486feefSafresh1    sub {
203*5486feefSafresh1        my (\$self, \$meth) = \@_;
204*5486feefSafresh1        if (\$self->SUPER::can(\$meth)) {
205*5486feefSafresh1            return \$self->SUPER::can(\$meth);
206*5486feefSafresh1        }
207*5486feefSafresh1        elsif (ref \$self && reftype \$self eq 'HASH' && exists \$self->{\$meth}) {
208*5486feefSafresh1            return sub { shift->\$meth(\@_) };
209*5486feefSafresh1        }
210*5486feefSafresh1        return undef;
211*5486feefSafresh1    }
212*5486feefSafresh1EOT
213*5486feefSafresh1
214*5486feefSafresh1    {
215*5486feefSafresh1        local $self->{+_TRACK} = 0;
216*5486feefSafresh1        $self->add(AUTOLOAD => $autoload);
217*5486feefSafresh1        $self->add(can => $can);
218*5486feefSafresh1    }
219*5486feefSafresh1}
220*5486feefSafresh1
221*5486feefSafresh1sub before {
222*5486feefSafresh1    my $self = shift;
223*5486feefSafresh1    my ($name, $sub) = @_;
224*5486feefSafresh1    $self->_check();
225*5486feefSafresh1    my $orig = $self->current($name, required => 1);
226*5486feefSafresh1    $self->_inject({}, $name => set_prototype(sub { $sub->(@_); $orig->(@_) }, prototype $sub));
227*5486feefSafresh1}
228*5486feefSafresh1
229*5486feefSafresh1sub after {
230*5486feefSafresh1    my $self = shift;
231*5486feefSafresh1    my ($name, $sub) = @_;
232*5486feefSafresh1    $self->_check();
233*5486feefSafresh1    my $orig = $self->current($name, required => 1);
234*5486feefSafresh1    $self->_inject(
235*5486feefSafresh1        {},
236*5486feefSafresh1        $name => set_prototype(
237*5486feefSafresh1            sub {
238*5486feefSafresh1                my @out;
239*5486feefSafresh1
240*5486feefSafresh1                my $want = wantarray;
241*5486feefSafresh1
242*5486feefSafresh1                if ($want) {
243*5486feefSafresh1                    @out = $orig->(@_);
244*5486feefSafresh1                }
245*5486feefSafresh1                elsif (defined $want) {
246*5486feefSafresh1                    $out[0] = $orig->(@_);
247*5486feefSafresh1                }
248*5486feefSafresh1                else {
249*5486feefSafresh1                    $orig->(@_);
250*5486feefSafresh1                }
251*5486feefSafresh1
252*5486feefSafresh1                $sub->(@_);
253*5486feefSafresh1
254*5486feefSafresh1                return @out    if $want;
255*5486feefSafresh1                return $out[0] if defined $want;
256*5486feefSafresh1                return;
257*5486feefSafresh1            },
258*5486feefSafresh1            prototype $sub,
259*5486feefSafresh1        )
260*5486feefSafresh1    );
261*5486feefSafresh1}
262*5486feefSafresh1
263*5486feefSafresh1sub around {
264*5486feefSafresh1    my $self = shift;
265*5486feefSafresh1    my ($name, $sub) = @_;
266*5486feefSafresh1    $self->_check();
267*5486feefSafresh1    my $orig = $self->current($name, required => 1);
268*5486feefSafresh1    $self->_inject({}, $name => set_prototype(sub { $sub->($orig, @_) }, prototype $sub));
269*5486feefSafresh1}
270*5486feefSafresh1
271*5486feefSafresh1sub add {
272*5486feefSafresh1    my $self = shift;
273*5486feefSafresh1    $self->_check();
274*5486feefSafresh1    $self->_inject({add => 1}, @_);
275*5486feefSafresh1}
276*5486feefSafresh1
277*5486feefSafresh1sub override {
278*5486feefSafresh1    my $self = shift;
279*5486feefSafresh1    $self->_check();
280*5486feefSafresh1    $self->_inject({}, @_);
281*5486feefSafresh1}
282*5486feefSafresh1
283*5486feefSafresh1sub set {
284*5486feefSafresh1    my $self = shift;
285*5486feefSafresh1    $self->_check();
286*5486feefSafresh1    $self->_inject({set => 1}, @_);
287*5486feefSafresh1}
288*5486feefSafresh1
289*5486feefSafresh1sub current {
290*5486feefSafresh1    my $self = shift;
291*5486feefSafresh1    my ($sym, %params) = @_;
292*5486feefSafresh1
293*5486feefSafresh1    my $out = get_symbol($sym, $self->{+CLASS});
294*5486feefSafresh1    return $out unless $params{required};
295*5486feefSafresh1    confess "Attempt to modify a sub that does not exist '$self->{+CLASS}\::$sym' (Mock operates on packages, not classes, are you looking for a symbol in a parent class?)"
296*5486feefSafresh1        unless $out;
297*5486feefSafresh1    return $out;
298*5486feefSafresh1}
299*5486feefSafresh1
300*5486feefSafresh1sub orig {
301*5486feefSafresh1    my $self = shift;
302*5486feefSafresh1    my ($sym) = @_;
303*5486feefSafresh1
304*5486feefSafresh1    $sym = "&$sym" unless $sym =~ m/^[&\$\%\@]/;
305*5486feefSafresh1
306*5486feefSafresh1    my $syms = $self->{+_SYMBOLS}
307*5486feefSafresh1        or croak "No symbols have been mocked yet";
308*5486feefSafresh1
309*5486feefSafresh1    my $ref = $syms->{$sym};
310*5486feefSafresh1
311*5486feefSafresh1    croak "Symbol '$sym' is not mocked"
312*5486feefSafresh1        unless $ref && @$ref;
313*5486feefSafresh1
314*5486feefSafresh1    my ($orig) = @$ref;
315*5486feefSafresh1
316*5486feefSafresh1    return $orig;
317*5486feefSafresh1}
318*5486feefSafresh1
319*5486feefSafresh1sub track {
320*5486feefSafresh1    my $self = shift;
321*5486feefSafresh1
322*5486feefSafresh1    ($self->{+_TRACK}) = @_ if @_;
323*5486feefSafresh1
324*5486feefSafresh1    return $self->{+_TRACK};
325*5486feefSafresh1}
326*5486feefSafresh1
327*5486feefSafresh1sub clear_call_tracking { @{shift->{+CALL_TRACKING}} = () }
328*5486feefSafresh1
329*5486feefSafresh1sub clear_sub_tracking {
330*5486feefSafresh1    my $self = shift;
331*5486feefSafresh1
332*5486feefSafresh1    unless (@_) {
333*5486feefSafresh1        %{$self->{+SUB_TRACKING}} = ();
334*5486feefSafresh1        return;
335*5486feefSafresh1    }
336*5486feefSafresh1
337*5486feefSafresh1    for my $item (@_) {
338*5486feefSafresh1        delete $self->{+SUB_TRACKING}->{$item};
339*5486feefSafresh1    }
340*5486feefSafresh1
341*5486feefSafresh1    return;
342*5486feefSafresh1}
343*5486feefSafresh1
344*5486feefSafresh1sub _parse_inject {
345*5486feefSafresh1    my $self = shift;
346*5486feefSafresh1    my ($param, $arg) = @_;
347*5486feefSafresh1
348*5486feefSafresh1    if ($param =~ m/^-(.*)$/) {
349*5486feefSafresh1        my $sym = $1;
350*5486feefSafresh1        my $sig = slot_to_sig(reftype($arg));
351*5486feefSafresh1        my $ref = $arg;
352*5486feefSafresh1        return ($sig, $sym, $ref);
353*5486feefSafresh1    }
354*5486feefSafresh1
355*5486feefSafresh1    return ('&', $param, $arg)
356*5486feefSafresh1        if ref($arg) && reftype($arg) eq 'CODE';
357*5486feefSafresh1
358*5486feefSafresh1    my ($is, $field, $val);
359*5486feefSafresh1
360*5486feefSafresh1    if(defined($arg) && !ref($arg) && $arg =~ m/^(rw|ro|wo)$/) {
361*5486feefSafresh1        $is    = $arg;
362*5486feefSafresh1        $field = $param;
363*5486feefSafresh1    }
364*5486feefSafresh1    elsif (!ref($arg)) {
365*5486feefSafresh1        $val = $arg;
366*5486feefSafresh1        $is  = 'val';
367*5486feefSafresh1    }
368*5486feefSafresh1    elsif (reftype($arg) eq 'HASH') {
369*5486feefSafresh1        $field = delete $arg->{field} || $param;
370*5486feefSafresh1
371*5486feefSafresh1        $val = delete $arg->{val};
372*5486feefSafresh1        $is  = delete $arg->{is};
373*5486feefSafresh1
374*5486feefSafresh1        croak "Cannot specify 'is' and 'val' together" if $val && $is;
375*5486feefSafresh1
376*5486feefSafresh1        $is ||= $val ? 'val' : 'rw';
377*5486feefSafresh1
378*5486feefSafresh1        croak "The following keys are not valid when defining a mocked sub with a hashref: " . join(", " => keys %$arg)
379*5486feefSafresh1            if keys %$arg;
380*5486feefSafresh1    }
381*5486feefSafresh1    else {
382*5486feefSafresh1        confess "'$arg' is not a valid argument when defining a mocked sub";
383*5486feefSafresh1    }
384*5486feefSafresh1
385*5486feefSafresh1    my $sub;
386*5486feefSafresh1    if ($is eq 'rw') {
387*5486feefSafresh1        $sub = gen_accessor($field);
388*5486feefSafresh1    }
389*5486feefSafresh1    elsif ($is eq 'ro') {
390*5486feefSafresh1        $sub = gen_reader($field);
391*5486feefSafresh1    }
392*5486feefSafresh1    elsif ($is eq 'wo') {
393*5486feefSafresh1        $sub = gen_writer($field);
394*5486feefSafresh1    }
395*5486feefSafresh1    else { # val
396*5486feefSafresh1        $sub = sub { $val };
397*5486feefSafresh1    }
398*5486feefSafresh1
399*5486feefSafresh1    return ('&', $param, $sub);
400*5486feefSafresh1}
401*5486feefSafresh1
402*5486feefSafresh1sub _inject {
403*5486feefSafresh1    my $self = shift;
404*5486feefSafresh1    my ($params, @pairs) = @_;
405*5486feefSafresh1
406*5486feefSafresh1    my $add = $params->{add};
407*5486feefSafresh1    my $set = $params->{set};
408*5486feefSafresh1
409*5486feefSafresh1    my $class = $self->{+CLASS};
410*5486feefSafresh1
411*5486feefSafresh1    $self->{+_SYMBOLS} ||= {};
412*5486feefSafresh1    my $syms = $self->{+_SYMBOLS};
413*5486feefSafresh1
414*5486feefSafresh1    while (my $param = shift @pairs) {
415*5486feefSafresh1        my $arg = shift @pairs;
416*5486feefSafresh1        my ($sig, $sym, $ref) = $self->_parse_inject($param, $arg);
417*5486feefSafresh1        my $orig = $self->current("$sig$sym");
418*5486feefSafresh1
419*5486feefSafresh1        croak "Cannot override '$sig$class\::$sym', symbol is not already defined"
420*5486feefSafresh1            unless $orig || $add || $set || ($sig eq '&' && $class->can($sym));
421*5486feefSafresh1
422*5486feefSafresh1        # Cannot be too sure about scalars in globs
423*5486feefSafresh1        croak "Cannot add '$sig$class\::$sym', symbol is already defined"
424*5486feefSafresh1            if $add && $orig
425*5486feefSafresh1            && (reftype($orig) ne 'SCALAR' || defined($$orig));
426*5486feefSafresh1
427*5486feefSafresh1        $syms->{"$sig$sym"} ||= [];
428*5486feefSafresh1        push @{$syms->{"$sig$sym"}} => $orig; # Might be undef, thats expected
429*5486feefSafresh1
430*5486feefSafresh1        if ($self->{+_TRACK} && $sig eq '&') {
431*5486feefSafresh1            my $sub_tracker  = $self->{+SUB_TRACKING};
432*5486feefSafresh1            my $call_tracker = $self->{+CALL_TRACKING};
433*5486feefSafresh1            my $sub = $ref;
434*5486feefSafresh1            $ref = sub {
435*5486feefSafresh1                my $call = {sub_name => $sym, sub_ref => $sub, args => [@_]};
436*5486feefSafresh1                push @{$sub_tracker->{$param}} => $call;
437*5486feefSafresh1                push @$call_tracker => $call;
438*5486feefSafresh1                goto &$sub;
439*5486feefSafresh1            };
440*5486feefSafresh1        }
441*5486feefSafresh1
442*5486feefSafresh1        no strict 'refs';
443*5486feefSafresh1        no warnings 'redefine';
444*5486feefSafresh1        *{"$class\::$sym"} = $ref;
445*5486feefSafresh1    }
446*5486feefSafresh1
447*5486feefSafresh1    return;
448*5486feefSafresh1}
449*5486feefSafresh1
450*5486feefSafresh1sub _set_or_unset {
451*5486feefSafresh1    my $self = shift;
452*5486feefSafresh1    my ($symbol, $set) = @_;
453*5486feefSafresh1
454*5486feefSafresh1    my $class = $self->{+CLASS};
455*5486feefSafresh1
456*5486feefSafresh1    return purge_symbol($symbol, $class)
457*5486feefSafresh1        unless $set;
458*5486feefSafresh1
459*5486feefSafresh1    my $sym = parse_symbol($symbol, $class);
460*5486feefSafresh1    no strict 'refs';
461*5486feefSafresh1    no warnings 'redefine';
462*5486feefSafresh1    *{"$class\::$sym->{name}"} = $set;
463*5486feefSafresh1}
464*5486feefSafresh1
465*5486feefSafresh1sub restore {
466*5486feefSafresh1    my $self = shift;
467*5486feefSafresh1    my ($sym) = @_;
468*5486feefSafresh1    $self->_check();
469*5486feefSafresh1
470*5486feefSafresh1    $sym = "&$sym" unless $sym =~ m/^[&\$\%\@]/;
471*5486feefSafresh1
472*5486feefSafresh1    my $syms = $self->{+_SYMBOLS}
473*5486feefSafresh1        or croak "No symbols are mocked";
474*5486feefSafresh1
475*5486feefSafresh1    my $ref = $syms->{$sym};
476*5486feefSafresh1
477*5486feefSafresh1    croak "Symbol '$sym' is not mocked"
478*5486feefSafresh1        unless $ref && @$ref;
479*5486feefSafresh1
480*5486feefSafresh1    my $old = pop @$ref;
481*5486feefSafresh1    delete $syms->{$sym} unless @$ref;
482*5486feefSafresh1
483*5486feefSafresh1    return $self->_set_or_unset($sym, $old);
484*5486feefSafresh1}
485*5486feefSafresh1
486*5486feefSafresh1sub reset {
487*5486feefSafresh1    my $self = shift;
488*5486feefSafresh1    my ($sym) = @_;
489*5486feefSafresh1    $self->_check();
490*5486feefSafresh1
491*5486feefSafresh1    $sym = "&$sym" unless $sym =~ m/^[&\$\%\@]/;
492*5486feefSafresh1
493*5486feefSafresh1    my $syms = $self->{+_SYMBOLS}
494*5486feefSafresh1        or croak "No symbols are mocked";
495*5486feefSafresh1
496*5486feefSafresh1    my $ref = delete $syms->{$sym};
497*5486feefSafresh1
498*5486feefSafresh1    croak "Symbol '$sym' is not mocked"
499*5486feefSafresh1        unless $ref && @$ref;
500*5486feefSafresh1
501*5486feefSafresh1    my ($old) = @$ref;
502*5486feefSafresh1
503*5486feefSafresh1    return $self->_set_or_unset($sym, $old);
504*5486feefSafresh1}
505*5486feefSafresh1
506*5486feefSafresh1sub reset_all {
507*5486feefSafresh1    my $self = shift;
508*5486feefSafresh1    $self->_check();
509*5486feefSafresh1
510*5486feefSafresh1    my $syms = $self->{+_SYMBOLS} || return;
511*5486feefSafresh1
512*5486feefSafresh1    $self->reset($_) for keys %$syms;
513*5486feefSafresh1
514*5486feefSafresh1    delete $self->{+_SYMBOLS};
515*5486feefSafresh1}
516*5486feefSafresh1
517*5486feefSafresh1sub _purge {
518*5486feefSafresh1    my $self = shift;
519*5486feefSafresh1    my $stash = $self->stash;
520*5486feefSafresh1    delete $stash->{$_} for keys %$stash;
521*5486feefSafresh1}
522*5486feefSafresh1
523*5486feefSafresh1sub DESTROY {
524*5486feefSafresh1    my $self = shift;
525*5486feefSafresh1
526*5486feefSafresh1    delete $self->{+CHILD};
527*5486feefSafresh1    $self->reset_all if $self->{+_SYMBOLS};
528*5486feefSafresh1
529*5486feefSafresh1    delete $INC{$self->file} if $self->{+_BLOCKED_LOAD};
530*5486feefSafresh1
531*5486feefSafresh1    $self->_purge if $self->{+_PURGE_ON_DESTROY};
532*5486feefSafresh1}
533*5486feefSafresh1
534*5486feefSafresh11;
535*5486feefSafresh1
536*5486feefSafresh1__END__
537*5486feefSafresh1
538*5486feefSafresh1=pod
539*5486feefSafresh1
540*5486feefSafresh1=encoding UTF-8
541*5486feefSafresh1
542*5486feefSafresh1=head1 NAME
543*5486feefSafresh1
544*5486feefSafresh1Test2::Mock - Module for managing mocked classes and instances.
545*5486feefSafresh1
546*5486feefSafresh1=head1 DESCRIPTION
547*5486feefSafresh1
548*5486feefSafresh1This module lets you add and override methods for any package temporarily. When
549*5486feefSafresh1the instance is destroyed it will restore the package to its original state.
550*5486feefSafresh1
551*5486feefSafresh1=head1 SYNOPSIS
552*5486feefSafresh1
553*5486feefSafresh1    use Test2::Mock;
554*5486feefSafresh1    use MyClass;
555*5486feefSafresh1
556*5486feefSafresh1    my $mock = Test2::Mock->new(
557*5486feefSafresh1        track => $BOOL, # enable call tracking if desired
558*5486feefSafresh1        class => 'MyClass',
559*5486feefSafresh1        override => [
560*5486feefSafresh1            name => sub { 'fred' },
561*5486feefSafresh1            ...
562*5486feefSafresh1        ],
563*5486feefSafresh1        add => [
564*5486feefSafresh1            is_mocked => sub { 1 }
565*5486feefSafresh1            ...
566*5486feefSafresh1        ],
567*5486feefSafresh1        ...
568*5486feefSafresh1    );
569*5486feefSafresh1
570*5486feefSafresh1    # Unmock the 'name' sub
571*5486feefSafresh1    $mock->restore('name');
572*5486feefSafresh1
573*5486feefSafresh1    ...
574*5486feefSafresh1
575*5486feefSafresh1    $mock = undef; # Will remove all the mocking
576*5486feefSafresh1
577*5486feefSafresh1=head1 CONSTRUCTION
578*5486feefSafresh1
579*5486feefSafresh1=head1 METHODS
580*5486feefSafresh1
581*5486feefSafresh1=over 4
582*5486feefSafresh1
583*5486feefSafresh1=item $mock = Test2::Mock->new(class => $CLASS, ...)
584*5486feefSafresh1
585*5486feefSafresh1This will create a new instance of L<Test2::Mock> that manages mocking
586*5486feefSafresh1for the specified C<$CLASS>.
587*5486feefSafresh1
588*5486feefSafresh1Any C<Test2::Mock> method can be used as a constructor argument, each
589*5486feefSafresh1should be followed by an arrayref of arguments to be used within the method. For
590*5486feefSafresh1instance the C<add()> method:
591*5486feefSafresh1
592*5486feefSafresh1    my $mock = Test2::Mock->new(
593*5486feefSafresh1        class => 'AClass',
594*5486feefSafresh1        add => [foo => sub { 'foo' }],
595*5486feefSafresh1    );
596*5486feefSafresh1
597*5486feefSafresh1is identical to this:
598*5486feefSafresh1
599*5486feefSafresh1    my $mock = Test2::Mock->new(
600*5486feefSafresh1        class => 'AClass',
601*5486feefSafresh1    );
602*5486feefSafresh1    $mock->add(foo => sub { 'foo' });
603*5486feefSafresh1
604*5486feefSafresh1=item $mock->track($bool)
605*5486feefSafresh1
606*5486feefSafresh1Turn tracking on or off. Any sub added/overridden/set when tracking is on will
607*5486feefSafresh1log every call in a hash retrievable via C<< $mock->tracking >>. Changing the
608*5486feefSafresh1tracking toggle will not affect subs already altered, but will affect any
609*5486feefSafresh1additional alterations.
610*5486feefSafresh1
611*5486feefSafresh1=item $hashref = $mock->sub_tracking
612*5486feefSafresh1
613*5486feefSafresh1The tracking data looks like this:
614*5486feefSafresh1
615*5486feefSafresh1    {
616*5486feefSafresh1        sub_name => [
617*5486feefSafresh1            {sub_name => $sub_name, sub_ref => $mock_subref, args => [... copy of @_ from the call ... ]},
618*5486feefSafresh1            ...,
619*5486feefSafresh1            ...,
620*5486feefSafresh1        ],
621*5486feefSafresh1    }
622*5486feefSafresh1
623*5486feefSafresh1Unlike call_tracking, this lists all calls by sub, so you can choose to only
624*5486feefSafresh1look at the sub specific calls.
625*5486feefSafresh1
626*5486feefSafresh1B<Please note:> The hashref items with the subname and args are shared with
627*5486feefSafresh1call_tracking, modifying one modifies the other, so copy first!
628*5486feefSafresh1
629*5486feefSafresh1=item $arrayref = $mock->call_tracking
630*5486feefSafresh1
631*5486feefSafresh1The tracking data looks like this:
632*5486feefSafresh1
633*5486feefSafresh1    [
634*5486feefSafresh1        {sub_name => $sub_name, sub_ref => $mock_subref, args => [... copy of @_ from the call ... ]},
635*5486feefSafresh1        ...,
636*5486feefSafresh1        ...,
637*5486feefSafresh1    ]
638*5486feefSafresh1
639*5486feefSafresh1Unlike sub_tracking this lists all calls to any mocked sub, in the order they
640*5486feefSafresh1were called. To filter by sub use sub_tracking.
641*5486feefSafresh1
642*5486feefSafresh1B<Please note:> The hashref items with the subname and args are shared with
643*5486feefSafresh1sub_tracking, modifying one modifies the other, so copy first!
644*5486feefSafresh1
645*5486feefSafresh1=item $mock->clear_sub_tracking()
646*5486feefSafresh1
647*5486feefSafresh1=item $mock->clear_sub_tracking(\@subnames)
648*5486feefSafresh1
649*5486feefSafresh1Clear tracking data. With no arguments ALL tracking data is cleared. When
650*5486feefSafresh1arguments are provided then only those specific keys will be cleared.
651*5486feefSafresh1
652*5486feefSafresh1=item $mock->clear_call_tracking()
653*5486feefSafresh1
654*5486feefSafresh1Clear all items from call_tracking.
655*5486feefSafresh1
656*5486feefSafresh1=item $mock->add('symbol' => ..., 'symbol2' => ...)
657*5486feefSafresh1
658*5486feefSafresh1=item $mock->override('symbol1' => ..., 'symbol2' => ...)
659*5486feefSafresh1
660*5486feefSafresh1=item $mock->set('symbol1' => ..., 'symbol2' => ...)
661*5486feefSafresh1
662*5486feefSafresh1C<add()> and C<override()> are the primary ways to add/modify methods for a
663*5486feefSafresh1class. Both accept the exact same type of arguments. The difference is that
664*5486feefSafresh1C<override> will fail unless the symbol you are overriding already exists,
665*5486feefSafresh1C<add> on the other hand will fail if the symbol does already exist.
666*5486feefSafresh1
667*5486feefSafresh1C<set()> was more recently added for cases where you may not know if the sub
668*5486feefSafresh1already exists. These cases are rare, and set should be avoided (think of it
669*5486feefSafresh1like 'no strict'). However there are valid use cases, so it was added.
670*5486feefSafresh1
671*5486feefSafresh1B<Note:> Think of override as a push operation. If you call override on the
672*5486feefSafresh1same symbol multiple times it will track that. You can use C<restore()> as a
673*5486feefSafresh1pop operation to go back to the previous mock. C<reset> can be used to remove
674*5486feefSafresh1all the mocking for a symbol.
675*5486feefSafresh1
676*5486feefSafresh1Arguments must be a symbol name, with optional sigil, followed by a new
677*5486feefSafresh1specification of the symbol. If no sigil is specified then '&' (sub) is
678*5486feefSafresh1assumed. A simple example of overriding a sub:
679*5486feefSafresh1
680*5486feefSafresh1    $mock->override(foo => sub { 'overridden foo' });
681*5486feefSafresh1    my $val = $class->foo; # Runs our override
682*5486feefSafresh1    # $val is now set to 'overridden foo'
683*5486feefSafresh1
684*5486feefSafresh1You can also simply provide a value and it will be wrapped in a sub for you:
685*5486feefSafresh1
686*5486feefSafresh1    $mock->override( foo => 'foo' );
687*5486feefSafresh1
688*5486feefSafresh1The example above will generate a sub that always returns the string 'foo'.
689*5486feefSafresh1
690*5486feefSafresh1There are three *special* values that can be used to generate accessors:
691*5486feefSafresh1
692*5486feefSafresh1    $mock->add(
693*5486feefSafresh1        name => 'rw',   # Generates a read/write accessor
694*5486feefSafresh1        age  => 'ro',   # Generates a read only accessor
695*5486feefSafresh1        size => 'wo',   # Generates a write only accessor
696*5486feefSafresh1    );
697*5486feefSafresh1
698*5486feefSafresh1If you want to have a sub that actually returns one of the three special strings, or
699*5486feefSafresh1that returns a coderef, you can use a hashref as the spec:
700*5486feefSafresh1
701*5486feefSafresh1    my $ref = sub { 'my sub' };
702*5486feefSafresh1    $mock->add(
703*5486feefSafresh1        rw_string => { val => 'rw' },
704*5486feefSafresh1        ro_string => { val => 'ro' },
705*5486feefSafresh1        wo_string => { val => 'wo' },
706*5486feefSafresh1        coderef   => { val => $ref }, # the coderef method returns $ref each time
707*5486feefSafresh1    );
708*5486feefSafresh1
709*5486feefSafresh1You can also override/add other symbol types, such as hash:
710*5486feefSafresh1
711*5486feefSafresh1    package Foo;
712*5486feefSafresh1    ...
713*5486feefSafresh1
714*5486feefSafresh1    $mock->add('%foo' => {a => 1});
715*5486feefSafresh1
716*5486feefSafresh1    print $Foo::foo{a}; # prints '1'
717*5486feefSafresh1
718*5486feefSafresh1You can also tell mock to deduce the symbol type for the add/override from the
719*5486feefSafresh1reference, rules are similar to glob assignments:
720*5486feefSafresh1
721*5486feefSafresh1    $mock->add(
722*5486feefSafresh1        -foo => sub { 'foo' },     # Adds the &foo sub to the package
723*5486feefSafresh1        -foo => { foo => 1 },      # Adds the %foo hash to the package
724*5486feefSafresh1        -foo => [ 'f', 'o', 'o' ], # Adds the @foo array to the package
725*5486feefSafresh1        -foo => \"foo",            # Adds the $foo scalar to the package
726*5486feefSafresh1    );
727*5486feefSafresh1
728*5486feefSafresh1=item $mock->restore($SYMBOL)
729*5486feefSafresh1
730*5486feefSafresh1Restore the symbol to what it was before the last override. If the symbol was
731*5486feefSafresh1recently added this will remove it. If the symbol has been overridden multiple
732*5486feefSafresh1times this will ONLY restore it to the previous state. Think of C<override> as a
733*5486feefSafresh1push operation, and C<restore> as the pop operation.
734*5486feefSafresh1
735*5486feefSafresh1=item $mock->reset($SYMBOL)
736*5486feefSafresh1
737*5486feefSafresh1Remove all mocking of the symbol and restore the original symbol. If the symbol
738*5486feefSafresh1was initially added then it will be completely removed.
739*5486feefSafresh1
740*5486feefSafresh1=item $mock->orig($SYMBOL)
741*5486feefSafresh1
742*5486feefSafresh1This will return the original symbol, before any mocking. For symbols that were
743*5486feefSafresh1added this will return undef.
744*5486feefSafresh1
745*5486feefSafresh1=item $mock->current($SYMBOL)
746*5486feefSafresh1
747*5486feefSafresh1This will return the current symbol.
748*5486feefSafresh1
749*5486feefSafresh1=item $mock->reset_all
750*5486feefSafresh1
751*5486feefSafresh1Remove all added symbols, and restore all overridden symbols to their originals.
752*5486feefSafresh1
753*5486feefSafresh1=item $mock->add_constructor($NAME => $TYPE)
754*5486feefSafresh1
755*5486feefSafresh1=item $mock->override_constructor($NAME => $TYPE)
756*5486feefSafresh1
757*5486feefSafresh1This can be used to inject constructors. The first argument should be the name
758*5486feefSafresh1of the constructor. The second argument specifies the constructor type.
759*5486feefSafresh1
760*5486feefSafresh1The C<hash> type is the most common, all arguments are used to create a new
761*5486feefSafresh1hash that is blessed.
762*5486feefSafresh1
763*5486feefSafresh1    hash => sub  {
764*5486feefSafresh1        my ($class, %params) = @_;
765*5486feefSafresh1        return bless \%params, $class;
766*5486feefSafresh1    };
767*5486feefSafresh1
768*5486feefSafresh1The C<array> type is similar to the hash type, but accepts a list instead of
769*5486feefSafresh1key/value pairs:
770*5486feefSafresh1
771*5486feefSafresh1    array => sub {
772*5486feefSafresh1        my ($class, @params) = @_;
773*5486feefSafresh1        return bless \@params, $class;
774*5486feefSafresh1    };
775*5486feefSafresh1
776*5486feefSafresh1The C<ref> type takes a reference and blesses it. This will modify your
777*5486feefSafresh1original input argument.
778*5486feefSafresh1
779*5486feefSafresh1    ref => sub {
780*5486feefSafresh1        my ($class, $params) = @_;
781*5486feefSafresh1        return bless $params, $class;
782*5486feefSafresh1    };
783*5486feefSafresh1
784*5486feefSafresh1The C<ref_copy> type will copy your reference and bless the copy:
785*5486feefSafresh1
786*5486feefSafresh1    ref_copy => sub {
787*5486feefSafresh1        my ($class, $params) = @_;
788*5486feefSafresh1        my $type = reftype($params);
789*5486feefSafresh1
790*5486feefSafresh1        return bless {%$params}, $class
791*5486feefSafresh1            if $type eq 'HASH';
792*5486feefSafresh1
793*5486feefSafresh1        return bless [@$params], $class
794*5486feefSafresh1            if $type eq 'ARRAY';
795*5486feefSafresh1
796*5486feefSafresh1        croak "Not sure how to construct a '$class' from '$params'";
797*5486feefSafresh1    };
798*5486feefSafresh1
799*5486feefSafresh1=item $mock->before($NAME, sub { ... })
800*5486feefSafresh1
801*5486feefSafresh1This will replace the original sub C<$NAME> with a new sub that calls your
802*5486feefSafresh1custom code just before calling the original method. The return from your
803*5486feefSafresh1custom sub is ignored. Your sub and the original both get the unmodified
804*5486feefSafresh1arguments.
805*5486feefSafresh1
806*5486feefSafresh1=item $mock->after($NAME, sub { ... })
807*5486feefSafresh1
808*5486feefSafresh1This is similar to before, except your callback runs after the original code.
809*5486feefSafresh1The return from your callback is ignored.
810*5486feefSafresh1
811*5486feefSafresh1=item $mock->around($NAME, sub { ... })
812*5486feefSafresh1
813*5486feefSafresh1This gives you the chance to wrap the original sub:
814*5486feefSafresh1
815*5486feefSafresh1    $mock->around(foo => sub {
816*5486feefSafresh1        my $orig = shift;
817*5486feefSafresh1        my $self = shift;
818*5486feefSafresh1        my (@args) = @_;
819*5486feefSafresh1
820*5486feefSafresh1        ...
821*5486feefSafresh1        $self->$orig(@args);
822*5486feefSafresh1        ...
823*5486feefSafresh1
824*5486feefSafresh1        return ...;
825*5486feefSafresh1    });
826*5486feefSafresh1
827*5486feefSafresh1The original sub is passed in as the first argument, even before C<$self>. You
828*5486feefSafresh1are responsible for making sure your wrapper sub returns the correct thing.
829*5486feefSafresh1
830*5486feefSafresh1=item $mock->autoload
831*5486feefSafresh1
832*5486feefSafresh1This will inject an C<AUTOLOAD> sub into the class. This autoload will
833*5486feefSafresh1automatically generate read-write accessors for any sub called that does not
834*5486feefSafresh1already exist.
835*5486feefSafresh1
836*5486feefSafresh1=item $mock->block_load
837*5486feefSafresh1
838*5486feefSafresh1This will prevent the real class from loading until the mock is destroyed. This
839*5486feefSafresh1will fail if the class is already loaded. This will let you mock a class
840*5486feefSafresh1completely without loading the original module.
841*5486feefSafresh1
842*5486feefSafresh1=item $pm_file = $mock->file
843*5486feefSafresh1
844*5486feefSafresh1This returns the relative path to the file for the module. This corresponds to
845*5486feefSafresh1the C<%INC> entry.
846*5486feefSafresh1
847*5486feefSafresh1=item $bool = $mock->purge_on_destroy($bool)
848*5486feefSafresh1
849*5486feefSafresh1When true, this will cause the package stash to be completely obliterated when
850*5486feefSafresh1the mock object falls out of scope or is otherwise destroyed. You do not
851*5486feefSafresh1normally want this.
852*5486feefSafresh1
853*5486feefSafresh1=item $stash = $mock->stash
854*5486feefSafresh1
855*5486feefSafresh1This returns the stash for the class being mocked. This is the equivalent of:
856*5486feefSafresh1
857*5486feefSafresh1    my $stash = \%{"${class}\::"};
858*5486feefSafresh1
859*5486feefSafresh1This saves you from needing to turn off strict.
860*5486feefSafresh1
861*5486feefSafresh1=item $class = $mock->class
862*5486feefSafresh1
863*5486feefSafresh1The class being mocked by this instance.
864*5486feefSafresh1
865*5486feefSafresh1=item $p = $mock->parent
866*5486feefSafresh1
867*5486feefSafresh1If you mock a class twice the first instance is the parent, the second is the
868*5486feefSafresh1child. This prevents the parent from being destroyed before the child, which
869*5486feefSafresh1would lead to a very unpleasant situation.
870*5486feefSafresh1
871*5486feefSafresh1=item $c = $mock->child
872*5486feefSafresh1
873*5486feefSafresh1Returns the child mock, if any.
874*5486feefSafresh1
875*5486feefSafresh1=back
876*5486feefSafresh1
877*5486feefSafresh1=head1 SOURCE
878*5486feefSafresh1
879*5486feefSafresh1The source code repository for Test2-Suite can be found at
880*5486feefSafresh1L<https://github.com/Test-More/Test2-Suite/>.
881*5486feefSafresh1
882*5486feefSafresh1=head1 MAINTAINERS
883*5486feefSafresh1
884*5486feefSafresh1=over 4
885*5486feefSafresh1
886*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
887*5486feefSafresh1
888*5486feefSafresh1=back
889*5486feefSafresh1
890*5486feefSafresh1=head1 AUTHORS
891*5486feefSafresh1
892*5486feefSafresh1=over 4
893*5486feefSafresh1
894*5486feefSafresh1=item Chad Granum E<lt>exodist@cpan.orgE<gt>
895*5486feefSafresh1
896*5486feefSafresh1=back
897*5486feefSafresh1
898*5486feefSafresh1=head1 COPYRIGHT
899*5486feefSafresh1
900*5486feefSafresh1Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
901*5486feefSafresh1
902*5486feefSafresh1This program is free software; you can redistribute it and/or
903*5486feefSafresh1modify it under the same terms as Perl itself.
904*5486feefSafresh1
905*5486feefSafresh1See L<https://dev.perl.org/licenses/>
906*5486feefSafresh1
907*5486feefSafresh1=cut
908