Skip to content

Comments

feat: override Cwd::abs_path to resolve mocked symlinks (#139)#211

Draft
Koan-Bot wants to merge 1 commit intocpanel:masterfrom
atoomic:koan.atoomic/fix-cwd-abs-path
Draft

feat: override Cwd::abs_path to resolve mocked symlinks (#139)#211
Koan-Bot wants to merge 1 commit intocpanel:masterfrom
atoomic:koan.atoomic/fix-cwd-abs-path

Conversation

@Koan-Bot
Copy link
Contributor

Summary

Fixes #139.

Cwd::abs_path() uses XS (bsd_realpath) which calls the real readlink() at the C level, completely bypassing our CORE::GLOBAL::readlink override. This means mocked symlinks are invisible to abs_path/realpath.

Fix: Override Cwd::abs_path, Cwd::realpath, Cwd::fast_abs_path, and Cwd::fast_realpath with a wrapper that walks path components one-by-one, resolving mocked symlinks from %files_being_mocked. When no mocked paths are involved, the original implementation is called.

What works:

  • Direct mocked symlinks: abs_path('/mocked_link') → follows to target
  • Chained symlinks: a → b → c
  • Intermediate symlinks: abs_path('/link/subdir/file') when /link is mocked
  • Relative symlink targets (including ..)
  • Circular symlink detection (ELOOP)
  • Falls through to original XS abs_path for non-mocked paths
  • Works with both Cwd::abs_path(...) and imported abs_path(...)

Limitation:

When Cwd is imported BEFORE Test::MockFile (e.g. use Cwd qw(abs_path); use Test::MockFile), the imported abs_path captures the original XS function. Load Test::MockFile first for the import to work. The fully qualified Cwd::abs_path(...) always works regardless of load order.

Test plan

🤖 Generated with Claude Code

Koan-Bot added a commit to atoomic/Test-MockFile that referenced this pull request Feb 24, 2026
IO::File::open() uses CORE::open internally, which bypasses
CORE::GLOBAL::open and makes IO::File->new() ignore mocked files.

Fix: replace IO::File::open with a wrapper that checks if the target
file is mocked. If mocked, tie the existing IO::File glob directly
(avoiding the need to create a new handle). If not mocked, fall
through to the original IO::File::open via goto.

This follows the same pattern as the Cwd::abs_path override (PR cpanel#211)
— intercept at the namespace level when C/XS code bypasses CORE::GLOBAL.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@atoomic atoomic self-assigned this Feb 24, 2026
@Koan-Bot
Copy link
Contributor Author

Rebase: feat: override Cwd::abs_path to resolve mocked symlinks (#139)

Branch koan.atoomic/fix-cwd-abs-path has been rebased onto master and force-pushed.

Actions

  • Rebased koan.atoomic/fix-cwd-abs-path onto origin/master
  • Force-pushed koan.atoomic/fix-cwd-abs-path to origin

Automated by Kōan

@Koan-Bot Koan-Bot force-pushed the koan.atoomic/fix-cwd-abs-path branch from c60db1b to d9f0de2 Compare February 24, 2026 21:30
Koan-Bot added a commit to atoomic/Test-MockFile that referenced this pull request Feb 24, 2026
IO::File::open() uses CORE::open internally, which bypasses
CORE::GLOBAL::open and makes IO::File->new() ignore mocked files.

Fix: replace IO::File::open with a wrapper that checks if the target
file is mocked. If mocked, tie the existing IO::File glob directly
(avoiding the need to create a new handle). If not mocked, fall
through to the original IO::File::open via goto.

This follows the same pattern as the Cwd::abs_path override (PR cpanel#211)
— intercept at the namespace level when C/XS code bypasses CORE::GLOBAL.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Cwd::abs_path uses XS (bsd_realpath) which bypasses CORE::GLOBAL::readlink,
so mocked symlinks were invisible to it. Fix: replace Cwd::abs_path,
realpath, fast_abs_path, and fast_realpath with a wrapper that walks
path components, resolves mocked symlinks, and falls through to the
original implementation when no mocked paths are involved.

The override is installed in the same BEGIN block as the CORE::GLOBAL
overrides. When Cwd is imported AFTER Test::MockFile, the imported
functions also get the override.

Features:
- Resolves direct mocked symlinks
- Follows chained symlinks (a -> b -> c)
- Resolves intermediate symlinks in paths (/link/subdir/file)
- Handles relative symlink targets (including ..)
- Detects circular symlinks (ELOOP)
- Falls through to original for non-mocked paths

21 new tests in t/cwd_abs_path.t.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@Koan-Bot Koan-Bot force-pushed the koan.atoomic/fix-cwd-abs-path branch from d9f0de2 to 344bcc7 Compare February 25, 2026 05:04
Koan-Bot added a commit to atoomic/Test-MockFile that referenced this pull request Feb 25, 2026
IO::File::open() uses CORE::open internally, which bypasses
CORE::GLOBAL::open and makes IO::File->new() ignore mocked files.

Fix: replace IO::File::open with a wrapper that checks if the target
file is mocked. If mocked, tie the existing IO::File glob directly
(avoiding the need to create a new handle). If not mocked, fall
through to the original IO::File::open via goto.

This follows the same pattern as the Cwd::abs_path override (PR cpanel#211)
— intercept at the namespace level when C/XS code bypasses CORE::GLOBAL.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Test::MockFile symlinks does not play nice with Cwd...

2 participants