Skip to content

Commit 9361461

Browse files
authored
fix(Cache): isolate the factories cache files used by the tests (#10647)
1 parent 1eed7d9 commit 9361461

7 files changed

Lines changed: 202 additions & 23 deletions

File tree

‎system/Cache/FactoriesCache/FileVarExportHandler.php‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313

1414
namespace CodeIgniter\Cache\FactoriesCache;
1515

16-
final class FileVarExportHandler
16+
final readonly class FileVarExportHandler
1717
{
18-
private string $path = WRITEPATH . 'cache';
18+
private string $path;
19+
20+
/**
21+
* @param string|null $path The directory where the cache files are stored.
22+
* Defaults to the "writable/cache" directory.
23+
*/
24+
public function __construct(?string $path = null)
25+
{
26+
$this->path = rtrim($path ?? WRITEPATH . 'cache', '/');
27+
}
1928

2029
public function save(string $key, mixed $val): void
2130
{
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of CodeIgniter 4 framework.
7+
*
8+
* (c) CodeIgniter Foundation <admin@codeigniter.com>
9+
*
10+
* For the full copyright and license information, please view
11+
* the LICENSE file that was distributed with this source code.
12+
*/
13+
14+
namespace Tests\Support\Cache;
15+
16+
use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler;
17+
18+
/**
19+
* Gives the test class its own directory for the files created
20+
* by the FileVarExportHandler.
21+
*
22+
* The handler stores its files in the shared "writable/cache" directory by
23+
* default. Since the random test execution runs the components in parallel,
24+
* one component may delete the cache file that another component is
25+
* asserting on. A dedicated directory per test class avoids that.
26+
*/
27+
trait IsolatedCacheTrait
28+
{
29+
/**
30+
* The directory used to store the cache files of the test class.
31+
*/
32+
private static string $isolatedCachePath;
33+
34+
public static function setUpBeforeClassIsolatedCache(): void
35+
{
36+
helper('filesystem');
37+
38+
self::$isolatedCachePath = WRITEPATH . 'cache_test_' . str_replace('\\', '_', static::class);
39+
40+
// Remove the files left over by a previous run.
41+
if (is_dir(self::$isolatedCachePath)) {
42+
delete_files(self::$isolatedCachePath, true);
43+
@rmdir(self::$isolatedCachePath);
44+
}
45+
}
46+
47+
public static function tearDownAfterClassIsolatedCache(): void
48+
{
49+
if (! isset(self::$isolatedCachePath) || ! is_dir(self::$isolatedCachePath)) {
50+
return;
51+
}
52+
53+
delete_files(self::$isolatedCachePath, true);
54+
@rmdir(self::$isolatedCachePath);
55+
}
56+
57+
/**
58+
* Returns a handler that stores its files in the isolated directory.
59+
*/
60+
protected function getIsolatedCacheHandler(): FileVarExportHandler
61+
{
62+
return new FileVarExportHandler(self::$isolatedCachePath);
63+
}
64+
65+
/**
66+
* Returns the path of the isolated cache directory.
67+
*/
68+
protected static function getIsolatedCachePath(): string
69+
{
70+
return self::$isolatedCachePath;
71+
}
72+
}

‎tests/system/Autoloader/FileLocatorCachedTest.php‎

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,30 @@
1717
use Config\Autoload;
1818
use Config\Modules;
1919
use PHPUnit\Framework\Attributes\Group;
20+
use Tests\Support\Cache\IsolatedCacheTrait;
2021

2122
/**
2223
* @internal
2324
*/
2425
#[Group('Others')]
2526
final class FileLocatorCachedTest extends FileLocatorTest
2627
{
28+
use IsolatedCacheTrait;
29+
2730
private FileVarExportHandler $handler;
2831

32+
public static function setUpBeforeClass(): void
33+
{
34+
parent::setUpBeforeClass();
35+
36+
self::setUpBeforeClassIsolatedCache();
37+
}
38+
2939
public static function tearDownAfterClass(): void
3040
{
31-
parent::tearDownAfterClass();
41+
self::tearDownAfterClassIsolatedCache();
3242

33-
// Delete cache file.
34-
$autoloader = new Autoloader();
35-
$handler = new FileVarExportHandler();
36-
$fileLocator = new FileLocator($autoloader);
37-
$locator = new FileLocatorCached($fileLocator, $handler);
38-
$locator->deleteCache();
43+
parent::tearDownAfterClass();
3944
}
4045

4146
protected function setUp(): void
@@ -61,7 +66,7 @@ protected function setUp(): void
6166
'Acme\Sample' => TESTPATH . '_support/does/not/exists',
6267
]);
6368

64-
$this->handler = new FileVarExportHandler();
69+
$this->handler = $this->getIsolatedCacheHandler();
6570
$fileLocator = new FileLocator($autoloader);
6671
$this->locator = new FileLocatorCached($fileLocator, $this->handler);
6772
}
@@ -70,6 +75,10 @@ protected function tearDown(): void
7075
{
7176
$this->locator->__destruct();
7277

78+
// Prevent the destructor from writing the cache file
79+
// when the test object is destroyed.
80+
$this->locator->deleteCache();
81+
7382
parent::tearDown();
7483
}
7584

‎tests/system/Cache/FactoriesCacheFileVarExportHandlerTest.php‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,50 @@
1515

1616
use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler;
1717
use PHPUnit\Framework\Attributes\Group;
18+
use Tests\Support\Cache\IsolatedCacheTrait;
1819

1920
/**
2021
* @internal
2122
*/
2223
#[Group('Others')]
2324
final class FactoriesCacheFileVarExportHandlerTest extends AbstractFactoriesCacheHandlerTestCase
2425
{
26+
use IsolatedCacheTrait;
27+
2528
public static function setUpBeforeClass(): void
2629
{
2730
parent::setUpBeforeClass();
2831

29-
helper('filesystem');
32+
self::setUpBeforeClassIsolatedCache();
33+
}
34+
35+
public static function tearDownAfterClass(): void
36+
{
37+
self::tearDownAfterClassIsolatedCache();
38+
39+
parent::tearDownAfterClass();
3040
}
3141

3242
protected function createFactoriesCache(): void
3343
{
34-
$this->handler = new FileVarExportHandler();
44+
$this->handler = $this->getIsolatedCacheHandler();
3545
$this->cache = new FactoriesCache($this->handler);
3646
}
3747

48+
public function testUsesTheWritableCacheDirectoryByDefault(): void
49+
{
50+
$handler = new FileVarExportHandler();
51+
52+
$this->assertSame(WRITEPATH . 'cache', $this->getPrivateProperty($handler, 'path'));
53+
}
54+
3855
public function testSaveCreatesDirectoryAndFileWithCorrectPermissions(): void
3956
{
4057
$dir = WRITEPATH . 'cache_test_dir_' . uniqid('', true);
4158
$oldUmask = umask(0000);
4259

4360
try {
44-
$handler = new FileVarExportHandler();
45-
$this->setPrivateProperty($handler, 'path', $dir);
61+
$handler = new FileVarExportHandler($dir);
4662

4763
$handler->save('test_key', ['data']);
4864

‎tests/system/Commands/Cache/ClearCacheTest.php‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use CodeIgniter\Autoloader\FileLocator;
1717
use CodeIgniter\Autoloader\FileLocatorCached;
1818
use CodeIgniter\Cache\CacheFactory;
19-
use CodeIgniter\Cache\FactoriesCache\FileVarExportHandler;
2019
use CodeIgniter\Cache\Handlers\FileHandler;
2120
use CodeIgniter\CLI\CLI;
2221
use CodeIgniter\Config\Factories;
@@ -25,17 +24,33 @@
2524
use Config\Cache;
2625
use Config\Services;
2726
use PHPUnit\Framework\Attributes\Group;
27+
use Tests\Support\Cache\IsolatedCacheTrait;
2828

2929
/**
3030
* @internal
3131
*/
3232
#[Group('Others')]
3333
final class ClearCacheTest extends CIUnitTestCase
3434
{
35+
use IsolatedCacheTrait;
3536
use StreamFilterTrait;
3637

3738
private Cache $config;
3839

40+
public static function setUpBeforeClass(): void
41+
{
42+
parent::setUpBeforeClass();
43+
44+
self::setUpBeforeClassIsolatedCache();
45+
}
46+
47+
public static function tearDownAfterClass(): void
48+
{
49+
self::tearDownAfterClassIsolatedCache();
50+
51+
parent::tearDownAfterClass();
52+
}
53+
3954
protected function setUp(): void
4055
{
4156
parent::setUp();
@@ -98,7 +113,7 @@ public function testClearCacheWorks(): void
98113

99114
public function testClearCacheDiscardsSharedLocatorCache(): void
100115
{
101-
$handler = new FileVarExportHandler();
116+
$handler = $this->getIsolatedCacheHandler();
102117
$handler->delete('FileLocatorCache');
103118

104119
$locator = new FileLocatorCached(new FileLocator(service('autoloader')), $handler);
@@ -117,6 +132,10 @@ public function testClearCacheDiscardsSharedLocatorCache(): void
117132
$this->assertArrayHasKey('Config/Cache', $cached['search']);
118133

119134
$handler->delete('FileLocatorCache');
135+
136+
// Prevent the destructor from writing the cache file
137+
// when the locator is destroyed.
138+
$locator->deleteCache();
120139
}
121140

122141
public function testClearCacheFails(): void

‎tests/system/Commands/Utilities/OptimizeTest.php‎

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,39 @@
2222
use CodeIgniter\Test\StreamFilterTrait;
2323
use Config\Services;
2424
use PHPUnit\Framework\Attributes\Group;
25+
use Tests\Support\Cache\IsolatedCacheTrait;
2526

2627
/**
2728
* @internal
2829
*/
2930
#[Group('Others')]
3031
final class OptimizeTest extends CIUnitTestCase
3132
{
33+
use IsolatedCacheTrait;
3234
use ReflectionHelper;
3335
use StreamFilterTrait;
3436

3537
private FileVarExportHandler $handler;
3638

39+
public static function setUpBeforeClass(): void
40+
{
41+
parent::setUpBeforeClass();
42+
43+
self::setUpBeforeClassIsolatedCache();
44+
}
45+
46+
public static function tearDownAfterClass(): void
47+
{
48+
self::tearDownAfterClassIsolatedCache();
49+
50+
parent::tearDownAfterClass();
51+
}
52+
3753
protected function setUp(): void
3854
{
3955
parent::setUp();
4056

41-
$this->handler = new FileVarExportHandler();
57+
$this->handler = $this->getIsolatedCacheHandler();
4258
$this->handler->delete('FileLocatorCache');
4359
}
4460

@@ -74,15 +90,52 @@ public function testClearCacheDiscardsSharedLocatorCache(): void
7490
$this->assertArrayNotHasKey('Config/App', $cached['search']);
7591
$this->assertArrayHasKey('Config/Cache', $cached['search']);
7692
$this->assertStringContainsString('Removed FileLocatorCache.', $this->getStreamFilterBuffer());
93+
94+
// Prevent the destructor from writing the cache file
95+
// when the locator is destroyed.
96+
$locator->deleteCache();
7797
}
7898

7999
public function testClearCacheDeletesTheFileWithoutSharedLocatorCache(): void
80100
{
81-
$this->handler->save('FileLocatorCache', ['search' => []]);
82-
83-
($this->getClearCache())();
84-
85-
$this->assertFalse($this->handler->get('FileLocatorCache'));
86-
$this->assertStringContainsString('Removed FileLocatorCache.', $this->getStreamFilterBuffer());
101+
$writePath = rtrim(sys_get_temp_dir(), '\\/') . DIRECTORY_SEPARATOR . 'ci_test_optimize_' . uniqid('', true) . DIRECTORY_SEPARATOR;
102+
mkdir($writePath . 'cache', 0755, true);
103+
104+
$escapedWritePath = var_export($writePath, true);
105+
$escapedBootstrap = var_export(SYSTEMPATH . 'Test/bootstrap.php', true);
106+
107+
try {
108+
$code = <<<PHP
109+
define('WRITEPATH', {$escapedWritePath});
110+
require {$escapedBootstrap};
111+
112+
use CodeIgniter\\Cache\\FactoriesCache\\FileVarExportHandler;
113+
use CodeIgniter\\Commands\\Utilities\\Optimize;
114+
use CodeIgniter\\Test\\ReflectionHelper;
115+
116+
\$handler = new FileVarExportHandler();
117+
\$handler->save('FileLocatorCache', ['search' => []]);
118+
119+
\$clearCache = ReflectionHelper::getPrivateMethodInvoker(
120+
new Optimize(service('logger'), service('commands')),
121+
'clearCache'
122+
);
123+
\$clearCache();
124+
125+
if (\$handler->get('FileLocatorCache') !== false) {
126+
exit(1);
127+
}
128+
PHP;
129+
130+
$cmd = sprintf('%s -r %s 2>&1', escapeshellarg(PHP_BINARY), escapeshellarg($code));
131+
exec($cmd, $output, $status);
132+
133+
$this->assertSame(0, $status, implode(PHP_EOL, $output));
134+
$this->assertStringContainsString('Removed FileLocatorCache.', implode(PHP_EOL, $output));
135+
} finally {
136+
helper('filesystem');
137+
delete_files($writePath, true);
138+
@rmdir($writePath);
139+
}
87140
}
88141
}

‎user_guide_src/source/changelogs/v4.7.6.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Message Changes
2525
Changes
2626
*******
2727

28+
- **Cache:** Added an optional ``$path`` parameter to ``FileVarExportHandler::__construct()`` so the cache files can be stored in a custom directory.
2829
- **HTTP:** Added ``ContentSecurityPolicy::addDefaultSrc()`` to allow appending sources to the ``default-src`` directive.
2930

3031
************

0 commit comments

Comments
 (0)