OptionsResolverTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  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\OptionsResolver\Tests;
  11. use Symfony\Component\OptionsResolver\OptionsResolver;
  12. use Symfony\Component\OptionsResolver\Options;
  13. class OptionsResolverTest extends \PHPUnit_Framework_TestCase
  14. {
  15. /**
  16. * @var OptionsResolver
  17. */
  18. private $resolver;
  19. protected function setUp()
  20. {
  21. $this->resolver = new OptionsResolver();
  22. }
  23. public function testResolve()
  24. {
  25. $this->resolver->setDefaults(array(
  26. 'one' => '1',
  27. 'two' => '2',
  28. ));
  29. $options = array(
  30. 'two' => '20',
  31. );
  32. $this->assertEquals(array(
  33. 'one' => '1',
  34. 'two' => '20',
  35. ), $this->resolver->resolve($options));
  36. }
  37. public function testResolveLazy()
  38. {
  39. $this->resolver->setDefaults(array(
  40. 'one' => '1',
  41. 'two' => function (Options $options) {
  42. return '20';
  43. },
  44. ));
  45. $this->assertEquals(array(
  46. 'one' => '1',
  47. 'two' => '20',
  48. ), $this->resolver->resolve(array()));
  49. }
  50. public function testResolveLazyDependencyOnOptional()
  51. {
  52. $this->resolver->setDefaults(array(
  53. 'one' => '1',
  54. 'two' => function (Options $options) {
  55. return $options['one'].'2';
  56. },
  57. ));
  58. $options = array(
  59. 'one' => '10',
  60. );
  61. $this->assertEquals(array(
  62. 'one' => '10',
  63. 'two' => '102',
  64. ), $this->resolver->resolve($options));
  65. }
  66. public function testResolveLazyDependencyOnMissingOptionalWithoutDefault()
  67. {
  68. $test = $this;
  69. $this->resolver->setOptional(array(
  70. 'one',
  71. ));
  72. $this->resolver->setDefaults(array(
  73. 'two' => function (Options $options) use ($test) {
  74. /* @var \PHPUnit_Framework_TestCase $test */
  75. $test->assertFalse(isset($options['one']));
  76. return '2';
  77. },
  78. ));
  79. $options = array(
  80. );
  81. $this->assertEquals(array(
  82. 'two' => '2',
  83. ), $this->resolver->resolve($options));
  84. }
  85. public function testResolveLazyDependencyOnOptionalWithoutDefault()
  86. {
  87. $test = $this;
  88. $this->resolver->setOptional(array(
  89. 'one',
  90. ));
  91. $this->resolver->setDefaults(array(
  92. 'two' => function (Options $options) use ($test) {
  93. /* @var \PHPUnit_Framework_TestCase $test */
  94. $test->assertTrue(isset($options['one']));
  95. return $options['one'].'2';
  96. },
  97. ));
  98. $options = array(
  99. 'one' => '10',
  100. );
  101. $this->assertEquals(array(
  102. 'one' => '10',
  103. 'two' => '102',
  104. ), $this->resolver->resolve($options));
  105. }
  106. public function testResolveLazyDependencyOnRequired()
  107. {
  108. $this->resolver->setRequired(array(
  109. 'one',
  110. ));
  111. $this->resolver->setDefaults(array(
  112. 'two' => function (Options $options) {
  113. return $options['one'].'2';
  114. },
  115. ));
  116. $options = array(
  117. 'one' => '10',
  118. );
  119. $this->assertEquals(array(
  120. 'one' => '10',
  121. 'two' => '102',
  122. ), $this->resolver->resolve($options));
  123. }
  124. public function testResolveLazyReplaceDefaults()
  125. {
  126. $test = $this;
  127. $this->resolver->setDefaults(array(
  128. 'one' => function (Options $options) use ($test) {
  129. /* @var \PHPUnit_Framework_TestCase $test */
  130. $test->fail('Previous closure should not be executed');
  131. },
  132. ));
  133. $this->resolver->replaceDefaults(array(
  134. 'one' => function (Options $options, $previousValue) {
  135. return '1';
  136. },
  137. ));
  138. $this->assertEquals(array(
  139. 'one' => '1',
  140. ), $this->resolver->resolve(array()));
  141. }
  142. /**
  143. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  144. */
  145. public function testResolveFailsIfNonExistingOption()
  146. {
  147. $this->resolver->setDefaults(array(
  148. 'one' => '1',
  149. ));
  150. $this->resolver->setRequired(array(
  151. 'two',
  152. ));
  153. $this->resolver->setOptional(array(
  154. 'three',
  155. ));
  156. $this->resolver->resolve(array(
  157. 'foo' => 'bar',
  158. ));
  159. }
  160. /**
  161. * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
  162. */
  163. public function testResolveFailsIfMissingRequiredOption()
  164. {
  165. $this->resolver->setRequired(array(
  166. 'one',
  167. ));
  168. $this->resolver->setDefaults(array(
  169. 'two' => '2',
  170. ));
  171. $this->resolver->resolve(array(
  172. 'two' => '20',
  173. ));
  174. }
  175. public function testResolveSucceedsIfOptionValueAllowed()
  176. {
  177. $this->resolver->setDefaults(array(
  178. 'one' => '1',
  179. ));
  180. $this->resolver->setAllowedValues(array(
  181. 'one' => array('1', 'one'),
  182. ));
  183. $options = array(
  184. 'one' => 'one',
  185. );
  186. $this->assertEquals(array(
  187. 'one' => 'one',
  188. ), $this->resolver->resolve($options));
  189. }
  190. public function testResolveSucceedsIfOptionValueAllowed2()
  191. {
  192. $this->resolver->setDefaults(array(
  193. 'one' => '1',
  194. 'two' => '2',
  195. ));
  196. $this->resolver->setAllowedValues(array(
  197. 'one' => '1',
  198. 'two' => '2',
  199. ));
  200. $this->resolver->addAllowedValues(array(
  201. 'one' => 'one',
  202. 'two' => 'two',
  203. ));
  204. $options = array(
  205. 'one' => '1',
  206. 'two' => 'two',
  207. );
  208. $this->assertEquals(array(
  209. 'one' => '1',
  210. 'two' => 'two',
  211. ), $this->resolver->resolve($options));
  212. }
  213. public function testResolveSucceedsIfOptionalWithAllowedValuesNotSet()
  214. {
  215. $this->resolver->setRequired(array(
  216. 'one',
  217. ));
  218. $this->resolver->setOptional(array(
  219. 'two',
  220. ));
  221. $this->resolver->setAllowedValues(array(
  222. 'one' => array('1', 'one'),
  223. 'two' => array('2', 'two'),
  224. ));
  225. $options = array(
  226. 'one' => '1',
  227. );
  228. $this->assertEquals(array(
  229. 'one' => '1',
  230. ), $this->resolver->resolve($options));
  231. }
  232. /**
  233. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  234. */
  235. public function testResolveFailsIfOptionValueNotAllowed()
  236. {
  237. $this->resolver->setDefaults(array(
  238. 'one' => '1',
  239. ));
  240. $this->resolver->setAllowedValues(array(
  241. 'one' => array('1', 'one'),
  242. ));
  243. $this->resolver->resolve(array(
  244. 'one' => '2',
  245. ));
  246. }
  247. public function testResolveSucceedsIfOptionTypeAllowed()
  248. {
  249. $this->resolver->setDefaults(array(
  250. 'one' => '1',
  251. ));
  252. $this->resolver->setAllowedTypes(array(
  253. 'one' => 'string',
  254. ));
  255. $options = array(
  256. 'one' => 'one',
  257. );
  258. $this->assertEquals(array(
  259. 'one' => 'one',
  260. ), $this->resolver->resolve($options));
  261. }
  262. public function testResolveSucceedsIfOptionTypeAllowedPassArray()
  263. {
  264. $this->resolver->setDefaults(array(
  265. 'one' => '1',
  266. ));
  267. $this->resolver->setAllowedTypes(array(
  268. 'one' => array('string', 'bool'),
  269. ));
  270. $options = array(
  271. 'one' => true,
  272. );
  273. $this->assertEquals(array(
  274. 'one' => true,
  275. ), $this->resolver->resolve($options));
  276. }
  277. public function testResolveSucceedsIfOptionTypeAllowedPassObject()
  278. {
  279. $this->resolver->setDefaults(array(
  280. 'one' => '1',
  281. ));
  282. $this->resolver->setAllowedTypes(array(
  283. 'one' => 'object',
  284. ));
  285. $object = new \stdClass();
  286. $options = array(
  287. 'one' => $object,
  288. );
  289. $this->assertEquals(array(
  290. 'one' => $object,
  291. ), $this->resolver->resolve($options));
  292. }
  293. public function testResolveSucceedsIfOptionTypeAllowedPassClass()
  294. {
  295. $this->resolver->setDefaults(array(
  296. 'one' => '1',
  297. ));
  298. $this->resolver->setAllowedTypes(array(
  299. 'one' => '\stdClass',
  300. ));
  301. $object = new \stdClass();
  302. $options = array(
  303. 'one' => $object,
  304. );
  305. $this->assertEquals(array(
  306. 'one' => $object,
  307. ), $this->resolver->resolve($options));
  308. }
  309. public function testResolveSucceedsIfOptionTypeAllowedAddTypes()
  310. {
  311. $this->resolver->setDefaults(array(
  312. 'one' => '1',
  313. 'two' => '2',
  314. ));
  315. $this->resolver->setAllowedTypes(array(
  316. 'one' => 'string',
  317. 'two' => 'bool',
  318. ));
  319. $this->resolver->addAllowedTypes(array(
  320. 'one' => 'float',
  321. 'two' => 'integer',
  322. ));
  323. $options = array(
  324. 'one' => 1.23,
  325. 'two' => false,
  326. );
  327. $this->assertEquals(array(
  328. 'one' => 1.23,
  329. 'two' => false,
  330. ), $this->resolver->resolve($options));
  331. }
  332. public function testResolveSucceedsIfOptionalWithTypeAndWithoutValue()
  333. {
  334. $this->resolver->setOptional(array(
  335. 'one',
  336. 'two',
  337. ));
  338. $this->resolver->setAllowedTypes(array(
  339. 'one' => 'string',
  340. 'two' => 'int',
  341. ));
  342. $options = array(
  343. 'two' => 1,
  344. );
  345. $this->assertEquals(array(
  346. 'two' => 1,
  347. ), $this->resolver->resolve($options));
  348. }
  349. /**
  350. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  351. */
  352. public function testResolveFailsIfOptionTypeNotAllowed()
  353. {
  354. $this->resolver->setDefaults(array(
  355. 'one' => '1',
  356. ));
  357. $this->resolver->setAllowedTypes(array(
  358. 'one' => array('string', 'bool'),
  359. ));
  360. $this->resolver->resolve(array(
  361. 'one' => 1.23,
  362. ));
  363. }
  364. /**
  365. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  366. */
  367. public function testResolveFailsIfOptionTypeNotAllowedMultipleOptions()
  368. {
  369. $this->resolver->setDefaults(array(
  370. 'one' => '1',
  371. 'two' => '2',
  372. ));
  373. $this->resolver->setAllowedTypes(array(
  374. 'one' => 'string',
  375. 'two' => 'bool',
  376. ));
  377. $this->resolver->resolve(array(
  378. 'one' => 'foo',
  379. 'two' => 1.23,
  380. ));
  381. }
  382. /**
  383. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  384. */
  385. public function testResolveFailsIfOptionTypeNotAllowedAddTypes()
  386. {
  387. $this->resolver->setDefaults(array(
  388. 'one' => '1',
  389. ));
  390. $this->resolver->setAllowedTypes(array(
  391. 'one' => 'string',
  392. ));
  393. $this->resolver->addAllowedTypes(array(
  394. 'one' => 'bool',
  395. ));
  396. $this->resolver->resolve(array(
  397. 'one' => 1.23,
  398. ));
  399. }
  400. /**
  401. * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
  402. */
  403. public function testSetRequiredFailsIfDefaultIsPassed()
  404. {
  405. $this->resolver->setRequired(array(
  406. 'one' => '1',
  407. ));
  408. }
  409. /**
  410. * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
  411. */
  412. public function testSetOptionalFailsIfDefaultIsPassed()
  413. {
  414. $this->resolver->setOptional(array(
  415. 'one' => '1',
  416. ));
  417. }
  418. public function testFluidInterface()
  419. {
  420. $this->resolver->setDefaults(array('one' => '1'))
  421. ->replaceDefaults(array('one' => '2'))
  422. ->setAllowedValues(array('one' => array('1', '2')))
  423. ->addAllowedValues(array('one' => array('3')))
  424. ->setRequired(array('two'))
  425. ->setOptional(array('three'));
  426. $options = array(
  427. 'two' => '2',
  428. );
  429. $this->assertEquals(array(
  430. 'one' => '2',
  431. 'two' => '2',
  432. ), $this->resolver->resolve($options));
  433. }
  434. public function testKnownIfDefaultWasSet()
  435. {
  436. $this->assertFalse($this->resolver->isKnown('foo'));
  437. $this->resolver->setDefaults(array(
  438. 'foo' => 'bar',
  439. ));
  440. $this->assertTrue($this->resolver->isKnown('foo'));
  441. }
  442. public function testKnownIfRequired()
  443. {
  444. $this->assertFalse($this->resolver->isKnown('foo'));
  445. $this->resolver->setRequired(array(
  446. 'foo',
  447. ));
  448. $this->assertTrue($this->resolver->isKnown('foo'));
  449. }
  450. public function testKnownIfOptional()
  451. {
  452. $this->assertFalse($this->resolver->isKnown('foo'));
  453. $this->resolver->setOptional(array(
  454. 'foo',
  455. ));
  456. $this->assertTrue($this->resolver->isKnown('foo'));
  457. }
  458. public function testRequiredIfRequired()
  459. {
  460. $this->assertFalse($this->resolver->isRequired('foo'));
  461. $this->resolver->setRequired(array(
  462. 'foo',
  463. ));
  464. $this->assertTrue($this->resolver->isRequired('foo'));
  465. }
  466. public function testNotRequiredIfRequiredAndDefaultValue()
  467. {
  468. $this->assertFalse($this->resolver->isRequired('foo'));
  469. $this->resolver->setRequired(array(
  470. 'foo',
  471. ));
  472. $this->resolver->setDefaults(array(
  473. 'foo' => 'bar',
  474. ));
  475. $this->assertFalse($this->resolver->isRequired('foo'));
  476. }
  477. public function testNormalizersTransformFinalOptions()
  478. {
  479. $this->resolver->setDefaults(array(
  480. 'foo' => 'bar',
  481. 'bam' => 'baz',
  482. ));
  483. $this->resolver->setNormalizers(array(
  484. 'foo' => function (Options $options, $value) {
  485. return $options['bam'].'['.$value.']';
  486. },
  487. ));
  488. $expected = array(
  489. 'foo' => 'baz[bar]',
  490. 'bam' => 'baz',
  491. );
  492. $this->assertEquals($expected, $this->resolver->resolve(array()));
  493. $expected = array(
  494. 'foo' => 'boo[custom]',
  495. 'bam' => 'boo',
  496. );
  497. $this->assertEquals($expected, $this->resolver->resolve(array(
  498. 'foo' => 'custom',
  499. 'bam' => 'boo',
  500. )));
  501. }
  502. public function testResolveWithoutOptionSucceedsIfRequiredAndDefaultValue()
  503. {
  504. $this->resolver->setRequired(array(
  505. 'foo',
  506. ));
  507. $this->resolver->setDefaults(array(
  508. 'foo' => 'bar',
  509. ));
  510. $this->assertEquals(array(
  511. 'foo' => 'bar'
  512. ), $this->resolver->resolve(array()));
  513. }
  514. public function testResolveWithoutOptionSucceedsIfDefaultValueAndRequired()
  515. {
  516. $this->resolver->setDefaults(array(
  517. 'foo' => 'bar',
  518. ));
  519. $this->resolver->setRequired(array(
  520. 'foo',
  521. ));
  522. $this->assertEquals(array(
  523. 'foo' => 'bar'
  524. ), $this->resolver->resolve(array()));
  525. }
  526. public function testResolveSucceedsIfOptionRequiredAndValueAllowed()
  527. {
  528. $this->resolver->setRequired(array(
  529. 'one', 'two',
  530. ));
  531. $this->resolver->setAllowedValues(array(
  532. 'two' => array('2'),
  533. ));
  534. $options = array(
  535. 'one' => '1',
  536. 'two' => '2'
  537. );
  538. $this->assertEquals($options, $this->resolver->resolve($options));
  539. }
  540. public function testClone()
  541. {
  542. $this->resolver->setDefaults(array('one' => '1'));
  543. $clone = clone $this->resolver;
  544. // Changes after cloning don't affect each other
  545. $this->resolver->setDefaults(array('two' => '2'));
  546. $clone->setDefaults(array('three' => '3'));
  547. $this->assertEquals(array(
  548. 'one' => '1',
  549. 'two' => '2',
  550. ), $this->resolver->resolve());
  551. $this->assertEquals(array(
  552. 'one' => '1',
  553. 'three' => '3',
  554. ), $clone->resolve());
  555. }
  556. }