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