Skip to content

Commit

Permalink
test: run the whole integration test suite with & without cache injected
Browse files Browse the repository at this point in the history
This allows us to test the exact same test cases and assertions, but
with a completely different code path (as the file cache is used only in
one scenario).
  • Loading branch information
romm committed Feb 15, 2024
1 parent 105281b commit 1688bc6
Show file tree
Hide file tree
Showing 61 changed files with 362 additions and 333 deletions.
13 changes: 13 additions & 0 deletions src/Cache/FileSystemCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use function mkdir;
use function random_bytes;
use function rename;
use function rmdir;
use function str_contains;
use function unlink;
use function var_export;
Expand Down Expand Up @@ -124,22 +125,34 @@ public function clear(): bool
}

$success = true;
$shouldDeleteRootDir = true;

/** @var FilesystemIterator $file */
foreach (new FilesystemIterator($this->cacheDir) as $file) {
if ($file->getFilename() === '.valinor.tmp') {
$success = @rmdir($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
continue;
}

if (! $file->isFile()) {
$shouldDeleteRootDir = false;
continue;
}

$line = $file->openFile()->getCurrentLine();

if (! $line || ! str_contains($line, self::GENERATED_MESSAGE)) {
$shouldDeleteRootDir = false;
continue;
}

$success = @unlink($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
}

if ($shouldDeleteRootDir) {
$success = @rmdir($this->cacheDir) && $success;
}

return $success;
}

Expand Down
3 changes: 1 addition & 2 deletions tests/Integration/Cache/CacheInjectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use CuyZ\Valinor\Cache\FileSystemCache;
use CuyZ\Valinor\Cache\FileWatchingCache;
use CuyZ\Valinor\Mapper\TreeMapper;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;
use CuyZ\Valinor\Tests\Integration\Mapping\Fixture\SimpleObject;
use org\bovigo\vfs\vfsStream;
Expand Down Expand Up @@ -53,7 +52,7 @@ public function test_cache_entries_are_written_once_during_mapping(): void
*/
private function createMapper(CacheInterface $cache): TreeMapper
{
return (new MapperBuilder())
return $this->mapperBuilder()
->withCache($cache)
// The cache should be able to cache function definitions…
->registerConstructor(fn (): stdClass => new stdClass())
Expand Down
6 changes: 3 additions & 3 deletions tests/Integration/Cache/CacheWarmupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ protected function setUp(): void
parent::setUp();

$this->cache = new FakeCache();
$this->mapper = (new MapperBuilder())->withCache($this->cache);
$this->mapper = $this->mapperBuilder()->withCache($this->cache);
}

public function test_cache_warmup_is_called_only_once(): void
{
$cache = new FakeCacheWithWarmup();
$mapper = (new MapperBuilder())->withCache($cache);
$mapper = $this->mapperBuilder()->withCache($cache);

$mapper->warmup();
$mapper->warmup();
Expand All @@ -41,7 +41,7 @@ public function test_cache_warmup_is_called_only_once(): void
*/
public function test_cache_warmup_does_not_call_delegate_warmup_if_not_handled(): void
{
$mapper = new MapperBuilder(); // no cache registered
$mapper = $this->mapperBuilder(); // no cache registered
$mapper->warmup();
}

Expand Down
52 changes: 52 additions & 0 deletions tests/Integration/IntegrationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,67 @@

namespace CuyZ\Valinor\Tests\Integration;

use CuyZ\Valinor\Cache\FileSystemCache;
use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\Mapping\Namespace\NamespacedInterfaceInferringTest;
use PHPUnit\Framework\TestCase;
use Psr\SimpleCache\CacheInterface;

use function bin2hex;
use function implode;
use function iterator_to_array;
use function random_bytes;
use function sys_get_temp_dir;

abstract class IntegrationTestCase extends TestCase
{
/** @var CacheInterface<mixed> */
private CacheInterface $cacheToInject;

/**
* After the test has run, it is run again with the cache injected. This
* allows us to test the exact same case and assertions, but with a
* completely different code path (as the file cache is used only in one
* scenario).
*/
protected function tearDown(): void
{
// Excluding this test because it cannot run twice in the same process.
if (static::class === NamespacedInterfaceInferringTest::class) {
return;
}

$cacheDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16));
$this->cacheToInject = new FileSystemCache($cacheDir);

// First rerun of the test: the cache entries will be injected.
parent::runTest();

// Second rerun of the test: the cache entries will be used and tested.
parent::runTest();

$this->cacheToInject->clear();
}

/**
* This method *must* be used by every integration test in replacement of a
* direct creation of a MapperBuilder instance. The goal is to ensure that
* a test is run twice: once without a cache and once with the internal
* filesystem cache injected.
*/
protected function mapperBuilder(): MapperBuilder
{
$builder = new MapperBuilder();

if (isset($this->cacheToInject)) {
$builder = $builder->withCache($this->cacheToInject);
}

return $builder;
}

protected function mappingFail(MappingError $error): never
{
$errorFinder = static function (Node $node, callable $errorFinder) {
Expand Down
3 changes: 1 addition & 2 deletions tests/Integration/Mapping/AnonymousClassMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace CuyZ\Valinor\Tests\Integration\Mapping;

use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;

final class AnonymousClassMappingTest extends IntegrationTestCase
Expand All @@ -14,7 +13,7 @@ public function test_map_to_string_or_anonymous_class_with_string_works_correctl
public string $bar;
};

$res = (new MapperBuilder())
$res = $this->mapperBuilder()
->mapper()
->map('string|' . $class::class, 'foo');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
namespace CuyZ\Valinor\Tests\Integration\Mapping;

use CuyZ\Valinor\Mapper\Tree\Exception\CannotInferFinalClass;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;
use LogicException;

final class ClassInheritanceInferringMappingTest extends IntegrationTestCase
{
public function test_infer_abstract_class_works_as_expected(): void
{
$result = (new MapperBuilder())
$result = $this->mapperBuilder()
->infer(
SomeAbstractClass::class,
fn () => SomeAbstractChildClass::class
Expand All @@ -33,7 +32,7 @@ public function test_infer_abstract_class_works_as_expected(): void

public function test_infer_abstract_class_with_argument_in_callback_works_as_expected(): void
{
$result = (new MapperBuilder())
$result = $this->mapperBuilder()
->infer(
SomeAbstractClass::class,
/** @return class-string<SomeAbstractChildClass> */
Expand All @@ -58,7 +57,7 @@ public function test_infer_abstract_class_with_argument_in_callback_works_as_exp

public function test_infer_class_works_as_expected(): void
{
$result = (new MapperBuilder())
$result = $this->mapperBuilder()
->infer(
SomeParentClass::class,
fn () => SomeChildClass::class
Expand All @@ -78,7 +77,7 @@ public function test_infer_class_works_as_expected(): void

public function test_infer_class_with_argument_in_callback_works_as_expected(): void
{
$result = (new MapperBuilder())
$result = $this->mapperBuilder()
->infer(
SomeParentClass::class,
/** @return class-string<SomeChildClass> */
Expand Down Expand Up @@ -107,7 +106,7 @@ public function test_infer_final_class_throws_exception(): void
$this->expectExceptionCode(1671468163);
$this->expectExceptionMessage('Cannot infer final class `' . SomeAbstractChildClass::class . '` with function');

(new MapperBuilder())
$this->mapperBuilder()
->infer(SomeAbstractChildClass::class, fn () => SomeAbstractChildClass::class)
->mapper()
->map(SomeAbstractChildClass::class, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace CuyZ\Valinor\Tests\Integration\Mapping;

use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;
use CuyZ\Valinor\Tests\Integration\Mapping\Fixture\Error;

Expand All @@ -12,7 +11,7 @@ final class ClassNameCollisionTestCollision extends IntegrationTestCase
public function test_mapping_to_class_with_same_class_name_as_native_class_works_properly(): void
{
try {
$result = (new MapperBuilder())
$result = $this->mapperBuilder()
->mapper()
->map(ObjectWithErrorsClassNameCollision::class, ['foo', 'bar']);
} catch (MappingError $error) {
Expand Down
13 changes: 6 additions & 7 deletions tests/Integration/Mapping/Closure/ArgumentsMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace CuyZ\Valinor\Tests\Integration\Mapping\Closure;

use CuyZ\Valinor\Mapper\MappingError;
use CuyZ\Valinor\MapperBuilder;
use CuyZ\Valinor\Tests\Integration\IntegrationTestCase;

final class ArgumentsMappingTest extends IntegrationTestCase
Expand All @@ -15,7 +14,7 @@ public function test_can_map_to_anonymous_function(): void
$function = fn (string $foo, int $bar): string => "$foo / $bar";

try {
$arguments = (new MapperBuilder())->argumentsMapper()->mapArguments($function, [
$arguments = $this->mapperBuilder()->argumentsMapper()->mapArguments($function, [
'foo' => 'foo',
'bar' => 42,
]);
Expand All @@ -31,7 +30,7 @@ public function test_can_map_to_class_method(): void
$object = new SomeClassWithMethods();

try {
$arguments = (new MapperBuilder())->argumentsMapper()->mapArguments($object->somePublicMethod(...), [
$arguments = $this->mapperBuilder()->argumentsMapper()->mapArguments($object->somePublicMethod(...), [
'foo' => 'foo',
'bar' => 42,
]);
Expand All @@ -45,7 +44,7 @@ public function test_can_map_to_class_method(): void
public function test_can_map_to_class_static_method(): void
{
try {
$arguments = (new MapperBuilder())->argumentsMapper()->mapArguments(SomeClassWithMethods::somePublicStaticMethod(...), [
$arguments = $this->mapperBuilder()->argumentsMapper()->mapArguments(SomeClassWithMethods::somePublicStaticMethod(...), [
'foo' => 'foo',
'bar' => 42,
]);
Expand All @@ -61,7 +60,7 @@ public function test_can_map_to_function_with_single_argument(): void
$function = fn (string $foo): string => $foo;

try {
$arguments = (new MapperBuilder())->argumentsMapper()->mapArguments($function, ['foo' => 'foo']);
$arguments = $this->mapperBuilder()->argumentsMapper()->mapArguments($function, ['foo' => 'foo']);
} catch (MappingError $error) {
$this->mappingFail($error);
}
Expand All @@ -74,7 +73,7 @@ public function test_invalid_source_with_one_error_throws_mapping_error(): void
$function = fn (string $foo, int $bar): string => "$foo / $bar";

try {
(new MapperBuilder())->argumentsMapper()->mapArguments($function, [
$this->mapperBuilder()->argumentsMapper()->mapArguments($function, [
'foo' => false,
'bar' => false,
]);
Expand All @@ -91,7 +90,7 @@ public function test_invalid_source_with_two_errors_throws_mapping_error(): void
$function = fn (string $foo, int $bar): string => "$foo / $bar";

try {
(new MapperBuilder())->argumentsMapper()->mapArguments($function, [
$this->mapperBuilder()->argumentsMapper()->mapArguments($function, [
'foo' => false,
'bar' => 42,
]);
Expand Down
Loading

0 comments on commit 1688bc6

Please sign in to comment.