ArrayNodeDefinitionTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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\Config\Tests\Definition\Builder;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
  13. use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
  14. use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
  15. use Symfony\Component\Config\Definition\Processor;
  16. class ArrayNodeDefinitionTest extends TestCase
  17. {
  18. public function testAppendingSomeNode()
  19. {
  20. $parent = new ArrayNodeDefinition('root');
  21. $child = new ScalarNodeDefinition('child');
  22. $parent
  23. ->children()
  24. ->scalarNode('foo')->end()
  25. ->scalarNode('bar')->end()
  26. ->end()
  27. ->append($child);
  28. $this->assertCount(3, $this->getField($parent, 'children'));
  29. $this->assertContains($child, $this->getField($parent, 'children'));
  30. }
  31. /**
  32. * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
  33. * @dataProvider providePrototypeNodeSpecificCalls
  34. */
  35. public function testPrototypeNodeSpecificOption($method, $args)
  36. {
  37. $node = new ArrayNodeDefinition('root');
  38. $node->{$method}(...$args);
  39. $node->getNode();
  40. }
  41. public function providePrototypeNodeSpecificCalls()
  42. {
  43. return [
  44. ['defaultValue', [[]]],
  45. ['addDefaultChildrenIfNoneSet', []],
  46. ['requiresAtLeastOneElement', []],
  47. ['cannotBeEmpty', []],
  48. ['useAttributeAsKey', ['foo']],
  49. ];
  50. }
  51. /**
  52. * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
  53. */
  54. public function testConcreteNodeSpecificOption()
  55. {
  56. $node = new ArrayNodeDefinition('root');
  57. $node
  58. ->addDefaultsIfNotSet()
  59. ->prototype('array')
  60. ;
  61. $node->getNode();
  62. }
  63. /**
  64. * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
  65. */
  66. public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren()
  67. {
  68. $node = new ArrayNodeDefinition('root');
  69. $node
  70. ->defaultValue([])
  71. ->addDefaultChildrenIfNoneSet('foo')
  72. ->prototype('array')
  73. ;
  74. $node->getNode();
  75. }
  76. public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren()
  77. {
  78. $node = new ArrayNodeDefinition('root');
  79. $node
  80. ->addDefaultChildrenIfNoneSet()
  81. ->prototype('array')
  82. ;
  83. $tree = $node->getNode();
  84. $this->assertEquals([[]], $tree->getDefaultValue());
  85. }
  86. /**
  87. * @dataProvider providePrototypedArrayNodeDefaults
  88. */
  89. public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults)
  90. {
  91. $node = new ArrayNodeDefinition('root');
  92. $node
  93. ->addDefaultChildrenIfNoneSet($args)
  94. ->prototype('array')
  95. ;
  96. try {
  97. $tree = $node->getNode();
  98. $this->assertFalse($shouldThrowWhenNotUsingAttrAsKey);
  99. $this->assertEquals($defaults, $tree->getDefaultValue());
  100. } catch (InvalidDefinitionException $e) {
  101. $this->assertTrue($shouldThrowWhenNotUsingAttrAsKey);
  102. }
  103. $node = new ArrayNodeDefinition('root');
  104. $node
  105. ->useAttributeAsKey('attr')
  106. ->addDefaultChildrenIfNoneSet($args)
  107. ->prototype('array')
  108. ;
  109. try {
  110. $tree = $node->getNode();
  111. $this->assertFalse($shouldThrowWhenUsingAttrAsKey);
  112. $this->assertEquals($defaults, $tree->getDefaultValue());
  113. } catch (InvalidDefinitionException $e) {
  114. $this->assertTrue($shouldThrowWhenUsingAttrAsKey);
  115. }
  116. }
  117. public function providePrototypedArrayNodeDefaults()
  118. {
  119. return [
  120. [null, true, false, [[]]],
  121. [2, true, false, [[], []]],
  122. ['2', false, true, ['2' => []]],
  123. ['foo', false, true, ['foo' => []]],
  124. [['foo'], false, true, ['foo' => []]],
  125. [['foo', 'bar'], false, true, ['foo' => [], 'bar' => []]],
  126. ];
  127. }
  128. public function testNestedPrototypedArrayNodes()
  129. {
  130. $nodeDefinition = new ArrayNodeDefinition('root');
  131. $nodeDefinition
  132. ->addDefaultChildrenIfNoneSet()
  133. ->prototype('array')
  134. ->prototype('array')
  135. ;
  136. $node = $nodeDefinition->getNode();
  137. $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node);
  138. $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node->getPrototype());
  139. }
  140. public function testEnabledNodeDefaults()
  141. {
  142. $node = new ArrayNodeDefinition('root');
  143. $node
  144. ->canBeEnabled()
  145. ->children()
  146. ->scalarNode('foo')->defaultValue('bar')->end()
  147. ;
  148. $this->assertEquals(['enabled' => false, 'foo' => 'bar'], $node->getNode()->getDefaultValue());
  149. }
  150. /**
  151. * @dataProvider getEnableableNodeFixtures
  152. */
  153. public function testTrueEnableEnabledNode($expected, $config, $message)
  154. {
  155. $processor = new Processor();
  156. $node = new ArrayNodeDefinition('root');
  157. $node
  158. ->canBeEnabled()
  159. ->children()
  160. ->scalarNode('foo')->defaultValue('bar')->end()
  161. ;
  162. $this->assertEquals(
  163. $expected,
  164. $processor->process($node->getNode(), $config),
  165. $message
  166. );
  167. }
  168. public function testCanBeDisabled()
  169. {
  170. $node = new ArrayNodeDefinition('root');
  171. $node->canBeDisabled();
  172. $this->assertTrue($this->getField($node, 'addDefaults'));
  173. $this->assertEquals(['enabled' => false], $this->getField($node, 'falseEquivalent'));
  174. $this->assertEquals(['enabled' => true], $this->getField($node, 'trueEquivalent'));
  175. $this->assertEquals(['enabled' => true], $this->getField($node, 'nullEquivalent'));
  176. $nodeChildren = $this->getField($node, 'children');
  177. $this->assertArrayHasKey('enabled', $nodeChildren);
  178. $enabledNode = $nodeChildren['enabled'];
  179. $this->assertTrue($this->getField($enabledNode, 'default'));
  180. $this->assertTrue($this->getField($enabledNode, 'defaultValue'));
  181. }
  182. public function testIgnoreExtraKeys()
  183. {
  184. $node = new ArrayNodeDefinition('root');
  185. $this->assertFalse($this->getField($node, 'ignoreExtraKeys'));
  186. $result = $node->ignoreExtraKeys();
  187. $this->assertEquals($node, $result);
  188. $this->assertTrue($this->getField($node, 'ignoreExtraKeys'));
  189. }
  190. public function testNormalizeKeys()
  191. {
  192. $node = new ArrayNodeDefinition('root');
  193. $this->assertTrue($this->getField($node, 'normalizeKeys'));
  194. $result = $node->normalizeKeys(false);
  195. $this->assertEquals($node, $result);
  196. $this->assertFalse($this->getField($node, 'normalizeKeys'));
  197. }
  198. public function testUnsetChild()
  199. {
  200. $node = new ArrayNodeDefinition('root');
  201. $node
  202. ->children()
  203. ->scalarNode('value')
  204. ->beforeNormalization()
  205. ->ifTrue(function ($value) {
  206. return empty($value);
  207. })
  208. ->thenUnset()
  209. ->end()
  210. ->end()
  211. ->end()
  212. ;
  213. $this->assertSame([], $node->getNode()->normalize(['value' => null]));
  214. }
  215. public function testPrototypeVariable()
  216. {
  217. $node = new ArrayNodeDefinition('root');
  218. $this->assertEquals($node->prototype('variable'), $node->variablePrototype());
  219. }
  220. public function testPrototypeScalar()
  221. {
  222. $node = new ArrayNodeDefinition('root');
  223. $this->assertEquals($node->prototype('scalar'), $node->scalarPrototype());
  224. }
  225. public function testPrototypeBoolean()
  226. {
  227. $node = new ArrayNodeDefinition('root');
  228. $this->assertEquals($node->prototype('boolean'), $node->booleanPrototype());
  229. }
  230. public function testPrototypeInteger()
  231. {
  232. $node = new ArrayNodeDefinition('root');
  233. $this->assertEquals($node->prototype('integer'), $node->integerPrototype());
  234. }
  235. public function testPrototypeFloat()
  236. {
  237. $node = new ArrayNodeDefinition('root');
  238. $this->assertEquals($node->prototype('float'), $node->floatPrototype());
  239. }
  240. public function testPrototypeArray()
  241. {
  242. $node = new ArrayNodeDefinition('root');
  243. $this->assertEquals($node->prototype('array'), $node->arrayPrototype());
  244. }
  245. public function testPrototypeEnum()
  246. {
  247. $node = new ArrayNodeDefinition('root');
  248. $this->assertEquals($node->prototype('enum'), $node->enumPrototype());
  249. }
  250. public function getEnableableNodeFixtures()
  251. {
  252. return [
  253. [['enabled' => true, 'foo' => 'bar'], [true], 'true enables an enableable node'],
  254. [['enabled' => true, 'foo' => 'bar'], [null], 'null enables an enableable node'],
  255. [['enabled' => true, 'foo' => 'bar'], [['enabled' => true]], 'An enableable node can be enabled'],
  256. [['enabled' => true, 'foo' => 'baz'], [['foo' => 'baz']], 'any configuration enables an enableable node'],
  257. [['enabled' => false, 'foo' => 'baz'], [['foo' => 'baz', 'enabled' => false]], 'An enableable node can be disabled'],
  258. [['enabled' => false, 'foo' => 'bar'], [false], 'false disables an enableable node'],
  259. ];
  260. }
  261. public function testRequiresAtLeastOneElement()
  262. {
  263. $node = new ArrayNodeDefinition('root');
  264. $node
  265. ->requiresAtLeastOneElement()
  266. ->integerPrototype();
  267. $node->getNode()->finalize([1]);
  268. $this->addToAssertionCount(1);
  269. }
  270. /**
  271. * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
  272. * @expectedExceptionMessage The path "root" should have at least 1 element(s) defined.
  273. */
  274. public function testCannotBeEmpty()
  275. {
  276. $node = new ArrayNodeDefinition('root');
  277. $node
  278. ->cannotBeEmpty()
  279. ->integerPrototype();
  280. $node->getNode()->finalize([]);
  281. }
  282. public function testSetDeprecated()
  283. {
  284. $node = new ArrayNodeDefinition('root');
  285. $node
  286. ->children()
  287. ->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end()
  288. ->end()
  289. ;
  290. $deprecatedNode = $node->getNode()->getChildren()['foo'];
  291. $this->assertTrue($deprecatedNode->isDeprecated());
  292. $this->assertSame('The "root.foo" node is deprecated.', $deprecatedNode->getDeprecationMessage($deprecatedNode->getName(), $deprecatedNode->getPath()));
  293. }
  294. /**
  295. * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
  296. * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to concrete nodes at path "root"
  297. */
  298. public function testCannotBeEmptyOnConcreteNode()
  299. {
  300. $node = new ArrayNodeDefinition('root');
  301. $node->cannotBeEmpty();
  302. $node->getNode()->finalize([]);
  303. }
  304. protected function getField($object, $field)
  305. {
  306. $reflection = new \ReflectionProperty($object, $field);
  307. $reflection->setAccessible(true);
  308. return $reflection->getValue($object);
  309. }
  310. }