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