Skip to content

Commit

Permalink
extended tests to properly assert requirements of new chainloader err…
Browse files Browse the repository at this point in the history
…or handling implementation and updates per reviews from @dbu and @franmomu
  • Loading branch information
robfrawley committed Nov 29, 2021
1 parent e49602b commit 3a76b4b
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 99 deletions.
30 changes: 15 additions & 15 deletions src/Exception/Binary/Loader/ChainAttemptNotLoadableException.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,39 @@

final class ChainAttemptNotLoadableException extends NotLoadableException
{
private string $configName;
private LoaderInterface $loaderInst;
private string $loaderIndex;
private LoaderInterface $loaderClass;

public function __construct(string $configName, LoaderInterface $loaderInst, NotLoadableException $loaderException)
public function __construct(string $loaderIndex, LoaderInterface $loaderClass, NotLoadableException $loaderException)
{
$this->configName = $configName;
$this->loaderInst = $loaderInst;
$this->loaderIndex = $loaderIndex;
$this->loaderClass = $loaderClass;

parent::__construct($this->compileFailureText(), 0, $loaderException);
parent::__construct($this->compileMessageTxt(), 0, $loaderException);
}

public function getLoaderConfigName(): string
public function getLoaderIndex(): string
{
return $this->configName;
return $this->loaderIndex;
}

public function getLoaderObjectInst(): LoaderInterface
public function getLoaderClass(): LoaderInterface
{
return $this->loaderInst;
return $this->loaderClass;
}

public function getLoaderObjectName(): string
public function getLoaderClassName(): string
{
return (new \ReflectionObject($this->getLoaderObjectInst()))->getShortName();
return (new \ReflectionObject($this->getLoaderClass()))->getShortName();
}

public function getLoaderPriorError(): string
public function getLoaderException(): string
{
return $this->getPrevious()->getMessage();
}

private function compileFailureText(): string
private function compileMessageTxt(): string
{
return sprintf('%s=[%s]', $this->getLoaderObjectName(), $this->getLoaderConfigName());
return sprintf('%s=[%s]', $this->getLoaderClassName(), $this->getLoaderIndex());
}
}
13 changes: 8 additions & 5 deletions src/Exception/Binary/Loader/ChainNotLoadableException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,28 @@ public function __construct(string $path, ChainAttemptNotLoadableException ...$e
private static function compileExceptionMessage(string $path, ChainAttemptNotLoadableException ...$exceptions): string
{
return vsprintf('Source image not resolvable "%s" using "%s" %d loaders (internal exceptions: %s).', [
$path, self::compileLoaderConfigMaps(...$exceptions), \count($exceptions), self::compileLoaderErrorsList(...$exceptions),
$path,
self::compileLoaderConfigMaps(...$exceptions),
\count($exceptions),
self::compileLoaderErrorsList(...$exceptions),
]);
}

private static function compileLoaderConfigMaps(ChainAttemptNotLoadableException ...$exceptions): string
{
return self::implodeArrayMappedExceptions(static function (ChainAttemptNotLoadableException $e): string {
return self::implodeMappedExceptions(static function (ChainAttemptNotLoadableException $e): string {
return $e->getMessage();
}, ...$exceptions);
}

private static function compileLoaderErrorsList(ChainAttemptNotLoadableException ...$exceptions): string
{
return self::implodeArrayMappedExceptions(static function (ChainAttemptNotLoadableException $e): string {
return sprintf('%s=[%s]', $e->getLoaderObjectName(), $e->getLoaderPriorError());
return self::implodeMappedExceptions(static function (ChainAttemptNotLoadableException $e): string {
return sprintf('%s=[%s]', $e->getLoaderClassName(), $e->getLoaderException());
}, ...$exceptions);
}

private static function implodeArrayMappedExceptions(\Closure $mapper, ChainAttemptNotLoadableException ...$exceptions): string
private static function implodeMappedExceptions(\Closure $mapper, ChainAttemptNotLoadableException ...$exceptions): string
{
return implode(', ', array_map($mapper, $exceptions));
}
Expand Down
35 changes: 29 additions & 6 deletions tests/AbstractTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,16 +271,39 @@ protected function createObjectMock(string $object, array $methods = [], bool $c
return $builder->getMock();
}

/**
* @param object $object
*/
protected function getVisibilityRestrictedMethod($object, string $name): \ReflectionMethod
protected static function getReflectionObject(object $object): \ReflectionObject
{
return new \ReflectionObject($object);
}

protected static function getReflectionObjectName(object $object): string
{
$r = new \ReflectionObject($object);
return self::getReflectionObject($object)->getName();
}

$m = $r->getMethod($name);
protected function getVisibilityRestrictedMethod($object, string $name): \ReflectionMethod
{
$m = self::getReflectionObject($object)->getMethod($name);
$m->setAccessible(true);

return $m;
}

protected static function generateRandomInteger(int $lowerBound = null, int $upperBound = null): int
{
$lowerBound = $lowerBound ?? 0;
$upperBound = $upperBound ?? 100000000;

if ($lowerBound > $upperBound) {
throw new \LogicException('Lower-bound argument must be less-than or equal-to upper-bound argument!');
}

try {
return random_int($lowerBound, $upperBound);
} catch (\Throwable $e) {
throw new \RuntimeException(vsprintf('Failed to generate random number between %d and %d: %s', [
$lowerBound, $upperBound, $e->getMessage()
]));
}
}
}
174 changes: 101 additions & 73 deletions tests/Binary/Loader/ChainLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Liip\ImagineBundle\Binary\Loader\LoaderInterface;
use Liip\ImagineBundle\Binary\Locator\FileSystemLocator;
use Liip\ImagineBundle\Binary\Locator\LocatorInterface;
use Liip\ImagineBundle\Exception\Binary\Loader\ChainAttemptNotLoadableException;
use Liip\ImagineBundle\Exception\Binary\Loader\ChainNotLoadableException;
use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;
use Liip\ImagineBundle\Model\FileBinary;
use Liip\ImagineBundle\Tests\AbstractTest;
Expand All @@ -28,63 +30,68 @@
*/
class ChainLoaderTest extends AbstractTest
{
public function testImplementsLoaderInterface(): void
public function testChainLoaderImplementsLoaderInterface(): void
{
$this->assertInstanceOf(LoaderInterface::class, $this->getChainLoader());
$this->assertInstanceOf(LoaderInterface::class, self::instantiateChainLoader());
}

/**
* @return array[]
*/
public static function provideLoadCases(): array
public function testChainAttemptNotLoadableExceptionImplementsNotLoadableException(): void
{
$this->assertInstanceOfNotLoadableException(
self::instantiateChainAttemptNotLoadableException(),
vsprintf('Expected "%s" to be an instance of "%s"', [
ChainAttemptNotLoadableException::class,
NotLoadableException::class,
])
);
}

public function testChainNotLoadableExceptionImplementsNotLoadableException(): void
{
$this->assertInstanceOfNotLoadableException(
self::instantiateChainNotLoadableException()
);
}

private function assertInstanceOfNotLoadableException(object $provided, string $message = ''): void
{
$this->assertInstanceOf(NotLoadableException::class, $provided, $message
?? vsprintf('Expected class "%s" to be an instance of "%s"', [
self::getReflectionObjectName($provided), NotLoadableException::class,
])
);
}

public static function provideLoadCases(): \Generator
{
$file = pathinfo(__FILE__, PATHINFO_BASENAME);

return [
[
__DIR__,
$file,
],
[
__DIR__.'/',
$file,
],
[
__DIR__, '/'.
$file,
],
[
__DIR__.'/../../Binary/Loader',
'/'.$file,
],
[
realpath(__DIR__.'/..'),
'Loader/'.$file,
],
[
__DIR__.'/../',
'/Loader/../../Binary/Loader/'.$file,
],
];
yield [__DIR__, $file];
yield [__DIR__.'/', $file];
yield [__DIR__, '/'.$file];
yield [__DIR__.'/../../Binary/Loader', '/'.$file];
yield [realpath(__DIR__.'/..'), 'Loader/'.$file];
yield [__DIR__.'/../', '/Loader/../../Binary/Loader/'.$file];
}

/**
* @dataProvider provideLoadCases
*/
public function testLoad(string $root, string $path): void
{
$this->assertValidLoaderFindReturn($this->getChainLoader([$root])->find($path));
$this->assertValidLoaderFindReturn(self::instantiateChainLoader([$root])->find($path), vsprintf(
'Expected valid "%s::find()" return with root of "%s" and file path of "%s".', [
ChainLoader::class,
$root,
$path,
]
));
}

/**
* @return array[]
*/
public function provideInvalidPathsData(): array
public function provideInvalidPathsData(): \Generator
{
return [
['../Loader/../../Binary/Loader/../../../Resources/config/routing.yaml'],
['../../Binary/'],
];
yield ['../Loader/../../Binary/Loader/../../../Resources/config/routing.yaml'];
yield ['../../Binary/'];
}

/**
Expand All @@ -93,9 +100,10 @@ public function provideInvalidPathsData(): array
public function testThrowsIfFileDoesNotExist(string $path): void
{
$this->expectException(NotLoadableException::class);
$this->expectException(ChainNotLoadableException::class);
$this->expectExceptionMessageMatchesBC('{Source image not resolvable "[^"]+" using "FileSystemLoader=\[foo\]" 1 loaders}');

$this->getChainLoader()->find($path);
self::instantiateChainLoader()->find($path);
}

/**
Expand All @@ -104,59 +112,79 @@ public function testThrowsIfFileDoesNotExist(string $path): void
public function testThrowsIfFileDoesNotExistWithMultipleLoaders(string $path): void
{
$this->expectException(NotLoadableException::class);
$this->expectExceptionMessageMatchesBC('{Source image not resolvable "[^"]+" using "FileSystemLoader=\[foo\], FileSystemLoader=\[bar\]" 2 loaders \(internal exceptions: FileSystemLoader=\[.+\], FileSystemLoader=\[.+\]\)\.}');
$this->expectException(ChainNotLoadableException::class);
$this->expectExceptionMessageMatchesBC(
'{Source image not resolvable "[^"]+" using "FileSystemLoader=\[foo\], '.
'FileSystemLoader=\[bar\]" 2 loaders \(internal exceptions: FileSystemLoader=\[.+\], '.
'FileSystemLoader=\[.+\]\)\.}'
);

$this->getChainLoader([], [
'foo' => $this->createFileSystemLoader(
$this->getFileSystemLocator([
self::instantiateChainLoader([], [
'foo' => self::instantiateFileSystemLoader(
self::instantiateFileSystemLocator([
realpath(__DIR__.'/../../'),
])
),
'bar' => $this->createFileSystemLoader(
$this->getFileSystemLocator([
'bar' => self::instantiateFileSystemLoader(
self::instantiateFileSystemLocator([
realpath(__DIR__.'/../../../'),
])
),
])->find($path);
}

/**
* @param string[] $paths
*/
private function getFileSystemLocator(array $paths = []): FileSystemLocator
private function assertValidLoaderFindReturn(FileBinary $return, string $message = ''): void
{
return new FileSystemLocator($paths);
$this->assertInstanceOf(FileBinary::class, $return, $message);
$this->assertStringStartsWith('text/', $return->getMimeType(), $message);
}

/**
* @param string[] $paths
* @param FileSystemLoader[] $loaders
*/
private function getChainLoader(array $paths = [], array $loaders = null): ChainLoader
private static function instantiateRandomlyPopulatedChainNotLoadableException(string $loaderPath = null, int $maxInternalExceptions = 10): ChainNotLoadableException
{
if (null === $loaders) {
$loaders = [
'foo' => $this->createFileSystemLoader($this->getFileSystemLocator($paths ?: [__DIR__])),
];
}

return new ChainLoader($loaders);
self::instantiateChainNotLoadableException($loaderPath, ...array_map(function (): ChainAttemptNotLoadableException {
return self::instantiateChainAttemptNotLoadableException();
}, range(0, self::generateRandomInteger(1, $maxInternalExceptions))));
}

private function assertValidLoaderFindReturn(FileBinary $return, string $message = ''): void
private static function instantiateChainNotLoadableException(string $loaderPath = null, ChainAttemptNotLoadableException ...$exceptions): ChainNotLoadableException
{
$this->assertInstanceOf(FileBinary::class, $return, $message);
$this->assertStringStartsWith('text/', $return->getMimeType(), $message);
return new ChainNotLoadableException($loaderPath ?? (__DIR__), ...$exceptions);
}

private function createFileSystemLoader(LocatorInterface $locator): FileSystemLoader
private static function instantiateChainAttemptNotLoadableException(): ChainAttemptNotLoadableException
{
$mimeTypes = MimeTypes::getDefault();
return new ChainAttemptNotLoadableException(
$name = sprintf('conf-item-%d', static::generateRandomInteger(1000, 9999)),
self::instantiateFileSystemLoader(self::instantiateFileSystemLocator()),
new NotLoadableException(sprintf('the "%s" loader encountered an error', $name))
);
}

private static function instantiateFileSystemLoader(LocatorInterface $locator = null): FileSystemLoader
{
return new FileSystemLoader(
$mimeTypes,
$mimeTypes,
$locator
$mime = MimeTypes::getDefault(), $mime, $locator ?? self::instantiateFileSystemLocator()
);
}

/**
* @param string[] $paths
*/
private static function instantiateFileSystemLocator(array $paths = []): FileSystemLocator
{
return new FileSystemLocator($paths);
}

/**
* @param string[] $paths
* @param array<string, FileSystemLoader> $loaders
*/
private static function instantiateChainLoader(array $paths = [], array $loaders = null): ChainLoader
{
return new ChainLoader($loaders ?? [
'foo' => self::instantiateFileSystemLoader(
self::instantiateFileSystemLocator($paths ?: [__DIR__])
),
]);
}
}

0 comments on commit 3a76b4b

Please sign in to comment.