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