Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

1627 linhas
54 KiB

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