Skip to content

Commit ef2d717

Browse files
committed
Add TypeRoutingTest
1 parent 71c33f3 commit ef2d717

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

phpunit.xml.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
<directory>tests/ChangeDetector</directory>
2929
</testsuite>
3030

31+
<testsuite name="Type">
32+
<directory>tests/Type</directory>
33+
</testsuite>
34+
3135
<testsuite name="TypedMap">
3236
<directory>tests/TypedMap</directory>
3337
</testsuite>

tests/Type/TypeRoutingTest.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Typhoon\Type;
6+
7+
use PHPUnit\Framework\Assert;
8+
use PHPUnit\Framework\Attributes\CoversClass;
9+
use PHPUnit\Framework\Attributes\DataProvider;
10+
use PHPUnit\Framework\TestCase;
11+
use Typhoon\DeclarationId\Id;
12+
use Typhoon\Type\Visitor\DefaultTypeVisitor;
13+
14+
#[CoversClass(Internal\AliasType::class)]
15+
#[CoversClass(Internal\ArgumentType::class)]
16+
#[CoversClass(Internal\ArrayType::class)]
17+
#[CoversClass(Internal\CallableType::class)]
18+
#[CoversClass(Internal\ClassConstantMaskType::class)]
19+
#[CoversClass(Internal\ClassConstantType::class)]
20+
#[CoversClass(Internal\ClassStringType::class)]
21+
#[CoversClass(Internal\ConditionalType::class)]
22+
#[CoversClass(Internal\ConstantType::class)]
23+
#[CoversClass(Internal\FloatType::class)]
24+
#[CoversClass(Internal\FloatValueType::class)]
25+
#[CoversClass(Internal\IntersectionType::class)]
26+
#[CoversClass(Internal\IntMaskType::class)]
27+
#[CoversClass(Internal\IntType::class)]
28+
#[CoversClass(Internal\IntValueType::class)]
29+
#[CoversClass(Internal\IterableType::class)]
30+
#[CoversClass(Internal\KeyType::class)]
31+
#[CoversClass(Internal\ListType::class)]
32+
#[CoversClass(Internal\LiteralType::class)]
33+
#[CoversClass(Internal\NamedObjectType::class)]
34+
#[CoversClass(Internal\NonEmptyArrayType::class)]
35+
#[CoversClass(Internal\NotType::class)]
36+
#[CoversClass(Internal\ObjectType::class)]
37+
#[CoversClass(Internal\OffsetType::class)]
38+
#[CoversClass(Internal\ParentType::class)]
39+
#[CoversClass(Internal\SelfType::class)]
40+
#[CoversClass(Internal\StaticType::class)]
41+
#[CoversClass(Internal\StringValueType::class)]
42+
#[CoversClass(Internal\TemplateType::class)]
43+
#[CoversClass(Internal\UnionType::class)]
44+
#[CoversClass(Internal\VarianceAwareType::class)]
45+
#[CoversClass(types::class)]
46+
final class TypeRoutingTest extends TestCase
47+
{
48+
/**
49+
* @return \Generator<Type, array{0: non-empty-string, 1?: list<mixed>}>
50+
*/
51+
public static function cases(): \Generator
52+
{
53+
yield types::never => ['never'];
54+
yield types::void => ['void'];
55+
yield types::null => ['null'];
56+
yield types::true => ['true'];
57+
yield types::false => ['false'];
58+
yield types::int => ['int', [types::PHP_INT_MIN, types::PHP_INT_MAX]];
59+
yield types::PHP_INT_MIN => ['constant', [Id::constant('PHP_INT_MIN')]];
60+
yield types::PHP_INT_MAX => ['constant', [Id::constant('PHP_INT_MAX')]];
61+
yield types::negativeInt => ['int', [types::PHP_INT_MIN, types::int(-1)]];
62+
yield types::nonPositiveInt => ['int', [types::PHP_INT_MIN, types::int(0)]];
63+
yield types::nonNegativeInt => ['int', [types::int(0), types::PHP_INT_MAX]];
64+
yield types::positiveInt => ['int', [types::int(1), types::PHP_INT_MAX]];
65+
yield types::nonZeroInt => ['union', [[types::positiveInt, types::negativeInt]]];
66+
yield types::literalInt => ['literal', [types::int]];
67+
yield types::float => ['float', [types::PHP_FLOAT_MIN, types::PHP_FLOAT_MAX]];
68+
yield types::PHP_FLOAT_MIN => ['constant', [Id::constant('PHP_FLOAT_MIN')]];
69+
yield types::PHP_FLOAT_MAX => ['constant', [Id::constant('PHP_FLOAT_MAX')]];
70+
yield types::literalFloat => ['literal', [types::float]];
71+
yield types::string => ['string'];
72+
yield types::nonEmptyString => ['intersection', [[types::string, types::not(types::string(''))]]];
73+
yield types::numericString => ['intersection', [[types::string, types::numeric]]];
74+
yield types::truthyString => ['intersection', [[types::string, types::not(types::string('')), types::not(types::string('0'))]]];
75+
yield types::nonFalsyString => ['intersection', [[types::string, types::not(types::string('')), types::not(types::string('0'))]]];
76+
}
77+
78+
/**
79+
* @return \Generator<non-empty-string, array{Type, non-empty-string, list<mixed>}>
80+
*/
81+
public static function namedCases(): \Generator
82+
{
83+
$index = 0;
84+
85+
foreach (self::cases() as $type => $case) {
86+
yield $index . '. ' . stringify($type) => [$type, $case[0], $case[1] ?? []];
87+
++$index;
88+
}
89+
}
90+
91+
#[DataProvider('namedCases')]
92+
public function test(Type $type, string $expectedMethod, array $expectedArgs = []): void
93+
{
94+
$type->accept(
95+
new
96+
/** @extends DefaultTypeVisitor<null> */
97+
class ($type, $expectedMethod, $expectedArgs) extends DefaultTypeVisitor {
98+
public function __construct(
99+
private readonly Type $expectedType,
100+
private readonly string $expectedMethod,
101+
private readonly array $expectedArgs,
102+
) {}
103+
104+
protected function default(Type $type): mixed
105+
{
106+
$trace = debug_backtrace(limit: 2)[1];
107+
108+
Assert::assertSame($trace['function'], $this->expectedMethod);
109+
Assert::assertEquals($trace['args'] ?? [], [$this->expectedType, ...$this->expectedArgs]);
110+
111+
return null;
112+
}
113+
},
114+
);
115+
}
116+
}

0 commit comments

Comments
 (0)