Skip to content

Commit b5f82c2

Browse files
authored
feat: Improve the FileSystemTestCase (#57)
Simplify the usage by no longer requiring to implement an abstract method. A significant (internal, but observable) change is that we no longer use a namespaced directory. Indeed, instead of having: ``` /path/to/system/default-tmp/ TestCaseThreadSafeNamespace/ tmpDir1 tmpDir2 ... ``` We now have: ``` /path/to/system/default-tmp/ tmpDir1 tmpDir2 ... ``` Indeed, the creation of the temporary directory is already thread safe, so there is no need for the user to implement an abstract method. And because of it, we can remove the usage of a namespace which is not that useful and if anything may mess up the thread safety.
1 parent 52f923b commit b5f82c2

File tree

5 files changed

+78
-54
lines changed

5 files changed

+78
-54
lines changed

README.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ An example of a PHPUnit test:
123123

124124
<?php declare(strict_types=1);
125125

126-
namespace App;
126+
namespace App\Tests;
127127

128128
use Fidry\FileSystem\FS;
129129
use Fidry\FileSystem\Test\FileSystemTestCase;
@@ -134,25 +134,12 @@ use function is_string;
134134

135135
final class MyAppFileSystemTest extends FileSystemTestCase
136136
{
137-
// This method needs to be implemented by your test or base filesystem test class.
138-
public static function getTmpDirNamespace(): string
139-
{
140-
// This is to make it thread safe with Infection. If you are not using
141-
// infection or do not need thread safety, this can return a constant
142-
// string, e.g. your project/library name.
143-
$threadId = getenv('TEST_TOKEN');
144-
145-
if (!is_string($threadId)) {
146-
$threadId = '';
147-
}
148-
149-
return 'MyApp'.$threadId;
150-
}
151-
152137
public function test_it_works(): void
153138
{
139+
// The temporary directory can be changed by overriding `::getTmpDirPrefix()`.
140+
154141
// This file is dumped into a temporary directory. Here,
155-
// something like '/private/var/folders/p3/lkw0cgjj2fq0656q_9rd0mk80000gn/T/MyApp/MyAppFileSystemTest10000'
142+
// something like '/private/var/folders/p3/lkw0cgjj2fq0656q_9rd0mk80000gn/T/AppTestsMyAppFileSystemTest10000'
156143
// on OSX.
157144
FS::dumpFile('file1', '');
158145

@@ -162,6 +149,17 @@ final class MyAppFileSystemTest extends FileSystemTestCase
162149

163150
self::assertSame(['file1'], $this->normalizePaths($files));
164151
}
152+
153+
// Utility methods available:
154+
/**
155+
* @param iterable<string|Stringable> $paths
156+
*
157+
* @return list<string> File real paths relative to the current temporary directory
158+
*/
159+
function normalizePaths(iterable $paths): array;
160+
161+
static function safeChdir(string $directory): void;
162+
static function safeGetCurrentWorkingDirectory(): string;
165163
}
166164

167165
```

UPGRADE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
- The class `FileSystem` was renamed `NativeFileSystem`. `FileSystem` is now an
44
interface.
5+
- Made explicit the dependency on `ext-mbstring`. If you do not have this dependency
6+
met use [symfony/polyfill-mbstring](https://packagist.org/packages/symfony/polyfill-mbstring).
57
- Deprecated the `::getFileContents()` method in favour of `::readFile()`.
68
- Deprecated the `::isAbsolutePath()` method in favour of `Path::isAbsolute()`.
79
- Deprecated the `::isRelativePath()` method in favour of `Path::isRelative()`.
10+
- Deprecated the `::makeTmpDir()` method in favour of `::tmpDir()`.
11+
- Deprecated the `::getNamespacedTmpDir()` method in favour of `::tmpDir()`.
12+
- Deprecated the `FileSystemTestCase::getTmpDirNamespace()` has been removed. This was requiring too
13+
much boilerplate. Instead, the temporary directory is now already thread safe although it no longer
14+
creates one unique directory.

infection.json.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,11 @@
4040
"FunctionCallRemoval": false,
4141
"MethodCallRemoval": false,
4242
"PublicVisibility": false,
43+
"ProtectedVisibility": false,
44+
"UnwrapStrReplace": {
45+
"ignore": [
46+
"Fidry\\FileSystem\\Test\\FileSystemTestCase::getTmpDirPrefix"
47+
]
48+
}
4349
}
4450
}

src/Test/FileSystemTestCase.php

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
use function array_map;
4444
use function array_values;
4545
use function chdir;
46-
use function dirname;
4746
use function getcwd;
4847
use function is_array;
4948
use function iterator_to_array;
@@ -55,34 +54,18 @@
5554

5655
abstract class FileSystemTestCase extends TestCase
5756
{
58-
protected static ?string $lastKnownTmpNamespace = null;
59-
6057
protected string $cwd = '';
6158
protected string $tmp = '';
6259

63-
/**
64-
* To make it thread safe you cna make the namespace different on a thread basis,
65-
* e.g. based on an environment variable which indicates the thread "ID".
66-
*/
67-
abstract public static function getTmpDirNamespace(): string;
68-
69-
public static function tearDownAfterClass(): void
70-
{
71-
// Cleans up whatever was there before. Indeed, upon failure PHPUnit may fail to trigger the
72-
// `tearDown()` method and as a result some temporary files may still remain.
73-
if (null !== static::$lastKnownTmpNamespace) {
74-
FS::remove(static::$lastKnownTmpNamespace);
75-
}
76-
}
77-
7860
protected function setUp(): void
7961
{
62+
$tmpDirPrefix = $this->getTmpDirPrefix();
63+
8064
$this->cwd = self::safeGetCurrentWorkingDirectory();
81-
$this->tmp = FS::makeTmpDir(
82-
static::getTmpDirNamespace(),
83-
static::class,
84-
);
85-
static::$lastKnownTmpNamespace = dirname($this->tmp);
65+
// We use the real path here as, for example, in OSX, the path returned
66+
// is otherwise a link.
67+
$this->tmp = FS::realPath(FS::tmpDir($tmpDirPrefix));
68+
8669
self::safeChdir($this->tmp);
8770
}
8871

@@ -94,6 +77,9 @@ protected function tearDown(): void
9477
self::safeChdir($this->cwd);
9578
FS::remove($this->tmp);
9679
}
80+
81+
$this->cwd = '';
82+
$this->tmp = '';
9783
}
9884

9985
/**
@@ -118,7 +104,7 @@ final protected function normalizePaths(iterable $paths): array
118104
return array_values($normalizedPaths);
119105
}
120106

121-
private static function safeChdir(string $directory): void
107+
final protected static function safeChdir(string $directory): void
122108
{
123109
$chdirResult = chdir($directory);
124110

@@ -132,7 +118,7 @@ private static function safeChdir(string $directory): void
132118
}
133119
}
134120

135-
private static function safeGetCurrentWorkingDirectory(): string
121+
final protected static function safeGetCurrentWorkingDirectory(): string
136122
{
137123
$result = getcwd();
138124

@@ -142,4 +128,14 @@ private static function safeGetCurrentWorkingDirectory(): string
142128

143129
return $result;
144130
}
131+
132+
/**
133+
* If the test case is `App\Tests\MyFilesystemServiceTestCase`, the default prefix will be "App\Tests\MyFilesystemServiceTestCase".
134+
*
135+
* @return string
136+
*/
137+
protected function getTmpDirPrefix(): string
138+
{
139+
return str_replace('\\', '', static::class);
140+
}
145141
}

tests/Test/FilesystemTestCaseTest.php

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@
4141
use PHPUnit\Framework\Attributes\CoversClass;
4242
use PHPUnit\Framework\Attributes\Small;
4343
use Symfony\Component\Finder\Finder;
44-
use function getenv;
45-
use function is_string;
44+
use function getcwd;
4645

4746
/**
4847
* @internal
@@ -51,22 +50,40 @@
5150
#[Small]
5251
final class FilesystemTestCaseTest extends FileSystemTestCase
5352
{
54-
public static function getTmpDirNamespace(): string
53+
private string $cwdBeforeSetUp = '';
54+
55+
protected function setUp(): void
56+
{
57+
$this->cwdBeforeSetUp = getcwd();
58+
59+
parent::setUp();
60+
}
61+
62+
protected function tearDown(): void
5563
{
56-
$threadId = getenv('TEST_TOKEN');
64+
$tmpBeforeCleanup = $this->tmp;
65+
66+
parent::tearDown();
5767

58-
if (!is_string($threadId)) {
59-
$threadId = '';
60-
}
68+
$cwdAfterTearDown = getcwd();
6169

62-
return 'FidryFilesystem'.$threadId;
70+
// It is not very elegant to test in a teardown, but it's easier that
71+
// way...
72+
73+
self::assertSame('', $this->cwd, 'Expected the current working directory to have been reset.');
74+
self::assertSame('', $this->tmp, 'Expected the current temporary directory to have been reset.');
75+
self::assertSame($this->cwdBeforeSetUp, $cwdAfterTearDown, 'Expected the current working directory to have been restored.');
76+
self::assertDirectoryDoesNotExist($tmpBeforeCleanup, 'Expected the temporary directory to have been removed.');
6377
}
6478

65-
public function test_it_works(): void
79+
public function test_it_creates_a_temporary_directory_to_which_we_switch_to(): void
6680
{
67-
self::assertNotNull($this->cwd);
68-
self::assertNotNull($this->tmp);
69-
self::assertNotNull(self::$lastKnownTmpNamespace);
81+
$cwd = getcwd();
82+
83+
self::assertNotSame('', $this->cwd, 'Expected the current working directory to be set.');
84+
self::assertNotSame('', $this->tmp, 'Expected the current temporary directory to be set.');
85+
self::assertSame($this->cwdBeforeSetUp, $this->cwd, 'Expected the current working directory before setup to have been stored.');
86+
self::assertSame($this->tmp, $cwd, 'Expected the current working directory to be the temporary directory.');
7087
}
7188

7289
public function test_it_can_provide_the_relative_paths(): void

0 commit comments

Comments
 (0)