1<?php
2
3declare(strict_types=1);
4
5namespace JMS\Serializer\Tests\Serializer\Type;
6
7use JMS\Serializer\Type\Exception\SyntaxError;
8use JMS\Serializer\Type\Parser;
9use JMS\Serializer\Type\ParserInterface;
10use PHPUnit\Framework\TestCase;
11
12class ParserTest extends TestCase
13{
14    /** @var ParserInterface */
15    private $parser;
16
17    protected function setUp(): void
18    {
19        $this->parser = new Parser();
20    }
21
22    /**
23     * @dataProvider validTypesProvider
24     */
25    public function testParse(string $sourceType, array $expectedType): void
26    {
27        self::assertSame(
28            $expectedType,
29            $this->parser->parse($sourceType)
30        );
31    }
32
33    /**
34     * @return mixed[][]
35     */
36    public function validTypesProvider(): iterable
37    {
38        $type = static function (string $name, array $params = []): array {
39            return ['name' => $name, 'params' => $params];
40        };
41
42        yield [
43            'string',
44            $type('string'),
45        ];
46        yield [
47            'array<Foo>',
48            $type('array', [['name' => 'Foo', 'params' => []]]),
49        ];
50        yield [
51            'Foo<\'a\'>',
52            $type('Foo', ['a']),
53        ];
54        yield [
55            'Foo<5>',
56            $type('Foo', [5]),
57        ];
58        yield [
59            'Foo<5.5>',
60            $type('Foo', [5.5]),
61        ];
62        yield [
63            'Foo<null>',
64            $type('Foo', [null]),
65        ];
66        yield [
67            'Foo<\'a\',\'b\',\'c\'>',
68            $type('Foo', ['a', 'b', 'c']),
69        ];
70        yield [
71            'Foo<\'a\',\'\'>',
72            $type('Foo', ['a', '']),
73        ];
74        yield [
75            'array<Foo,Bar>',
76            $type('array', [['name' => 'Foo', 'params' => []], ['name' => 'Bar', 'params' => []]]),
77        ];
78        yield [
79            'array<Foo\Bar, Baz\Boo>',
80            $type('array', [['name' => 'Foo\Bar', 'params' => []], ['name' => 'Baz\Boo', 'params' => []]]),
81        ];
82        yield [
83            'a<b<c,d>,e>',
84            $type('a', [['name' => 'b', 'params' => [['name' => 'c', 'params' => []], ['name' => 'd', 'params' => []]]], ['name' => 'e', 'params' => []]]),
85
86        ];
87        yield [
88            'Foo',
89            $type('Foo'),
90        ];
91        yield [
92            'Foo\Bar',
93            $type('Foo\Bar'),
94        ];
95        yield [
96            'Foo<"asdf asdf">',
97            $type('Foo', ['asdf asdf']),
98        ];
99    }
100
101    public function testEmptyString(): void
102    {
103        $this->expectException(SyntaxError::class);
104        $this->expectExceptionMessage(
105            "Unexpected token \"EOF\" (EOF) at line 1 and column 1:\n"
106            . "\n"
107            . '↑'
108        );
109
110        $this->parser->parse('');
111    }
112
113    public function testParamTypeMustEndWithBracket(): void
114    {
115        $this->expectException(SyntaxError::class);
116        $this->expectExceptionMessage(
117            "Unexpected token \"EOF\" (EOF) at line 1 and column 8:\n"
118            . "Foo<bar\n"
119            . '       ↑'
120        );
121
122        $this->parser->parse('Foo<bar');
123    }
124
125    public function testMustStartWithName(): void
126    {
127        $this->expectException(SyntaxError::class);
128        $this->expectExceptionMessage(
129            "Unexpected token \",\" (comma) at line 1 and column 1:\n"
130            . ",\n"
131            . '↑'
132        );
133
134        $this->parser->parse(',');
135    }
136
137    public function testEmptyParams(): void
138    {
139        $this->expectException(SyntaxError::class);
140        $this->expectExceptionMessage(
141            "Unexpected token \">\" (_parenthesis) at line 1 and column 5:\n"
142            . "Foo<>\n"
143            . '    ↑'
144        );
145
146        $this->parser->parse('Foo<>');
147    }
148
149    public function testNoTrailingComma(): void
150    {
151        $this->expectException(SyntaxError::class);
152        $this->expectExceptionMessage(
153            "Unexpected token \",\" (comma) at line 1 and column 7:\n"
154            . "Foo<aa,>\n"
155            . '      ↑'
156        );
157
158        $this->parser->parse('Foo<aa,>');
159    }
160
161    public function testLeadingBackslash(): void
162    {
163        $this->expectException(SyntaxError::class);
164        $this->expectExceptionMessage(
165            "Unrecognized token \"\\\" at line 1 and column 5:\n"
166            . "Foo<\Bar>\n"
167            . '    ↑'
168        );
169
170        $this->parser->parse('Foo<\Bar>');
171    }
172}
173