Skip to content

Commit 9e2421a

Browse files
committed
Add supported types tests
1 parent b688643 commit 9e2421a

File tree

7 files changed

+450
-0
lines changed

7 files changed

+450
-0
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Platform;
6+
7+
use PHPUnit\Framework\Attributes\DataProvider;
8+
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
9+
use TypeLang\Mapper\Context\Direction;
10+
use TypeLang\Mapper\Exception\Definition\TypeNotFoundException;
11+
use TypeLang\Mapper\Platform\PlatformInterface;
12+
use TypeLang\Mapper\Runtime\Repository\TypeRepository;
13+
use TypeLang\Mapper\Tests\Platform\Stub\ExampleClassStub;
14+
use TypeLang\Mapper\Tests\Platform\Stub\ExampleInterfaceStub;
15+
use TypeLang\Mapper\Tests\Platform\Stub\IntBackedEnumStub;
16+
use TypeLang\Mapper\Tests\Platform\Stub\StringBackedEnumStub;
17+
use TypeLang\Mapper\Tests\Platform\Stub\UnitEnumStub;
18+
use TypeLang\Mapper\Tests\TestCase;
19+
use TypeLang\Parser\Exception\ParseException;
20+
21+
abstract class PlatformTestCase extends TestCase
22+
{
23+
protected const TYPES_PHPSTAN = [
24+
'int',
25+
'integer',
26+
'positive-int',
27+
'negative-int',
28+
'non-positive-int',
29+
'non-negative-int',
30+
'non-zero-int',
31+
'string',
32+
'lowercase-string',
33+
'literal-string',
34+
'class-string',
35+
'interface-string',
36+
'trait-string',
37+
'enum-string',
38+
'callable-string',
39+
'array-key',
40+
'scalar',
41+
'empty-scalar',
42+
'non-empty-scalar',
43+
'number',
44+
'numeric',
45+
'numeric-string',
46+
'non-empty-string',
47+
'non-empty-lowercase-string',
48+
'truthy-string',
49+
'non-falsy-string',
50+
'non-empty-literal-string',
51+
'bool',
52+
'boolean',
53+
'true',
54+
'false',
55+
'null',
56+
'float',
57+
'double',
58+
'array',
59+
'associative-array',
60+
'non-empty-array',
61+
'iterable',
62+
'callable',
63+
'pure-callable',
64+
'pure-closure',
65+
'resource',
66+
'open-resource',
67+
'closed-resource',
68+
'mixed',
69+
'non-empty-mixed',
70+
'void',
71+
'object',
72+
'callable-object',
73+
'callable-array',
74+
'never',
75+
'noreturn',
76+
'never-return',
77+
'never-returns',
78+
'no-return',
79+
'list',
80+
'non-empty-list',
81+
'empty',
82+
'__stringandstringable',
83+
'self',
84+
'static',
85+
'parent',
86+
'key-of',
87+
'value-of',
88+
'int-mask-of',
89+
'int-mask',
90+
'__benevolent',
91+
'template-type',
92+
'new',
93+
];
94+
95+
protected const TYPES_PHAN = [
96+
'integer',
97+
'string',
98+
'double',
99+
'object',
100+
'boolean',
101+
'array',
102+
'iterable',
103+
'array-key',
104+
'bool',
105+
'false',
106+
'float',
107+
'int',
108+
'mixed',
109+
'null',
110+
'true',
111+
'class-string',
112+
'associative-array',
113+
'non-empty-associative-array',
114+
'non-empty-array',
115+
'non-empty-list',
116+
'non-empty-string',
117+
'non-empty-lowercase-string',
118+
'non-zero-int',
119+
'resource',
120+
'callable',
121+
'callable-array',
122+
'callable-object',
123+
'callable-string',
124+
'closure',
125+
'phan-intersection-type',
126+
'non-empty-mixed',
127+
'non-null-mixed',
128+
'scalar',
129+
'lowercase-string',
130+
'numeric-string',
131+
'void',
132+
'never',
133+
'no-return',
134+
'never-return',
135+
'never-returns',
136+
'static',
137+
'$this',
138+
];
139+
140+
protected const TYPES_PSALM = [
141+
'int',
142+
'float',
143+
'string',
144+
'bool',
145+
'void',
146+
'array-key',
147+
'iterable',
148+
'never',
149+
'never-return',
150+
'never-returns',
151+
'no-return',
152+
'empty',
153+
'object',
154+
'callable',
155+
'pure-callable',
156+
'array',
157+
'associative-array',
158+
'non-empty-array',
159+
'callable-array',
160+
'list',
161+
'non-empty-list',
162+
'non-empty-string',
163+
'truthy-string',
164+
'non-falsy-string',
165+
'lowercase-string',
166+
'non-empty-lowercase-string',
167+
'resource',
168+
'resource (closed)',
169+
'closed-resource',
170+
'positive-int',
171+
'non-positive-int',
172+
'negative-int',
173+
'non-negative-int',
174+
'numeric',
175+
'true',
176+
'false',
177+
'scalar',
178+
'null',
179+
'mixed',
180+
'callable-object',
181+
'stringable-object',
182+
'class-string',
183+
'interface-string',
184+
'enum-string',
185+
'trait-string',
186+
'callable-string',
187+
'numeric-string',
188+
'literal-string',
189+
'non-empty-literal-string',
190+
'literal-int',
191+
'$this',
192+
'non-empty-scalar',
193+
'empty-scalar',
194+
'non-empty-mixed',
195+
'Closure',
196+
'traversable',
197+
'countable',
198+
'arrayaccess',
199+
'pure-closure',
200+
'boolean',
201+
'integer',
202+
'double',
203+
'real',
204+
'self',
205+
'static',
206+
'key-of',
207+
'value-of',
208+
'properties-of',
209+
'public-properties-of',
210+
'protected-properties-of',
211+
'private-properties-of',
212+
'non-empty-countable',
213+
'class-string-map',
214+
'open-resource',
215+
'arraylike-object',
216+
'int-mask',
217+
'int-mask-of',
218+
];
219+
220+
protected const TYPES_PHP = [
221+
// TODO: Self references not supported yet
222+
'self',
223+
// TODO: Parent references not supported yet
224+
'parent',
225+
'array',
226+
ExampleClassStub::class,
227+
// TODO: Interfaces not supported yet
228+
ExampleInterfaceStub::class,
229+
// TODO: Callable types not supported yet
230+
'callable',
231+
'int',
232+
'integer',
233+
'bool',
234+
'boolean',
235+
'float',
236+
'real',
237+
'double',
238+
'string',
239+
// TODO: Void types not supported yet
240+
'void',
241+
'?int',
242+
'iterable',
243+
'object',
244+
'int|false',
245+
# TODO: Static references not supported yet
246+
'static',
247+
'mixed',
248+
# TODO: Void types not supported yet
249+
'never',
250+
# TODO: Intersection types not supported yet
251+
'int&string',
252+
UnitEnumStub::class,
253+
IntBackedEnumStub::class,
254+
StringBackedEnumStub::class,
255+
'null',
256+
'false',
257+
'true',
258+
# TODO: Intersection types not supported yet
259+
'int|(true&int)',
260+
];
261+
262+
263+
public static function typesDataProvider(): iterable
264+
{
265+
foreach (self::TYPES_PHPSTAN as $type) {
266+
yield 'PHPStan(' . $type . ')' => [$type, false];
267+
}
268+
269+
foreach (self::TYPES_PHAN as $type) {
270+
yield 'Phan(' . $type . ')' => [$type, false];
271+
}
272+
273+
foreach (self::TYPES_PSALM as $type) {
274+
yield 'Psalm(' . $type . ')' => [$type, false];
275+
}
276+
277+
foreach (self::TYPES_PHP as $type) {
278+
yield 'PHP(' . $type . ')' => [$type, false];
279+
}
280+
}
281+
282+
abstract protected function createTypePlatform(): PlatformInterface;
283+
284+
private function testTypeIsAvailable(string $definition, bool $supports, Direction $direction): void
285+
{
286+
if (!$supports) {
287+
$this->expectException(ParseException::class);
288+
$this->expectExceptionMessage('not allowed');
289+
}
290+
291+
$parser = $this->createTypeParser();
292+
$platform = $this->createTypePlatform();
293+
$statement = $parser->getStatementByDefinition($definition);
294+
295+
if (!$supports) {
296+
$this->expectException(TypeNotFoundException::class);
297+
$this->expectExceptionMessage(\sprintf('Type "%s" is not defined', $definition));
298+
} else {
299+
$this->expectNotToPerformAssertions();
300+
}
301+
302+
$repository = new TypeRepository($parser, $platform->getTypes($direction));
303+
$repository->getTypeByStatement($statement);
304+
}
305+
306+
#[DataProvider('typesDataProvider')]
307+
public function testNormalizationTypeIsAvailable(string $definition, bool $supports): void
308+
{
309+
$this->testTypeIsAvailable($definition, $supports, Direction::Normalize);
310+
}
311+
312+
#[DataProvider('typesDataProvider')]
313+
public function testDenormalizationTypeIsAvailable(string $definition, bool $supports): void
314+
{
315+
$this->testTypeIsAvailable($definition, $supports, Direction::Denormalize);
316+
}
317+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Platform;
6+
7+
use PHPUnit\Framework\Attributes\Group;
8+
use TypeLang\Mapper\Platform\PlatformInterface;
9+
use TypeLang\Mapper\Platform\StandardPlatform;
10+
use TypeLang\Mapper\Tests\Platform\Stub\ExampleClassStub;
11+
use TypeLang\Mapper\Tests\Platform\Stub\IntBackedEnumStub;
12+
use TypeLang\Mapper\Tests\Platform\Stub\StringBackedEnumStub;
13+
use TypeLang\Mapper\Tests\Platform\Stub\UnitEnumStub;
14+
15+
#[Group('platform')]
16+
final class StandardPlatformTest extends PlatformTestCase
17+
{
18+
protected const SUPPORTED_TYPES = [
19+
'int',
20+
'integer',
21+
'string',
22+
'bool',
23+
'boolean',
24+
'float',
25+
'double',
26+
'real',
27+
'true',
28+
'false',
29+
'null',
30+
'array',
31+
'iterable',
32+
'mixed',
33+
'object',
34+
'list',
35+
'traversable',
36+
\Traversable::class,
37+
ExampleClassStub::class,
38+
UnitEnumStub::class,
39+
StringBackedEnumStub::class,
40+
IntBackedEnumStub::class,
41+
// special
42+
'array-key',
43+
'numeric-string',
44+
'non-empty-string',
45+
// composite
46+
'?int',
47+
'int|false',
48+
];
49+
50+
#[\Override]
51+
public static function typesDataProvider(): iterable
52+
{
53+
foreach (parent::typesDataProvider() as $title => [$type]) {
54+
$supports = \in_array($type, self::SUPPORTED_TYPES, true);
55+
56+
yield $title => [$type, $supports];
57+
}
58+
}
59+
60+
protected function createTypePlatform(): PlatformInterface
61+
{
62+
return new StandardPlatform();
63+
}
64+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Tests\Platform\Stub;
6+
7+
/**
8+
* @internal this is an internal library class, please do not use it in your code
9+
* @psalm-internal TypeLang\Mapper\Tests\Platform
10+
*/
11+
final class ExampleClassStub
12+
{
13+
public function __construct(
14+
public readonly string $name,
15+
) {}
16+
}

0 commit comments

Comments
 (0)