Skip to content

Commit

Permalink
100% code coverage, mutation score, and 99.1% type coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
thunderer committed Oct 1, 2020
1 parent f074982 commit 7ddda41
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ before_script:
script:
- vendor/bin/phpunit --coverage-text
- vendor/bin/psalm --threads=8 --no-cache --shepherd
- vendor/bin/infection
- vendor/bin/infection -j2
- cat infection.log

matrix:
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ test: test-phpunit
test-phpunit:
PHP_VERSION=7.4 docker-compose run --rm php php -v
PHP_VERSION=7.4 docker-compose run --rm php php vendor/bin/phpunit --coverage-text
make test-infection
make qa-psalm
make qa-infection
test-phpunit-local:
php -v
php vendor/bin/phpunit --coverage-text
php vendor/bin/psalm --no-cache
php vendor/bin/infection
test-infection:
PHP_VERSION=7.4 docker-compose run --rm php php vendor/bin/infection -j2

travis:
PHP_VERSION=7.1.3 make travis-job
Expand All @@ -34,8 +36,6 @@ travis-job:

qa-psalm:
PHP_VERSION=7.4 docker-compose run --rm php php vendor/bin/psalm --no-cache
qa-infection:
PHP_VERSION=7.4 docker-compose run --rm php php vendor/bin/infection

run-php:
PHP_VERSION=7.4 docker-compose run --rm php php ${FILE}
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"ext-json": "*",
"phpunit/phpunit": ">=6.0",
"hirak/prestissimo": "^0.3.8",
"vimeo/psalm": "^3.10",
"vimeo/psalm": "^3.14",
"doctrine/dbal": "^2.9",
"doctrine/orm": "^2.7",
"infection/infection": ">=0.13"
Expand Down
13 changes: 9 additions & 4 deletions src/Doctrine/PlatenumDoctrineType.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public static function registerString(string $alias, string $class): void
{
/** @psalm-suppress MissingClosureParamType */
$toString = function($value): string {
return (string)$value;
/** @psalm-suppress MixedArgument */
return strval($value);
};
$sql = function(array $declaration, AbstractPlatform $platform): string {
return $platform->getVarcharTypeDeclarationSQL([]);
Expand Down Expand Up @@ -86,14 +87,18 @@ private static function allTraitsOf(string $class): array
$traits = [];

do {
$traits = array_merge(class_uses($class, true), $traits);
foreach(class_uses($class, true) as $fqcn) {
$traits[] = $fqcn;
}
} while($class = get_parent_class($class));

foreach ($traits as $trait => $same) {
$traits = array_merge(class_uses($trait, true), $traits);
foreach(class_uses($same, true) as $fqcn) {
$traits[] = $fqcn;
}
}

return array_values(array_unique($traits));
return $traits;
}

public function getName(): string
Expand Down
10 changes: 4 additions & 6 deletions src/Enum/CallbackEnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,11 @@ final public static function initialize(callable $callback): void

final private static function resolve(): array
{
if(false === array_key_exists(static::class, static::$callbacks)) {
throw PlatenumException::fromInvalidCallback(static::class);
}
if(false === is_callable(static::$callbacks[static::class])) {
throw PlatenumException::fromInvalidCallback(static::class);
$class = static::class;
if(false === (array_key_exists($class, static::$callbacks) && is_callable(static::$callbacks[$class]))) {
throw PlatenumException::fromInvalidCallback($class);
}

return (static::$callbacks[static::class])();
return (static::$callbacks[$class])();
}
}
25 changes: 9 additions & 16 deletions src/Enum/EnumTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,8 @@ final public function fromInstance(&$enum): void

/* --- EXCEPTIONS --- */

protected static function throwInvalidMemberException(string $member): void
private static function throwInvalidMemberException(string $member): void
{
static::throwDefaultInvalidMemberException($member);
}

private static function throwDefaultInvalidMemberException(string $member): void
Expand All @@ -113,9 +112,8 @@ private static function throwDefaultInvalidMemberException(string $member): void
}

/** @param mixed $value */
protected static function throwInvalidValueException($value): void
private static function throwInvalidValueException($value): void
{
static::throwDefaultInvalidValueException($value);
}

/** @param mixed $value */
Expand Down Expand Up @@ -188,29 +186,23 @@ final public function hasValue($value): bool
/** @return int|string */
final public static function memberToValue(string $member)
{
static::resolveMembers();

$class = static::class;
if(false === static::memberExists($member)) {
static::throwInvalidMemberException($member);
static::throwDefaultInvalidMemberException($member);
}

return static::$members[$class][$member];
return static::$members[static::class][$member];
}

/** @param int|string $value */
final public static function valueToMember($value): string
{
static::resolveMembers();

$class = static::class;
if(false === static::valueExists($value)) {
static::throwInvalidValueException($value);
static::throwDefaultInvalidValueException($value);
}

return (string)array_search($value, static::$members[$class], true);
return (string)array_search($value, static::$members[static::class], true);
}

final public static function getMembers(): array
Expand Down Expand Up @@ -243,13 +235,14 @@ final private static function resolveMembers(): void
return;
}

