feat: handle literal boolean true / false types

Allows the usage of boolean values, as follows:

```php
class Foo
{
    /** @var int|false */
    public readonly int|bool $value;
}
```
This commit is contained in:
Daniil Gentili 2022-05-09 21:14:46 +02:00 committed by GitHub
parent 790df8a3b8
commit afcedf9e56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 378 additions and 39 deletions

View File

@ -782,11 +782,17 @@ final class SomeClass
/** @var array<SomeInterface|AnotherInterface> */
private array $unionInsideArray,
/** @var int|true */
private int|bool $unionWithLiteralTrueType;
/** @var int|false */
private int|bool $unionWithLiteralFalseType;
/** @var 404.42|1337.42 */
private string $unionOfFloatValues,
private float $unionOfFloatValues,
/** @var 42|1337 */
private string $unionOfIntegerValues,
private int $unionOfIntegerValues,
/** @var 'foo'|'bar' */
private string $unionOfStringValues,

View File

@ -7,12 +7,11 @@ namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\Exception\TypeCannotBeCompiled;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\ArrayType;
use CuyZ\Valinor\Type\Types\BooleanValueType;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\ArrayType;
use CuyZ\Valinor\Type\Types\EnumType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\FloatValueType;
use CuyZ\Valinor\Type\Types\IntegerRangeType;
use CuyZ\Valinor\Type\Types\IntegerValueType;
@ -21,6 +20,8 @@ use CuyZ\Valinor\Type\Types\IntersectionType;
use CuyZ\Valinor\Type\Types\IterableType;
use CuyZ\Valinor\Type\Types\ListType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\NativeIntegerType;
use CuyZ\Valinor\Type\Types\NativeStringType;
use CuyZ\Valinor\Type\Types\NegativeIntegerType;
@ -50,7 +51,7 @@ final class TypeCompiler
switch (true) {
case $type instanceof NullType:
case $type instanceof BooleanType:
case $type instanceof NativeBooleanType:
case $type instanceof NativeFloatType:
case $type instanceof NativeIntegerType:
case $type instanceof PositiveIntegerType:
@ -60,6 +61,10 @@ final class TypeCompiler
case $type instanceof UndefinedObjectType:
case $type instanceof MixedType:
return "$class::get()";
case $type instanceof BooleanValueType:
return $type->value() === true
? "$class::true()"
: "$class::false()";
case $type instanceof IntegerRangeType:
return "new $class({$type->min()}, {$type->max()})";
case $type instanceof StringValueType:

11
src/Type/BooleanType.php Normal file
View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Type;
/** @api */
interface BooleanType extends ScalarType
{
public function cast($value): bool;
}

View File

@ -7,9 +7,10 @@ namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\BooleanValueType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\NativeStringType;
use CuyZ\Valinor\Type\Types\NegativeIntegerType;
use CuyZ\Valinor\Type\Types\NonEmptyStringType;
@ -57,6 +58,10 @@ final class NativeToken implements TraversingToken
switch (strtolower($symbol)) {
case 'null':
return NullType::get();
case 'true':
return BooleanValueType::true();
case 'false':
return BooleanValueType::false();
case 'mixed':
return MixedType::get();
case 'float':
@ -71,7 +76,7 @@ final class NativeToken implements TraversingToken
return NonEmptyStringType::get();
case 'bool':
case 'boolean':
return BooleanType::get();
return NativeBooleanType::get();
case 'array-key':
return ArrayKeyType::default();
case 'object':

View File

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Type\Types;
use CuyZ\Valinor\Type\BooleanType;
use CuyZ\Valinor\Type\FixedType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\Exception\CannotCastValue;
/** @api */
final class BooleanValueType implements BooleanType, FixedType
{
private static self $true;
private static self $false;
private bool $value;
/**
* @codeCoverageIgnore
*/
private function __construct(bool $value)
{
$this->value = $value;
}
public static function true(): self
{
return self::$true ??= new self(true);
}
public static function false(): self
{
return self::$false ??= new self(false);
}
public function accepts($value): bool
{
return $value === $this->value;
}
public function matches(Type $other): bool
{
if ($other instanceof UnionType) {
return $other->isMatchedBy($this);
}
return $other === $this
|| $other instanceof MixedType
|| $other instanceof NativeBooleanType;
}
public function canCast($value): bool
{
if ($value === $this->value) {
return true;
}
if ($this->value === true) {
return $value === '1' || $value === 1 || $value === 'true';
}
return $value === '0' || $value === 0 || $value === 'false';
}
public function cast($value): bool
{
if (! $this->canCast($value)) {
throw new CannotCastValue($value, $this);
}
return $this->value;
}
public function value(): bool
{
return $this->value;
}
public function __toString(): string
{
return $this->value ? 'true' : 'false';
}
}

View File

@ -58,7 +58,7 @@ final class FloatValueType implements FloatType, FixedType
return $value;
}
public function value()
public function value(): float
{
return $this->value;
}

View File

@ -12,7 +12,7 @@ use CuyZ\Valinor\Utility\IsSingleton;
use function is_bool;
/** @api */
final class BooleanType implements ScalarType
final class NativeBooleanType implements ScalarType
{
use IsSingleton;

View File

@ -7,7 +7,7 @@ namespace CuyZ\Valinor\Tests\Fake\Type;
use CuyZ\Valinor\Tests\Fixture\Object\StringableObject;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeStringType;
@ -40,7 +40,7 @@ final class FakeType implements Type
}
if ($raw === 'bool') {
return BooleanType::get();
return NativeBooleanType::get();
}
if ($raw === 'array-key') {

View File

@ -7,11 +7,10 @@ namespace CuyZ\Valinor\Tests\Functional\Definition\Repository\Cache\Compiler;
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\TypeCompiler;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\ArrayType;
use CuyZ\Valinor\Type\Types\BooleanValueType;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\ArrayType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\FloatValueType;
use CuyZ\Valinor\Type\Types\IntegerRangeType;
use CuyZ\Valinor\Type\Types\IntegerValueType;
@ -20,6 +19,8 @@ use CuyZ\Valinor\Type\Types\IntersectionType;
use CuyZ\Valinor\Type\Types\IterableType;
use CuyZ\Valinor\Type\Types\ListType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\NativeIntegerType;
use CuyZ\Valinor\Type\Types\NativeStringType;
use CuyZ\Valinor\Type\Types\NegativeIntegerType;
@ -73,7 +74,9 @@ final class TypeCompilerTest extends TestCase
public function type_is_compiled_correctly_data_provider(): iterable
{
yield [NullType::get()];
yield [BooleanType::get()];
yield [BooleanValueType::true()];
yield [BooleanValueType::false()];
yield [NativeBooleanType::get()];
yield [NativeFloatType::get()];
yield [new FloatValueType(1337.42)];
yield [new FloatValueType(-1337.42)];

View File

@ -37,10 +37,9 @@ use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\BooleanValueType;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\FloatValueType;
use CuyZ\Valinor\Type\Types\IntegerRangeType;
use CuyZ\Valinor\Type\Types\IntegerValueType;
@ -49,6 +48,8 @@ use CuyZ\Valinor\Type\Types\IntersectionType;
use CuyZ\Valinor\Type\Types\IterableType;
use CuyZ\Valinor\Type\Types\ListType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\NonEmptyArrayType;
use CuyZ\Valinor\Type\Types\NonEmptyListType;
use CuyZ\Valinor\Type\Types\NonEmptyStringType;
@ -106,6 +107,26 @@ final class NativeLexerTest extends TestCase
'transformed' => 'null',
'type' => NullType::class,
],
'True type' => [
'raw' => 'true',
'transformed' => 'true',
'type' => BooleanValueType::class,
],
'True type - uppercase' => [
'raw' => 'TRUE',
'transformed' => 'true',
'type' => BooleanValueType::class,
],
'False type' => [
'raw' => 'false',
'transformed' => 'false',
'type' => BooleanValueType::class,
],
'False type - uppercase' => [
'raw' => 'FALSE',
'transformed' => 'false',
'type' => BooleanValueType::class,
],
'Mixed type' => [
'raw' => 'mixed',
'transformed' => 'mixed',
@ -304,27 +325,27 @@ final class NativeLexerTest extends TestCase
'Boolean type' => [
'raw' => 'bool',
'transformed' => 'bool',
'type' => BooleanType::class,
'type' => NativeBooleanType::class,
],
'Boolean type - uppercase' => [
'raw' => 'BOOL',
'transformed' => 'bool',
'type' => BooleanType::class,
'type' => NativeBooleanType::class,
],
'Boolean type (longer version)' => [
'raw' => 'boolean',
'transformed' => 'bool',
'type' => BooleanType::class,
'type' => NativeBooleanType::class,
],
'Boolean type (longer version) - uppercase' => [
'raw' => 'BOOLEAN',
'transformed' => 'bool',
'type' => BooleanType::class,
'type' => NativeBooleanType::class,
],
'Boolean type followed by description' => [
'raw' => 'bool lorem ipsum',
'transformed' => 'bool',
'type' => BooleanType::class,
'type' => NativeBooleanType::class,
],
'Undefined object type' => [
'raw' => 'object',

View File

@ -18,17 +18,29 @@ class NativeUnionValues
public string|null $nullableWithString = 'Schwifty!';
public string|null $nullableWithNull = 'Schwifty!';
/** @var int|true */
public int|bool $intOrLiteralTrue = 42;
/** @var int|false */
public int|bool $intOrLiteralFalse = 42;
}
class NativeUnionValuesWithConstructor extends NativeUnionValues
{
/**
* @param int|true $intOrLiteralTrue
* @param int|false $intOrLiteralFalse
*/
public function __construct(
bool|float|int|string $scalarWithBoolean = 'Schwifty!',
bool|float|int|string $scalarWithFloat = 'Schwifty!',
bool|float|int|string $scalarWithInteger = 'Schwifty!',
bool|float|int|string $scalarWithString = 'Schwifty!',
string|null $nullableWithString = 'Schwifty!',
string|null $nullableWithNull = 'Schwifty!'
string|null $nullableWithNull = 'Schwifty!',
int|bool $intOrLiteralTrue = 42,
int|bool $intOrLiteralFalse = 42
) {
$this->scalarWithBoolean = $scalarWithBoolean;
$this->scalarWithFloat = $scalarWithFloat;
@ -36,5 +48,7 @@ class NativeUnionValuesWithConstructor extends NativeUnionValues
$this->scalarWithString = $scalarWithString;
$this->nullableWithString = $nullableWithString;
$this->nullableWithNull = $nullableWithNull;
$this->intOrLiteralTrue = $intOrLiteralTrue;
$this->intOrLiteralFalse = $intOrLiteralFalse;
}
}

View File

@ -20,6 +20,8 @@ final class UnionValuesMappingTest extends IntegrationTest
'scalarWithString' => 'foo',
'nullableWithString' => 'bar',
'nullableWithNull' => null,
'intOrLiteralTrue' => true,
'intOrLiteralFalse' => false,
'positiveFloatValue' => 1337.42,
'negativeFloatValue' => -1337.42,
'positiveIntegerValue' => 1337,
@ -48,6 +50,8 @@ final class UnionValuesMappingTest extends IntegrationTest
self::assertSame('foo', $result->scalarWithString);
self::assertSame('bar', $result->nullableWithString);
self::assertSame(null, $result->nullableWithNull);
self::assertSame(true, $result->intOrLiteralTrue);
self::assertSame(false, $result->intOrLiteralFalse);
if ($result instanceof UnionValues) {
self::assertSame(1337.42, $result->positiveFloatValue);
@ -81,6 +85,12 @@ class UnionValues
/** @var string|null|float */
public $nullableWithNull = 'Schwifty!';
/** @var int|true */
public $intOrLiteralTrue = 42;
/** @var int|false */
public $intOrLiteralFalse = 42;
/** @var 404.42|1337.42 */
public float $positiveFloatValue = 404.42;
@ -109,6 +119,8 @@ class UnionValuesWithConstructor extends UnionValues
* @param bool|float|int|string $scalarWithString
* @param string|null|float $nullableWithString
* @param string|null|float $nullableWithNull
* @param int|true $intOrLiteralTrue
* @param int|false $intOrLiteralFalse
* @param 404.42|1337.42 $positiveFloatValue
* @param -404.42|-1337.42 $negativeFloatValue
* @param 42|1337 $positiveIntegerValue
@ -123,6 +135,8 @@ class UnionValuesWithConstructor extends UnionValues
$scalarWithString = 'Schwifty!',
$nullableWithString = 'Schwifty!',
$nullableWithNull = 'Schwifty!',
$intOrLiteralTrue = 42,
$intOrLiteralFalse = 42,
float $positiveFloatValue = 404.42,
float $negativeFloatValue = -404.42,
int $positiveIntegerValue = 42,
@ -136,6 +150,8 @@ class UnionValuesWithConstructor extends UnionValues
$this->scalarWithString = $scalarWithString;
$this->nullableWithString = $nullableWithString;
$this->nullableWithNull = $nullableWithNull;
$this->intOrLiteralTrue = $intOrLiteralTrue;
$this->intOrLiteralFalse = $intOrLiteralFalse;
$this->positiveFloatValue = $positiveFloatValue;
$this->negativeFloatValue = $negativeFloatValue;
$this->positiveIntegerValue = $positiveIntegerValue;

View File

@ -16,7 +16,7 @@ use CuyZ\Valinor\Tests\Fake\Definition\Repository\FakeAttributesRepository;
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
use CuyZ\Valinor\Tests\Fake\Type\Parser\Factory\FakeTypeParserFactory;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\ClassType;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
@ -61,7 +61,7 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase
self::assertTrue($properties->get('propertyWithDefaultValue')->hasDefaultValue());
self::assertSame('Default value for property', $properties->get('propertyWithDefaultValue')->defaultValue());
self::assertInstanceOf(BooleanType::class, $properties->get('propertyWithDocBlockType')->type());
self::assertInstanceOf(NativeBooleanType::class, $properties->get('propertyWithDocBlockType')->type());
self::assertInstanceOf(MixedType::class, $properties->get('propertyWithNoType')->type());
@ -71,7 +71,7 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase
self::assertInstanceOf(StringType::class, $properties->get('protectedProperty')->type());
self::assertFalse($properties->get('protectedProperty')->isPublic());
self::assertInstanceOf(BooleanType::class, $properties->get('privateProperty')->type());
self::assertInstanceOf(NativeBooleanType::class, $properties->get('privateProperty')->type());
self::assertFalse($properties->get('privateProperty')->isPublic());
}
@ -140,7 +140,7 @@ final class ReflectionClassDefinitionRepositoryTest extends TestCase
self::assertSame($className . '::publicMethod($parameterWithDocBlockType)', $parameterWithDocBlockType->signature());
self::assertSame($className . '::publicMethod($optionalParameter)', $optionalParameter->signature());
self::assertInstanceOf(BooleanType::class, $mandatoryParameter->type());
self::assertInstanceOf(NativeBooleanType::class, $mandatoryParameter->type());
self::assertFalse($mandatoryParameter->isOptional());
self::assertInstanceOf(MixedType::class, $parameterWithNoType->type());

View File

@ -10,7 +10,7 @@ use CuyZ\Valinor\Type\Resolver\Exception\CannotResolveTypeFromUnion;
use CuyZ\Valinor\Type\Resolver\Union\UnionScalarNarrower;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\NativeFloatType;
use CuyZ\Valinor\Type\Types\NativeIntegerType;
use CuyZ\Valinor\Type\Types\NativeStringType;
@ -49,7 +49,7 @@ final class UnionScalarNarrowerTest extends TestCase
NativeIntegerType::get(),
NativeFloatType::get(),
NativeStringType::get(),
BooleanType::get(),
NativeBooleanType::get(),
);
return [
@ -76,7 +76,7 @@ final class UnionScalarNarrowerTest extends TestCase
'int|float|string|bool with boolean value' => [
'Union type' => $scalarUnion,
'Source' => true,
'Expected type' => BooleanType::class,
'Expected type' => NativeBooleanType::class,
],
'int|object with object value' => [
'Union type' => new UnionType(NativeIntegerType::get(), UndefinedObjectType::get()),
@ -97,7 +97,7 @@ final class UnionScalarNarrowerTest extends TestCase
public function test_several_possible_types_throws_exception(): void
{
$unionType = new UnionType(BooleanType::get(), NativeIntegerType::get(), NativeFloatType::get());
$unionType = new UnionType(NativeBooleanType::get(), NativeIntegerType::get(), NativeFloatType::get());
$this->expectException(CannotResolveTypeFromUnion::class);
$this->expectExceptionCode(1607027306);

View File

@ -0,0 +1,172 @@
<?php
declare(strict_types=1);
namespace CuyZ\Valinor\Tests\Unit\Type\Types;
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
use CuyZ\Valinor\Type\Types\BooleanValueType;
use CuyZ\Valinor\Type\Types\Exception\CannotCastValue;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\UnionType;
use PHPUnit\Framework\TestCase;
use stdClass;
final class BooleanValueTypeTest extends TestCase
{
public function test_named_constructors_return_singleton_instances(): void
{
self::assertSame(BooleanValueType::true(), BooleanValueType::true());
self::assertSame(BooleanValueType::false(), BooleanValueType::false());
}
public function test_string_value_is_correct(): void
{
self::assertSame('true', (string)BooleanValueType::true());
self::assertSame('false', (string)BooleanValueType::false());
}
public function test_accepts_correct_values(): void
{
self::assertTrue(BooleanValueType::true()->accepts(true));
self::assertTrue(BooleanValueType::false()->accepts(false));
}
public function test_does_not_accept_incorrect_values(): void
{
self::assertFalse(BooleanValueType::true()->accepts('Schwifty!'));
self::assertFalse(BooleanValueType::true()->accepts(42.1337));
self::assertFalse(BooleanValueType::true()->accepts(404));
self::assertFalse(BooleanValueType::true()->accepts(['foo' => 'bar']));
self::assertFalse(BooleanValueType::true()->accepts(false));
self::assertFalse(BooleanValueType::true()->accepts(null));
self::assertFalse(BooleanValueType::true()->accepts(new stdClass()));
self::assertFalse(BooleanValueType::false()->accepts('Schwifty!'));
self::assertFalse(BooleanValueType::false()->accepts(42.1337));
self::assertFalse(BooleanValueType::false()->accepts(404));
self::assertFalse(BooleanValueType::false()->accepts(['foo' => 'bar']));
self::assertFalse(BooleanValueType::false()->accepts(true));
self::assertFalse(BooleanValueType::false()->accepts(null));
self::assertFalse(BooleanValueType::false()->accepts(new stdClass()));
}
public function test_can_cast_boolean_value(): void
{
self::assertTrue(BooleanValueType::true()->canCast(true));
self::assertTrue(BooleanValueType::false()->canCast(false));
}
public function test_can_cast_string_integer_value(): void
{
self::assertTrue(BooleanValueType::true()->canCast('1'));
self::assertTrue(BooleanValueType::false()->canCast('0'));
}
public function test_can_cast_integer_value(): void
{
self::assertTrue(BooleanValueType::true()->canCast(1));
self::assertTrue(BooleanValueType::false()->canCast(0));
}
public function test_can_cast_string_value(): void
{
self::assertTrue(BooleanValueType::true()->canCast('true'));
self::assertTrue(BooleanValueType::false()->canCast('false'));
}
public function test_cannot_cast_other_types(): void
{
self::assertFalse(BooleanValueType::true()->canCast(null));
self::assertFalse(BooleanValueType::true()->canCast(false));
self::assertFalse(BooleanValueType::true()->canCast(42.1337));
self::assertFalse(BooleanValueType::true()->canCast(404));
self::assertFalse(BooleanValueType::true()->canCast('Schwifty!'));
self::assertFalse(BooleanValueType::true()->canCast(['foo' => 'bar']));
self::assertFalse(BooleanValueType::true()->canCast(new stdClass()));
self::assertFalse(BooleanValueType::false()->canCast(null));
self::assertFalse(BooleanValueType::false()->canCast(true));
self::assertFalse(BooleanValueType::false()->canCast(42.1337));
self::assertFalse(BooleanValueType::false()->canCast(404));
self::assertFalse(BooleanValueType::false()->canCast('Schwifty!'));
self::assertFalse(BooleanValueType::false()->canCast(['foo' => 'bar']));
self::assertFalse(BooleanValueType::false()->canCast(new stdClass()));
}
public function test_cast_value_returns_correct_result(): void
{
self::assertSame(true, BooleanValueType::true()->cast(true));
self::assertSame(true, BooleanValueType::true()->cast('1'));
self::assertSame(true, BooleanValueType::true()->cast(1));
self::assertSame(true, BooleanValueType::true()->cast('true'));
self::assertSame(false, BooleanValueType::false()->cast(false));
self::assertSame(false, BooleanValueType::false()->cast('0'));
self::assertSame(false, BooleanValueType::false()->cast(0));
self::assertSame(false, BooleanValueType::false()->cast('false'));
}
public function test_cast_invalid_value_to_true_throws_exception(): void
{
$this->expectException(CannotCastValue::class);
$this->expectExceptionCode(1603216198);
$this->expectExceptionMessage('Cannot cast from `string` to `true`.');
BooleanValueType::true()->cast('foo');
}
public function test_cast_invalid_value_to_false_throws_exception(): void
{
$this->expectException(CannotCastValue::class);
$this->expectExceptionCode(1603216198);
$this->expectExceptionMessage('Cannot cast from `string` to `false`.');
BooleanValueType::false()->cast('foo');
}
public function test_matches_same_type(): void
{
self::assertTrue(BooleanValueType::true()->matches(BooleanValueType::true()));
self::assertTrue(BooleanValueType::false()->matches(BooleanValueType::false()));
}
public function test_matches_native_boolean_type(): void
{
self::assertTrue(BooleanValueType::true()->matches(new NativeBooleanType()));
self::assertTrue(BooleanValueType::false()->matches(new NativeBooleanType()));
}
public function test_matches_mixed_type(): void
{
self::assertTrue(BooleanValueType::true()->matches(new MixedType()));
self::assertTrue(BooleanValueType::false()->matches(new MixedType()));
}
public function test_matches_union_type_containing_same_type(): void
{
$unionTypeWithTrue = new UnionType(
new FakeType(),
BooleanValueType::true(),
new FakeType(),
);
$unionTypeWithFalse = new UnionType(
new FakeType(),
BooleanValueType::false(),
new FakeType(),
);
self::assertTrue(BooleanValueType::true()->matches($unionTypeWithTrue));
self::assertTrue(BooleanValueType::false()->matches($unionTypeWithFalse));
}
public function test_does_not_match_union_type_not_containing_same_type(): void
{
$unionType = new UnionType(new FakeType(), new FakeType());
self::assertFalse(BooleanValueType::true()->matches($unionType));
self::assertFalse(BooleanValueType::false()->matches($unionType));
}
}

View File

@ -6,24 +6,24 @@ namespace CuyZ\Valinor\Tests\Unit\Type\Types;
use CuyZ\Valinor\Tests\Fake\Type\FakeType;
use CuyZ\Valinor\Tests\Traits\TestIsSingleton;
use CuyZ\Valinor\Type\Types\BooleanType;
use CuyZ\Valinor\Type\Types\NativeBooleanType;
use CuyZ\Valinor\Type\Types\Exception\CannotCastValue;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\UnionType;
use PHPUnit\Framework\TestCase;
use stdClass;
final class BooleanTypeTest extends TestCase
final class NativeBooleanTypeTest extends TestCase
{
use TestIsSingleton;
private BooleanType $booleanType;
private NativeBooleanType $booleanType;
protected function setUp(): void
{
parent::setUp();
$this->booleanType = new BooleanType();
$this->booleanType = new NativeBooleanType();
}
public function test_accepts_correct_values(): void
@ -123,7 +123,7 @@ final class BooleanTypeTest extends TestCase
public function test_matches_same_type(): void
{
self::assertTrue((new BooleanType())->matches(new BooleanType()));
self::assertTrue((new NativeBooleanType())->matches(new NativeBooleanType()));
}
public function test_does_not_match_other_type(): void
@ -140,7 +140,7 @@ final class BooleanTypeTest extends TestCase
{
$unionType = new UnionType(
new FakeType(),
new BooleanType(),
new NativeBooleanType(),
new FakeType(),
);