OptionsResolverTest.php 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653
  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 PHPUnit\Framework\Assert;
  12. use PHPUnit\Framework\TestCase;
  13. use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
  14. use Symfony\Component\OptionsResolver\Options;
  15. use Symfony\Component\OptionsResolver\OptionsResolver;
  16. class OptionsResolverTest extends TestCase
  17. {
  18. /**
  19. * @var OptionsResolver
  20. */
  21. private $resolver;
  22. protected function setUp()
  23. {
  24. $this->resolver = new OptionsResolver();
  25. }
  26. ////////////////////////////////////////////////////////////////////////////
  27. // resolve()
  28. ////////////////////////////////////////////////////////////////////////////
  29. /**
  30. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  31. * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "a", "z".
  32. */
  33. public function testResolveFailsIfNonExistingOption()
  34. {
  35. $this->resolver->setDefault('z', '1');
  36. $this->resolver->setDefault('a', '2');
  37. $this->resolver->resolve(array('foo' => 'bar'));
  38. }
  39. /**
  40. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  41. * @expectedExceptionMessage The options "baz", "foo", "ping" do not exist. Defined options are: "a", "z".
  42. */
  43. public function testResolveFailsIfMultipleNonExistingOptions()
  44. {
  45. $this->resolver->setDefault('z', '1');
  46. $this->resolver->setDefault('a', '2');
  47. $this->resolver->resolve(array('ping' => 'pong', 'foo' => 'bar', 'baz' => 'bam'));
  48. }
  49. /**
  50. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  51. */
  52. public function testResolveFailsFromLazyOption()
  53. {
  54. $this->resolver->setDefault('foo', function (Options $options) {
  55. $options->resolve(array());
  56. });
  57. $this->resolver->resolve();
  58. }
  59. ////////////////////////////////////////////////////////////////////////////
  60. // setDefault()/hasDefault()
  61. ////////////////////////////////////////////////////////////////////////////
  62. public function testSetDefaultReturnsThis()
  63. {
  64. $this->assertSame($this->resolver, $this->resolver->setDefault('foo', 'bar'));
  65. }
  66. public function testSetDefault()
  67. {
  68. $this->resolver->setDefault('one', '1');
  69. $this->resolver->setDefault('two', '20');
  70. $this->assertEquals(array(
  71. 'one' => '1',
  72. 'two' => '20',
  73. ), $this->resolver->resolve());
  74. }
  75. /**
  76. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  77. */
  78. public function testFailIfSetDefaultFromLazyOption()
  79. {
  80. $this->resolver->setDefault('lazy', function (Options $options) {
  81. $options->setDefault('default', 42);
  82. });
  83. $this->resolver->resolve();
  84. }
  85. public function testHasDefault()
  86. {
  87. $this->assertFalse($this->resolver->hasDefault('foo'));
  88. $this->resolver->setDefault('foo', 42);
  89. $this->assertTrue($this->resolver->hasDefault('foo'));
  90. }
  91. public function testHasDefaultWithNullValue()
  92. {
  93. $this->assertFalse($this->resolver->hasDefault('foo'));
  94. $this->resolver->setDefault('foo', null);
  95. $this->assertTrue($this->resolver->hasDefault('foo'));
  96. }
  97. ////////////////////////////////////////////////////////////////////////////
  98. // lazy setDefault()
  99. ////////////////////////////////////////////////////////////////////////////
  100. public function testSetLazyReturnsThis()
  101. {
  102. $this->assertSame($this->resolver, $this->resolver->setDefault('foo', function (Options $options) {}));
  103. }
  104. public function testSetLazyClosure()
  105. {
  106. $this->resolver->setDefault('foo', function (Options $options) {
  107. return 'lazy';
  108. });
  109. $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
  110. }
  111. public function testClosureWithoutTypeHintNotInvoked()
  112. {
  113. $closure = function ($options) {
  114. Assert::fail('Should not be called');
  115. };
  116. $this->resolver->setDefault('foo', $closure);
  117. $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
  118. }
  119. public function testClosureWithoutParametersNotInvoked()
  120. {
  121. $closure = function () {
  122. Assert::fail('Should not be called');
  123. };
  124. $this->resolver->setDefault('foo', $closure);
  125. $this->assertSame(array('foo' => $closure), $this->resolver->resolve());
  126. }
  127. public function testAccessPreviousDefaultValue()
  128. {
  129. // defined by superclass
  130. $this->resolver->setDefault('foo', 'bar');
  131. // defined by subclass
  132. $this->resolver->setDefault('foo', function (Options $options, $previousValue) {
  133. Assert::assertEquals('bar', $previousValue);
  134. return 'lazy';
  135. });
  136. $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
  137. }
  138. public function testAccessPreviousLazyDefaultValue()
  139. {
  140. // defined by superclass
  141. $this->resolver->setDefault('foo', function (Options $options) {
  142. return 'bar';
  143. });
  144. // defined by subclass
  145. $this->resolver->setDefault('foo', function (Options $options, $previousValue) {
  146. Assert::assertEquals('bar', $previousValue);
  147. return 'lazy';
  148. });
  149. $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
  150. }
  151. public function testPreviousValueIsNotEvaluatedIfNoSecondArgument()
  152. {
  153. // defined by superclass
  154. $this->resolver->setDefault('foo', function () {
  155. Assert::fail('Should not be called');
  156. });
  157. // defined by subclass, no $previousValue argument defined!
  158. $this->resolver->setDefault('foo', function (Options $options) {
  159. return 'lazy';
  160. });
  161. $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve());
  162. }
  163. public function testOverwrittenLazyOptionNotEvaluated()
  164. {
  165. $this->resolver->setDefault('foo', function (Options $options) {
  166. Assert::fail('Should not be called');
  167. });
  168. $this->resolver->setDefault('foo', 'bar');
  169. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  170. }
  171. public function testInvokeEachLazyOptionOnlyOnce()
  172. {
  173. $calls = 0;
  174. $this->resolver->setDefault('lazy1', function (Options $options) use (&$calls) {
  175. Assert::assertSame(1, ++$calls);
  176. $options['lazy2'];
  177. });
  178. $this->resolver->setDefault('lazy2', function (Options $options) use (&$calls) {
  179. Assert::assertSame(2, ++$calls);
  180. });
  181. $this->resolver->resolve();
  182. $this->assertSame(2, $calls);
  183. }
  184. ////////////////////////////////////////////////////////////////////////////
  185. // setRequired()/isRequired()/getRequiredOptions()
  186. ////////////////////////////////////////////////////////////////////////////
  187. public function testSetRequiredReturnsThis()
  188. {
  189. $this->assertSame($this->resolver, $this->resolver->setRequired('foo'));
  190. }
  191. /**
  192. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  193. */
  194. public function testFailIfSetRequiredFromLazyOption()
  195. {
  196. $this->resolver->setDefault('foo', function (Options $options) {
  197. $options->setRequired('bar');
  198. });
  199. $this->resolver->resolve();
  200. }
  201. /**
  202. * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
  203. */
  204. public function testResolveFailsIfRequiredOptionMissing()
  205. {
  206. $this->resolver->setRequired('foo');
  207. $this->resolver->resolve();
  208. }
  209. public function testResolveSucceedsIfRequiredOptionSet()
  210. {
  211. $this->resolver->setRequired('foo');
  212. $this->resolver->setDefault('foo', 'bar');
  213. $this->assertNotEmpty($this->resolver->resolve());
  214. }
  215. public function testResolveSucceedsIfRequiredOptionPassed()
  216. {
  217. $this->resolver->setRequired('foo');
  218. $this->assertNotEmpty($this->resolver->resolve(array('foo' => 'bar')));
  219. }
  220. public function testIsRequired()
  221. {
  222. $this->assertFalse($this->resolver->isRequired('foo'));
  223. $this->resolver->setRequired('foo');
  224. $this->assertTrue($this->resolver->isRequired('foo'));
  225. }
  226. public function testRequiredIfSetBefore()
  227. {
  228. $this->assertFalse($this->resolver->isRequired('foo'));
  229. $this->resolver->setDefault('foo', 'bar');
  230. $this->resolver->setRequired('foo');
  231. $this->assertTrue($this->resolver->isRequired('foo'));
  232. }
  233. public function testStillRequiredAfterSet()
  234. {
  235. $this->assertFalse($this->resolver->isRequired('foo'));
  236. $this->resolver->setRequired('foo');
  237. $this->resolver->setDefault('foo', 'bar');
  238. $this->assertTrue($this->resolver->isRequired('foo'));
  239. }
  240. public function testIsNotRequiredAfterRemove()
  241. {
  242. $this->assertFalse($this->resolver->isRequired('foo'));
  243. $this->resolver->setRequired('foo');
  244. $this->resolver->remove('foo');
  245. $this->assertFalse($this->resolver->isRequired('foo'));
  246. }
  247. public function testIsNotRequiredAfterClear()
  248. {
  249. $this->assertFalse($this->resolver->isRequired('foo'));
  250. $this->resolver->setRequired('foo');
  251. $this->resolver->clear();
  252. $this->assertFalse($this->resolver->isRequired('foo'));
  253. }
  254. public function testGetRequiredOptions()
  255. {
  256. $this->resolver->setRequired(array('foo', 'bar'));
  257. $this->resolver->setDefault('bam', 'baz');
  258. $this->resolver->setDefault('foo', 'boo');
  259. $this->assertSame(array('foo', 'bar'), $this->resolver->getRequiredOptions());
  260. }
  261. ////////////////////////////////////////////////////////////////////////////
  262. // isMissing()/getMissingOptions()
  263. ////////////////////////////////////////////////////////////////////////////
  264. public function testIsMissingIfNotSet()
  265. {
  266. $this->assertFalse($this->resolver->isMissing('foo'));
  267. $this->resolver->setRequired('foo');
  268. $this->assertTrue($this->resolver->isMissing('foo'));
  269. }
  270. public function testIsNotMissingIfSet()
  271. {
  272. $this->resolver->setDefault('foo', 'bar');
  273. $this->assertFalse($this->resolver->isMissing('foo'));
  274. $this->resolver->setRequired('foo');
  275. $this->assertFalse($this->resolver->isMissing('foo'));
  276. }
  277. public function testIsNotMissingAfterRemove()
  278. {
  279. $this->resolver->setRequired('foo');
  280. $this->resolver->remove('foo');
  281. $this->assertFalse($this->resolver->isMissing('foo'));
  282. }
  283. public function testIsNotMissingAfterClear()
  284. {
  285. $this->resolver->setRequired('foo');
  286. $this->resolver->clear();
  287. $this->assertFalse($this->resolver->isRequired('foo'));
  288. }
  289. public function testGetMissingOptions()
  290. {
  291. $this->resolver->setRequired(array('foo', 'bar'));
  292. $this->resolver->setDefault('bam', 'baz');
  293. $this->resolver->setDefault('foo', 'boo');
  294. $this->assertSame(array('bar'), $this->resolver->getMissingOptions());
  295. }
  296. ////////////////////////////////////////////////////////////////////////////
  297. // setDefined()/isDefined()/getDefinedOptions()
  298. ////////////////////////////////////////////////////////////////////////////
  299. /**
  300. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  301. */
  302. public function testFailIfSetDefinedFromLazyOption()
  303. {
  304. $this->resolver->setDefault('foo', function (Options $options) {
  305. $options->setDefined('bar');
  306. });
  307. $this->resolver->resolve();
  308. }
  309. public function testDefinedOptionsNotIncludedInResolvedOptions()
  310. {
  311. $this->resolver->setDefined('foo');
  312. $this->assertSame(array(), $this->resolver->resolve());
  313. }
  314. public function testDefinedOptionsIncludedIfDefaultSetBefore()
  315. {
  316. $this->resolver->setDefault('foo', 'bar');
  317. $this->resolver->setDefined('foo');
  318. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  319. }
  320. public function testDefinedOptionsIncludedIfDefaultSetAfter()
  321. {
  322. $this->resolver->setDefined('foo');
  323. $this->resolver->setDefault('foo', 'bar');
  324. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  325. }
  326. public function testDefinedOptionsIncludedIfPassedToResolve()
  327. {
  328. $this->resolver->setDefined('foo');
  329. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve(array('foo' => 'bar')));
  330. }
  331. public function testIsDefined()
  332. {
  333. $this->assertFalse($this->resolver->isDefined('foo'));
  334. $this->resolver->setDefined('foo');
  335. $this->assertTrue($this->resolver->isDefined('foo'));
  336. }
  337. public function testLazyOptionsAreDefined()
  338. {
  339. $this->assertFalse($this->resolver->isDefined('foo'));
  340. $this->resolver->setDefault('foo', function (Options $options) {});
  341. $this->assertTrue($this->resolver->isDefined('foo'));
  342. }
  343. public function testRequiredOptionsAreDefined()
  344. {
  345. $this->assertFalse($this->resolver->isDefined('foo'));
  346. $this->resolver->setRequired('foo');
  347. $this->assertTrue($this->resolver->isDefined('foo'));
  348. }
  349. public function testSetOptionsAreDefined()
  350. {
  351. $this->assertFalse($this->resolver->isDefined('foo'));
  352. $this->resolver->setDefault('foo', 'bar');
  353. $this->assertTrue($this->resolver->isDefined('foo'));
  354. }
  355. public function testGetDefinedOptions()
  356. {
  357. $this->resolver->setDefined(array('foo', 'bar'));
  358. $this->resolver->setDefault('baz', 'bam');
  359. $this->resolver->setRequired('boo');
  360. $this->assertSame(array('foo', 'bar', 'baz', 'boo'), $this->resolver->getDefinedOptions());
  361. }
  362. public function testRemovedOptionsAreNotDefined()
  363. {
  364. $this->assertFalse($this->resolver->isDefined('foo'));
  365. $this->resolver->setDefined('foo');
  366. $this->assertTrue($this->resolver->isDefined('foo'));
  367. $this->resolver->remove('foo');
  368. $this->assertFalse($this->resolver->isDefined('foo'));
  369. }
  370. public function testClearedOptionsAreNotDefined()
  371. {
  372. $this->assertFalse($this->resolver->isDefined('foo'));
  373. $this->resolver->setDefined('foo');
  374. $this->assertTrue($this->resolver->isDefined('foo'));
  375. $this->resolver->clear();
  376. $this->assertFalse($this->resolver->isDefined('foo'));
  377. }
  378. ////////////////////////////////////////////////////////////////////////////
  379. // setAllowedTypes()
  380. ////////////////////////////////////////////////////////////////////////////
  381. /**
  382. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  383. */
  384. public function testSetAllowedTypesFailsIfUnknownOption()
  385. {
  386. $this->resolver->setAllowedTypes('foo', 'string');
  387. }
  388. public function testResolveTypedArray()
  389. {
  390. $this->resolver->setDefined('foo');
  391. $this->resolver->setAllowedTypes('foo', 'string[]');
  392. $options = $this->resolver->resolve(array('foo' => array('bar', 'baz')));
  393. $this->assertSame(array('foo' => array('bar', 'baz')), $options);
  394. }
  395. /**
  396. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  397. */
  398. public function testFailIfSetAllowedTypesFromLazyOption()
  399. {
  400. $this->resolver->setDefault('foo', function (Options $options) {
  401. $options->setAllowedTypes('bar', 'string');
  402. });
  403. $this->resolver->setDefault('bar', 'baz');
  404. $this->resolver->resolve();
  405. }
  406. /**
  407. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  408. * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]".
  409. */
  410. public function testResolveFailsIfInvalidTypedArray()
  411. {
  412. $this->resolver->setDefined('foo');
  413. $this->resolver->setAllowedTypes('foo', 'int[]');
  414. $this->resolver->resolve(array('foo' => array(new \DateTime())));
  415. }
  416. /**
  417. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  418. * @expectedExceptionMessage The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string".
  419. */
  420. public function testResolveFailsWithNonArray()
  421. {
  422. $this->resolver->setDefined('foo');
  423. $this->resolver->setAllowedTypes('foo', 'int[]');
  424. $this->resolver->resolve(array('foo' => 'bar'));
  425. }
  426. /**
  427. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  428. * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]".
  429. */
  430. public function testResolveFailsIfTypedArrayContainsInvalidTypes()
  431. {
  432. $this->resolver->setDefined('foo');
  433. $this->resolver->setAllowedTypes('foo', 'int[]');
  434. $values = range(1, 5);
  435. $values[] = new \stdClass();
  436. $values[] = array();
  437. $values[] = new \DateTime();
  438. $values[] = 123;
  439. $this->resolver->resolve(array('foo' => $values));
  440. }
  441. /**
  442. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  443. * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]".
  444. */
  445. public function testResolveFailsWithCorrectLevelsButWrongScalar()
  446. {
  447. $this->resolver->setDefined('foo');
  448. $this->resolver->setAllowedTypes('foo', 'int[][]');
  449. $this->resolver->resolve(
  450. array(
  451. 'foo' => array(
  452. array(1.2),
  453. ),
  454. )
  455. );
  456. }
  457. /**
  458. * @dataProvider provideInvalidTypes
  459. */
  460. public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage)
  461. {
  462. $this->resolver->setDefined('option');
  463. $this->resolver->setAllowedTypes('option', $allowedType);
  464. if (method_exists($this, 'expectException')) {
  465. $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
  466. $this->expectExceptionMessage($exceptionMessage);
  467. } else {
  468. $this->setExpectedException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException', $exceptionMessage);
  469. }
  470. $this->resolver->resolve(array('option' => $actualType));
  471. }
  472. public function provideInvalidTypes()
  473. {
  474. return array(
  475. array(true, 'string', 'The option "option" with value true is expected to be of type "string", but is of type "boolean".'),
  476. array(false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "boolean".'),
  477. array(fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource".'),
  478. array(array(), 'string', 'The option "option" with value array is expected to be of type "string", but is of type "array".'),
  479. array(new OptionsResolver(), 'string', 'The option "option" with value Symfony\Component\OptionsResolver\OptionsResolver is expected to be of type "string", but is of type "Symfony\Component\OptionsResolver\OptionsResolver".'),
  480. array(42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'),
  481. array(null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'),
  482. array('bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'),
  483. );
  484. }
  485. public function testResolveSucceedsIfValidType()
  486. {
  487. $this->resolver->setDefault('foo', 'bar');
  488. $this->resolver->setAllowedTypes('foo', 'string');
  489. $this->assertNotEmpty($this->resolver->resolve());
  490. }
  491. /**
  492. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  493. * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string" or "bool", but is of type "integer".
  494. */
  495. public function testResolveFailsIfInvalidTypeMultiple()
  496. {
  497. $this->resolver->setDefault('foo', 42);
  498. $this->resolver->setAllowedTypes('foo', array('string', 'bool'));
  499. $this->resolver->resolve();
  500. }
  501. public function testResolveSucceedsIfValidTypeMultiple()
  502. {
  503. $this->resolver->setDefault('foo', true);
  504. $this->resolver->setAllowedTypes('foo', array('string', 'bool'));
  505. $this->assertNotEmpty($this->resolver->resolve());
  506. }
  507. public function testResolveSucceedsIfInstanceOfClass()
  508. {
  509. $this->resolver->setDefault('foo', new \stdClass());
  510. $this->resolver->setAllowedTypes('foo', '\stdClass');
  511. $this->assertNotEmpty($this->resolver->resolve());
  512. }
  513. public function testResolveSucceedsIfTypedArray()
  514. {
  515. $this->resolver->setDefault('foo', null);
  516. $this->resolver->setAllowedTypes('foo', array('null', 'DateTime[]'));
  517. $data = array(
  518. 'foo' => array(
  519. new \DateTime(),
  520. new \DateTime(),
  521. ),
  522. );
  523. $result = $this->resolver->resolve($data);
  524. $this->assertEquals($data, $result);
  525. }
  526. /**
  527. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  528. */
  529. public function testResolveFailsIfNotInstanceOfClass()
  530. {
  531. $this->resolver->setDefault('foo', 'bar');
  532. $this->resolver->setAllowedTypes('foo', '\stdClass');
  533. $this->resolver->resolve();
  534. }
  535. ////////////////////////////////////////////////////////////////////////////
  536. // addAllowedTypes()
  537. ////////////////////////////////////////////////////////////////////////////
  538. /**
  539. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  540. */
  541. public function testAddAllowedTypesFailsIfUnknownOption()
  542. {
  543. $this->resolver->addAllowedTypes('foo', 'string');
  544. }
  545. /**
  546. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  547. */
  548. public function testFailIfAddAllowedTypesFromLazyOption()
  549. {
  550. $this->resolver->setDefault('foo', function (Options $options) {
  551. $options->addAllowedTypes('bar', 'string');
  552. });
  553. $this->resolver->setDefault('bar', 'baz');
  554. $this->resolver->resolve();
  555. }
  556. /**
  557. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  558. */
  559. public function testResolveFailsIfInvalidAddedType()
  560. {
  561. $this->resolver->setDefault('foo', 42);
  562. $this->resolver->addAllowedTypes('foo', 'string');
  563. $this->resolver->resolve();
  564. }
  565. public function testResolveSucceedsIfValidAddedType()
  566. {
  567. $this->resolver->setDefault('foo', 'bar');
  568. $this->resolver->addAllowedTypes('foo', 'string');
  569. $this->assertNotEmpty($this->resolver->resolve());
  570. }
  571. /**
  572. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  573. */
  574. public function testResolveFailsIfInvalidAddedTypeMultiple()
  575. {
  576. $this->resolver->setDefault('foo', 42);
  577. $this->resolver->addAllowedTypes('foo', array('string', 'bool'));
  578. $this->resolver->resolve();
  579. }
  580. public function testResolveSucceedsIfValidAddedTypeMultiple()
  581. {
  582. $this->resolver->setDefault('foo', 'bar');
  583. $this->resolver->addAllowedTypes('foo', array('string', 'bool'));
  584. $this->assertNotEmpty($this->resolver->resolve());
  585. }
  586. public function testAddAllowedTypesDoesNotOverwrite()
  587. {
  588. $this->resolver->setDefault('foo', 'bar');
  589. $this->resolver->setAllowedTypes('foo', 'string');
  590. $this->resolver->addAllowedTypes('foo', 'bool');
  591. $this->resolver->setDefault('foo', 'bar');
  592. $this->assertNotEmpty($this->resolver->resolve());
  593. }
  594. public function testAddAllowedTypesDoesNotOverwrite2()
  595. {
  596. $this->resolver->setDefault('foo', 'bar');
  597. $this->resolver->setAllowedTypes('foo', 'string');
  598. $this->resolver->addAllowedTypes('foo', 'bool');
  599. $this->resolver->setDefault('foo', false);
  600. $this->assertNotEmpty($this->resolver->resolve());
  601. }
  602. ////////////////////////////////////////////////////////////////////////////
  603. // setAllowedValues()
  604. ////////////////////////////////////////////////////////////////////////////
  605. /**
  606. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  607. */
  608. public function testSetAllowedValuesFailsIfUnknownOption()
  609. {
  610. $this->resolver->setAllowedValues('foo', 'bar');
  611. }
  612. /**
  613. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  614. */
  615. public function testFailIfSetAllowedValuesFromLazyOption()
  616. {
  617. $this->resolver->setDefault('foo', function (Options $options) {
  618. $options->setAllowedValues('bar', 'baz');
  619. });
  620. $this->resolver->setDefault('bar', 'baz');
  621. $this->resolver->resolve();
  622. }
  623. /**
  624. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  625. * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar".
  626. */
  627. public function testResolveFailsIfInvalidValue()
  628. {
  629. $this->resolver->setDefined('foo');
  630. $this->resolver->setAllowedValues('foo', 'bar');
  631. $this->resolver->resolve(array('foo' => 42));
  632. }
  633. /**
  634. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  635. * @expectedExceptionMessage The option "foo" with value null is invalid. Accepted values are: "bar".
  636. */
  637. public function testResolveFailsIfInvalidValueIsNull()
  638. {
  639. $this->resolver->setDefault('foo', null);
  640. $this->resolver->setAllowedValues('foo', 'bar');
  641. $this->resolver->resolve();
  642. }
  643. /**
  644. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  645. */
  646. public function testResolveFailsIfInvalidValueStrict()
  647. {
  648. $this->resolver->setDefault('foo', 42);
  649. $this->resolver->setAllowedValues('foo', '42');
  650. $this->resolver->resolve();
  651. }
  652. public function testResolveSucceedsIfValidValue()
  653. {
  654. $this->resolver->setDefault('foo', 'bar');
  655. $this->resolver->setAllowedValues('foo', 'bar');
  656. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  657. }
  658. public function testResolveSucceedsIfValidValueIsNull()
  659. {
  660. $this->resolver->setDefault('foo', null);
  661. $this->resolver->setAllowedValues('foo', null);
  662. $this->assertEquals(array('foo' => null), $this->resolver->resolve());
  663. }
  664. /**
  665. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  666. * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar", false, null.
  667. */
  668. public function testResolveFailsIfInvalidValueMultiple()
  669. {
  670. $this->resolver->setDefault('foo', 42);
  671. $this->resolver->setAllowedValues('foo', array('bar', false, null));
  672. $this->resolver->resolve();
  673. }
  674. public function testResolveSucceedsIfValidValueMultiple()
  675. {
  676. $this->resolver->setDefault('foo', 'baz');
  677. $this->resolver->setAllowedValues('foo', array('bar', 'baz'));
  678. $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve());
  679. }
  680. public function testResolveFailsIfClosureReturnsFalse()
  681. {
  682. $this->resolver->setDefault('foo', 42);
  683. $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) {
  684. $passedValue = $value;
  685. return false;
  686. });
  687. try {
  688. $this->resolver->resolve();
  689. $this->fail('Should fail');
  690. } catch (InvalidOptionsException $e) {
  691. }
  692. $this->assertSame(42, $passedValue);
  693. }
  694. public function testResolveSucceedsIfClosureReturnsTrue()
  695. {
  696. $this->resolver->setDefault('foo', 'bar');
  697. $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) {
  698. $passedValue = $value;
  699. return true;
  700. });
  701. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  702. $this->assertSame('bar', $passedValue);
  703. }
  704. /**
  705. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  706. */
  707. public function testResolveFailsIfAllClosuresReturnFalse()
  708. {
  709. $this->resolver->setDefault('foo', 42);
  710. $this->resolver->setAllowedValues('foo', array(
  711. function () { return false; },
  712. function () { return false; },
  713. function () { return false; },
  714. ));
  715. $this->resolver->resolve();
  716. }
  717. public function testResolveSucceedsIfAnyClosureReturnsTrue()
  718. {
  719. $this->resolver->setDefault('foo', 'bar');
  720. $this->resolver->setAllowedValues('foo', array(
  721. function () { return false; },
  722. function () { return true; },
  723. function () { return false; },
  724. ));
  725. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  726. }
  727. ////////////////////////////////////////////////////////////////////////////
  728. // addAllowedValues()
  729. ////////////////////////////////////////////////////////////////////////////
  730. /**
  731. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  732. */
  733. public function testAddAllowedValuesFailsIfUnknownOption()
  734. {
  735. $this->resolver->addAllowedValues('foo', 'bar');
  736. }
  737. /**
  738. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  739. */
  740. public function testFailIfAddAllowedValuesFromLazyOption()
  741. {
  742. $this->resolver->setDefault('foo', function (Options $options) {
  743. $options->addAllowedValues('bar', 'baz');
  744. });
  745. $this->resolver->setDefault('bar', 'baz');
  746. $this->resolver->resolve();
  747. }
  748. /**
  749. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  750. */
  751. public function testResolveFailsIfInvalidAddedValue()
  752. {
  753. $this->resolver->setDefault('foo', 42);
  754. $this->resolver->addAllowedValues('foo', 'bar');
  755. $this->resolver->resolve();
  756. }
  757. public function testResolveSucceedsIfValidAddedValue()
  758. {
  759. $this->resolver->setDefault('foo', 'bar');
  760. $this->resolver->addAllowedValues('foo', 'bar');
  761. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  762. }
  763. public function testResolveSucceedsIfValidAddedValueIsNull()
  764. {
  765. $this->resolver->setDefault('foo', null);
  766. $this->resolver->addAllowedValues('foo', null);
  767. $this->assertEquals(array('foo' => null), $this->resolver->resolve());
  768. }
  769. /**
  770. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  771. */
  772. public function testResolveFailsIfInvalidAddedValueMultiple()
  773. {
  774. $this->resolver->setDefault('foo', 42);
  775. $this->resolver->addAllowedValues('foo', array('bar', 'baz'));
  776. $this->resolver->resolve();
  777. }
  778. public function testResolveSucceedsIfValidAddedValueMultiple()
  779. {
  780. $this->resolver->setDefault('foo', 'bar');
  781. $this->resolver->addAllowedValues('foo', array('bar', 'baz'));
  782. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  783. }
  784. public function testAddAllowedValuesDoesNotOverwrite()
  785. {
  786. $this->resolver->setDefault('foo', 'bar');
  787. $this->resolver->setAllowedValues('foo', 'bar');
  788. $this->resolver->addAllowedValues('foo', 'baz');
  789. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  790. }
  791. public function testAddAllowedValuesDoesNotOverwrite2()
  792. {
  793. $this->resolver->setDefault('foo', 'baz');
  794. $this->resolver->setAllowedValues('foo', 'bar');
  795. $this->resolver->addAllowedValues('foo', 'baz');
  796. $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve());
  797. }
  798. /**
  799. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  800. */
  801. public function testResolveFailsIfAllAddedClosuresReturnFalse()
  802. {
  803. $this->resolver->setDefault('foo', 42);
  804. $this->resolver->setAllowedValues('foo', function () { return false; });
  805. $this->resolver->addAllowedValues('foo', function () { return false; });
  806. $this->resolver->resolve();
  807. }
  808. public function testResolveSucceedsIfAnyAddedClosureReturnsTrue()
  809. {
  810. $this->resolver->setDefault('foo', 'bar');
  811. $this->resolver->setAllowedValues('foo', function () { return false; });
  812. $this->resolver->addAllowedValues('foo', function () { return true; });
  813. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  814. }
  815. public function testResolveSucceedsIfAnyAddedClosureReturnsTrue2()
  816. {
  817. $this->resolver->setDefault('foo', 'bar');
  818. $this->resolver->setAllowedValues('foo', function () { return true; });
  819. $this->resolver->addAllowedValues('foo', function () { return false; });
  820. $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve());
  821. }
  822. ////////////////////////////////////////////////////////////////////////////
  823. // setNormalizer()
  824. ////////////////////////////////////////////////////////////////////////////
  825. public function testSetNormalizerReturnsThis()
  826. {
  827. $this->resolver->setDefault('foo', 'bar');
  828. $this->assertSame($this->resolver, $this->resolver->setNormalizer('foo', function () {}));
  829. }
  830. public function testSetNormalizerClosure()
  831. {
  832. $this->resolver->setDefault('foo', 'bar');
  833. $this->resolver->setNormalizer('foo', function () {
  834. return 'normalized';
  835. });
  836. $this->assertEquals(array('foo' => 'normalized'), $this->resolver->resolve());
  837. }
  838. /**
  839. * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
  840. */
  841. public function testSetNormalizerFailsIfUnknownOption()
  842. {
  843. $this->resolver->setNormalizer('foo', function () {});
  844. }
  845. /**
  846. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  847. */
  848. public function testFailIfSetNormalizerFromLazyOption()
  849. {
  850. $this->resolver->setDefault('foo', function (Options $options) {
  851. $options->setNormalizer('foo', function () {});
  852. });
  853. $this->resolver->setDefault('bar', 'baz');
  854. $this->resolver->resolve();
  855. }
  856. public function testNormalizerReceivesSetOption()
  857. {
  858. $this->resolver->setDefault('foo', 'bar');
  859. $this->resolver->setNormalizer('foo', function (Options $options, $value) {
  860. return 'normalized['.$value.']';
  861. });
  862. $this->assertEquals(array('foo' => 'normalized[bar]'), $this->resolver->resolve());
  863. }
  864. public function testNormalizerReceivesPassedOption()
  865. {
  866. $this->resolver->setDefault('foo', 'bar');
  867. $this->resolver->setNormalizer('foo', function (Options $options, $value) {
  868. return 'normalized['.$value.']';
  869. });
  870. $resolved = $this->resolver->resolve(array('foo' => 'baz'));
  871. $this->assertEquals(array('foo' => 'normalized[baz]'), $resolved);
  872. }
  873. /**
  874. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  875. */
  876. public function testValidateTypeBeforeNormalization()
  877. {
  878. $this->resolver->setDefault('foo', 'bar');
  879. $this->resolver->setAllowedTypes('foo', 'int');
  880. $this->resolver->setNormalizer('foo', function () {
  881. Assert::fail('Should not be called.');
  882. });
  883. $this->resolver->resolve();
  884. }
  885. /**
  886. * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
  887. */
  888. public function testValidateValueBeforeNormalization()
  889. {
  890. $this->resolver->setDefault('foo', 'bar');
  891. $this->resolver->setAllowedValues('foo', 'baz');
  892. $this->resolver->setNormalizer('foo', function () {
  893. Assert::fail('Should not be called.');
  894. });
  895. $this->resolver->resolve();
  896. }
  897. public function testNormalizerCanAccessOtherOptions()
  898. {
  899. $this->resolver->setDefault('default', 'bar');
  900. $this->resolver->setDefault('norm', 'baz');
  901. $this->resolver->setNormalizer('norm', function (Options $options) {
  902. /* @var TestCase $test */
  903. Assert::assertSame('bar', $options['default']);
  904. return 'normalized';
  905. });
  906. $this->assertEquals(array(
  907. 'default' => 'bar',
  908. 'norm' => 'normalized',
  909. ), $this->resolver->resolve());
  910. }
  911. public function testNormalizerCanAccessLazyOptions()
  912. {
  913. $this->resolver->setDefault('lazy', function (Options $options) {
  914. return 'bar';
  915. });
  916. $this->resolver->setDefault('norm', 'baz');
  917. $this->resolver->setNormalizer('norm', function (Options $options) {
  918. /* @var TestCase $test */
  919. Assert::assertEquals('bar', $options['lazy']);
  920. return 'normalized';
  921. });
  922. $this->assertEquals(array(
  923. 'lazy' => 'bar',
  924. 'norm' => 'normalized',
  925. ), $this->resolver->resolve());
  926. }
  927. /**
  928. * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
  929. */
  930. public function testFailIfCyclicDependencyBetweenNormalizers()
  931. {
  932. $this->resolver->setDefault('norm1', 'bar');
  933. $this->resolver->setDefault('norm2', 'baz');
  934. $this->resolver->setNormalizer('norm1', function (Options $options) {
  935. $options['norm2'];
  936. });
  937. $this->resolver->setNormalizer('norm2', function (Options $options) {
  938. $options['norm1'];
  939. });
  940. $this->resolver->resolve();
  941. }
  942. /**
  943. * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
  944. */
  945. public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption()
  946. {
  947. $this->resolver->setDefault('lazy', function (Options $options) {
  948. $options['norm'];
  949. });
  950. $this->resolver->setDefault('norm', 'baz');
  951. $this->resolver->setNormalizer('norm', function (Options $options) {
  952. $options['lazy'];
  953. });
  954. $this->resolver->resolve();
  955. }
  956. public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver()
  957. {
  958. $throw = true;
  959. $this->resolver->setDefaults(array('catcher' => null, 'thrower' => null));
  960. $this->resolver->setNormalizer('catcher', function (Options $options) {
  961. try {
  962. return $options['thrower'];
  963. } catch (\Exception $e) {
  964. return false;
  965. }
  966. });
  967. $this->resolver->setNormalizer('thrower', function () use (&$throw) {
  968. if ($throw) {
  969. $throw = false;
  970. throw new \UnexpectedValueException('throwing');
  971. }
  972. return true;
  973. });
  974. $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve());
  975. }
  976. public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver()
  977. {
  978. $throw = true;
  979. $this->resolver->setDefault('catcher', function (Options $options) {
  980. try {
  981. return $options['thrower'];
  982. } catch (\Exception $e) {
  983. return false;
  984. }
  985. });
  986. $this->resolver->setDefault('thrower', function (Options $options) use (&$throw) {
  987. if ($throw) {
  988. $throw = false;
  989. throw new \UnexpectedValueException('throwing');
  990. }
  991. return true;
  992. });
  993. $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve());
  994. }
  995. public function testInvokeEachNormalizerOnlyOnce()
  996. {
  997. $calls = 0;
  998. $this->resolver->setDefault('norm1', 'bar');
  999. $this->resolver->setDefault('norm2', 'baz');
  1000. $this->resolver->setNormalizer('norm1', function ($options) use (&$calls) {
  1001. Assert::assertSame(1, ++$calls);
  1002. $options['norm2'];
  1003. });
  1004. $this->resolver->setNormalizer('norm2', function () use (&$calls) {
  1005. Assert::assertSame(2, ++$calls);
  1006. });
  1007. $this->resolver->resolve();
  1008. $this->assertSame(2, $calls);
  1009. }
  1010. public function testNormalizerNotCalledForUnsetOptions()
  1011. {
  1012. $this->resolver->setDefined('norm');
  1013. $this->resolver->setNormalizer('norm', function () {
  1014. Assert::fail('Should not be called.');
  1015. });
  1016. $this->assertEmpty($this->resolver->resolve());
  1017. }
  1018. ////////////////////////////////////////////////////////////////////////////
  1019. // setDefaults()
  1020. ////////////////////////////////////////////////////////////////////////////
  1021. public function testSetDefaultsReturnsThis()
  1022. {
  1023. $this->assertSame($this->resolver, $this->resolver->setDefaults(array('foo', 'bar')));
  1024. }
  1025. public function testSetDefaults()
  1026. {
  1027. $this->resolver->setDefault('one', '1');
  1028. $this->resolver->setDefault('two', 'bar');
  1029. $this->resolver->setDefaults(array(
  1030. 'two' => '2',
  1031. 'three' => '3',
  1032. ));
  1033. $this->assertEquals(array(
  1034. 'one' => '1',
  1035. 'two' => '2',
  1036. 'three' => '3',
  1037. ), $this->resolver->resolve());
  1038. }
  1039. /**
  1040. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1041. */
  1042. public function testFailIfSetDefaultsFromLazyOption()
  1043. {
  1044. $this->resolver->setDefault('foo', function (Options $options) {
  1045. $options->setDefaults(array('two' => '2'));
  1046. });
  1047. $this->resolver->resolve();
  1048. }
  1049. ////////////////////////////////////////////////////////////////////////////
  1050. // remove()
  1051. ////////////////////////////////////////////////////////////////////////////
  1052. public function testRemoveReturnsThis()
  1053. {
  1054. $this->resolver->setDefault('foo', 'bar');
  1055. $this->assertSame($this->resolver, $this->resolver->remove('foo'));
  1056. }
  1057. public function testRemoveSingleOption()
  1058. {
  1059. $this->resolver->setDefault('foo', 'bar');
  1060. $this->resolver->setDefault('baz', 'boo');
  1061. $this->resolver->remove('foo');
  1062. $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve());
  1063. }
  1064. public function testRemoveMultipleOptions()
  1065. {
  1066. $this->resolver->setDefault('foo', 'bar');
  1067. $this->resolver->setDefault('baz', 'boo');
  1068. $this->resolver->setDefault('doo', 'dam');
  1069. $this->resolver->remove(array('foo', 'doo'));
  1070. $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve());
  1071. }
  1072. public function testRemoveLazyOption()
  1073. {
  1074. $this->resolver->setDefault('foo', function (Options $options) {
  1075. return 'lazy';
  1076. });
  1077. $this->resolver->remove('foo');
  1078. $this->assertSame(array(), $this->resolver->resolve());
  1079. }
  1080. public function testRemoveNormalizer()
  1081. {
  1082. $this->resolver->setDefault('foo', 'bar');
  1083. $this->resolver->setNormalizer('foo', function (Options $options, $value) {
  1084. return 'normalized';
  1085. });
  1086. $this->resolver->remove('foo');
  1087. $this->resolver->setDefault('foo', 'bar');
  1088. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1089. }
  1090. public function testRemoveAllowedTypes()
  1091. {
  1092. $this->resolver->setDefault('foo', 'bar');
  1093. $this->resolver->setAllowedTypes('foo', 'int');
  1094. $this->resolver->remove('foo');
  1095. $this->resolver->setDefault('foo', 'bar');
  1096. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1097. }
  1098. public function testRemoveAllowedValues()
  1099. {
  1100. $this->resolver->setDefault('foo', 'bar');
  1101. $this->resolver->setAllowedValues('foo', array('baz', 'boo'));
  1102. $this->resolver->remove('foo');
  1103. $this->resolver->setDefault('foo', 'bar');
  1104. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1105. }
  1106. /**
  1107. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1108. */
  1109. public function testFailIfRemoveFromLazyOption()
  1110. {
  1111. $this->resolver->setDefault('foo', function (Options $options) {
  1112. $options->remove('bar');
  1113. });
  1114. $this->resolver->setDefault('bar', 'baz');
  1115. $this->resolver->resolve();
  1116. }
  1117. public function testRemoveUnknownOptionIgnored()
  1118. {
  1119. $this->assertNotNull($this->resolver->remove('foo'));
  1120. }
  1121. ////////////////////////////////////////////////////////////////////////////
  1122. // clear()
  1123. ////////////////////////////////////////////////////////////////////////////
  1124. public function testClearReturnsThis()
  1125. {
  1126. $this->assertSame($this->resolver, $this->resolver->clear());
  1127. }
  1128. public function testClearRemovesAllOptions()
  1129. {
  1130. $this->resolver->setDefault('one', 1);
  1131. $this->resolver->setDefault('two', 2);
  1132. $this->resolver->clear();
  1133. $this->assertEmpty($this->resolver->resolve());
  1134. }
  1135. public function testClearLazyOption()
  1136. {
  1137. $this->resolver->setDefault('foo', function (Options $options) {
  1138. return 'lazy';
  1139. });
  1140. $this->resolver->clear();
  1141. $this->assertSame(array(), $this->resolver->resolve());
  1142. }
  1143. public function testClearNormalizer()
  1144. {
  1145. $this->resolver->setDefault('foo', 'bar');
  1146. $this->resolver->setNormalizer('foo', function (Options $options, $value) {
  1147. return 'normalized';
  1148. });
  1149. $this->resolver->clear();
  1150. $this->resolver->setDefault('foo', 'bar');
  1151. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1152. }
  1153. public function testClearAllowedTypes()
  1154. {
  1155. $this->resolver->setDefault('foo', 'bar');
  1156. $this->resolver->setAllowedTypes('foo', 'int');
  1157. $this->resolver->clear();
  1158. $this->resolver->setDefault('foo', 'bar');
  1159. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1160. }
  1161. public function testClearAllowedValues()
  1162. {
  1163. $this->resolver->setDefault('foo', 'bar');
  1164. $this->resolver->setAllowedValues('foo', 'baz');
  1165. $this->resolver->clear();
  1166. $this->resolver->setDefault('foo', 'bar');
  1167. $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve());
  1168. }
  1169. /**
  1170. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1171. */
  1172. public function testFailIfClearFromLazyption()
  1173. {
  1174. $this->resolver->setDefault('foo', function (Options $options) {
  1175. $options->clear();
  1176. });
  1177. $this->resolver->setDefault('bar', 'baz');
  1178. $this->resolver->resolve();
  1179. }
  1180. public function testClearOptionAndNormalizer()
  1181. {
  1182. $this->resolver->setDefault('foo1', 'bar');
  1183. $this->resolver->setNormalizer('foo1', function (Options $options) {
  1184. return '';
  1185. });
  1186. $this->resolver->setDefault('foo2', 'bar');
  1187. $this->resolver->setNormalizer('foo2', function (Options $options) {
  1188. return '';
  1189. });
  1190. $this->resolver->clear();
  1191. $this->assertEmpty($this->resolver->resolve());
  1192. }
  1193. ////////////////////////////////////////////////////////////////////////////
  1194. // ArrayAccess
  1195. ////////////////////////////////////////////////////////////////////////////
  1196. public function testArrayAccess()
  1197. {
  1198. $this->resolver->setDefault('default1', 0);
  1199. $this->resolver->setDefault('default2', 1);
  1200. $this->resolver->setRequired('required');
  1201. $this->resolver->setDefined('defined');
  1202. $this->resolver->setDefault('lazy1', function (Options $options) {
  1203. return 'lazy';
  1204. });
  1205. $this->resolver->setDefault('lazy2', function (Options $options) {
  1206. Assert::assertArrayHasKey('default1', $options);
  1207. Assert::assertArrayHasKey('default2', $options);
  1208. Assert::assertArrayHasKey('required', $options);
  1209. Assert::assertArrayHasKey('lazy1', $options);
  1210. Assert::assertArrayHasKey('lazy2', $options);
  1211. Assert::assertArrayNotHasKey('defined', $options);
  1212. Assert::assertSame(0, $options['default1']);
  1213. Assert::assertSame(42, $options['default2']);
  1214. Assert::assertSame('value', $options['required']);
  1215. Assert::assertSame('lazy', $options['lazy1']);
  1216. // Obviously $options['lazy'] and $options['defined'] cannot be
  1217. // accessed
  1218. });
  1219. $this->resolver->resolve(array('default2' => 42, 'required' => 'value'));
  1220. }
  1221. /**
  1222. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1223. */
  1224. public function testArrayAccessGetFailsOutsideResolve()
  1225. {
  1226. $this->resolver->setDefault('default', 0);
  1227. $this->resolver['default'];
  1228. }
  1229. /**
  1230. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1231. */
  1232. public function testArrayAccessExistsFailsOutsideResolve()
  1233. {
  1234. $this->resolver->setDefault('default', 0);
  1235. isset($this->resolver['default']);
  1236. }
  1237. /**
  1238. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1239. */
  1240. public function testArrayAccessSetNotSupported()
  1241. {
  1242. $this->resolver['default'] = 0;
  1243. }
  1244. /**
  1245. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1246. */
  1247. public function testArrayAccessUnsetNotSupported()
  1248. {
  1249. $this->resolver->setDefault('default', 0);
  1250. unset($this->resolver['default']);
  1251. }
  1252. /**
  1253. * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException
  1254. * @expectedExceptionMessage The option "undefined" does not exist. Defined options are: "foo", "lazy".
  1255. */
  1256. public function testFailIfGetNonExisting()
  1257. {
  1258. $this->resolver->setDefault('foo', 'bar');
  1259. $this->resolver->setDefault('lazy', function (Options $options) {
  1260. $options['undefined'];
  1261. });
  1262. $this->resolver->resolve();
  1263. }
  1264. /**
  1265. * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException
  1266. * @expectedExceptionMessage The optional option "defined" has no value set. You should make sure it is set with "isset" before reading it.
  1267. */
  1268. public function testFailIfGetDefinedButUnset()
  1269. {
  1270. $this->resolver->setDefined('defined');
  1271. $this->resolver->setDefault('lazy', function (Options $options) {
  1272. $options['defined'];
  1273. });
  1274. $this->resolver->resolve();
  1275. }
  1276. /**
  1277. * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException
  1278. */
  1279. public function testFailIfCyclicDependency()
  1280. {
  1281. $this->resolver->setDefault('lazy1', function (Options $options) {
  1282. $options['lazy2'];
  1283. });
  1284. $this->resolver->setDefault('lazy2', function (Options $options) {
  1285. $options['lazy1'];
  1286. });
  1287. $this->resolver->resolve();
  1288. }
  1289. ////////////////////////////////////////////////////////////////////////////
  1290. // Countable
  1291. ////////////////////////////////////////////////////////////////////////////
  1292. public function testCount()
  1293. {
  1294. $this->resolver->setDefault('default', 0);
  1295. $this->resolver->setRequired('required');
  1296. $this->resolver->setDefined('defined');
  1297. $this->resolver->setDefault('lazy1', function () {});
  1298. $this->resolver->setDefault('lazy2', function (Options $options) {
  1299. Assert::assertCount(4, $options);
  1300. });
  1301. $this->assertCount(4, $this->resolver->resolve(array('required' => 'value')));
  1302. }
  1303. /**
  1304. * In resolve() we count the options that are actually set (which may be
  1305. * only a subset of the defined options). Outside of resolve(), it's not
  1306. * clear what is counted.
  1307. *
  1308. * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException
  1309. */
  1310. public function testCountFailsOutsideResolve()
  1311. {
  1312. $this->resolver->setDefault('foo', 0);
  1313. $this->resolver->setRequired('bar');
  1314. $this->resolver->setDefined('bar');
  1315. $this->resolver->setDefault('lazy1', function () {});
  1316. count($this->resolver);
  1317. }
  1318. }