InlineTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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\Bridge\PhpUnit\ErrorAssert;
  12. use Symfony\Component\Yaml\Inline;
  13. use Symfony\Component\Yaml\Yaml;
  14. class InlineTest extends \PHPUnit_Framework_TestCase
  15. {
  16. /**
  17. * @dataProvider getTestsForParse
  18. */
  19. public function testParse($yaml, $value)
  20. {
  21. $this->assertSame($value, Inline::parse($yaml), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml));
  22. }
  23. /**
  24. * @dataProvider getTestsForParseWithMapObjects
  25. */
  26. public function testParseWithMapObjects($yaml, $value)
  27. {
  28. $actual = Inline::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP);
  29. $this->assertSame(serialize($value), serialize($actual));
  30. }
  31. /**
  32. * @group legacy
  33. * @dataProvider getTestsForParseWithMapObjects
  34. */
  35. public function testParseWithMapObjectsPassingTrue($yaml, $value)
  36. {
  37. $actual = Inline::parse($yaml, false, false, true);
  38. $this->assertSame(serialize($value), serialize($actual));
  39. }
  40. /**
  41. * @dataProvider getTestsForDump
  42. */
  43. public function testDump($yaml, $value)
  44. {
  45. $this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml));
  46. $this->assertSame($value, Inline::parse(Inline::dump($value)), 'check consistency');
  47. }
  48. public function testDumpNumericValueWithLocale()
  49. {
  50. $locale = setlocale(LC_NUMERIC, 0);
  51. if (false === $locale) {
  52. $this->markTestSkipped('Your platform does not support locales.');
  53. }
  54. try {
  55. $requiredLocales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252');
  56. if (false === setlocale(LC_NUMERIC, $requiredLocales)) {
  57. $this->markTestSkipped('Could not set any of required locales: '.implode(', ', $requiredLocales));
  58. }
  59. $this->assertEquals('1.2', Inline::dump(1.2));
  60. $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0)));
  61. } finally {
  62. setlocale(LC_NUMERIC, $locale);
  63. }
  64. }
  65. public function testHashStringsResemblingExponentialNumericsShouldNotBeChangedToINF()
  66. {
  67. $value = '686e444';
  68. $this->assertSame($value, Inline::parse(Inline::dump($value)));
  69. }
  70. /**
  71. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  72. * @expectedExceptionMessage Found unknown escape character "\V".
  73. */
  74. public function testParseScalarWithNonEscapedBlackslashShouldThrowException()
  75. {
  76. Inline::parse('"Foo\Var"');
  77. }
  78. /**
  79. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  80. */
  81. public function testParseScalarWithNonEscapedBlackslashAtTheEndShouldThrowException()
  82. {
  83. Inline::parse('"Foo\\"');
  84. }
  85. /**
  86. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  87. */
  88. public function testParseScalarWithIncorrectlyQuotedStringShouldThrowException()
  89. {
  90. $value = "'don't do somthin' like that'";
  91. Inline::parse($value);
  92. }
  93. /**
  94. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  95. */
  96. public function testParseScalarWithIncorrectlyDoubleQuotedStringShouldThrowException()
  97. {
  98. $value = '"don"t do somthin" like that"';
  99. Inline::parse($value);
  100. }
  101. /**
  102. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  103. */
  104. public function testParseInvalidMappingKeyShouldThrowException()
  105. {
  106. $value = '{ "foo " bar": "bar" }';
  107. Inline::parse($value);
  108. }
  109. /**
  110. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  111. */
  112. public function testParseInvalidMappingShouldThrowException()
  113. {
  114. Inline::parse('[foo] bar');
  115. }
  116. /**
  117. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  118. */
  119. public function testParseInvalidSequenceShouldThrowException()
  120. {
  121. Inline::parse('{ foo: bar } bar');
  122. }
  123. public function testParseScalarWithCorrectlyQuotedStringShouldReturnString()
  124. {
  125. $value = "'don''t do somthin'' like that'";
  126. $expect = "don't do somthin' like that";
  127. $this->assertSame($expect, Inline::parseScalar($value));
  128. }
  129. /**
  130. * @dataProvider getDataForParseReferences
  131. */
  132. public function testParseReferences($yaml, $expected)
  133. {
  134. $this->assertSame($expected, Inline::parse($yaml, 0, array('var' => 'var-value')));
  135. }
  136. /**
  137. * @group legacy
  138. * @dataProvider getDataForParseReferences
  139. */
  140. public function testParseReferencesAsFifthArgument($yaml, $expected)
  141. {
  142. $this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value')));
  143. }
  144. public function getDataForParseReferences()
  145. {
  146. return array(
  147. 'scalar' => array('*var', 'var-value'),
  148. 'list' => array('[ *var ]', array('var-value')),
  149. 'list-in-list' => array('[[ *var ]]', array(array('var-value'))),
  150. 'map-in-list' => array('[ { key: *var } ]', array(array('key' => 'var-value'))),
  151. 'embedded-mapping-in-list' => array('[ key: *var ]', array(array('key' => 'var-value'))),
  152. 'map' => array('{ key: *var }', array('key' => 'var-value')),
  153. 'list-in-map' => array('{ key: [*var] }', array('key' => array('var-value'))),
  154. 'map-in-map' => array('{ foo: { bar: *var } }', array('foo' => array('bar' => 'var-value'))),
  155. );
  156. }
  157. public function testParseMapReferenceInSequence()
  158. {
  159. $foo = array(
  160. 'a' => 'Steve',
  161. 'b' => 'Clark',
  162. 'c' => 'Brian',
  163. );
  164. $this->assertSame(array($foo), Inline::parse('[*foo]', 0, array('foo' => $foo)));
  165. }
  166. /**
  167. * @group legacy
  168. */
  169. public function testParseMapReferenceInSequenceAsFifthArgument()
  170. {
  171. $foo = array(
  172. 'a' => 'Steve',
  173. 'b' => 'Clark',
  174. 'c' => 'Brian',
  175. );
  176. $this->assertSame(array($foo), Inline::parse('[*foo]', false, false, false, array('foo' => $foo)));
  177. }
  178. /**
  179. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  180. * @expectedExceptionMessage A reference must contain at least one character.
  181. */
  182. public function testParseUnquotedAsterisk()
  183. {
  184. Inline::parse('{ foo: * }');
  185. }
  186. /**
  187. * @expectedException \Symfony\Component\Yaml\Exception\ParseException
  188. * @expectedExceptionMessage A reference must contain at least one character.
  189. */
  190. public function testParseUnquotedAsteriskFollowedByAComment()
  191. {
  192. Inline::parse('{ foo: * #foo }');
  193. }
  194. /**
  195. * @dataProvider getReservedIndicators
  196. * @expectedException Symfony\Component\Yaml\Exception\ParseException
  197. * @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
  198. */
  199. public function testParseUnquotedScalarStartingWithReservedIndicator($indicator)
  200. {
  201. Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
  202. }
  203. public function getReservedIndicators()
  204. {
  205. return array(array('@'), array('`'));
  206. }
  207. /**
  208. * @dataProvider getScalarIndicators
  209. * @expectedException Symfony\Component\Yaml\Exception\ParseException
  210. * @expectedExceptionMessage cannot start a plain scalar; you need to quote the scalar.
  211. */
  212. public function testParseUnquotedScalarStartingWithScalarIndicator($indicator)
  213. {
  214. Inline::parse(sprintf('{ foo: %sfoo }', $indicator));
  215. }
  216. public function getScalarIndicators()
  217. {
  218. return array(array('|'), array('>'));
  219. }
  220. /**
  221. * @group legacy
  222. * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered
  223. * throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
  224. */
  225. public function testParseUnquotedScalarStartingWithPercentCharacter()
  226. {
  227. ErrorAssert::assertDeprecationsAreTriggered('Not quoting the scalar "%foo " starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', function () {
  228. Inline::parse('{ foo: %foo }');
  229. });
  230. }
  231. /**
  232. * @dataProvider getDataForIsHash
  233. */
  234. public function testIsHash($array, $expected)
  235. {
  236. $this->assertSame($expected, Inline::isHash($array));
  237. }
  238. public function getDataForIsHash()
  239. {
  240. return array(
  241. array(array(), false),
  242. array(array(1, 2, 3), false),
  243. array(array(2 => 1, 1 => 2, 0 => 3), true),
  244. array(array('foo' => 1, 'bar' => 2), true),
  245. );
  246. }
  247. public function getTestsForParse()
  248. {
  249. return array(
  250. array('', ''),
  251. array('null', null),
  252. array('false', false),
  253. array('true', true),
  254. array('12', 12),
  255. array('-12', -12),
  256. array('"quoted string"', 'quoted string'),
  257. array("'quoted string'", 'quoted string'),
  258. array('12.30e+02', 12.30e+02),
  259. array('0x4D2', 0x4D2),
  260. array('02333', 02333),
  261. array('.Inf', -log(0)),
  262. array('-.Inf', log(0)),
  263. array("'686e444'", '686e444'),
  264. array('686e444', 646e444),
  265. array('123456789123456789123456789123456789', '123456789123456789123456789123456789'),
  266. array('"foo\r\nbar"', "foo\r\nbar"),
  267. array("'foo#bar'", 'foo#bar'),
  268. array("'foo # bar'", 'foo # bar'),
  269. array("'#cfcfcf'", '#cfcfcf'),
  270. array('::form_base.html.twig', '::form_base.html.twig'),
  271. // Pre-YAML-1.2 booleans
  272. array("'y'", 'y'),
  273. array("'n'", 'n'),
  274. array("'yes'", 'yes'),
  275. array("'no'", 'no'),
  276. array("'on'", 'on'),
  277. array("'off'", 'off'),
  278. array('2007-10-30', gmmktime(0, 0, 0, 10, 30, 2007)),
  279. array('2007-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 2007)),
  280. array('2007-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 2007)),
  281. array('1960-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 1960)),
  282. array('1730-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 1730)),
  283. array('"a \\"string\\" with \'quoted strings inside\'"', 'a "string" with \'quoted strings inside\''),
  284. array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''),
  285. // sequences
  286. // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon
  287. array('[foo, http://urls.are/no/mappings, false, null, 12]', array('foo', 'http://urls.are/no/mappings', false, null, 12)),
  288. array('[ foo , bar , false , null , 12 ]', array('foo', 'bar', false, null, 12)),
  289. array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
  290. // mappings
  291. array('{foo:bar,bar:foo,false:false,null:null,integer:12}', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
  292. array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
  293. array('{foo: \'bar\', bar: \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')),
  294. array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', array('foo' => 'bar', 'bar' => 'foo: bar')),
  295. array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', array('foo\'' => 'bar', 'bar"' => 'foo: bar')),
  296. array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', array('foo: ' => 'bar', 'bar: ' => 'foo: bar')),
  297. // nested sequences and mappings
  298. array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))),
  299. array('[foo, {bar: foo}]', array('foo', array('bar' => 'foo'))),
  300. array('{ foo: {bar: foo} }', array('foo' => array('bar' => 'foo'))),
  301. array('{ foo: [bar, foo] }', array('foo' => array('bar', 'foo'))),
  302. array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))),
  303. array('[{ foo: {bar: foo} }]', array(array('foo' => array('bar' => 'foo')))),
  304. array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))),
  305. array('[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))),
  306. array('[foo, bar: { foo: bar }]', array('foo', '1' => array('bar' => array('foo' => 'bar')))),
  307. array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')),
  308. );
  309. }
  310. public function getTestsForParseWithMapObjects()
  311. {
  312. return array(
  313. array('', ''),
  314. array('null', null),
  315. array('false', false),
  316. array('true', true),
  317. array('12', 12),
  318. array('-12', -12),
  319. array('"quoted string"', 'quoted string'),
  320. array("'quoted string'", 'quoted string'),
  321. array('12.30e+02', 12.30e+02),
  322. array('0x4D2', 0x4D2),
  323. array('02333', 02333),
  324. array('.Inf', -log(0)),
  325. array('-.Inf', log(0)),
  326. array("'686e444'", '686e444'),
  327. array('686e444', 646e444),
  328. array('123456789123456789123456789123456789', '123456789123456789123456789123456789'),
  329. array('"foo\r\nbar"', "foo\r\nbar"),
  330. array("'foo#bar'", 'foo#bar'),
  331. array("'foo # bar'", 'foo # bar'),
  332. array("'#cfcfcf'", '#cfcfcf'),
  333. array('::form_base.html.twig', '::form_base.html.twig'),
  334. array('2007-10-30', gmmktime(0, 0, 0, 10, 30, 2007)),
  335. array('2007-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 2007)),
  336. array('2007-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 2007)),
  337. array('1960-10-30 02:59:43 Z', gmmktime(2, 59, 43, 10, 30, 1960)),
  338. array('1730-10-30T02:59:43Z', gmmktime(2, 59, 43, 10, 30, 1730)),
  339. array('"a \\"string\\" with \'quoted strings inside\'"', 'a "string" with \'quoted strings inside\''),
  340. array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''),
  341. // sequences
  342. // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon
  343. array('[foo, http://urls.are/no/mappings, false, null, 12]', array('foo', 'http://urls.are/no/mappings', false, null, 12)),
  344. array('[ foo , bar , false , null , 12 ]', array('foo', 'bar', false, null, 12)),
  345. array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
  346. // mappings
  347. array('{foo:bar,bar:foo,false:false,null:null,integer:12}', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
  348. array('{ foo : bar, bar : foo, false : false, null : null, integer : 12 }', (object) array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
  349. array('{foo: \'bar\', bar: \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')),
  350. array('{\'foo\': \'bar\', "bar": \'foo: bar\'}', (object) array('foo' => 'bar', 'bar' => 'foo: bar')),
  351. array('{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}', (object) array('foo\'' => 'bar', 'bar"' => 'foo: bar')),
  352. array('{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}', (object) array('foo: ' => 'bar', 'bar: ' => 'foo: bar')),
  353. // nested sequences and mappings
  354. array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))),
  355. array('[foo, {bar: foo}]', array('foo', (object) array('bar' => 'foo'))),
  356. array('{ foo: {bar: foo} }', (object) array('foo' => (object) array('bar' => 'foo'))),
  357. array('{ foo: [bar, foo] }', (object) array('foo' => array('bar', 'foo'))),
  358. array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))),
  359. array('[{ foo: {bar: foo} }]', array((object) array('foo' => (object) array('bar' => 'foo')))),
  360. array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))),
  361. array('[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]', array('foo', (object) array('bar' => 'foo', 'foo' => array('foo', (object) array('bar' => 'foo'))), array('foo', (object) array('bar' => 'foo')))),
  362. array('[foo, bar: { foo: bar }]', array('foo', '1' => (object) array('bar' => (object) array('foo' => 'bar')))),
  363. array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', (object) array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')),
  364. array('{}', new \stdClass()),
  365. array('{ foo : bar, bar : {} }', (object) array('foo' => 'bar', 'bar' => new \stdClass())),
  366. array('{ foo : [], bar : {} }', (object) array('foo' => array(), 'bar' => new \stdClass())),
  367. array('{foo: \'bar\', bar: {} }', (object) array('foo' => 'bar', 'bar' => new \stdClass())),
  368. array('{\'foo\': \'bar\', "bar": {}}', (object) array('foo' => 'bar', 'bar' => new \stdClass())),
  369. array('{\'foo\': \'bar\', "bar": \'{}\'}', (object) array('foo' => 'bar', 'bar' => '{}')),
  370. array('[foo, [{}, {}]]', array('foo', array(new \stdClass(), new \stdClass()))),
  371. array('[foo, [[], {}]]', array('foo', array(array(), new \stdClass()))),
  372. array('[foo, [[{}, {}], {}]]', array('foo', array(array(new \stdClass(), new \stdClass()), new \stdClass()))),
  373. array('[foo, {bar: {}}]', array('foo', '1' => (object) array('bar' => new \stdClass()))),
  374. );
  375. }
  376. public function getTestsForDump()
  377. {
  378. return array(
  379. array('null', null),
  380. array('false', false),
  381. array('true', true),
  382. array('12', 12),
  383. array("'quoted string'", 'quoted string'),
  384. array('!!float 1230', 12.30e+02),
  385. array('1234', 0x4D2),
  386. array('1243', 02333),
  387. array('.Inf', -log(0)),
  388. array('-.Inf', log(0)),
  389. array("'686e444'", '686e444'),
  390. array('"foo\r\nbar"', "foo\r\nbar"),
  391. array("'foo#bar'", 'foo#bar'),
  392. array("'foo # bar'", 'foo # bar'),
  393. array("'#cfcfcf'", '#cfcfcf'),
  394. array("'a \"string\" with ''quoted strings inside'''", 'a "string" with \'quoted strings inside\''),
  395. array("'-dash'", '-dash'),
  396. array("'-'", '-'),
  397. // Pre-YAML-1.2 booleans
  398. array("'y'", 'y'),
  399. array("'n'", 'n'),
  400. array("'yes'", 'yes'),
  401. array("'no'", 'no'),
  402. array("'on'", 'on'),
  403. array("'off'", 'off'),
  404. // sequences
  405. array('[foo, bar, false, null, 12]', array('foo', 'bar', false, null, 12)),
  406. array('[\'foo,bar\', \'foo bar\']', array('foo,bar', 'foo bar')),
  407. // mappings
  408. array('{ foo: bar, bar: foo, \'false\': false, \'null\': null, integer: 12 }', array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12)),
  409. array('{ foo: bar, bar: \'foo: bar\' }', array('foo' => 'bar', 'bar' => 'foo: bar')),
  410. // nested sequences and mappings
  411. array('[foo, [bar, foo]]', array('foo', array('bar', 'foo'))),
  412. array('[foo, [bar, [foo, [bar, foo]], foo]]', array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo'))),
  413. array('{ foo: { bar: foo } }', array('foo' => array('bar' => 'foo'))),
  414. array('[foo, { bar: foo }]', array('foo', array('bar' => 'foo'))),
  415. array('[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]', array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo')))),
  416. array('[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']', array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%'), true, '@service_container')),
  417. array('{ foo: { bar: { 1: 2, baz: 3 } } }', array('foo' => array('bar' => array(1 => 2, 'baz' => 3)))),
  418. );
  419. }
  420. /**
  421. * @dataProvider getTimestampTests
  422. */
  423. public function testParseTimestampAsUnixTimestampByDefault($yaml, $year, $month, $day, $hour, $minute, $second)
  424. {
  425. $this->assertSame(gmmktime($hour, $minute, $second, $month, $day, $year), Inline::parse($yaml));
  426. }
  427. /**
  428. * @dataProvider getTimestampTests
  429. */
  430. public function testParseTimestampAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second)
  431. {
  432. $expected = new \DateTime($yaml);
  433. $expected->setTimeZone(new \DateTimeZone('UTC'));
  434. $expected->setDate($year, $month, $day);
  435. $expected->setTime($hour, $minute, $second);
  436. $this->assertEquals($expected, Inline::parse($yaml, Yaml::PARSE_DATETIME));
  437. }
  438. public function getTimestampTests()
  439. {
  440. return array(
  441. 'canonical' => array('2001-12-15T02:59:43.1Z', 2001, 12, 15, 2, 59, 43),
  442. 'ISO-8601' => array('2001-12-15t21:59:43.10-05:00', 2001, 12, 16, 2, 59, 43),
  443. 'spaced' => array('2001-12-15 21:59:43.10 -5', 2001, 12, 16, 2, 59, 43),
  444. 'date' => array('2001-12-15', 2001, 12, 15, 0, 0, 0),
  445. );
  446. }
  447. /**
  448. * @dataProvider getTimestampTests
  449. */
  450. public function testParseNestedTimestampListAsDateTimeObject($yaml, $year, $month, $day, $hour, $minute, $second)
  451. {
  452. $expected = new \DateTime($yaml);
  453. $expected->setTimeZone(new \DateTimeZone('UTC'));
  454. $expected->setDate($year, $month, $day);
  455. $expected->setTime($hour, $minute, $second);
  456. $expectedNested = array('nested' => array($expected));
  457. $yamlNested = "{nested: [$yaml]}";
  458. $this->assertEquals($expectedNested, Inline::parse($yamlNested, Yaml::PARSE_DATETIME));
  459. }
  460. /**
  461. * @dataProvider getDateTimeDumpTests
  462. */
  463. public function testDumpDateTime($dateTime, $expected)
  464. {
  465. $this->assertSame($expected, Inline::dump($dateTime));
  466. }
  467. public function getDateTimeDumpTests()
  468. {
  469. $tests = array();
  470. $dateTime = new \DateTime('2001-12-15 21:59:43', new \DateTimeZone('UTC'));
  471. $tests['date-time-utc'] = array($dateTime, '2001-12-15T21:59:43+00:00');
  472. $dateTime = new \DateTimeImmutable('2001-07-15 21:59:43', new \DateTimeZone('Europe/Berlin'));
  473. $tests['immutable-date-time-europe-berlin'] = array($dateTime, '2001-07-15T21:59:43+02:00');
  474. return $tests;
  475. }
  476. /**
  477. * @dataProvider getBinaryData
  478. */
  479. public function testParseBinaryData($data)
  480. {
  481. $this->assertSame('Hello world', Inline::parse($data));
  482. }
  483. public function getBinaryData()
  484. {
  485. return array(
  486. 'enclosed with double quotes' => array('!!binary "SGVsbG8gd29ybGQ="'),
  487. 'enclosed with single quotes' => array("!!binary 'SGVsbG8gd29ybGQ='"),
  488. 'containing spaces' => array('!!binary "SGVs bG8gd 29ybGQ="'),
  489. );
  490. }
  491. /**
  492. * @dataProvider getInvalidBinaryData
  493. */
  494. public function testParseInvalidBinaryData($data, $expectedMessage)
  495. {
  496. $this->setExpectedExceptionRegExp('\Symfony\Component\Yaml\Exception\ParseException', $expectedMessage);
  497. Inline::parse($data);
  498. }
  499. public function getInvalidBinaryData()
  500. {
  501. return array(
  502. 'length not a multiple of four' => array('!!binary "SGVsbG8d29ybGQ="', '/The normalized base64 encoded data \(data without whitespace characters\) length must be a multiple of four \(\d+ bytes given\)/'),
  503. 'invalid characters' => array('!!binary "SGVsbG8#d29ybGQ="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  504. 'too many equals characters' => array('!!binary "SGVsbG8gd29yb==="', '/The base64 encoded data \(.*\) contains invalid characters/'),
  505. 'misplaced equals character' => array('!!binary "SGVsbG8gd29ybG=Q"', '/The base64 encoded data \(.*\) contains invalid characters/'),
  506. );
  507. }
  508. }