No Description

ParserTest.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Yaml\Tests;
  11. use Symfony\Component\Yaml\Yaml;
  12. use Symfony\Component\Yaml\Parser;
  13. class ParserTest extends \PHPUnit_Framework_TestCase
  14. {
  15. protected $parser;
  16. protected function setUp()
  17. {
  18. $this->parser = new Parser();
  19. }
  20. protected function tearDown()
  21. {
  22. $this->parser = null;
  23. }
  24. /**
  25. * @dataProvider getDataFormSpecifications
  26. */
  27. public function testSpecifications($file, $expected, $yaml, $comment)
  28. {
  29. $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
  30. }
  31. public function getDataFormSpecifications()
  32. {
  33. $parser = new Parser();
  34. $path = __DIR__.'/Fixtures';
  35. $tests = array();
  36. $files = $parser->parse(file_get_contents($path.'/index.yml'));
  37. foreach ($files as $file) {
  38. $yamls = file_get_contents($path.'/'.$file.'.yml');
  39. // split YAMLs documents
  40. foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
  41. if (!$yaml) {
  42. continue;
  43. }
  44. $test = $parser->parse($yaml);
  45. if (isset($test['todo']) && $test['todo']) {
  46. // TODO
  47. } else {
  48. eval('$expected = '.trim($test['php']).';');
  49. $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']);
  50. }
  51. }
  52. }
  53. return $tests;
  54. }
  55. public function testTabsInYaml()
  56. {
  57. // test tabs in YAML
  58. $yamls = array(
  59. "foo:\n bar",
  60. "foo:\n bar",
  61. "foo:\n bar",
  62. "foo:\n bar",
  63. );
  64. foreach ($yamls as $yaml) {
  65. try {
  66. $content = $this->parser->parse($yaml);
  67. $this->fail('YAML files must not contain tabs');
  68. } catch (\Exception $e) {
  69. $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
  70. $this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs');
  71. }
  72. }
  73. }
  74. public function testEndOfTheDocumentMarker()
  75. {
  76. $yaml = <<<EOF
  77. --- %YAML:1.0
  78. foo
  79. ...
  80. EOF;
  81. $this->assertEquals('foo', $this->parser->parse($yaml));
  82. }
  83. public function getBlockChompingTests()
  84. {
  85. $tests = array();
  86. $yaml = <<<'EOF'
  87. foo: |-
  88. one
  89. two
  90. bar: |-
  91. one
  92. two
  93. EOF;
  94. $expected = array(
  95. 'foo' => "one\ntwo",
  96. 'bar' => "one\ntwo",
  97. );
  98. $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);
  99. $yaml = <<<'EOF'
  100. foo: |-
  101. one
  102. two
  103. bar: |-
  104. one
  105. two
  106. EOF;
  107. $expected = array(
  108. 'foo' => "one\ntwo",
  109. 'bar' => "one\ntwo",
  110. );
  111. $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  112. $yaml = <<<'EOF'
  113. foo: |-
  114. one
  115. two
  116. bar: |-
  117. one
  118. two
  119. EOF;
  120. $expected = array(
  121. 'foo' => "one\ntwo",
  122. 'bar' => "one\ntwo",
  123. );
  124. $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml);
  125. $yaml = <<<'EOF'
  126. foo: |
  127. one
  128. two
  129. bar: |
  130. one
  131. two
  132. EOF;
  133. $expected = array(
  134. 'foo' => "one\ntwo\n",
  135. 'bar' => "one\ntwo\n",
  136. );
  137. $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);
  138. $yaml = <<<'EOF'
  139. foo: |
  140. one
  141. two
  142. bar: |
  143. one
  144. two
  145. EOF;
  146. $expected = array(
  147. 'foo' => "one\ntwo\n",
  148. 'bar' => "one\ntwo\n",
  149. );
  150. $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  151. $yaml = <<<'EOF'
  152. foo: |
  153. one
  154. two
  155. bar: |
  156. one
  157. two
  158. EOF;
  159. $expected = array(
  160. 'foo' => "one\ntwo\n",
  161. 'bar' => "one\ntwo",
  162. );
  163. $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);
  164. $yaml = <<<'EOF'
  165. foo: |+
  166. one
  167. two
  168. bar: |+
  169. one
  170. two
  171. EOF;
  172. $expected = array(
  173. 'foo' => "one\ntwo\n",
  174. 'bar' => "one\ntwo\n",
  175. );
  176. $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);
  177. $yaml = <<<'EOF'
  178. foo: |+
  179. one
  180. two
  181. bar: |+
  182. one
  183. two
  184. EOF;
  185. $expected = array(
  186. 'foo' => "one\ntwo\n\n",
  187. 'bar' => "one\ntwo\n\n",
  188. );
  189. $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  190. $yaml = <<<'EOF'
  191. foo: |+
  192. one
  193. two
  194. bar: |+
  195. one
  196. two
  197. EOF;
  198. $expected = array(
  199. 'foo' => "one\ntwo\n",
  200. 'bar' => "one\ntwo",
  201. );
  202. $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);
  203. $yaml = <<<'EOF'
  204. foo: >-
  205. one
  206. two
  207. bar: >-
  208. one
  209. two
  210. EOF;
  211. $expected = array(
  212. 'foo' => "one two",
  213. 'bar' => "one two",
  214. );
  215. $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);
  216. $yaml = <<<'EOF'
  217. foo: >-
  218. one
  219. two
  220. bar: >-
  221. one
  222. two
  223. EOF;
  224. $expected = array(
  225. 'foo' => "one two",
  226. 'bar' => "one two",
  227. );
  228. $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
  229. $yaml = <<<'EOF'
  230. foo: >-
  231. one
  232. two
  233. bar: >-
  234. one
  235. two
  236. EOF;
  237. $expected = array(
  238. 'foo' => "one two",
  239. 'bar' => "one two",
  240. );
  241. $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml);
  242. $yaml = <<<'EOF'
  243. foo: >
  244. one
  245. two
  246. bar: >
  247. one
  248. two
  249. EOF;
  250. $expected = array(
  251. 'foo' => "one two\n",
  252. 'bar' => "one two\n",
  253. );
  254. $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);
  255. $yaml = <<<'EOF'
  256. foo: >
  257. one
  258. two
  259. bar: >
  260. one
  261. two
  262. EOF;
  263. $expected = array(
  264. 'foo' => "one two\n",
  265. 'bar' => "one two\n",
  266. );
  267. $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
  268. $yaml = <<<'EOF'
  269. foo: >
  270. one
  271. two
  272. bar: >
  273. one
  274. two
  275. EOF;
  276. $expected = array(
  277. 'foo' => "one two\n",
  278. 'bar' => "one two",
  279. );
  280. $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);
  281. $yaml = <<<'EOF'
  282. foo: >+
  283. one
  284. two
  285. bar: >+
  286. one
  287. two
  288. EOF;
  289. $expected = array(
  290. 'foo' => "one two\n",
  291. 'bar' => "one two\n",
  292. );
  293. $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);
  294. $yaml = <<<'EOF'
  295. foo: >+
  296. one
  297. two
  298. bar: >+
  299. one
  300. two
  301. EOF;
  302. $expected = array(
  303. 'foo' => "one two\n\n",
  304. 'bar' => "one two\n\n",
  305. );
  306. $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
  307. $yaml = <<<'EOF'
  308. foo: >+
  309. one
  310. two
  311. bar: >+
  312. one
  313. two
  314. EOF;
  315. $expected = array(
  316. 'foo' => "one two\n",
  317. 'bar' => "one two",
  318. );
  319. $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
  320. return $tests;
  321. }
  322. /**
  323. * @dataProvider getBlockChompingTests
  324. */
  325. public function testBlockChomping($expected, $yaml)
  326. {
  327. $this->assertSame($expected, $this->parser->parse($yaml));
  328. }
  329. /**
  330. * Regression test for issue #7989.
  331. *
  332. * @see https://github.com/symfony/symfony/issues/7989
  333. */
  334. public function testBlockLiteralWithLeadingNewlines()
  335. {
  336. $yaml = <<<'EOF'
  337. foo: |-
  338. bar
  339. EOF;
  340. $expected = array(
  341. 'foo' => "\n\nbar",
  342. );
  343. $this->assertSame($expected, $this->parser->parse($yaml));
  344. }
  345. public function testObjectSupportEnabled()
  346. {
  347. $input = <<<EOF
  348. foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
  349. bar: 1
  350. EOF;
  351. $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
  352. }
  353. public function testObjectSupportDisabledButNoExceptions()
  354. {
  355. $input = <<<EOF
  356. foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
  357. bar: 1
  358. EOF;
  359. $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
  360. }
  361. /**
  362. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  363. */
  364. public function testObjectsSupportDisabledWithExceptions()
  365. {
  366. $this->parser->parse('foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}', true, false);
  367. }
  368. public function testNonUtf8Exception()
  369. {
  370. if (!function_exists('iconv')) {
  371. $this->markTestSkipped('Exceptions for non-utf8 charsets require the iconv() function.');
  372. return;
  373. }
  374. $yamls = array(
  375. iconv("UTF-8", "ISO-8859-1", "foo: 'äöüß'"),
  376. iconv("UTF-8", "ISO-8859-15", "euro: '€'"),
  377. iconv("UTF-8", "CP1252", "cp1252: '©ÉÇáñ'"),
  378. );
  379. foreach ($yamls as $yaml) {
  380. try {
  381. $this->parser->parse($yaml);
  382. $this->fail('charsets other than UTF-8 are rejected.');
  383. } catch (\Exception $e) {
  384. $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
  385. }
  386. }
  387. }
  388. /**
  389. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  390. */
  391. public function testUnindentedCollectionException()
  392. {
  393. $yaml = <<<EOF
  394. collection:
  395. -item1
  396. -item2
  397. -item3
  398. EOF;
  399. $this->parser->parse($yaml);
  400. }
  401. /**
  402. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  403. */
  404. public function testShortcutKeyUnindentedCollectionException()
  405. {
  406. $yaml = <<<EOF
  407. collection:
  408. - key: foo
  409. foo: bar
  410. EOF;
  411. $this->parser->parse($yaml);
  412. }
  413. /**
  414. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  415. * @expectedExceptionMessage Multiple documents are not supported.
  416. */
  417. public function testMultipleDocumentsNotSupportedException()
  418. {
  419. Yaml::parse(<<<EOL
  420. # Ranking of 1998 home runs
  421. ---
  422. - Mark McGwire
  423. - Sammy Sosa
  424. - Ken Griffey
  425. # Team ranking
  426. ---
  427. - Chicago Cubs
  428. - St Louis Cardinals
  429. EOL
  430. );
  431. }
  432. /**
  433. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  434. */
  435. public function testSequenceInAMapping()
  436. {
  437. Yaml::parse(<<<EOF
  438. yaml:
  439. hash: me
  440. - array stuff
  441. EOF
  442. );
  443. }
  444. /**
  445. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  446. */
  447. public function testMappingInASequence()
  448. {
  449. Yaml::parse(<<<EOF
  450. yaml:
  451. - array stuff
  452. hash: me
  453. EOF
  454. );
  455. }
  456. /**
  457. * > It is an error for two equal keys to appear in the same mapping node.
  458. * > In such a case the YAML processor may continue, ignoring the second
  459. * > `key: value` pair and issuing an appropriate warning. This strategy
  460. * > preserves a consistent information model for one-pass and random access
  461. * > applications.
  462. *
  463. * @see http://yaml.org/spec/1.2/spec.html#id2759572
  464. * @see http://yaml.org/spec/1.1/#id932806
  465. *
  466. * @covers \Symfony\Component\Yaml\Parser::parse
  467. */
  468. public function testMappingDuplicateKeyBlock()
  469. {
  470. $input = <<<EOD
  471. parent:
  472. child: first
  473. child: duplicate
  474. parent:
  475. child: duplicate
  476. child: duplicate
  477. EOD;
  478. $expected = array(
  479. 'parent' => array(
  480. 'child' => 'first',
  481. ),
  482. );
  483. $this->assertSame($expected, Yaml::parse($input));
  484. }
  485. /**
  486. * @covers \Symfony\Component\Yaml\Inline::parseMapping
  487. */
  488. public function testMappingDuplicateKeyFlow()
  489. {
  490. $input = <<<EOD
  491. parent: { child: first, child: duplicate }
  492. parent: { child: duplicate, child: duplicate }
  493. EOD;
  494. $expected = array(
  495. 'parent' => array(
  496. 'child' => 'first',
  497. ),
  498. );
  499. $this->assertSame($expected, Yaml::parse($input));
  500. }
  501. public function testEmptyValue()
  502. {
  503. $input = <<<EOF
  504. hash:
  505. EOF;
  506. $this->assertEquals(array('hash' => null), Yaml::parse($input));
  507. }
  508. public function testStringBlockWithComments()
  509. {
  510. $this->assertEquals(array('content' => <<<EOT
  511. # comment 1
  512. header
  513. # comment 2
  514. <body>
  515. <h1>title</h1>
  516. </body>
  517. footer # comment3
  518. EOT
  519. ), Yaml::parse(<<<EOF
  520. content: |
  521. # comment 1
  522. header
  523. # comment 2
  524. <body>
  525. <h1>title</h1>
  526. </body>
  527. footer # comment3
  528. EOF
  529. ));
  530. }
  531. public function testFoldedStringBlockWithComments()
  532. {
  533. $this->assertEquals(array(array('content' => <<<EOT
  534. # comment 1
  535. header
  536. # comment 2
  537. <body>
  538. <h1>title</h1>
  539. </body>
  540. footer # comment3
  541. EOT
  542. )), Yaml::parse(<<<EOF
  543. -
  544. content: |
  545. # comment 1
  546. header
  547. # comment 2
  548. <body>
  549. <h1>title</h1>
  550. </body>
  551. footer # comment3
  552. EOF
  553. ));
  554. }
  555. public function testNestedFoldedStringBlockWithComments()
  556. {
  557. $this->assertEquals(array(array(
  558. 'title' => 'some title',
  559. 'content' => <<<EOT
  560. # comment 1
  561. header
  562. # comment 2
  563. <body>
  564. <h1>title</h1>
  565. </body>
  566. footer # comment3
  567. EOT
  568. )), Yaml::parse(<<<EOF
  569. -
  570. title: some title
  571. content: |
  572. # comment 1
  573. header
  574. # comment 2
  575. <body>
  576. <h1>title</h1>
  577. </body>
  578. footer # comment3
  579. EOF
  580. ));
  581. }
  582. public function testReferenceResolvingInInlineStrings()
  583. {
  584. $this->assertEquals(array(
  585. 'var' => 'var-value',
  586. 'scalar' => 'var-value',
  587. 'list' => array('var-value'),
  588. 'list_in_list' => array(array('var-value')),
  589. 'map_in_list' => array(array('key' => 'var-value')),
  590. 'embedded_mapping' => array(array('key' => 'var-value')),
  591. 'map' => array('key' => 'var-value'),
  592. 'list_in_map' => array('key' => array('var-value')),
  593. 'map_in_map' => array('foo' => array('bar' => 'var-value')),
  594. ), Yaml::parse(<<<EOF
  595. var: &var var-value
  596. scalar: *var
  597. list: [ *var ]
  598. list_in_list: [[ *var ]]
  599. map_in_list: [ { key: *var } ]
  600. embedded_mapping: [ key: *var ]
  601. map: { key: *var }
  602. list_in_map: { key: [*var] }
  603. map_in_map: { foo: { bar: *var } }
  604. EOF
  605. ));
  606. }
  607. public function testYamlDirective()
  608. {
  609. $yaml = <<<EOF
  610. %YAML 1.2
  611. ---
  612. foo: 1
  613. bar: 2
  614. EOF;
  615. $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
  616. }
  617. }
  618. class B
  619. {
  620. public $b = 'foo';
  621. }