When __rename re-keys directory children in %files_being_mocked, the new hash entries are stored as strong references. Every other insertion point (new(), _new_link_for_broken_symlink(), _maybe_autovivify()) immediately weakens the entry via Scalar::Util::weaken(), but __rename omits this step.
Impact
After renaming a directory, child mock objects cannot be garbage collected. When the user's variable goes out of scope, the mock persists in %files_being_mocked and continues intercepting file operations on those paths.
Reproduction
use Test::MockFile qw(nostrict);
use Scalar::Util qw(isweak);
my $dir = Test::MockFile->new_dir("/tmp/testdir");
my $child = Test::MockFile->file("/tmp/testdir/child.txt", "hello");
my $dest = Test::MockFile->dir("/tmp/newdir");
rename("/tmp/testdir", "/tmp/newdir") or die;
# Entry should be weak but isn't:
say isweak($Test::MockFile::files_being_mocked{"/tmp/newdir/child.txt"}); # prints nothing (false)
# Mock leaks — persists after user drops reference:
undef $child;
say defined $Test::MockFile::files_being_mocked{"/tmp/newdir/child.txt"}; # prints 1 (should be false)
Fix
Add Scalar::Util::weaken( $files_being_mocked{$new_key} ) after the re-keying assignment in __rename.
🤖 Created by Kōan from autonomous session
When
__renamere-keys directory children in%files_being_mocked, the new hash entries are stored as strong references. Every other insertion point (new(),_new_link_for_broken_symlink(),_maybe_autovivify()) immediately weakens the entry viaScalar::Util::weaken(), but__renameomits this step.Impact
After renaming a directory, child mock objects cannot be garbage collected. When the user's variable goes out of scope, the mock persists in
%files_being_mockedand continues intercepting file operations on those paths.Reproduction
Fix
Add
Scalar::Util::weaken( $files_being_mocked{$new_key} )after the re-keying assignment in__rename.🤖 Created by Kōan from autonomous session