1: <?php
2:
3: /*
4: * This file is part of the Symfony package.
5: *
6: * (c) Fabien Potencier <fabien@symfony.com>
7: *
8: * For the full copyright and license information, please view the LICENSE
9: * file that was distributed with this source code.
10: */
11:
12: namespace Symfony\Component\Yaml\Tests;
13:
14: use PHPUnit\Framework\TestCase;
15: use Symfony\Component\Yaml\Parser;
16: use Symfony\Component\Yaml\Yaml;
17:
18: class ParserTest extends TestCase
19: {
20: /** @var Parser */
21: protected $parser;
22:
23: protected function setUp()
24: {
25: $this->parser = new Parser();
26: }
27:
28: protected function tearDown()
29: {
30: $this->parser = null;
31: }
32:
33: /**
34: * @dataProvider getDataFormSpecifications
35: */
36: public function testSpecifications($file, $expected, $yaml, $comment)
37: {
38: $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment);
39: }
40:
41: public function getDataFormSpecifications()
42: {
43: $parser = new Parser();
44: $path = __DIR__.'/Fixtures';
45:
46: $tests = array();
47: $files = $parser->parse(file_get_contents($path.'/index.yml'));
48: foreach ($files as $file) {
49: $yamls = file_get_contents($path.'/'.$file.'.yml');
50:
51: // split YAMLs documents
52: foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) {
53: if (!$yaml) {
54: continue;
55: }
56:
57: $test = $parser->parse($yaml);
58: if (isset($test['todo']) && $test['todo']) {
59: // TODO
60: } else {
61: eval('$expected = '.trim($test['php']).';');
62:
63: $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']);
64: }
65: }
66: }
67:
68: return $tests;
69: }
70:
71: public function testTabsInYaml()
72: {
73: // test tabs in YAML
74: $yamls = array(
75: "foo:\n bar",
76: "foo:\n bar",
77: "foo:\n bar",
78: "foo:\n bar",
79: );
80:
81: foreach ($yamls as $yaml) {
82: try {
83: $content = $this->parser->parse($yaml);
84:
85: $this->fail('YAML files must not contain tabs');
86: } catch (\Exception $e) {
87: $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs');
88: $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');
89: }
90: }
91: }
92:
93: public function testEndOfTheDocumentMarker()
94: {
95: $yaml = <<<'EOF'
96: --- %YAML:1.0
97: foo
98: ...
99: EOF;
100:
101: $this->assertEquals('foo', $this->parser->parse($yaml));
102: }
103:
104: public function getBlockChompingTests()
105: {
106: $tests = array();
107:
108: $yaml = <<<'EOF'
109: foo: |-
110: one
111: two
112: bar: |-
113: one
114: two
115:
116: EOF;
117: $expected = array(
118: 'foo' => "one\ntwo",
119: 'bar' => "one\ntwo",
120: );
121: $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml);
122:
123: $yaml = <<<'EOF'
124: foo: |-
125: one
126: two
127:
128: bar: |-
129: one
130: two
131:
132:
133: EOF;
134: $expected = array(
135: 'foo' => "one\ntwo",
136: 'bar' => "one\ntwo",
137: );
138: $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
139:
140: $yaml = <<<'EOF'
141: {}
142:
143:
144: EOF;
145: $expected = array();
146: $tests['Literal block chomping strip with multiple trailing newlines after a 1-liner'] = array($expected, $yaml);
147:
148: $yaml = <<<'EOF'
149: foo: |-
150: one
151: two
152: bar: |-
153: one
154: two
155: EOF;
156: $expected = array(
157: 'foo' => "one\ntwo",
158: 'bar' => "one\ntwo",
159: );
160: $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml);
161:
162: $yaml = <<<'EOF'
163: foo: |
164: one
165: two
166: bar: |
167: one
168: two
169:
170: EOF;
171: $expected = array(
172: 'foo' => "one\ntwo\n",
173: 'bar' => "one\ntwo\n",
174: );
175: $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml);
176:
177: $yaml = <<<'EOF'
178: foo: |
179: one
180: two
181:
182: bar: |
183: one
184: two
185:
186:
187: EOF;
188: $expected = array(
189: 'foo' => "one\ntwo\n",
190: 'bar' => "one\ntwo\n",
191: );
192: $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
193:
194: $yaml = <<<'EOF'
195: foo:
196: - bar: |
197: one
198:
199: two
200: EOF;
201: $expected = array(
202: 'foo' => array(
203: array(
204: 'bar' => "one\n\ntwo",
205: ),
206: ),
207: );
208: $tests['Literal block chomping clip with embedded blank line inside unindented collection'] = array($expected, $yaml);
209:
210: $yaml = <<<'EOF'
211: foo: |
212: one
213: two
214: bar: |
215: one
216: two
217: EOF;
218: $expected = array(
219: 'foo' => "one\ntwo\n",
220: 'bar' => "one\ntwo",
221: );
222: $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml);
223:
224: $yaml = <<<'EOF'
225: foo: |+
226: one
227: two
228: bar: |+
229: one
230: two
231:
232: EOF;
233: $expected = array(
234: 'foo' => "one\ntwo\n",
235: 'bar' => "one\ntwo\n",
236: );
237: $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml);
238:
239: $yaml = <<<'EOF'
240: foo: |+
241: one
242: two
243:
244: bar: |+
245: one
246: two
247:
248:
249: EOF;
250: $expected = array(
251: 'foo' => "one\ntwo\n\n",
252: 'bar' => "one\ntwo\n\n",
253: );
254: $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
255:
256: $yaml = <<<'EOF'
257: foo: |+
258: one
259: two
260: bar: |+
261: one
262: two
263: EOF;
264: $expected = array(
265: 'foo' => "one\ntwo\n",
266: 'bar' => "one\ntwo",
267: );
268: $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml);
269:
270: $yaml = <<<'EOF'
271: foo: >-
272: one
273: two
274: bar: >-
275: one
276: two
277:
278: EOF;
279: $expected = array(
280: 'foo' => 'one two',
281: 'bar' => 'one two',
282: );
283: $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml);
284:
285: $yaml = <<<'EOF'
286: foo: >-
287: one
288: two
289:
290: bar: >-
291: one
292: two
293:
294:
295: EOF;
296: $expected = array(
297: 'foo' => 'one two',
298: 'bar' => 'one two',
299: );
300: $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml);
301:
302: $yaml = <<<'EOF'
303: foo: >-
304: one
305: two
306: bar: >-
307: one
308: two
309: EOF;
310: $expected = array(
311: 'foo' => 'one two',
312: 'bar' => 'one two',
313: );
314: $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml);
315:
316: $yaml = <<<'EOF'
317: foo: >
318: one
319: two
320: bar: >
321: one
322: two
323:
324: EOF;
325: $expected = array(
326: 'foo' => "one two\n",
327: 'bar' => "one two\n",
328: );
329: $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml);
330:
331: $yaml = <<<'EOF'
332: foo: >
333: one
334: two
335:
336: bar: >
337: one
338: two
339:
340:
341: EOF;
342: $expected = array(
343: 'foo' => "one two\n",
344: 'bar' => "one two\n",
345: );
346: $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml);
347:
348: $yaml = <<<'EOF'
349: foo: >
350: one
351: two
352: bar: >
353: one
354: two
355: EOF;
356: $expected = array(
357: 'foo' => "one two\n",
358: 'bar' => 'one two',
359: );
360: $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml);
361:
362: $yaml = <<<'EOF'
363: foo: >+
364: one
365: two
366: bar: >+
367: one
368: two
369:
370: EOF;
371: $expected = array(
372: 'foo' => "one two\n",
373: 'bar' => "one two\n",
374: );
375: $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml);
376:
377: $yaml = <<<'EOF'
378: foo: >+
379: one
380: two
381:
382: bar: >+
383: one
384: two
385:
386:
387: EOF;
388: $expected = array(
389: 'foo' => "one two\n\n",
390: 'bar' => "one two\n\n",
391: );
392: $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml);
393:
394: $yaml = <<<'EOF'
395: foo: >+
396: one
397: two
398: bar: >+
399: one
400: two
401: EOF;
402: $expected = array(
403: 'foo' => "one two\n",
404: 'bar' => 'one two',
405: );
406: $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml);
407:
408: return $tests;
409: }
410:
411: /**
412: * @dataProvider getBlockChompingTests
413: */
414: public function testBlockChomping($expected, $yaml)
415: {
416: $this->assertSame($expected, $this->parser->parse($yaml));
417: }
418:
419: /**
420: * Regression test for issue #7989.
421: *
422: * @see https://github.com/symfony/symfony/issues/7989
423: */
424: public function testBlockLiteralWithLeadingNewlines()
425: {
426: $yaml = <<<'EOF'
427: foo: |-
428:
429:
430: bar
431:
432: EOF;
433: $expected = array(
434: 'foo' => "\n\nbar",
435: );
436:
437: $this->assertSame($expected, $this->parser->parse($yaml));
438: }
439:
440: public function testObjectSupportEnabled()
441: {
442: $input = <<<'EOF'
443: foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
444: bar: 1
445: EOF;
446: $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
447:
448: $input = <<<'EOF'
449: foo: !php/object:O:30:"Symfony\Component\Yaml\Tests\B":1:{s:1:"b";s:3:"foo";}
450: bar: 1
451: EOF;
452: $this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
453: }
454:
455: /**
456: * @dataProvider invalidDumpedObjectProvider
457: */
458: public function testObjectSupportDisabledButNoExceptions($input)
459: {
460: $this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
461: }
462:
463: /**
464: * @dataProvider getObjectForMapTests
465: */
466: public function testObjectForMap($yaml, $expected)
467: {
468: $this->assertEquals($expected, $this->parser->parse($yaml, false, false, true));
469: }
470:
471: public function getObjectForMapTests()
472: {
473: $tests = array();
474:
475: $yaml = <<<'EOF'
476: foo:
477: fiz: [cat]
478: EOF;
479: $expected = new \stdClass();
480: $expected->foo = new \stdClass();
481: $expected->foo->fiz = array('cat');
482: $tests['mapping'] = array($yaml, $expected);
483:
484: $yaml = '{ "foo": "bar", "fiz": "cat" }';
485: $expected = new \stdClass();
486: $expected->foo = 'bar';
487: $expected->fiz = 'cat';
488: $tests['inline-mapping'] = array($yaml, $expected);
489:
490: $yaml = "foo: bar\nbaz: foobar";
491: $expected = new \stdClass();
492: $expected->foo = 'bar';
493: $expected->baz = 'foobar';
494: $tests['object-for-map-is-applied-after-parsing'] = array($yaml, $expected);
495:
496: $yaml = <<<'EOT'
497: array:
498: - key: one
499: - key: two
500: EOT;
501: $expected = new \stdClass();
502: $expected->array = array();
503: $expected->array[0] = new \stdClass();
504: $expected->array[0]->key = 'one';
505: $expected->array[1] = new \stdClass();
506: $expected->array[1]->key = 'two';
507: $tests['nest-map-and-sequence'] = array($yaml, $expected);
508:
509: $yaml = <<<'YAML'
510: map:
511: 1: one
512: 2: two
513: YAML;
514: $expected = new \stdClass();
515: $expected->map = new \stdClass();
516: $expected->map->{1} = 'one';
517: $expected->map->{2} = 'two';
518: $tests['numeric-keys'] = array($yaml, $expected);
519:
520: $yaml = <<<'YAML'
521: map:
522: 0: one
523: 1: two
524: YAML;
525: $expected = new \stdClass();
526: $expected->map = new \stdClass();
527: $expected->map->{0} = 'one';
528: $expected->map->{1} = 'two';
529: $tests['zero-indexed-numeric-keys'] = array($yaml, $expected);
530:
531: return $tests;
532: }
533:
534: /**
535: * @dataProvider invalidDumpedObjectProvider
536: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
537: */
538: public function testObjectsSupportDisabledWithExceptions($yaml)
539: {
540: $this->parser->parse($yaml, true, false);
541: }
542:
543: public function invalidDumpedObjectProvider()
544: {
545: $yamlTag = <<<'EOF'
546: foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
547: bar: 1
548: EOF;
549: $localTag = <<<'EOF'
550: foo: !php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
551: bar: 1
552: EOF;
553:
554: return array(
555: 'yaml-tag' => array($yamlTag),
556: 'local-tag' => array($localTag),
557: );
558: }
559:
560: /**
561: * @requires extension iconv
562: */
563: public function testNonUtf8Exception()
564: {
565: $yamls = array(
566: iconv('UTF-8', 'ISO-8859-1', "foo: 'äöüß'"),
567: iconv('UTF-8', 'ISO-8859-15', "euro: '€'"),
568: iconv('UTF-8', 'CP1252', "cp1252: '©ÉÇáñ'"),
569: );
570:
571: foreach ($yamls as $yaml) {
572: try {
573: $this->parser->parse($yaml);
574:
575: $this->fail('charsets other than UTF-8 are rejected.');
576: } catch (\Exception $e) {
577: $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.');
578: }
579: }
580: }
581:
582: /**
583: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
584: */
585: public function testUnindentedCollectionException()
586: {
587: $yaml = <<<'EOF'
588:
589: collection:
590: -item1
591: -item2
592: -item3
593:
594: EOF;
595:
596: $this->parser->parse($yaml);
597: }
598:
599: /**
600: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
601: */
602: public function testShortcutKeyUnindentedCollectionException()
603: {
604: $yaml = <<<'EOF'
605:
606: collection:
607: - key: foo
608: foo: bar
609:
610: EOF;
611:
612: $this->parser->parse($yaml);
613: }
614:
615: /**
616: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
617: * @expectedExceptionMessageRegExp /^Multiple documents are not supported.+/
618: */
619: public function testMultipleDocumentsNotSupportedException()
620: {
621: Yaml::parse(<<<'EOL'
622: # Ranking of 1998 home runs
623: ---
624: - Mark McGwire
625: - Sammy Sosa
626: - Ken Griffey
627:
628: # Team ranking
629: ---
630: - Chicago Cubs
631: - St Louis Cardinals
632: EOL
633: );
634: }
635:
636: /**
637: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
638: */
639: public function testSequenceInAMapping()
640: {
641: Yaml::parse(<<<'EOF'
642: yaml:
643: hash: me
644: - array stuff
645: EOF
646: );
647: }
648:
649: public function testSequenceInMappingStartedBySingleDashLine()
650: {
651: $yaml = <<<'EOT'
652: a:
653: -
654: b:
655: -
656: bar: baz
657: - foo
658: d: e
659: EOT;
660: $expected = array(
661: 'a' => array(
662: array(
663: 'b' => array(
664: array(
665: 'bar' => 'baz',
666: ),
667: ),
668: ),
669: 'foo',
670: ),
671: 'd' => 'e',
672: );
673:
674: $this->assertSame($expected, $this->parser->parse($yaml));
675: }
676:
677: public function testSequenceFollowedByCommentEmbeddedInMapping()
678: {
679: $yaml = <<<'EOT'
680: a:
681: b:
682: - c
683: # comment
684: d: e
685: EOT;
686: $expected = array(
687: 'a' => array(
688: 'b' => array('c'),
689: 'd' => 'e',
690: ),
691: );
692:
693: $this->assertSame($expected, $this->parser->parse($yaml));
694: }
695:
696: /**
697: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
698: */
699: public function testMappingInASequence()
700: {
701: Yaml::parse(<<<'EOF'
702: yaml:
703: - array stuff
704: hash: me
705: EOF
706: );
707: }
708:
709: /**
710: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
711: * @expectedExceptionMessage missing colon
712: */
713: public function testScalarInSequence()
714: {
715: Yaml::parse(<<<'EOF'
716: foo:
717: - bar
718: "missing colon"
719: foo: bar
720: EOF
721: );
722: }
723:
724: /**
725: * > It is an error for two equal keys to appear in the same mapping node.
726: * > In such a case the YAML processor may continue, ignoring the second
727: * > `key: value` pair and issuing an appropriate warning. This strategy
728: * > preserves a consistent information model for one-pass and random access
729: * > applications.
730: *
731: * @see http://yaml.org/spec/1.2/spec.html#id2759572
732: * @see http://yaml.org/spec/1.1/#id932806
733: */
734: public function testMappingDuplicateKeyBlock()
735: {
736: $input = <<<'EOD'
737: parent:
738: child: first
739: child: duplicate
740: parent:
741: child: duplicate
742: child: duplicate
743: EOD;
744: $expected = array(
745: 'parent' => array(
746: 'child' => 'first',
747: ),
748: );
749: $this->assertSame($expected, Yaml::parse($input));
750: }
751:
752: public function testMappingDuplicateKeyFlow()
753: {
754: $input = <<<'EOD'
755: parent: { child: first, child: duplicate }
756: parent: { child: duplicate, child: duplicate }
757: EOD;
758: $expected = array(
759: 'parent' => array(
760: 'child' => 'first',
761: ),
762: );
763: $this->assertSame($expected, Yaml::parse($input));
764: }
765:
766: public function testEmptyValue()
767: {
768: $input = <<<'EOF'
769: hash:
770: EOF;
771:
772: $this->assertEquals(array('hash' => null), Yaml::parse($input));
773: }
774:
775: public function testCommentAtTheRootIndent()
776: {
777: $this->assertEquals(array(
778: 'services' => array(
779: 'app.foo_service' => array(
780: 'class' => 'Foo',
781: ),
782: 'app/bar_service' => array(
783: 'class' => 'Bar',
784: ),
785: ),
786: ), Yaml::parse(<<<'EOF'
787: # comment 1
788: services:
789: # comment 2
790: # comment 3
791: app.foo_service:
792: class: Foo
793: # comment 4
794: # comment 5
795: app/bar_service:
796: class: Bar
797: EOF
798: ));
799: }
800:
801: public function testStringBlockWithComments()
802: {
803: $this->assertEquals(array('content' => <<<'EOT'
804: # comment 1
805: header
806:
807: # comment 2
808: <body>
809: <h1>title</h1>
810: </body>
811:
812: footer # comment3
813: EOT
814: ), Yaml::parse(<<<'EOF'
815: content: |
816: # comment 1
817: header
818:
819: # comment 2
820: <body>
821: <h1>title</h1>
822: </body>
823:
824: footer # comment3
825: EOF
826: ));
827: }
828:
829: public function testFoldedStringBlockWithComments()
830: {
831: $this->assertEquals(array(array('content' => <<<'EOT'
832: # comment 1
833: header
834:
835: # comment 2
836: <body>
837: <h1>title</h1>
838: </body>
839:
840: footer # comment3
841: EOT
842: )), Yaml::parse(<<<'EOF'
843: -
844: content: |
845: # comment 1
846: header
847:
848: # comment 2
849: <body>
850: <h1>title</h1>
851: </body>
852:
853: footer # comment3
854: EOF
855: ));
856: }
857:
858: public function testNestedFoldedStringBlockWithComments()
859: {
860: $this->assertEquals(array(array(
861: 'title' => 'some title',
862: 'content' => <<<'EOT'
863: # comment 1
864: header
865:
866: # comment 2
867: <body>
868: <h1>title</h1>
869: </body>
870:
871: footer # comment3
872: EOT
873: )), Yaml::parse(<<<'EOF'
874: -
875: title: some title
876: content: |
877: # comment 1
878: header
879:
880: # comment 2
881: <body>
882: <h1>title</h1>
883: </body>
884:
885: footer # comment3
886: EOF
887: ));
888: }
889:
890: public function testReferenceResolvingInInlineStrings()
891: {
892: $this->assertEquals(array(
893: 'var' => 'var-value',
894: 'scalar' => 'var-value',
895: 'list' => array('var-value'),
896: 'list_in_list' => array(array('var-value')),
897: 'map_in_list' => array(array('key' => 'var-value')),
898: 'embedded_mapping' => array(array('key' => 'var-value')),
899: 'map' => array('key' => 'var-value'),
900: 'list_in_map' => array('key' => array('var-value')),
901: 'map_in_map' => array('foo' => array('bar' => 'var-value')),
902: ), Yaml::parse(<<<'EOF'
903: var: &var var-value
904: scalar: *var
905: list: [ *var ]
906: list_in_list: [[ *var ]]
907: map_in_list: [ { key: *var } ]
908: embedded_mapping: [ key: *var ]
909: map: { key: *var }
910: list_in_map: { key: [*var] }
911: map_in_map: { foo: { bar: *var } }
912: EOF
913: ));
914: }
915:
916: public function testYamlDirective()
917: {
918: $yaml = <<<'EOF'
919: %YAML 1.2
920: ---
921: foo: 1
922: bar: 2
923: EOF;
924: $this->assertEquals(array('foo' => 1, 'bar' => 2), $this->parser->parse($yaml));
925: }
926:
927: public function testFloatKeys()
928: {
929: $yaml = <<<'EOF'
930: foo:
931: 1.2: "bar"
932: 1.3: "baz"
933: EOF;
934:
935: $expected = array(
936: 'foo' => array(
937: '1.2' => 'bar',
938: '1.3' => 'baz',
939: ),
940: );
941:
942: $this->assertEquals($expected, $this->parser->parse($yaml));
943: }
944:
945: /**
946: * @group legacy
947: * @expectedDeprecation Using a colon in the unquoted mapping value "bar: baz" in line 1 is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.
948: * throw ParseException in Symfony 3.0
949: */
950: public function testColonInMappingValueException()
951: {
952: $yaml = <<<'EOF'
953: foo: bar: baz
954: EOF;
955:
956: $this->parser->parse($yaml);
957: }
958:
959: public function testColonInMappingValueExceptionNotTriggeredByColonInComment()
960: {
961: $yaml = <<<'EOT'
962: foo:
963: bar: foobar # Note: a comment after a colon
964: EOT;
965:
966: $this->assertSame(array('foo' => array('bar' => 'foobar')), $this->parser->parse($yaml));
967: }
968:
969: /**
970: * @dataProvider getCommentLikeStringInScalarBlockData
971: */
972: public function testCommentLikeStringsAreNotStrippedInBlockScalars($yaml, $expectedParserResult)
973: {
974: $this->assertSame($expectedParserResult, $this->parser->parse($yaml));
975: }
976:
977: public function getCommentLikeStringInScalarBlockData()
978: {
979: $tests = array();
980:
981: $yaml = <<<'EOT'
982: pages:
983: -
984: title: some title
985: content: |
986: # comment 1
987: header
988:
989: # comment 2
990: <body>
991: <h1>title</h1>
992: </body>
993:
994: footer # comment3
995: EOT;
996: $expected = array(
997: 'pages' => array(
998: array(
999: 'title' => 'some title',
1000: 'content' => <<<'EOT'
1001: # comment 1
1002: header
1003:
1004: # comment 2
1005: <body>
1006: <h1>title</h1>
1007: </body>
1008:
1009: footer # comment3
1010: EOT
1011: ,
1012: ),
1013: ),
1014: );
1015: $tests[] = array($yaml, $expected);
1016:
1017: $yaml = <<<'EOT'
1018: test: |
1019: foo
1020: # bar
1021: baz
1022: collection:
1023: - one: |
1024: foo
1025: # bar
1026: baz
1027: - two: |
1028: foo
1029: # bar
1030: baz
1031: EOT;
1032: $expected = array(
1033: 'test' => <<<'EOT'
1034: foo
1035: # bar
1036: baz
1037:
1038: EOT
1039: ,
1040: 'collection' => array(
1041: array(
1042: 'one' => <<<'EOT'
1043: foo
1044: # bar
1045: baz
1046:
1047: EOT
1048: ,
1049: ),
1050: array(
1051: 'two' => <<<'EOT'
1052: foo
1053: # bar
1054: baz
1055: EOT
1056: ,
1057: ),
1058: ),
1059: );
1060: $tests[] = array($yaml, $expected);
1061:
1062: $yaml = <<<'EOT'
1063: foo:
1064: bar:
1065: scalar-block: >
1066: line1
1067: line2>
1068: baz:
1069: # comment
1070: foobar: ~
1071: EOT;
1072: $expected = array(
1073: 'foo' => array(
1074: 'bar' => array(
1075: 'scalar-block' => "line1 line2>\n",
1076: ),
1077: 'baz' => array(
1078: 'foobar' => null,
1079: ),
1080: ),
1081: );
1082: $tests[] = array($yaml, $expected);
1083:
1084: $yaml = <<<'EOT'
1085: a:
1086: b: hello
1087: # c: |
1088: # first row
1089: # second row
1090: d: hello
1091: EOT;
1092: $expected = array(
1093: 'a' => array(
1094: 'b' => 'hello',
1095: 'd' => 'hello',
1096: ),
1097: );
1098: $tests[] = array($yaml, $expected);
1099:
1100: return $tests;
1101: }
1102:
1103: public function testBlankLinesAreParsedAsNewLinesInFoldedBlocks()
1104: {
1105: $yaml = <<<'EOT'
1106: test: >
1107: <h2>A heading</h2>
1108:
1109: <ul>
1110: <li>a list</li>
1111: <li>may be a good example</li>
1112: </ul>
1113: EOT;
1114:
1115: $this->assertSame(
1116: array(
1117: 'test' => <<<'EOT'
1118: <h2>A heading</h2>
1119: <ul> <li>a list</li> <li>may be a good example</li> </ul>
1120: EOT
1121: ,
1122: ),
1123: $this->parser->parse($yaml)
1124: );
1125: }
1126:
1127: public function testAdditionallyIndentedLinesAreParsedAsNewLinesInFoldedBlocks()
1128: {
1129: $yaml = <<<'EOT'
1130: test: >
1131: <h2>A heading</h2>
1132:
1133: <ul>
1134: <li>a list</li>
1135: <li>may be a good example</li>
1136: </ul>
1137: EOT;
1138:
1139: $this->assertSame(
1140: array(
1141: 'test' => <<<'EOT'
1142: <h2>A heading</h2>
1143: <ul>
1144: <li>a list</li>
1145: <li>may be a good example</li>
1146: </ul>
1147: EOT
1148: ,
1149: ),
1150: $this->parser->parse($yaml)
1151: );
1152: }
1153:
1154: /**
1155: * @param $lineNumber
1156: * @param $yaml
1157: * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider
1158: */
1159: public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml)
1160: {
1161: if (method_exists($this, 'expectException')) {
1162: $this->expectException('\Symfony\Component\Yaml\Exception\ParseException');
1163: $this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
1164: } else {
1165: $this->setExpectedException('\Symfony\Component\Yaml\Exception\ParseException', sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber));
1166: }
1167:
1168: $this->parser->parse($yaml);
1169: }
1170:
1171: public function parserThrowsExceptionWithCorrectLineNumberProvider()
1172: {
1173: return array(
1174: array(
1175: 4,
1176: <<<'YAML'
1177: foo:
1178: -
1179: # bar
1180: bar: "123",
1181: YAML
1182: ),
1183: array(
1184: 5,
1185: <<<'YAML'
1186: foo:
1187: -
1188: # bar
1189: # bar
1190: bar: "123",
1191: YAML
1192: ),
1193: array(
1194: 8,
1195: <<<'YAML'
1196: foo:
1197: -
1198: # foobar
1199: baz: 123
1200: bar:
1201: -
1202: # bar
1203: bar: "123",
1204: YAML
1205: ),
1206: array(
1207: 10,
1208: <<<'YAML'
1209: foo:
1210: -
1211: # foobar
1212: # foobar
1213: baz: 123
1214: bar:
1215: -
1216: # bar
1217: # bar
1218: bar: "123",
1219: YAML
1220: ),
1221: );
1222: }
1223:
1224: public function testCanParseVeryLongValue()
1225: {
1226: $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000);
1227: $trickyVal = array('x' => $longStringWithSpaces);
1228:
1229: $yamlString = Yaml::dump($trickyVal);
1230: $arrayFromYaml = $this->parser->parse($yamlString);
1231:
1232: $this->assertEquals($trickyVal, $arrayFromYaml);
1233: }
1234:
1235: /**
1236: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1237: * @expectedExceptionMessage Reference "foo" does not exist at line 2
1238: */
1239: public function testParserCleansUpReferencesBetweenRuns()
1240: {
1241: $yaml = <<<YAML
1242: foo: &foo
1243: baz: foobar
1244: bar:
1245: <<: *foo
1246: YAML;
1247: $this->parser->parse($yaml);
1248:
1249: $yaml = <<<YAML
1250: bar:
1251: <<: *foo
1252: YAML;
1253: $this->parser->parse($yaml);
1254: }
1255:
1256: public function testParseReferencesOnMergeKeys()
1257: {
1258: $yaml = <<<YAML
1259: mergekeyrefdef:
1260: a: foo
1261: <<: &quux
1262: b: bar
1263: c: baz
1264: mergekeyderef:
1265: d: quux
1266: <<: *quux
1267: YAML;
1268: $expected = array(
1269: 'mergekeyrefdef' => array(
1270: 'a' => 'foo',
1271: 'b' => 'bar',
1272: 'c' => 'baz',
1273: ),
1274: 'mergekeyderef' => array(
1275: 'd' => 'quux',
1276: 'b' => 'bar',
1277: 'c' => 'baz',
1278: ),
1279: );
1280:
1281: $this->assertSame($expected, $this->parser->parse($yaml));
1282: }
1283:
1284: /**
1285: * @expectedException \Symfony\Component\Yaml\Exception\ParseException
1286: * @expectedExceptionMessage Reference "foo" does not exist
1287: */
1288: public function testEvalRefException()
1289: {
1290: $yaml = <<<EOE
1291: foo: { &foo { a: Steve, <<: *foo} }
1292: EOE;
1293: $this->parser->parse($yaml);
1294: }
1295: }
1296:
1297: class B
1298: {
1299: public $b = 'foo';
1300: }
1301: