Skip to content

Commit 9a87aef

Browse files
committed
Split coercion and match/cast tests
1 parent 3513e43 commit 9a87aef

18 files changed

+761
-562
lines changed

src/Type/Coercer/BoolTypeCoercer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function coerce(mixed $value, Context $context): bool
2727
//
2828
return $value !== false
2929
&& $value !== '0'
30+
&& $value !== ''
3031
&& $value !== []
3132
&& $value !== null
3233
&& $value !== 0

src/Type/Coercer/IntTypeCoercer.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ final class IntTypeCoercer implements TypeCoercerInterface
1414
{
1515
public function coerce(mixed $value, Context $context): int
1616
{
17-
1817
if ($value instanceof \BackedEnum && \is_int($value->value)) {
1918
return $value->value;
2019
}

tests/Mapping/MappingTestCase.php

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,57 +18,4 @@
1818
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
1919
use TypeLang\Mapper\Tests\TestCase;
2020

21-
abstract class MappingTestCase extends TestCase
22-
{
23-
protected function createConfiguration(): ConfigurationInterface
24-
{
25-
return new Configuration();
26-
}
27-
28-
protected function createTypeExtractor(): TypeExtractorInterface
29-
{
30-
return new NativeTypeExtractor();
31-
}
32-
33-
protected function createPlatform(): PlatformInterface
34-
{
35-
return new StandardPlatform();
36-
}
37-
38-
protected function createTypeParser(): TypeParserInterface
39-
{
40-
return TypeLangParser::createFromPlatform($this->createPlatform());
41-
}
42-
43-
protected function createTypeRepository(Direction $direction): TypeRepositoryInterface
44-
{
45-
$platform = $this->createPlatform();
46-
47-
return new TypeRepository(
48-
parser: $this->createTypeParser(),
49-
builders: $platform->getTypes($direction),
50-
);
51-
}
52-
53-
protected function createDenormalizationContext(mixed $value): RootContext
54-
{
55-
return RootContext::forDenormalization(
56-
value: $value,
57-
config: $this->createConfiguration(),
58-
extractor: $this->createTypeExtractor(),
59-
parser: $this->createTypeParser(),
60-
types: $this->createTypeRepository(Direction::Denormalize),
61-
);
62-
}
63-
64-
protected function createNormalizationContext(mixed $value): RootContext
65-
{
66-
return RootContext::forNormalization(
67-
value: $value,
68-
config: $this->createConfiguration(),
69-
extractor: $this->createTypeExtractor(),
70-
parser: $this->createTypeParser(),
71-
types: $this->createTypeRepository(Direction::Normalize),
72-
);
73-
}
74-
}
21+
abstract class MappingTestCase extends TestCase {}

tests/TestCase.php

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,121 @@
55
namespace TypeLang\Mapper\Tests;
66

77
use PHPUnit\Framework\TestCase as BaseTestCase;
8+
use TypeLang\Mapper\Context\Direction;
9+
use TypeLang\Mapper\Context\RootContext;
10+
use TypeLang\Mapper\Exception\Mapping\InvalidValueException;
11+
use TypeLang\Mapper\Platform\PlatformInterface;
12+
use TypeLang\Mapper\Platform\StandardPlatform;
13+
use TypeLang\Mapper\Runtime\Configuration;
14+
use TypeLang\Mapper\Runtime\ConfigurationInterface;
15+
use TypeLang\Mapper\Runtime\Extractor\NativeTypeExtractor;
16+
use TypeLang\Mapper\Runtime\Extractor\TypeExtractorInterface;
17+
use TypeLang\Mapper\Runtime\Parser\TypeLangParser;
18+
use TypeLang\Mapper\Runtime\Parser\TypeParserInterface;
19+
use TypeLang\Mapper\Runtime\Repository\TypeRepository;
20+
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
821

9-
abstract class TestCase extends BaseTestCase {}
22+
abstract class TestCase extends BaseTestCase
23+
{
24+
private static int $dataProviderIndex = 0;
25+
26+
protected static function dataProviderOf(iterable $data): iterable
27+
{
28+
foreach ($data as $value => $expected) {
29+
yield self::dataProviderKeyOf($value) => [$value, $expected];
30+
}
31+
}
32+
33+
/**
34+
* @return non-empty-string
35+
*/
36+
private static function dataProviderKeyOf(mixed $value): string
37+
{
38+
return \vsprintf('%s(%s)#%d', [
39+
\get_debug_type($value),
40+
\is_array($value) || \is_object($value) ? \json_encode($value) : \var_export($value, true),
41+
++self::$dataProviderIndex,
42+
]);
43+
}
44+
45+
protected function createConfiguration(bool $strictTypes = true): ConfigurationInterface
46+
{
47+
return new Configuration(
48+
isStrictTypes: $strictTypes,
49+
);
50+
}
51+
52+
protected function createTypeExtractor(): TypeExtractorInterface
53+
{
54+
return new NativeTypeExtractor();
55+
}
56+
57+
protected function createPlatform(): PlatformInterface
58+
{
59+
return new StandardPlatform();
60+
}
61+
62+
protected function createTypeParser(): TypeParserInterface
63+
{
64+
return TypeLangParser::createFromPlatform($this->createPlatform());
65+
}
66+
67+
protected function createTypeRepository(Direction $direction): TypeRepositoryInterface
68+
{
69+
$platform = $this->createPlatform();
70+
71+
return new TypeRepository(
72+
parser: $this->createTypeParser(),
73+
builders: $platform->getTypes($direction),
74+
);
75+
}
76+
77+
protected function createNormalizationContext(mixed $value, bool $strictTypes = true): RootContext
78+
{
79+
return RootContext::forNormalization(
80+
value: $value,
81+
config: $this->createConfiguration($strictTypes),
82+
extractor: $this->createTypeExtractor(),
83+
parser: $this->createTypeParser(),
84+
types: $this->createTypeRepository(Direction::Normalize),
85+
);
86+
}
87+
88+
protected function createDenormalizationContext(mixed $value, bool $strictTypes = true): RootContext
89+
{
90+
return RootContext::forDenormalization(
91+
value: $value,
92+
config: $this->createConfiguration($strictTypes),
93+
extractor: $this->createTypeExtractor(),
94+
parser: $this->createTypeParser(),
95+
types: $this->createTypeRepository(Direction::Denormalize),
96+
);
97+
}
98+
99+
protected function expectTypeErrorIfException(mixed $expected): void
100+
{
101+
if (!$expected instanceof \Throwable) {
102+
return;
103+
}
104+
105+
$this->expectExceptionMessage($expected->getMessage());
106+
$this->expectException(InvalidValueException::class);
107+
}
108+
109+
protected static function assertIfNotException(mixed $expected, mixed $actual): void
110+
{
111+
switch (true) {
112+
case $expected instanceof \Throwable:
113+
break;
114+
case \is_array($expected):
115+
case \is_object($expected):
116+
self::assertEquals($expected, $actual);
117+
break;
118+
case \is_float($expected) && \is_nan($expected):
119+
self::assertNan($actual);
120+
break;
121+
default:
122+
self::assertSame($expected, $actual);
123+
}
124+
}
125+
}

tests/Type/ArrayKeyTypeTest.php

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,19 @@
66

77
use PHPUnit\Framework\Attributes\CoversClass;
88
use PHPUnit\Framework\Attributes\Group;
9-
use TypeLang\Mapper\Tests\Type\Stub\IntBackedEnumStub;
10-
use TypeLang\Mapper\Tests\Type\Stub\StringBackedEnumStub;
11-
use TypeLang\Mapper\Tests\Type\Stub\UnitEnumStub;
129
use TypeLang\Mapper\Type\ArrayKeyType;
13-
use TypeLang\Mapper\Type\Coercer\ArrayKeyTypeCoercer;
1410
use TypeLang\Mapper\Type\TypeInterface;
1511

1612
#[Group('types')]
1713
#[CoversClass(ArrayKeyType::class)]
18-
#[CoversClass(ArrayKeyTypeCoercer::class)]
19-
final class ArrayKeyTypeTest extends SymmetricTypeTestCase
14+
final class ArrayKeyTypeTest extends TypeTestCase
2015
{
2116
protected static function createType(): TypeInterface
2217
{
2318
return new ArrayKeyType();
2419
}
2520

26-
protected static function matchValues(bool $strict): iterable
21+
protected static function matchValues(bool $normalize): iterable
2722
{
2823
foreach (self::defaultMatchDataProviderSamples() as $value => $default) {
2924
yield $value => match (true) {
@@ -63,7 +58,7 @@ protected static function matchValues(bool $strict): iterable
6358
}
6459
}
6560

66-
protected static function castValues(bool $strict): iterable
61+
protected static function castValues(bool $normalize): iterable
6762
{
6863
foreach (self::defaultCastDataProviderSamples() as $value => $default) {
6964
yield $value => match (true) {
@@ -98,31 +93,6 @@ protected static function castValues(bool $strict): iterable
9893
$value === 'false' => 'false',
9994
$value === 'non empty' => 'non empty',
10095
$value === '' => '',
101-
// Type casts
102-
$strict === false => match (true) {
103-
$value === "42" => 42,
104-
$value === "1" => 1,
105-
$value === "0" => 0,
106-
$value === "-1" => -1,
107-
$value === "-42" => -42,
108-
$value === 42.0 => 42,
109-
$value === 1.0 => 1,
110-
$value === 0.0 => 0,
111-
$value === -1.0 => -1,
112-
$value === -42.0 => -42,
113-
$value === "42.0" => 42,
114-
$value === "1.0" => 1,
115-
$value === "0.0" => 0,
116-
$value === "-1.0" => -1,
117-
$value === "-42.0" => -42,
118-
$value === null => 0,
119-
$value === true => 1,
120-
$value === false => 0,
121-
$value === IntBackedEnumStub::ExampleCase => IntBackedEnumStub::ExampleCase->value,
122-
$value === StringBackedEnumStub::ExampleCase => StringBackedEnumStub::ExampleCase->value,
123-
$value === UnitEnumStub::ExampleCase => UnitEnumStub::ExampleCase->name,
124-
default => $default,
125-
},
12696
default => $default,
12797
};
12898
}

tests/Type/BoolTypeTest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Type;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\Group;
9+
use TypeLang\Mapper\Type\BoolType;
10+
use TypeLang\Mapper\Type\TypeInterface;
11+
12+
#[Group('types')]
13+
#[CoversClass(BoolType::class)]
14+
final class BoolTypeTest extends TypeTestCase
15+
{
16+
protected static function createType(): TypeInterface
17+
{
18+
return new BoolType();
19+
}
20+
21+
protected static function matchValues(bool $normalize): iterable
22+
{
23+
foreach (self::defaultMatchDataProviderSamples() as $value => $default) {
24+
yield $value => match (true) {
25+
$value === true,
26+
$value === false => true,
27+
default => $default,
28+
};
29+
}
30+
}
31+
32+
protected static function castValues(bool $normalize): iterable
33+
{
34+
foreach (self::defaultCastDataProviderSamples() as $value => $default) {
35+
yield $value => match (true) {
36+
$value === true => true,
37+
$value === false => false,
38+
default => $default,
39+
};
40+
}
41+
}
42+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Type\Coercer;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\Group;
9+
use TypeLang\Mapper\Tests\Type\Stub\IntBackedEnumStub;
10+
use TypeLang\Mapper\Tests\Type\Stub\StringBackedEnumStub;
11+
use TypeLang\Mapper\Tests\Type\Stub\UnitEnumStub;
12+
use TypeLang\Mapper\Type\Coercer\ArrayKeyTypeCoercer;
13+
use TypeLang\Mapper\Type\Coercer\TypeCoercerInterface;
14+
15+
#[Group('coercer')]
16+
#[CoversClass(ArrayKeyTypeCoercer::class)]
17+
final class ArrayKeyTypeCoercerTest extends TypeCoercerTestCase
18+
{
19+
protected static function createCoercer(): TypeCoercerInterface
20+
{
21+
return new ArrayKeyTypeCoercer();
22+
}
23+
24+
protected static function castValues(bool $normalize): iterable
25+
{
26+
foreach (self::defaultCoercionSamples() as $value => $default) {
27+
yield $value => match (true) {
28+
$value === 42 => 42,
29+
$value === 1 => 1,
30+
$value === 0 => 0,
31+
$value === -1 => -1,
32+
$value === -42 => -42,
33+
$value === \PHP_INT_MAX => \PHP_INT_MAX,
34+
$value === \PHP_INT_MIN => \PHP_INT_MIN,
35+
$value === '9223372036854775808' => '9223372036854775808',
36+
$value === '9223372036854775807' => '9223372036854775807',
37+
$value === '42' => '42',
38+
$value === '1' => '1',
39+
$value === '0' => '0',
40+
$value === '-1' => '-1',
41+
$value === '-42' => '-42',
42+
$value === '-9223372036854775808' => '-9223372036854775808',
43+
$value === '-9223372036854775809' => '-9223372036854775809',
44+
$value === '9223372036854775808.0' => '9223372036854775808.0',
45+
$value === '9223372036854775807.0' => '9223372036854775807.0',
46+
$value === '42.5' => '42.5',
47+
$value === '42.0' => '42.0',
48+
$value === '1.0' => '1.0',
49+
$value === '0.0' => '0.0',
50+
$value === '-1.0' => '-1.0',
51+
$value === '-42.0' => '-42.0',
52+
$value === '-42.5' => '-42.5',
53+
$value === '-9223372036854775808.0' => '-9223372036854775808.0',
54+
$value === '-9223372036854775809.0' => '-9223372036854775809.0',
55+
$value === 'true' => 'true',
56+
$value === 'false' => 'false',
57+
$value === 'non empty' => 'non empty',
58+
$value === '' => '',
59+
$value === "42" => 42,
60+
$value === "1" => 1,
61+
$value === "0" => 0,
62+
$value === "-1" => -1,
63+
$value === "-42" => -42,
64+
$value === 42.0 => 42,
65+
$value === 1.0 => 1,
66+
$value === 0.0 => 0,
67+
$value === -1.0 => -1,
68+
$value === -42.0 => -42,
69+
$value === "42.0" => 42,
70+
$value === "1.0" => 1,
71+
$value === "0.0" => 0,
72+
$value === "-1.0" => -1,
73+
$value === "-42.0" => -42,
74+
$value === null => 0,
75+
$value === true => 1,
76+
$value === false => 0,
77+
$value === IntBackedEnumStub::ExampleCase => IntBackedEnumStub::ExampleCase->value,
78+
$value === StringBackedEnumStub::ExampleCase => StringBackedEnumStub::ExampleCase->value,
79+
$value === UnitEnumStub::ExampleCase => UnitEnumStub::ExampleCase->name,
80+
default => $default,
81+
};
82+
}
83+
}
84+
}

0 commit comments

Comments
 (0)