$throwMissingResolve = function(string $class): void {
throw PlatenumException::fromMissingResolve($class);
};
// reflection instead of method_exists because of PHP 7.4 bug #78632
// @see https://bugs.php.net/bug.php?id=78632
if(false === (new \ReflectionClass($class))->hasMethod('resolve')) {
throw PlatenumException::fromMissingResolve($class);
}
$hasResolve = (new \ReflectionClass($class))->hasMethod('resolve');
/** @var array<string,int|string> $members */
$members = static::resolve();
$members = $hasResolve ? static::resolve() : $throwMissingResolve($class);
if(empty($members)) {
throw PlatenumException::fromEmptyMembers($class);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/PlatenumException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static function fromInvalidMember(string $fqcn, string $member, array $me
/** @param mixed $value */
public static function fromInvalidValue(string $fqcn, $value): self
{
return new self(sprintf('Enum `%s` does not contain any member with value `%s`.', $fqcn, is_scalar($value) ? (string)$value : gettype($value)));
return new self(sprintf('Enum `%s` does not contain any member with value `%s`.', $fqcn, is_scalar($value) ? strval($value) : gettype($value)));
}

/* --- GENERIC --- */
Expand Down
16 changes: 16 additions & 0 deletions tests/CallbackEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
declare(strict_types=1);
namespace Thunder\Platenum\Tests;

use Thunder\Platenum\Enum\CallbackEnumTrait;
use Thunder\Platenum\Exception\PlatenumException;

/**
Expand Down Expand Up @@ -42,4 +43,19 @@ public function testExceptionAlreadyInitialized(): void
$this->expectExceptionMessage('Enum '.$class.' callback was already initialized.');
$class::initialize(function() use($members) { return $members; });
}

public function testImpossibleExceptionCallbackNotCallable(): void
{
$class = $this->computeUniqueClassName('CallbackTrait');
eval('final class '.$class.' implements \JsonSerializable { use '.CallbackEnumTrait::class.'; }');

$ref = new \ReflectionClass($class);
$callbacks = $ref->getProperty('callbacks');
$callbacks->setAccessible(true);
$callbacks->setValue($class, [$class => 'invalid']);

$this->expectException(PlatenumException::class);
$this->expectExceptionMessage('Enum '.$class.' requires static property $callback to be a valid callable returning members and values mapping.');
$class::FIRST();
}
}
32 changes: 32 additions & 0 deletions tests/DoctrineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Thunder\Platenum\Tests;

use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver;
Expand Down Expand Up @@ -46,4 +47,35 @@ public function testCreateFromMember(): void
$this->assertSame($entity->getStringValue(), $foundEntity->getStringValue());
$this->assertNull($foundEntity->getNullableValue());
}

public function testDoctrineType(): void
{
PlatenumDoctrineType::registerInteger('intEnum0', DoctrineIntEnum::class);
$intType = PlatenumDoctrineType::getType('intEnum0');

$platform = new MySqlPlatform();
$this->assertTrue($intType->requiresSQLCommentHint($platform));
$this->assertSame('intEnum0', $intType->getName());
$this->assertSame('INT', $intType->getSQLDeclaration([], $platform));

PlatenumDoctrineType::registerString('stringEnum0', DoctrineStringEnum::class);
$stringType = PlatenumDoctrineType::getType('stringEnum0');
$this->assertSame('VARCHAR(255)', $stringType->getSQLDeclaration([], $platform));
}

public function testInvalidClass(): void
{
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('PlatenumDoctrineType allows only Platenum enumerations, `stdClass` given.');
PlatenumDoctrineType::registerInteger('invalid', \stdClass::class);
}

public function testDuplicateAlias(): void
{
PlatenumDoctrineType::registerString('enumX', DoctrineIntEnum::class);

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Alias `'.DoctrineIntEnum::class.'` was already registered in PlatenumDoctrineType.');
PlatenumDoctrineType::registerString('enumX', DoctrineIntEnum::class);
}
}
14 changes: 13 additions & 1 deletion tests/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public function testExceptionNonStringMember(): void

public function testExceptionNonUniformValueType(): void
{
$enum = $this->makeRawEnum(['FIRST' => 1, 'SECOND' => '2']);
$enum = $this->makeRawEnum(['FIRST' => 1, 'SECOND' => '2', 'THIRD' => '3']);

$this->expectException(PlatenumException::class);
$this->expectExceptionMessage('Enum `'.$enum.'` member values must be of the same type, `integer,string` given.');
Expand Down Expand Up @@ -213,6 +213,7 @@ public function testDifferentEnumsInequality(): void
$enumA = $this->makeRawEnum(['FIRST' => 1, 'SECOND' => 2]);
$enumB = $this->makeRawEnum(['FIRST' => 'first', 'SECOND' => 'second']);

$this->assertFalse($enumA::FIRST()->equals($enumB::FIRST()));
$this->assertNotSame($enumA::FIRST(), $enumB::FIRST());
$this->assertNotSame($enumA::SECOND(), $enumB::SECOND());
}
Expand Down Expand Up @@ -307,6 +308,17 @@ public function testValueToMember(): void
$this->assertSame('FIRST', $stringEnum::valueToMember('first'));
}

public function testImpossibleValueToMemberInvalidMemberTypeException(): void
{
$enum = $this->makeRawEnum(['FIRST' => 'first', 'SECOND' => 'second']);
$ref = new \ReflectionClass($enum);
$members = $ref->getProperty('members');
$members->setAccessible(true);
$members->setValue($enum, [$enum => [0 => 'first']]);

$this->assertSame('0', $enum::valueToMember('first'));
}

public function testJsonEncode(): void
{
$enum = $this->makeRawEnum(['FIRST' => 1, 'SECOND' => 2]);
Expand Down

0 comments on commit 7ddda41

Please sign in to comment.