Test-MockFile

 view release on metacpan or  search on metacpan

lib/Test/MockFile.pm  view on Meta::CPAN

        keys %files_being_mocked
    };

    return @files_in_dir;
}

# Walk up the path to find the nearest ancestor directory with autovivify enabled.
# Returns the mock object if found, undef otherwise.
sub _find_autovivify_parent {
    my ($abs_path) = @_;

    return unless %_autovivify_dirs;

    my $dir = $abs_path;
    while ( $dir =~ s{/[^/]+$}{} && length $dir ) {
        if ( my $mock = $_autovivify_dirs{$dir} ) {
            return $mock;
        }
    }

    return;
}

# If $abs_path is under an autovivify directory, create a non-existent file mock
# for it and return the mock. Otherwise return undef.
sub _maybe_autovivify {
    my ($abs_path) = @_;

    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;



( run in 2.228 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )