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