Test-MockFile
view release on metacpan or search on metacpan
lib/Test/MockFile.pm view on Meta::CPAN
return unless defined $abs_path && length $abs_path;
# Already mocked? Nothing to do.
return $files_being_mocked{$abs_path} if $files_being_mocked{$abs_path};
my $parent = _find_autovivify_parent($abs_path) or return;
# Create a non-existent file mock (contents=undef means "not there yet")
my $mock = _new_nonexistent_file_mock($abs_path);
# Store in global hash (weak ref, as usual)
$files_being_mocked{$abs_path} = $mock;
Scalar::Util::weaken( $files_being_mocked{$abs_path} );
# Parent holds the strong ref so it stays alive until parent is destroyed
$parent->{'_autovivified_children'} //= [];
push @{ $parent->{'_autovivified_children'} }, $mock;
return $mock;
}
sub _abs_path_to_file {
my ($path) = shift;
return unless defined $path;
# Tilde expansion must happen before making the path absolute
# ~
# ~/...
# ~sawyer
if ( $path =~ m{ ^(~ ([^/]+)? ) }xms ) {
my $req_homedir = $1;
my $username = $2 || getpwuid($<);
my $pw_homedir;
# Reset iterator so we *definitely* start from the first one
# Then reset when done looping over pw entries
endpwent;
while ( my @pwdata = getpwent ) {
if ( $pwdata[0] eq $username ) {
$pw_homedir = $pwdata[7];
endpwent;
last;
}
}
endpwent;
$pw_homedir
or die;
$path =~ s{\Q$req_homedir\E}{$pw_homedir};
}
# Make path absolute if relative
if ( $path !~ m{^/}xms ) {
$path = Cwd::getcwd() . "/$path";
}
# Resolve path components: remove ".", resolve "..", collapse slashes
my @resolved;
for my $part ( split m{/}, $path ) {
next if $part eq '' || $part eq '.';
if ( $part eq '..' ) {
pop @resolved;
next;
}
push @resolved, $part;
}
return '/' . join( '/', @resolved );
}
# Override for Cwd::abs_path / Cwd::realpath that resolves mocked symlinks.
# When a path (or any component of it) involves a mocked symlink, we resolve
# the symlinks ourselves. Otherwise, we delegate to the original implementation.
sub __cwd_abs_path {
my ($path) = @_;
$path = '.' unless defined $path && length $path;
# Make absolute without collapsing .. (symlink-aware resolution does that)
if ( $path !~ m{^/} ) {
$path = Cwd::getcwd() . "/$path";
}
my @remaining = grep { $_ ne '' && $_ ne '.' } split( m{/}, $path );
my $resolved = '';
my $depth = 0;
my $involves_mock = 0;
while (@remaining) {
my $component = shift @remaining;
if ( $component eq '..' ) {
$resolved =~ s{/[^/]+$}{};
next;
}
my $candidate = "$resolved/$component";
my $mock_obj = $files_being_mocked{$candidate};
if ( $mock_obj && $mock_obj->is_link ) {
$involves_mock = 1;
$depth++;
if ( $depth > FOLLOW_LINK_MAX_DEPTH ) {
$! = ELOOP;
return undef;
}
my $target = $mock_obj->readlink;
# Broken symlink: undefined or empty target
return undef unless defined $target && length $target;
my @target_parts = grep { $_ ne '' && $_ ne '.' } split( m{/}, $target );
if ( $target =~ m{^/} ) {
# Absolute target: restart from root
$resolved = '';
}
( run in 0.593 second using v1.01-cache-2.11-cpan-71847e10f99 )