From b1568ac5e5cfb7ef3c15dd5c67e193261fea5412 Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Wed, 28 Oct 2026 17:21:24 +0100 Subject: [PATCH] Lock the fixture directory itself, so workers with their own TMPDIR share the lock --- .../PHPUnit/AbstractRectorTestCase.php | 30 +++++-------------- .../FixtureDirectoryLockTest.php | 17 +++++++++++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php index bfd706bbe3a..587887e3be0 100644 --- a/src/Testing/PHPUnit/AbstractRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -298,37 +298,23 @@ private function lockFixtureDirectory(string $directory): void // a test can call doTestFile() more than once; a second lock from this process would wait for the first $this->unlockFixtureDirectory(); - $lockFilePath = $this->createFixtureDirectoryLockFilePath($directory); + // PHP cannot open a directory on Windows + if ($this->isWindows()) { + return; + } - $fixtureDirectoryLock = fopen($lockFilePath, 'c'); + // lock the directory itself: a lock file in sys_get_temp_dir() is not shared by test runners + // that give each worker its own TMPDIR, like fast-unit + $fixtureDirectoryLock = fopen($directory, 'r'); if ($fixtureDirectoryLock === false) { - throw new ShouldNotHappenException('Cannot open the lock file ' . $lockFilePath); + throw new ShouldNotHappenException('Cannot open the fixture directory ' . $directory); } flock($fixtureDirectoryLock, LOCK_EX); $this->fixtureDirectoryLock = $fixtureDirectoryLock; } - /** - * The lock file lives in a directory in the system temp directory, so a project that tests its own rules gets no file in its fixture directories. - * The lock file is never deleted: a process waiting on a deleted file would hold a lock no other process sees. - */ - private function createFixtureDirectoryLockFilePath(string $directory): string - { - $realDirectory = realpath($directory); - - if ($realDirectory === false) { - $realDirectory = $directory; - } - - $lockDirectory = sys_get_temp_dir() . '/rector_fixture_lock'; - - FileSystem::createDir($lockDirectory); - - return $lockDirectory . '/' . sha1($realDirectory) . '.lock'; - } - private function unlockFixtureDirectory(): void { if (! is_resource($this->fixtureDirectoryLock)) { diff --git a/tests/Testing/FixtureDirectoryLock/FixtureDirectoryLockTest.php b/tests/Testing/FixtureDirectoryLock/FixtureDirectoryLockTest.php index ac977202249..71991915777 100644 --- a/tests/Testing/FixtureDirectoryLock/FixtureDirectoryLockTest.php +++ b/tests/Testing/FixtureDirectoryLock/FixtureDirectoryLockTest.php @@ -15,6 +15,23 @@ public function testDoesNotWriteLockFileIntoFixtureDirectory(): void $this->assertFileDoesNotExist(__DIR__ . '/Fixture/.fixture.lock'); } + public function testLocksFixtureDirectoryWhateverTheTempDirectory(): void + { + if ($this->isWindows()) { + $this->markTestSkipped('PHP cannot open a directory on Windows'); + } + + $this->doTestFile(__DIR__ . '/Fixture/some_function.php.inc'); + + // a worker with its own TMPDIR opens the same directory, so it must see the lock + $otherProcessHandle = fopen(__DIR__ . '/Fixture', 'r'); + $this->assertNotFalse($otherProcessHandle); + + $this->assertFalse(flock($otherProcessHandle, LOCK_EX | LOCK_NB)); + + fclose($otherProcessHandle); + } + public function provideConfigFilePath(): string { return __DIR__ . '/config/configured_rule.php';