forked from GNUsocial/gnu-social
		
	
		
			
	
	
		
			2518 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			2518 lines
		
	
	
		
			81 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php | ||
|  | 
 | ||
|  | /* | ||
|  |  * This file is part of the Symfony package. | ||
|  |  * | ||
|  |  * (c) Fabien Potencier <fabien@symfony.com> | ||
|  |  * | ||
|  |  * For the full copyright and license information, please view the LICENSE | ||
|  |  * file that was distributed with this source code. | ||
|  |  */ | ||
|  | 
 | ||
|  | namespace Symfony\Component\OptionsResolver\Tests; | ||
|  | 
 | ||
|  | use PHPUnit\Framework\Assert; | ||
|  | use PHPUnit\Framework\TestCase; | ||
|  | use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; | ||
|  | use Symfony\Component\OptionsResolver\Options; | ||
|  | use Symfony\Component\OptionsResolver\OptionsResolver; | ||
|  | 
 | ||
|  | class OptionsResolverTest extends TestCase | ||
|  | { | ||
|  |     /** | ||
|  |      * @var OptionsResolver | ||
|  |      */ | ||
|  |     private $resolver; | ||
|  | 
 | ||
|  |     protected function setUp() | ||
|  |     { | ||
|  |         $this->resolver = new OptionsResolver(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "a", "z". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfNonExistingOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('z', '1'); | ||
|  |         $this->resolver->setDefault('a', '2'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['foo' => 'bar']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      * @expectedExceptionMessage The options "baz", "foo", "ping" do not exist. Defined options are: "a", "z". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfMultipleNonExistingOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('z', '1'); | ||
|  |         $this->resolver->setDefault('a', '2'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['ping' => 'pong', 'foo' => 'bar', 'baz' => 'bam']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testResolveFailsFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->resolve([]); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetDefaultReturnsThis() | ||
|  |     { | ||
|  |         $this->assertSame($this->resolver, $this->resolver->setDefault('foo', 'bar')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetDefault() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('one', '1'); | ||
|  |         $this->resolver->setDefault('two', '20'); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'one' => '1', | ||
|  |             'two' => '20', | ||
|  |         ], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetDefaultFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('lazy', function (Options $options) { | ||
|  |             $options->setDefault('default', 42); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testHasDefault() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->hasDefault('foo')); | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->assertTrue($this->resolver->hasDefault('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testHasDefaultWithNullValue() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->hasDefault('foo')); | ||
|  |         $this->resolver->setDefault('foo', null); | ||
|  |         $this->assertTrue($this->resolver->hasDefault('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetLazyReturnsThis() | ||
|  |     { | ||
|  |         $this->assertSame($this->resolver, $this->resolver->setDefault('foo', function (Options $options) {})); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetLazyClosure() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClosureWithoutTypeHintNotInvoked() | ||
|  |     { | ||
|  |         $closure = function ($options) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }; | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', $closure); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => $closure], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClosureWithoutParametersNotInvoked() | ||
|  |     { | ||
|  |         $closure = function () { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }; | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', $closure); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => $closure], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAccessPreviousDefaultValue() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options, $previousValue) { | ||
|  |             Assert::assertEquals('bar', $previousValue); | ||
|  | 
 | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAccessPreviousLazyDefaultValue() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'bar'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options, $previousValue) { | ||
|  |             Assert::assertEquals('bar', $previousValue); | ||
|  | 
 | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testPreviousValueIsNotEvaluatedIfNoSecondArgument() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function () { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         // defined by subclass, no $previousValue argument defined!
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'lazy'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testOverwrittenLazyOptionNotEvaluated() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testInvokeEachLazyOptionOnlyOnce() | ||
|  |     { | ||
|  |         $calls = 0; | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy1', function (Options $options) use (&$calls) { | ||
|  |             Assert::assertSame(1, ++$calls); | ||
|  | 
 | ||
|  |             $options['lazy2']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy2', function (Options $options) use (&$calls) { | ||
|  |             Assert::assertSame(2, ++$calls); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  | 
 | ||
|  |         $this->assertSame(2, $calls); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetRequiredReturnsThis() | ||
|  |     { | ||
|  |         $this->assertSame($this->resolver, $this->resolver->setRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetRequiredFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setRequired('bar'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfRequiredOptionMissing() | ||
|  |     { | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfRequiredOptionSet() | ||
|  |     { | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfRequiredOptionPassed() | ||
|  |     { | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve(['foo' => 'bar'])); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsRequired() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->assertTrue($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRequiredIfSetBefore() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  | 
 | ||
|  |         $this->assertTrue($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testStillRequiredAfterSet() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  | 
 | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertTrue($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotRequiredAfterRemove() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotRequiredAfterClear() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testGetRequiredOptions() | ||
|  |     { | ||
|  |         $this->resolver->setRequired(['foo', 'bar']); | ||
|  |         $this->resolver->setDefault('bam', 'baz'); | ||
|  |         $this->resolver->setDefault('foo', 'boo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo', 'bar'], $this->resolver->getRequiredOptions()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsMissingIfNotSet() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isMissing('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->assertTrue($this->resolver->isMissing('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotMissingIfSet() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertFalse($this->resolver->isMissing('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->assertFalse($this->resolver->isMissing('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotMissingAfterRemove() | ||
|  |     { | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->assertFalse($this->resolver->isMissing('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotMissingAfterClear() | ||
|  |     { | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->assertFalse($this->resolver->isRequired('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testGetMissingOptions() | ||
|  |     { | ||
|  |         $this->resolver->setRequired(['foo', 'bar']); | ||
|  |         $this->resolver->setDefault('bam', 'baz'); | ||
|  |         $this->resolver->setDefault('foo', 'boo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['bar'], $this->resolver->getMissingOptions()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetDefinedFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setDefined('bar'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testDefinedOptionsNotIncludedInResolvedOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  | 
 | ||
|  |         $this->assertSame([], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testDefinedOptionsIncludedIfDefaultSetBefore() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testDefinedOptionsIncludedIfDefaultSetAfter() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testDefinedOptionsIncludedIfPassedToResolve() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve(['foo' => 'bar'])); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testLazyOptionsAreDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) {}); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRequiredOptionsAreDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setRequired('foo'); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetOptionsAreDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testGetDefinedOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefined(['foo', 'bar']); | ||
|  |         $this->resolver->setDefault('baz', 'bam'); | ||
|  |         $this->resolver->setRequired('boo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo', 'bar', 'baz', 'boo'], $this->resolver->getDefinedOptions()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemovedOptionsAreNotDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearedOptionsAreNotDefined() | ||
|  |     { | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->assertTrue($this->resolver->isDefined('foo')); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->assertFalse($this->resolver->isDefined('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetDeprecatedFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefault('bar', 'baz') | ||
|  |             ->setDefault('foo', function (Options $options) { | ||
|  |                 $options->setDeprecated('bar'); | ||
|  |             }) | ||
|  |             ->resolve() | ||
|  |         ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testSetDeprecatedFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->setDeprecated('foo'); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidArgumentException | ||
|  |      * @expectedExceptionMessage Invalid type for deprecation message argument, expected string or \Closure, but got "boolean". | ||
|  |      */ | ||
|  |     public function testSetDeprecatedFailsIfInvalidDeprecationMessageType() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefined('foo') | ||
|  |             ->setDeprecated('foo', true) | ||
|  |         ; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidArgumentException | ||
|  |      * @expectedExceptionMessage Invalid type for deprecation message, expected string but got "boolean", return an empty string to ignore. | ||
|  |      */ | ||
|  |     public function testLazyDeprecationFailsIfInvalidDeprecationMessageType() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefined('foo') | ||
|  |             ->setDeprecated('foo', function (Options $options, $value) { | ||
|  |                 return false; | ||
|  |             }) | ||
|  |         ; | ||
|  |         $this->resolver->resolve(['foo' => null]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      * @expectedExceptionMessage The options "foo", "bar" have a cyclic dependency. | ||
|  |      */ | ||
|  |     public function testFailsIfCyclicDependencyBetweenDeprecation() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefined(['foo', 'bar']) | ||
|  |             ->setDeprecated('foo', function (Options $options, $value) { | ||
|  |                 $options['bar']; | ||
|  |             }) | ||
|  |             ->setDeprecated('bar', function (Options $options, $value) { | ||
|  |                 $options['foo']; | ||
|  |             }) | ||
|  |         ; | ||
|  |         $this->resolver->resolve(['foo' => null, 'bar' => null]); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsDeprecated() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefined('foo') | ||
|  |             ->setDeprecated('foo') | ||
|  |         ; | ||
|  |         $this->assertTrue($this->resolver->isDeprecated('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNotDeprecatedIfEmptyString() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefined('foo') | ||
|  |             ->setDeprecated('foo', '') | ||
|  |         ; | ||
|  |         $this->assertFalse($this->resolver->isDeprecated('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideDeprecationData | ||
|  |      */ | ||
|  |     public function testDeprecationMessages(\Closure $configureOptions, array $options, ?array $expectedError, int $expectedCount) | ||
|  |     { | ||
|  |         $count = 0; | ||
|  |         error_clear_last(); | ||
|  |         set_error_handler(function () use (&$count) { | ||
|  |             ++$count; | ||
|  | 
 | ||
|  |             return false; | ||
|  |         }); | ||
|  |         $e = error_reporting(0); | ||
|  | 
 | ||
|  |         $configureOptions($this->resolver); | ||
|  |         $this->resolver->resolve($options); | ||
|  | 
 | ||
|  |         error_reporting($e); | ||
|  |         restore_error_handler(); | ||
|  | 
 | ||
|  |         $lastError = error_get_last(); | ||
|  |         unset($lastError['file'], $lastError['line']); | ||
|  | 
 | ||
|  |         $this->assertSame($expectedError, $lastError); | ||
|  |         $this->assertSame($expectedCount, $count); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function provideDeprecationData() | ||
|  |     { | ||
|  |         yield 'It deprecates an option with default message' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined(['foo', 'bar']) | ||
|  |                     ->setDeprecated('foo') | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => 'baz'], | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'The option "foo" is deprecated.', | ||
|  |             ], | ||
|  |             1, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It deprecates an option with custom message' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined('foo') | ||
|  |                     ->setDefault('bar', function (Options $options) { | ||
|  |                         return $options['foo']; | ||
|  |                     }) | ||
|  |                     ->setDeprecated('foo', 'The option "foo" is deprecated, use "bar" option instead.') | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => 'baz'], | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'The option "foo" is deprecated, use "bar" option instead.', | ||
|  |             ], | ||
|  |             2, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It deprecates an option evaluated in another definition' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 // defined by superclass
 | ||
|  |                 $resolver | ||
|  |                     ->setDefault('foo', null) | ||
|  |                     ->setDeprecated('foo') | ||
|  |                 ; | ||
|  |                 // defined by subclass
 | ||
|  |                 $resolver->setDefault('bar', function (Options $options) { | ||
|  |                     return $options['foo']; // It triggers a deprecation
 | ||
|  |                 }); | ||
|  |             }, | ||
|  |             [], | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'The option "foo" is deprecated.', | ||
|  |             ], | ||
|  |             1, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It deprecates allowed type and value' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefault('foo', null) | ||
|  |                     ->setAllowedTypes('foo', ['null', 'string', \stdClass::class]) | ||
|  |                     ->setDeprecated('foo', function (Options $options, $value) { | ||
|  |                         if ($value instanceof \stdClass) { | ||
|  |                             return sprintf('Passing an instance of "%s" to option "foo" is deprecated, pass its FQCN instead.', \stdClass::class); | ||
|  |                         } | ||
|  | 
 | ||
|  |                         return ''; | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => new \stdClass()], | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'Passing an instance of "stdClass" to option "foo" is deprecated, pass its FQCN instead.', | ||
|  |             ], | ||
|  |             1, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It triggers a deprecation based on the value only if option is provided by the user' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined('foo') | ||
|  |                     ->setAllowedTypes('foo', ['null', 'bool']) | ||
|  |                     ->setDeprecated('foo', function (Options $options, $value) { | ||
|  |                         if (!\is_bool($value)) { | ||
|  |                             return 'Passing a value different than true or false is deprecated.'; | ||
|  |                         } | ||
|  | 
 | ||
|  |                         return ''; | ||
|  |                     }) | ||
|  |                     ->setDefault('baz', null) | ||
|  |                     ->setAllowedTypes('baz', ['null', 'int']) | ||
|  |                     ->setDeprecated('baz', function (Options $options, $value) { | ||
|  |                         if (!\is_int($value)) { | ||
|  |                             return 'Not passing an integer is deprecated.'; | ||
|  |                         } | ||
|  | 
 | ||
|  |                         return ''; | ||
|  |                     }) | ||
|  |                     ->setDefault('bar', function (Options $options) { | ||
|  |                         $options['baz']; // It does not triggers a deprecation
 | ||
|  | 
 | ||
|  |                         return $options['foo']; // It does not triggers a deprecation
 | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => null], // It triggers a deprecation
 | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'Passing a value different than true or false is deprecated.', | ||
|  |             ], | ||
|  |             1, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It ignores a deprecation if closure returns an empty string' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefault('foo', null) | ||
|  |                     ->setDeprecated('foo', function (Options $options, $value) { | ||
|  |                         return ''; | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => Bar::class], | ||
|  |             null, | ||
|  |             0, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It deprecates value depending on other option value' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefault('widget', null) | ||
|  |                     ->setDefault('date_format', null) | ||
|  |                     ->setDeprecated('date_format', function (Options $options, $dateFormat) { | ||
|  |                         if (null !== $dateFormat && 'single_text' === $options['widget']) { | ||
|  |                             return 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.'; | ||
|  |                         } | ||
|  | 
 | ||
|  |                         return ''; | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['widget' => 'single_text', 'date_format' => 2], | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'Using the "date_format" option when the "widget" option is set to "single_text" is deprecated.', | ||
|  |             ], | ||
|  |             1, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It triggers a deprecation for each evaluation' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     // defined by superclass
 | ||
|  |                     ->setDefined('foo') | ||
|  |                     ->setDeprecated('foo') | ||
|  |                     // defined by subclass
 | ||
|  |                     ->setDefault('bar', function (Options $options) { | ||
|  |                         return $options['foo']; // It triggers a deprecation
 | ||
|  |                     }) | ||
|  |                     ->setNormalizer('bar', function (Options $options, $value) { | ||
|  |                         $options['foo']; // It triggers a deprecation
 | ||
|  |                         $options['foo']; // It triggers a deprecation
 | ||
|  | 
 | ||
|  |                         return $value; | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             ['foo' => 'baz'], // It triggers a deprecation
 | ||
|  |             [ | ||
|  |                 'type' => E_USER_DEPRECATED, | ||
|  |                 'message' => 'The option "foo" is deprecated.', | ||
|  |             ], | ||
|  |             4, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It ignores a deprecation if no option is provided by the user' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined('foo') | ||
|  |                     ->setDefault('bar', null) | ||
|  |                     ->setDeprecated('foo') | ||
|  |                     ->setDeprecated('bar') | ||
|  |                 ; | ||
|  |             }, | ||
|  |             [], | ||
|  |             null, | ||
|  |             0, | ||
|  |         ]; | ||
|  | 
 | ||
|  |         yield 'It explicitly ignores a deprecation' => [ | ||
|  |             function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefault('baz', function (Options $options) { | ||
|  |                         return $options->offsetGet('foo', false); | ||
|  |                     }) | ||
|  |                     ->setDefault('foo', null) | ||
|  |                     ->setDeprecated('foo') | ||
|  |                     ->setDefault('bar', function (Options $options) { | ||
|  |                         return $options->offsetGet('foo', false); | ||
|  |                     }) | ||
|  |                 ; | ||
|  |             }, | ||
|  |             [], | ||
|  |             null, | ||
|  |             0, | ||
|  |         ]; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testSetAllowedTypesFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string'); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveTypedArray() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string[]'); | ||
|  |         $options = $this->resolver->resolve(['foo' => ['bar', 'baz']]); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => ['bar', 'baz']], $options); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetAllowedTypesFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setAllowedTypes('bar', 'string'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidTypedArray() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[]'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['foo' => [new \DateTime()]]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string". | ||
|  |      */ | ||
|  |     public function testResolveFailsWithNonArray() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[]'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['foo' => 'bar']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfTypedArrayContainsInvalidTypes() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[]'); | ||
|  |         $values = range(1, 5); | ||
|  |         $values[] = new \stdClass(); | ||
|  |         $values[] = []; | ||
|  |         $values[] = new \DateTime(); | ||
|  |         $values[] = 123; | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['foo' => $values]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]". | ||
|  |      */ | ||
|  |     public function testResolveFailsWithCorrectLevelsButWrongScalar() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[][]'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [1.2], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @dataProvider provideInvalidTypes | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage) | ||
|  |     { | ||
|  |         $this->resolver->setDefined('option'); | ||
|  |         $this->resolver->setAllowedTypes('option', $allowedType); | ||
|  | 
 | ||
|  |         if (method_exists($this, 'expectException')) { | ||
|  |             $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); | ||
|  |             $this->expectExceptionMessage($exceptionMessage); | ||
|  |         } else { | ||
|  |             $this->setExpectedException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException', $exceptionMessage); | ||
|  |         } | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['option' => $actualType]); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function provideInvalidTypes() | ||
|  |     { | ||
|  |         return [ | ||
|  |             [true, 'string', 'The option "option" with value true is expected to be of type "string", but is of type "boolean".'], | ||
|  |             [false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "boolean".'], | ||
|  |             [fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource".'], | ||
|  |             [[], 'string', 'The option "option" with value array is expected to be of type "string", but is of type "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".'], | ||
|  |             [42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'], | ||
|  |             [null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'], | ||
|  |             ['bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'], | ||
|  |         ]; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidType() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string" or "bool", but is of type "integer". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidTypeMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedTypes('foo', ['string', 'bool']); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidTypeMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', true); | ||
|  |         $this->resolver->setAllowedTypes('foo', ['string', 'bool']); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfInstanceOfClass() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', new \stdClass()); | ||
|  |         $this->resolver->setAllowedTypes('foo', '\stdClass'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfTypedArray() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', null); | ||
|  |         $this->resolver->setAllowedTypes('foo', ['null', 'DateTime[]']); | ||
|  | 
 | ||
|  |         $data = [ | ||
|  |             'foo' => [ | ||
|  |                 new \DateTime(), | ||
|  |                 new \DateTime(), | ||
|  |             ], | ||
|  |         ]; | ||
|  |         $result = $this->resolver->resolve($data); | ||
|  |         $this->assertEquals($data, $result); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfNotInstanceOfClass() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', '\stdClass'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testAddAllowedTypesFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->addAllowedTypes('foo', 'string'); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfAddAllowedTypesFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->addAllowedTypes('bar', 'string'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidAddedType() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->addAllowedTypes('foo', 'string'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidAddedType() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedTypes('foo', 'string'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidAddedTypeMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->addAllowedTypes('foo', ['string', 'bool']); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidAddedTypeMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedTypes('foo', ['string', 'bool']); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddAllowedTypesDoesNotOverwrite() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string'); | ||
|  |         $this->resolver->addAllowedTypes('foo', 'bool'); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddAllowedTypesDoesNotOverwrite2() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string'); | ||
|  |         $this->resolver->addAllowedTypes('foo', 'bool'); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('foo', false); | ||
|  | 
 | ||
|  |         $this->assertNotEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testSetAllowedValuesFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetAllowedValuesFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setAllowedValues('bar', 'baz'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidValue() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['foo' => 42]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value null is invalid. Accepted values are: "bar". | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidValueIsNull() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', null); | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidValueStrict() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedValues('foo', '42'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidValue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidValueIsNull() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', null); | ||
|  |         $this->resolver->setAllowedValues('foo', null); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => null], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar", false, null. | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidValueMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedValues('foo', ['bar', false, null]); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidValueMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'baz'); | ||
|  |         $this->resolver->setAllowedValues('foo', ['bar', 'baz']); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'baz'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveFailsIfClosureReturnsFalse() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { | ||
|  |             $passedValue = $value; | ||
|  | 
 | ||
|  |             return false; | ||
|  |         }); | ||
|  | 
 | ||
|  |         try { | ||
|  |             $this->resolver->resolve(); | ||
|  |             $this->fail('Should fail'); | ||
|  |         } catch (InvalidOptionsException $e) { | ||
|  |         } | ||
|  | 
 | ||
|  |         $this->assertSame(42, $passedValue); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfClosureReturnsTrue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { | ||
|  |             $passedValue = $value; | ||
|  | 
 | ||
|  |             return true; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |         $this->assertSame('bar', $passedValue); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfAllClosuresReturnFalse() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedValues('foo', [ | ||
|  |             function () { return false; }, | ||
|  |             function () { return false; }, | ||
|  |             function () { return false; }, | ||
|  |         ]); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfAnyClosureReturnsTrue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', [ | ||
|  |             function () { return false; }, | ||
|  |             function () { return true; }, | ||
|  |             function () { return false; }, | ||
|  |         ]); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testAddAllowedValuesFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->addAllowedValues('foo', 'bar'); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfAddAllowedValuesFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->addAllowedValues('bar', 'baz'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidAddedValue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->addAllowedValues('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidAddedValue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedValues('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidAddedValueIsNull() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', null); | ||
|  |         $this->resolver->addAllowedValues('foo', null); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => null], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfInvalidAddedValueMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->addAllowedValues('foo', ['bar', 'baz']); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfValidAddedValueMultiple() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedValues('foo', ['bar', 'baz']); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddAllowedValuesDoesNotOverwrite() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedValues('foo', 'baz'); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddAllowedValuesDoesNotOverwrite2() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'baz'); | ||
|  |         $this->resolver->setAllowedValues('foo', 'bar'); | ||
|  |         $this->resolver->addAllowedValues('foo', 'baz'); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'baz'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testResolveFailsIfAllAddedClosuresReturnFalse() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 42); | ||
|  |         $this->resolver->setAllowedValues('foo', function () { return false; }); | ||
|  |         $this->resolver->addAllowedValues('foo', function () { return false; }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfAnyAddedClosureReturnsTrue() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', function () { return false; }); | ||
|  |         $this->resolver->addAllowedValues('foo', function () { return true; }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveSucceedsIfAnyAddedClosureReturnsTrue2() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', function () { return true; }); | ||
|  |         $this->resolver->addAllowedValues('foo', function () { return false; }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetNormalizerReturnsThis() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->assertSame($this->resolver, $this->resolver->setNormalizer('foo', function () {})); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetNormalizerClosure() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo', function () { | ||
|  |             return 'normalized'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'normalized'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testSetNormalizerFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->setNormalizer('foo', function () {}); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetNormalizerFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setNormalizer('foo', function () {}); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizerReceivesSetOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return 'normalized['.$value.']'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'normalized[bar]'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizerReceivesPassedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return 'normalized['.$value.']'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $resolved = $this->resolver->resolve(['foo' => 'baz']); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => 'normalized[baz]'], $resolved); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testValidateTypeBeforeNormalization() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('foo', function () { | ||
|  |             Assert::fail('Should not be called.'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      */ | ||
|  |     public function testValidateValueBeforeNormalization() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setAllowedValues('foo', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('foo', function () { | ||
|  |             Assert::fail('Should not be called.'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizerCanAccessOtherOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default', 'bar'); | ||
|  |         $this->resolver->setDefault('norm', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm', function (Options $options) { | ||
|  |             /* @var TestCase $test */ | ||
|  |             Assert::assertSame('bar', $options['default']); | ||
|  | 
 | ||
|  |             return 'normalized'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'default' => 'bar', | ||
|  |             'norm' => 'normalized', | ||
|  |         ], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizerCanAccessLazyOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('lazy', function (Options $options) { | ||
|  |             return 'bar'; | ||
|  |         }); | ||
|  |         $this->resolver->setDefault('norm', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm', function (Options $options) { | ||
|  |             /* @var TestCase $test */ | ||
|  |             Assert::assertEquals('bar', $options['lazy']); | ||
|  | 
 | ||
|  |             return 'normalized'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'lazy' => 'bar', | ||
|  |             'norm' => 'normalized', | ||
|  |         ], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailIfCyclicDependencyBetweenNormalizers() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('norm1', 'bar'); | ||
|  |         $this->resolver->setDefault('norm2', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm1', function (Options $options) { | ||
|  |             $options['norm2']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm2', function (Options $options) { | ||
|  |             $options['norm1']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('lazy', function (Options $options) { | ||
|  |             $options['norm']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('norm', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm', function (Options $options) { | ||
|  |             $options['lazy']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver() | ||
|  |     { | ||
|  |         $throw = true; | ||
|  | 
 | ||
|  |         $this->resolver->setDefaults(['catcher' => null, 'thrower' => null]); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('catcher', function (Options $options) { | ||
|  |             try { | ||
|  |                 return $options['thrower']; | ||
|  |             } catch (\Exception $e) { | ||
|  |                 return false; | ||
|  |             } | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('thrower', function () use (&$throw) { | ||
|  |             if ($throw) { | ||
|  |                 $throw = false; | ||
|  |                 throw new \UnexpectedValueException('throwing'); | ||
|  |             } | ||
|  | 
 | ||
|  |             return true; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertSame(['catcher' => false, 'thrower' => true], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver() | ||
|  |     { | ||
|  |         $throw = true; | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('catcher', function (Options $options) { | ||
|  |             try { | ||
|  |                 return $options['thrower']; | ||
|  |             } catch (\Exception $e) { | ||
|  |                 return false; | ||
|  |             } | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('thrower', function (Options $options) use (&$throw) { | ||
|  |             if ($throw) { | ||
|  |                 $throw = false; | ||
|  |                 throw new \UnexpectedValueException('throwing'); | ||
|  |             } | ||
|  | 
 | ||
|  |             return true; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertSame(['catcher' => false, 'thrower' => true], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testInvokeEachNormalizerOnlyOnce() | ||
|  |     { | ||
|  |         $calls = 0; | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('norm1', 'bar'); | ||
|  |         $this->resolver->setDefault('norm2', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm1', function ($options) use (&$calls) { | ||
|  |             Assert::assertSame(1, ++$calls); | ||
|  | 
 | ||
|  |             $options['norm2']; | ||
|  |         }); | ||
|  |         $this->resolver->setNormalizer('norm2', function () use (&$calls) { | ||
|  |             Assert::assertSame(2, ++$calls); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  | 
 | ||
|  |         $this->assertSame(2, $calls); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizerNotCalledForUnsetOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('norm'); | ||
|  | 
 | ||
|  |         $this->resolver->setNormalizer('norm', function () { | ||
|  |             Assert::fail('Should not be called.'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddNormalizerReturnsThis() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame($this->resolver, $this->resolver->addNormalizer('foo', function () {})); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAddNormalizerClosure() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return '1st-normalized-'.$value; | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->addNormalizer('foo', function (Options $options, $value) { | ||
|  |             return '2nd-normalized-'.$value; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testForcePrependNormalizerClosure() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return '2nd-normalized-'.$value; | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->addNormalizer('foo', function (Options $options, $value) { | ||
|  |             return '1st-normalized-'.$value; | ||
|  |         }, true); | ||
|  | 
 | ||
|  |         $this->assertEquals(['foo' => '2nd-normalized-1st-normalized-bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      */ | ||
|  |     public function testAddNormalizerFailsIfUnknownOption() | ||
|  |     { | ||
|  |         $this->resolver->addNormalizer('foo', function () {}); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfAddNormalizerFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->addNormalizer('foo', function () {}); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetDefaultsReturnsThis() | ||
|  |     { | ||
|  |         $this->assertSame($this->resolver, $this->resolver->setDefaults(['foo', 'bar'])); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testSetDefaults() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('one', '1'); | ||
|  |         $this->resolver->setDefault('two', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'two' => '2', | ||
|  |             'three' => '3', | ||
|  |         ]); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'one' => '1', | ||
|  |             'two' => '2', | ||
|  |             'three' => '3', | ||
|  |         ], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfSetDefaultsFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->setDefaults(['two' => '2']); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveReturnsThis() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame($this->resolver, $this->resolver->remove('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveSingleOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setDefault('baz', 'boo'); | ||
|  |         $this->resolver->remove('foo'); | ||
|  | 
 | ||
|  |         $this->assertSame(['baz' => 'boo'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveMultipleOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setDefault('baz', 'boo'); | ||
|  |         $this->resolver->setDefault('doo', 'dam'); | ||
|  | 
 | ||
|  |         $this->resolver->remove(['foo', 'doo']); | ||
|  | 
 | ||
|  |         $this->assertSame(['baz' => 'boo'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  |         $this->resolver->remove('foo'); | ||
|  | 
 | ||
|  |         $this->assertSame([], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveNormalizer() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return 'normalized'; | ||
|  |         }); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveAllowedTypes() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int'); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveAllowedValues() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', ['baz', 'boo']); | ||
|  |         $this->resolver->remove('foo'); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfRemoveFromLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->remove('bar'); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testRemoveUnknownOptionIgnored() | ||
|  |     { | ||
|  |         $this->assertNotNull($this->resolver->remove('foo')); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearReturnsThis() | ||
|  |     { | ||
|  |         $this->assertSame($this->resolver, $this->resolver->clear()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearRemovesAllOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('one', 1); | ||
|  |         $this->resolver->setDefault('two', 2); | ||
|  | 
 | ||
|  |         $this->resolver->clear(); | ||
|  | 
 | ||
|  |         $this->assertEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  |         $this->resolver->clear(); | ||
|  | 
 | ||
|  |         $this->assertSame([], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearNormalizer() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $value) { | ||
|  |             return 'normalized'; | ||
|  |         }); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearAllowedTypes() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int'); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearAllowedValues() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->resolver->setAllowedValues('foo', 'baz'); | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testFailIfClearFromLazyption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             $options->clear(); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('bar', 'baz'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testClearOptionAndNormalizer() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo1', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo1', function (Options $options) { | ||
|  |             return ''; | ||
|  |         }); | ||
|  |         $this->resolver->setDefault('foo2', 'bar'); | ||
|  |         $this->resolver->setNormalizer('foo2', function (Options $options) { | ||
|  |             return ''; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->clear(); | ||
|  |         $this->assertEmpty($this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testArrayAccess() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default1', 0); | ||
|  |         $this->resolver->setDefault('default2', 1); | ||
|  |         $this->resolver->setRequired('required'); | ||
|  |         $this->resolver->setDefined('defined'); | ||
|  |         $this->resolver->setDefault('lazy1', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy2', function (Options $options) { | ||
|  |             Assert::assertArrayHasKey('default1', $options); | ||
|  |             Assert::assertArrayHasKey('default2', $options); | ||
|  |             Assert::assertArrayHasKey('required', $options); | ||
|  |             Assert::assertArrayHasKey('lazy1', $options); | ||
|  |             Assert::assertArrayHasKey('lazy2', $options); | ||
|  |             Assert::assertArrayNotHasKey('defined', $options); | ||
|  | 
 | ||
|  |             Assert::assertSame(0, $options['default1']); | ||
|  |             Assert::assertSame(42, $options['default2']); | ||
|  |             Assert::assertSame('value', $options['required']); | ||
|  |             Assert::assertSame('lazy', $options['lazy1']); | ||
|  | 
 | ||
|  |             // Obviously $options['lazy'] and $options['defined'] cannot be
 | ||
|  |             // accessed
 | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(['default2' => 42, 'required' => 'value']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testArrayAccessGetFailsOutsideResolve() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default', 0); | ||
|  | 
 | ||
|  |         $this->resolver['default']; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testArrayAccessExistsFailsOutsideResolve() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default', 0); | ||
|  | 
 | ||
|  |         isset($this->resolver['default']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testArrayAccessSetNotSupported() | ||
|  |     { | ||
|  |         $this->resolver['default'] = 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testArrayAccessUnsetNotSupported() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default', 0); | ||
|  | 
 | ||
|  |         unset($this->resolver['default']); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException | ||
|  |      * @expectedExceptionMessage The option "undefined" does not exist. Defined options are: "foo", "lazy". | ||
|  |      */ | ||
|  |     public function testFailIfGetNonExisting() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy', function (Options $options) { | ||
|  |             $options['undefined']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException | ||
|  |      * @expectedExceptionMessage The optional option "defined" has no value set. You should make sure it is set with "isset" before reading it. | ||
|  |      */ | ||
|  |     public function testFailIfGetDefinedButUnset() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('defined'); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy', function (Options $options) { | ||
|  |             $options['defined']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailIfCyclicDependency() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('lazy1', function (Options $options) { | ||
|  |             $options['lazy2']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy2', function (Options $options) { | ||
|  |             $options['lazy1']; | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testCount() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('default', 0); | ||
|  |         $this->resolver->setRequired('required'); | ||
|  |         $this->resolver->setDefined('defined'); | ||
|  |         $this->resolver->setDefault('lazy1', function () {}); | ||
|  | 
 | ||
|  |         $this->resolver->setDefault('lazy2', function (Options $options) { | ||
|  |             Assert::assertCount(4, $options); | ||
|  |         }); | ||
|  | 
 | ||
|  |         $this->assertCount(4, $this->resolver->resolve(['required' => 'value'])); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * In resolve() we count the options that are actually set (which may be | ||
|  |      * only a subset of the defined options). Outside of resolve(), it's not | ||
|  |      * clear what is counted. | ||
|  |      * | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException | ||
|  |      */ | ||
|  |     public function testCountFailsOutsideResolve() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('foo', 0); | ||
|  |         $this->resolver->setRequired('bar'); | ||
|  |         $this->resolver->setDefined('bar'); | ||
|  |         $this->resolver->setDefault('lazy1', function () {}); | ||
|  | 
 | ||
|  |         \count($this->resolver); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNestedArrays() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[][]'); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'foo' => [ | ||
|  |                 [ | ||
|  |                     1, 2, | ||
|  |                 ], | ||
|  |             ], | ||
|  |         ], $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [1, 2], | ||
|  |             ], | ||
|  |         ])); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNested2Arrays() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[][][][]'); | ||
|  | 
 | ||
|  |         $this->assertEquals([ | ||
|  |             'foo' => [ | ||
|  |                 [ | ||
|  |                     [ | ||
|  |                         [ | ||
|  |                             1, 2, | ||
|  |                         ], | ||
|  |                     ], | ||
|  |                 ], | ||
|  |             ], | ||
|  |         ], $this->resolver->resolve( | ||
|  |             [ | ||
|  |                 'foo' => [ | ||
|  |                     [ | ||
|  |                         [ | ||
|  |                             [1, 2], | ||
|  |                         ], | ||
|  |                     ], | ||
|  |                 ], | ||
|  |             ] | ||
|  |         )); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]". | ||
|  |      */ | ||
|  |     public function testNestedArraysException() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'float[][][][]'); | ||
|  | 
 | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [ | ||
|  |                     [ | ||
|  |                         [1, 2], | ||
|  |                     ], | ||
|  |                 ], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". | ||
|  |      */ | ||
|  |     public function testNestedArrayException1() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[][]'); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [1, true, 'str', [2, 3]], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". | ||
|  |      */ | ||
|  |     public function testNestedArrayException2() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'int[][]'); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [true, 'str', [2, 3]], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]". | ||
|  |      */ | ||
|  |     public function testNestedArrayException3() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string[][][]'); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 ['str', [1, 2]], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]". | ||
|  |      */ | ||
|  |     public function testNestedArrayException4() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string[][][]'); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [ | ||
|  |                     ['str'], [1, 2], ], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]". | ||
|  |      */ | ||
|  |     public function testNestedArrayException5() | ||
|  |     { | ||
|  |         $this->resolver->setDefined('foo'); | ||
|  |         $this->resolver->setAllowedTypes('foo', 'string[]'); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'foo' => [ | ||
|  |                 [ | ||
|  |                     ['str'], [1, 2], ], | ||
|  |             ], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testIsNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefined(['host', 'port']); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->assertTrue($this->resolver->isNested('database')); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException | ||
|  |      * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "host", "port". | ||
|  |      */ | ||
|  |     public function testFailsIfUndefinedNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefined(['host', 'port']); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'database' => ['foo' => 'bar'], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException | ||
|  |      * @expectedExceptionMessage The required option "host" is missing. | ||
|  |      */ | ||
|  |     public function testFailsIfMissingRequiredNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setRequired('host'); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'database' => [], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The option "logging" with value null is expected to be of type "bool", but is of type "NULL". | ||
|  |      */ | ||
|  |     public function testFailsIfInvalidTypeNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined('logging') | ||
|  |                     ->setAllowedTypes('logging', 'bool'); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'database' => ['logging' => null], | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException | ||
|  |      * @expectedExceptionMessage The nested option "database" with value null is expected to be of type array, but is of type "NULL". | ||
|  |      */ | ||
|  |     public function testFailsIfNotArrayIsGivenForNestedOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefined('host'); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->resolver->resolve([ | ||
|  |             'database' => null, | ||
|  |         ]); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveNestedOptionsWithoutDefault() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefined(['host', 'port']); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve(); | ||
|  |         $expectedOptions = [ | ||
|  |             'name' => 'default', | ||
|  |             'database' => [], | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveNestedOptionsWithDefault() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefaults([ | ||
|  |                     'host' => 'localhost', | ||
|  |                     'port' => 3306, | ||
|  |                 ]); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve(); | ||
|  |         $expectedOptions = [ | ||
|  |             'name' => 'default', | ||
|  |             'database' => [ | ||
|  |                 'host' => 'localhost', | ||
|  |                 'port' => 3306, | ||
|  |             ], | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveMultipleNestedOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'name' => 'default', | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver | ||
|  |                     ->setRequired(['dbname', 'host']) | ||
|  |                     ->setDefaults([ | ||
|  |                         'port' => 3306, | ||
|  |                         'replicas' => function (OptionsResolver $resolver) { | ||
|  |                             $resolver->setDefaults([ | ||
|  |                                 'host' => 'replica1', | ||
|  |                                 'port' => 3306, | ||
|  |                             ]); | ||
|  |                         }, | ||
|  |                     ]); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve([ | ||
|  |             'name' => 'custom', | ||
|  |             'database' => [ | ||
|  |                 'dbname' => 'test', | ||
|  |                 'host' => 'localhost', | ||
|  |                 'port' => null, | ||
|  |                 'replicas' => ['host' => 'replica2'], | ||
|  |             ], | ||
|  |         ]); | ||
|  |         $expectedOptions = [ | ||
|  |             'name' => 'custom', | ||
|  |             'database' => [ | ||
|  |                 'port' => null, | ||
|  |                 'replicas' => ['port' => 3306, 'host' => 'replica2'], | ||
|  |                 'dbname' => 'test', | ||
|  |                 'host' => 'localhost', | ||
|  |             ], | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveLazyOptionUsingNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'version' => function (Options $options) { | ||
|  |                 return $options['database']['server_version']; | ||
|  |             }, | ||
|  |             'database' => function (OptionsResolver $resolver) { | ||
|  |                 $resolver->setDefault('server_version', '3.15'); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve(); | ||
|  |         $expectedOptions = [ | ||
|  |             'database' => ['server_version' => '3.15'], | ||
|  |             'version' => '3.15', | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizeNestedOptionValue() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefaults([ | ||
|  |                 'database' => function (OptionsResolver $resolver) { | ||
|  |                     $resolver->setDefaults([ | ||
|  |                         'port' => 3306, | ||
|  |                         'host' => 'localhost', | ||
|  |                         'dbname' => 'demo', | ||
|  |                     ]); | ||
|  |                 }, | ||
|  |             ]) | ||
|  |             ->setNormalizer('database', function (Options $options, $value) { | ||
|  |                 ksort($value); | ||
|  | 
 | ||
|  |                 return $value; | ||
|  |             }); | ||
|  |         $actualOptions = $this->resolver->resolve([ | ||
|  |             'database' => ['dbname' => 'test'], | ||
|  |         ]); | ||
|  |         $expectedOptions = [ | ||
|  |             'database' => ['dbname' => 'test', 'host' => 'localhost', 'port' => 3306], | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testOverwrittenNestedOptionNotEvaluatedIfLazyDefault() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             return 'lazy'; | ||
|  |         }); | ||
|  |         $this->assertSame(['foo' => 'lazy'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testOverwrittenNestedOptionNotEvaluatedIfScalarDefault() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', 'bar'); | ||
|  |         $this->assertSame(['foo' => 'bar'], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testOverwrittenLazyOptionNotEvaluatedIfNestedOption() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (Options $options) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             $resolver->setDefault('bar', 'baz'); | ||
|  |         }); | ||
|  |         $this->assertSame(['foo' => ['bar' => 'baz']], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveAllNestedOptionDefinitions() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             $resolver->setRequired('bar'); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             $resolver->setDefault('bar', 'baz'); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             $resolver->setDefault('ping', 'pong'); | ||
|  |         }); | ||
|  |         $this->assertSame(['foo' => ['ping' => 'pong', 'bar' => 'baz']], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNormalizeNestedValue() | ||
|  |     { | ||
|  |         // defined by superclass
 | ||
|  |         $this->resolver->setDefault('foo', function (OptionsResolver $resolver) { | ||
|  |             $resolver->setDefault('bar', null); | ||
|  |         }); | ||
|  |         // defined by subclass
 | ||
|  |         $this->resolver->setNormalizer('foo', function (Options $options, $resolvedValue) { | ||
|  |             if (null === $resolvedValue['bar']) { | ||
|  |                 $resolvedValue['bar'] = 'baz'; | ||
|  |             } | ||
|  | 
 | ||
|  |             return $resolvedValue; | ||
|  |         }); | ||
|  |         $this->assertSame(['foo' => ['bar' => 'baz']], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailsIfCyclicDependencyBetweenSameNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) { | ||
|  |             $resolver->setDefault('replicas', $parent['database']); | ||
|  |         }); | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailsIfCyclicDependencyBetweenNestedOptionAndParentLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'version' => function (Options $options) { | ||
|  |                 return $options['database']['server_version']; | ||
|  |             }, | ||
|  |             'database' => function (OptionsResolver $resolver, Options $parent) { | ||
|  |                 $resolver->setDefault('server_version', $parent['version']); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailsIfCyclicDependencyBetweenNormalizerAndNestedOption() | ||
|  |     { | ||
|  |         $this->resolver | ||
|  |             ->setDefault('name', 'default') | ||
|  |             ->setDefault('database', function (OptionsResolver $resolver, Options $parent) { | ||
|  |                 $resolver->setDefault('host', $parent['name']); | ||
|  |             }) | ||
|  |             ->setNormalizer('name', function (Options $options, $value) { | ||
|  |                 $options['database']; | ||
|  |             }); | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException | ||
|  |      */ | ||
|  |     public function testFailsIfCyclicDependencyBetweenNestedOptions() | ||
|  |     { | ||
|  |         $this->resolver->setDefault('database', function (OptionsResolver $resolver, Options $parent) { | ||
|  |             $resolver->setDefault('host', $parent['replica']['host']); | ||
|  |         }); | ||
|  |         $this->resolver->setDefault('replica', function (OptionsResolver $resolver, Options $parent) { | ||
|  |             $resolver->setDefault('host', $parent['database']['host']); | ||
|  |         }); | ||
|  |         $this->resolver->resolve(); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testGetAccessToParentOptionFromNestedOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'version' => 3.15, | ||
|  |             'database' => function (OptionsResolver $resolver, Options $parent) { | ||
|  |                 $resolver->setDefault('server_version', $parent['version']); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $this->assertSame(['version' => 3.15, 'database' => ['server_version' => 3.15]], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNestedClosureWithoutTypeHintNotInvoked() | ||
|  |     { | ||
|  |         $closure = function ($resolver) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }; | ||
|  |         $this->resolver->setDefault('foo', $closure); | ||
|  |         $this->assertSame(['foo' => $closure], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testNestedClosureWithoutTypeHint2ndArgumentNotInvoked() | ||
|  |     { | ||
|  |         $closure = function (OptionsResolver $resolver, $parent) { | ||
|  |             Assert::fail('Should not be called'); | ||
|  |         }; | ||
|  |         $this->resolver->setDefault('foo', $closure); | ||
|  |         $this->assertSame(['foo' => $closure], $this->resolver->resolve()); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testResolveLazyOptionWithTransitiveDefaultDependency() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'ip' => null, | ||
|  |             'database' => function (OptionsResolver $resolver, Options $parent) { | ||
|  |                 $resolver->setDefault('host', $parent['ip']); | ||
|  |                 $resolver->setDefault('primary_replica', function (OptionsResolver $resolver, Options $parent) { | ||
|  |                     $resolver->setDefault('host', $parent['host']); | ||
|  |                 }); | ||
|  |             }, | ||
|  |             'secondary_replica' => function (Options $options) { | ||
|  |                 return $options['database']['primary_replica']['host']; | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve(['ip' => '127.0.0.1']); | ||
|  |         $expectedOptions = [ | ||
|  |             'ip' => '127.0.0.1', | ||
|  |             'database' => [ | ||
|  |                 'host' => '127.0.0.1', | ||
|  |                 'primary_replica' => ['host' => '127.0.0.1'], | ||
|  |             ], | ||
|  |             'secondary_replica' => '127.0.0.1', | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function testAccessToParentOptionFromNestedNormalizerAndLazyOption() | ||
|  |     { | ||
|  |         $this->resolver->setDefaults([ | ||
|  |             'debug' => true, | ||
|  |             'database' => function (OptionsResolver $resolver, Options $parent) { | ||
|  |                 $resolver | ||
|  |                     ->setDefined('logging') | ||
|  |                     ->setDefault('profiling', function (Options $options) use ($parent) { | ||
|  |                         return $parent['debug']; | ||
|  |                     }) | ||
|  |                     ->setNormalizer('logging', function (Options $options, $value) use ($parent) { | ||
|  |                         return false === $parent['debug'] ? true : $value; | ||
|  |                     }); | ||
|  |             }, | ||
|  |         ]); | ||
|  |         $actualOptions = $this->resolver->resolve([ | ||
|  |             'debug' => false, | ||
|  |             'database' => ['logging' => false], | ||
|  |         ]); | ||
|  |         $expectedOptions = [ | ||
|  |             'debug' => false, | ||
|  |             'database' => ['profiling' => false, 'logging' => true], | ||
|  |         ]; | ||
|  |         $this->assertSame($expectedOptions, $actualOptions); | ||
|  |     } | ||
|  | } |