From 3a5e84f4a7eab00a9ea624eaa573bfe0d8b975c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20H=C3=A9rault?= Date: Sat, 28 Apr 2012 16:29:28 -0500 Subject: [PATCH] [Validator] Add CollectionSize constraint [Validator] Rename constraint Size to Range [Validator] Rename constraint CollectionSize to Size [Validator] Merge the SizeLength into the Size constraint [Validator] Update messages in Size constraint for consistancy [Validator] Add english and french translation for Size messages [Validator] Tweak expected types for exceptions in SizeValidator [Validator] Fix CS in SizeValidator [Validator] Update the ValidatorTypeGuesser [Validator] Tweak SizeValidator [Validator] Update CHANGELOG [Validator] Complete previous CHANGELOG updates [Form] Update validator type guesser [Validator] Pluralize collection size english messages [Validator] Pluralize Size french messages --- .../Resources/translations/validators.en.xlf | 12 ++ .../Resources/translations/validators.fr.xlf | 18 +- .../Validator/ValidatorTypeGuesser.php | 41 +++- src/Symfony/Component/Validator/CHANGELOG.md | 4 +- .../Constraints/{SizeLength.php => Range.php} | 9 +- ...LengthValidator.php => RangeValidator.php} | 38 ++-- .../Component/Validator/Constraints/Size.php | 69 +++++- .../Validator/Constraints/SizeValidator.php | 153 +++++++++++--- .../Tests/Constraints/RangeValidatorTest.php | 121 +++++++++++ .../Constraints/SizeLengthValidatorTest.php | 172 --------------- .../Validator/Tests/Constraints/SizeTest.php | 98 +++++++++ .../Tests/Constraints/SizeValidatorTest.php | 199 +++++++++++++----- 12 files changed, 633 insertions(+), 301 deletions(-) rename src/Symfony/Component/Validator/Constraints/{SizeLength.php => Range.php} (60%) rename src/Symfony/Component/Validator/Constraints/{SizeLengthValidator.php => RangeValidator.php} (51%) create mode 100644 src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php delete mode 100644 src/Symfony/Component/Validator/Tests/Constraints/SizeLengthValidatorTest.php create mode 100644 src/Symfony/Component/Validator/Tests/Constraints/SizeTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.en.xlf b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.en.xlf index a59fe1f6f82d..6b06b941c799 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.en.xlf +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.en.xlf @@ -214,6 +214,18 @@ A PHP extension caused the upload to fail. A PHP extension caused the upload to fail. + + This collection should contain {{ limit }} elements or more. + This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. + + + This collection should contain {{ limit }} elements or less. + This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. + + + This collection should contain exactly {{ limit }} elements. + This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.fr.xlf b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.fr.xlf index cf3b24187e9f..dd0beb0db589 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.fr.xlf +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/translations/validators.fr.xlf @@ -76,7 +76,7 @@ This value is too long. It should have {{ limit }} characters or less. - Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractères. + Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractère.|Cette chaine est trop longue. Elle doit avoir au maximum {{ limit }} caractères. This value should be {{ limit }} or more. @@ -84,7 +84,7 @@ This value is too short. It should have {{ limit }} characters or more. - Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractères. + Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractère.|Cette chaine est trop courte. Elle doit avoir au minimum {{ limit }} caractères. This value should not be blank. @@ -192,7 +192,7 @@ This value should have exactly {{ limit }} characters. - Cette chaine doit avoir exactement {{ limit }} caractères. + Cette chaine doit avoir exactement {{ limit }} caractère.|Cette chaine doit avoir exactement {{ limit }} caractères. The file was only partially uploaded. @@ -214,6 +214,18 @@ A PHP extension caused the upload to fail. Une extension PHP a empêché le transfert du fichier. + + This collection should contain {{ limit }} elements or more. + Cette collection doit contenir {{ limit }} élément ou plus.|Cette collection doit contenir {{ limit }} éléments ou plus. + + + This collection should contain {{ limit }} elements or less. + Cette collection doit contenir {{ limit }} élément ou moins.|Cette collection doit contenir {{ limit }} éléments ou moins. + + + This collection should contain exactly {{ limit }} elements. + Cette collection doit contenir exactement {{ limit }} élément.|Cette collection doit contenir exactement {{ limit }} éléments. + diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php index 27c59fa06366..b412b43f6eab 100755 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php @@ -155,11 +155,19 @@ public function guessTypeForConstraint(Constraint $constraint) case 'Symfony\Component\Validator\Constraints\MaxLength': case 'Symfony\Component\Validator\Constraints\MinLength': case 'Symfony\Component\Validator\Constraints\Regex': - case 'Symfony\Component\Validator\Constraints\SizeLength': return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); - case 'Symfony\Component\Validator\Constraints\Min': case 'Symfony\Component\Validator\Constraints\Size': + switch ($constraint->type) { + case 'string': + return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE); + case 'collection': + return new TypeGuess('collection', array(), Guess::MEDIUM_CONFIDENCE); + } + break; + + case 'Symfony\Component\Validator\Constraints\Min': + case 'Symfony\Component\Validator\Constraints\Range': case 'Symfony\Component\Validator\Constraints\Max': return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE); } @@ -194,8 +202,11 @@ public function guessMaxLengthForConstraint(Constraint $constraint) case 'Symfony\Component\Validator\Constraints\MaxLength': return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE); - case 'Symfony\Component\Validator\Constraints\SizeLength': - return new ValueGuess($constraint->max, Guess::HIGH_CONFIDENCE); + case 'Symfony\Component\Validator\Constraints\Size': + if ('string' === $constraint->type && null !== $constraint->max) { + return new ValueGuess($constraint->max, Guess::HIGH_CONFIDENCE); + } + break; case 'Symfony\Component\Validator\Constraints\Type': if (in_array($constraint->type, array('double', 'float', 'numeric', 'real'))) { @@ -206,7 +217,7 @@ public function guessMaxLengthForConstraint(Constraint $constraint) case 'Symfony\Component\Validator\Constraints\Max': return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE); - case 'Symfony\Component\Validator\Constraints\Size': + case 'Symfony\Component\Validator\Constraints\Range': return new ValueGuess(strlen((string) $constraint->max), Guess::LOW_CONFIDENCE); } } @@ -224,7 +235,23 @@ public function guessPatternForConstraint(Constraint $constraint) case 'Symfony\Component\Validator\Constraints\MinLength': return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE); - case 'Symfony\Component\Validator\Constraints\SizeLength': + case 'Symfony\Component\Validator\Constraints\Size': + if ('string' !== $constraint->type) { + return; + } + + if ($constraint->min === $constraint->max) { + return new ValueGuess(sprintf('.{%s}', (string) $constraint->min), Guess::LOW_CONFIDENCE); + } + + if (null === $constraint->min) { + return new ValueGuess(sprintf('.{0,%s}', (string) $constraint->max), Guess::LOW_CONFIDENCE); + } + + if (null === $constraint->max) { + return new ValueGuess(sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE); + } + return new ValueGuess(sprintf('.{%s,%s}', (string) $constraint->min, (string) $constraint->max), Guess::LOW_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\Regex': @@ -233,7 +260,7 @@ public function guessPatternForConstraint(Constraint $constraint) case 'Symfony\Component\Validator\Constraints\Min': return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE); - case 'Symfony\Component\Validator\Constraints\Size': + case 'Symfony\Component\Validator\Constraints\Range': return new ValueGuess(sprintf('.{%s,%s}', strlen((string) $constraint->min), strlen((string) $constraint->max)), Guess::LOW_CONFIDENCE); case 'Symfony\Component\Validator\Constraints\Type': diff --git a/src/Symfony/Component/Validator/CHANGELOG.md b/src/Symfony/Component/Validator/CHANGELOG.md index 3566cdd28f41..34cc3061c198 100644 --- a/src/Symfony/Component/Validator/CHANGELOG.md +++ b/src/Symfony/Component/Validator/CHANGELOG.md @@ -5,8 +5,8 @@ CHANGELOG ----- * added support for `ctype_*` assertions in `TypeValidator` - * added a Size validator - * added a SizeLength validator + * added a Range validator for numeric values + * added a Size validator for string & collections * improved the ImageValidator with min width, max width, min height, and max height constraints * added support for MIME with wildcard in FileValidator * changed Collection validator to add "missing" and "extra" errors to diff --git a/src/Symfony/Component/Validator/Constraints/SizeLength.php b/src/Symfony/Component/Validator/Constraints/Range.php similarity index 60% rename from src/Symfony/Component/Validator/Constraints/SizeLength.php rename to src/Symfony/Component/Validator/Constraints/Range.php index 3704c66a2a1f..849cd952c207 100644 --- a/src/Symfony/Component/Validator/Constraints/SizeLength.php +++ b/src/Symfony/Component/Validator/Constraints/Range.php @@ -18,14 +18,13 @@ * * @api */ -class SizeLength extends Constraint +class Range extends Constraint { - public $minMessage = 'This value is too short. It should have {{ limit }} characters or more.'; - public $maxMessage = 'This value is too long. It should have {{ limit }} characters or less.'; - public $exactMessage = 'This value should have exactly {{ limit }} characters.'; + public $minMessage = 'This value should be {{ limit }} or more.'; + public $maxMessage = 'This value should be {{ limit }} or less.'; + public $invalidMessage = 'This value should be a valid number.'; public $min; public $max; - public $charset = 'UTF-8'; /** * {@inheritDoc} diff --git a/src/Symfony/Component/Validator/Constraints/SizeLengthValidator.php b/src/Symfony/Component/Validator/Constraints/RangeValidator.php similarity index 51% rename from src/Symfony/Component/Validator/Constraints/SizeLengthValidator.php rename to src/Symfony/Component/Validator/Constraints/RangeValidator.php index 04808f127bc9..32b3cf4cd9e1 100644 --- a/src/Symfony/Component/Validator/Constraints/SizeLengthValidator.php +++ b/src/Symfony/Component/Validator/Constraints/RangeValidator.php @@ -13,12 +13,13 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; /** + * @author Bernhard Schussek + * * @api */ -class SizeLengthValidator extends ConstraintValidator +class RangeValidator extends ConstraintValidator { /** * Checks if the passed value is valid. @@ -26,51 +27,38 @@ class SizeLengthValidator extends ConstraintValidator * @param mixed $value The value that should be validated * @param Constraint $constraint The constraint for the validation * + * @return Boolean Whether or not the value is valid + * * @api */ public function validate($value, Constraint $constraint) { - if (null === $value || '' === $value) { + if (null === $value) { return; } - if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { - throw new UnexpectedTypeException($value, 'string'); - } - - $value = (string) $value; - - if (function_exists('grapheme_strlen') && 'UTF-8' === $constraint->charset) { - $length = grapheme_strlen($value); - } elseif (function_exists('mb_strlen')) { - $length = mb_strlen($value, $constraint->charset); - } else { - $length = strlen($value); - } - - if ($constraint->min == $constraint->max && $length != $constraint->max) { - $this->context->addViolation($constraint->exactMessage, array( + if (!is_numeric($value)) { + $this->context->addViolation($constraint->invalidMessage, array( '{{ value }}' => $value, - '{{ limit }}' => $constraint->max, - ), null, (int) $constraint->max); + )); return; } - if ($length > $constraint->max) { + if ($value > $constraint->max) { $this->context->addViolation($constraint->maxMessage, array( '{{ value }}' => $value, '{{ limit }}' => $constraint->max, - ), null, (int) $constraint->max); + )); return; } - if ($length < $constraint->min) { + if ($value < $constraint->min) { $this->context->addViolation($constraint->minMessage, array( '{{ value }}' => $value, '{{ limit }}' => $constraint->min, - ), null, (int) $constraint->min); + )); } } } diff --git a/src/Symfony/Component/Validator/Constraints/Size.php b/src/Symfony/Component/Validator/Constraints/Size.php index 627f5a9f6ea6..d60dbaa4b9c2 100644 --- a/src/Symfony/Component/Validator/Constraints/Size.php +++ b/src/Symfony/Component/Validator/Constraints/Size.php @@ -20,17 +20,70 @@ */ class Size extends Constraint { - public $minMessage = 'This value should be {{ limit }} or more.'; - public $maxMessage = 'This value should be {{ limit }} or less.'; - public $invalidMessage = 'This value should be a valid number.'; + const TYPE_STRING = 'string'; + const TYPE_COLLECTION = 'collection'; + + public $minMessage; + public $maxMessage; + public $exactMessage; + public $type; public $min; public $max; + public $charset = 'UTF-8'; + + private $stringMinMessage = 'This value is too short. It should have {{ limit }} characters or more.'; + private $stringMaxMessage = 'This value is too long. It should have {{ limit }} characters or less.'; + private $stringExactMessage = 'This value should have exactly {{ limit }} characters.'; + + private $collectionMinMessage = 'This collection should contain {{ limit }} elements or more.'; + private $collectionMaxMessage = 'This collection should contain {{ limit }} elements or less.'; + private $collectionExactMessage = 'This collection should contain exactly {{ limit }} elements.'; + + public function getMinMessage($type) + { + if (null !== $this->minMessage) { + return $this->minMessage; + } + + switch ($type) { + case static::TYPE_STRING: + return $this->stringMinMessage; + case static::TYPE_COLLECTION: + return $this->collectionMinMessage; + default: + throw new \InvalidArgumentException('Invalid type specified.'); + } + } + + public function getMaxMessage($type) + { + if (null !== $this->maxMessage) { + return $this->maxMessage; + } - /** - * {@inheritDoc} - */ - public function getRequiredOptions() + switch ($type) { + case static::TYPE_STRING: + return $this->stringMaxMessage; + case static::TYPE_COLLECTION: + return $this->collectionMaxMessage; + default: + throw new \InvalidArgumentException('Invalid type specified.'); + } + } + + public function getExactMessage($type) { - return array('min', 'max'); + if (null !== $this->exactMessage) { + return $this->exactMessage; + } + + switch ($type) { + case static::TYPE_STRING: + return $this->stringExactMessage; + case static::TYPE_COLLECTION: + return $this->collectionExactMessage; + default: + throw new \InvalidArgumentException('Invalid type specified.'); + } } } diff --git a/src/Symfony/Component/Validator/Constraints/SizeValidator.php b/src/Symfony/Component/Validator/Constraints/SizeValidator.php index 7ccbed737c5e..842d3ef016e3 100644 --- a/src/Symfony/Component/Validator/Constraints/SizeValidator.php +++ b/src/Symfony/Component/Validator/Constraints/SizeValidator.php @@ -13,52 +13,151 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; -/** - * @author Bernhard Schussek - * - * @api - */ class SizeValidator extends ConstraintValidator { /** - * Checks if the passed value is valid. - * - * @param mixed $value The value that should be validated - * @param Constraint $constraint The constraint for the validation - * - * @return Boolean Whether or not the value is valid - * - * @api + * {@inheritDoc} */ public function validate($value, Constraint $constraint) + { + if (null === $constraint->min && null === $constraint->max) { + throw new ConstraintDefinitionException( + 'Either "min" or "max" must be specified on constraint Size' + ); + } + + if (null === $constraint->type) { + $type = $this->guessType($value); + } else { + $type = $constraint->type; + } + + switch ($type) { + case Size::TYPE_STRING: + return $this->validateString($value, $constraint); + case Size::TYPE_COLLECTION: + return $this->validateCollection($value, $constraint); + default: + throw new ConstraintDefinitionException(sprintf( + 'The "type" on constraint Size must be either "%s" or "%s", "%s" given.', + Size::TYPE_STRING, + Size::TYPE_COLLECTION, + $type + )); + } + + } + + private function validateString($value, Constraint $constraint) + { + if (null === $value || '' === $value) { + return; + } + + if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string, scalar or object with __toString()'); + } + + $value = (string) $value; + + if (function_exists('grapheme_strlen') && 'UTF-8' === $constraint->charset) { + $length = grapheme_strlen($value); + } elseif (function_exists('mb_strlen')) { + $length = mb_strlen($value, $constraint->charset); + } else { + $length = strlen($value); + } + + $this->validateSize( + $constraint, + $length, + Size::TYPE_STRING, + array('{{ value }}' => $value) + ); + } + + private function validateCollection($value, Constraint $constraint) { if (null === $value) { return; } - if (!is_numeric($value)) { - $this->context->addViolation($constraint->invalidMessage, array( - '{{ value }}' => $value, - )); + if (!is_array($value) && !$value instanceof \Countable) { + throw new UnexpectedTypeException($value, 'array or Countable'); + } + + $count = count($value); + + $this->validateSize( + $constraint, + $count, + Size::TYPE_COLLECTION, + array('{{ count }}' => $count) + ); + } + + private function validateSize(Constraint $constraint, $size, $type, array $parameters) + { + if ($constraint->min == $constraint->max && $size != $constraint->max) { + $this->context->addViolation( + $constraint->getExactMessage($type), + array_merge(array('{{ limit }}' => $constraint->max), $parameters), + null, + (int) $constraint->max + ); return; } - if ($value > $constraint->max) { - $this->context->addViolation($constraint->maxMessage, array( - '{{ value }}' => $value, - '{{ limit }}' => $constraint->max, - )); + if (null !== $constraint->max && $size > $constraint->max) { + $this->context->addViolation( + $constraint->getMaxMessage($type), + array_merge(array('{{ limit }}' => $constraint->max), $parameters), + null, + (int) $constraint->max + ); return; } - if ($value < $constraint->min) { - $this->context->addViolation($constraint->minMessage, array( - '{{ value }}' => $value, - '{{ limit }}' => $constraint->min, - )); + if (null !== $constraint->min && $size < $constraint->min) { + $this->context->addViolation( + $constraint->getMinMessage($type), + array_merge(array('{{ limit }}' => $constraint->min), $parameters), + null, + (int) $constraint->min + ); } } + + private function guessType($value) + { + if (null === $value || is_scalar($value)) { + return Size::TYPE_STRING; + } + + if (is_object($value) && method_exists($value, '__toString')) { + if ($value instanceof \Countable) { + throw new \RuntimeException( + 'The "type" must be specified on constraint Size because the '. + 'validator is not able to guess it since the value is an object '. + 'implementing both the __toString() method and the Countable '. + 'interface.' + ); + } + + return Size::TYPE_STRING; + } + + if (is_array($value) || $value instanceof \Countable) { + return Size::TYPE_COLLECTION; + } + + throw new UnexpectedTypeException( + $value, 'scalar, string, array, Countable or object with __toString()' + ); + } } diff --git a/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php new file mode 100644 index 000000000000..2b202782eb3e --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/RangeValidatorTest.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints; + +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\RangeValidator; + +class RangeValidatorTest extends \PHPUnit_Framework_TestCase +{ + protected $context; + protected $validator; + + protected function setUp() + { + $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false); + $this->validator = new RangeValidator(); + $this->validator->initialize($this->context); + } + + public function testNullIsValid() + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate(null, new Range(array('min' => 10, 'max' => 20))); + } + + /** + * @dataProvider getValidValues + */ + public function testValidValues($value) + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $constraint = new Range(array('min' => 10, 'max' => 20)); + $this->validator->validate($value, $constraint); + } + + public function getValidValues() + { + return array( + array(10.00001), + array(19.99999), + array('10.00001'), + array('19.99999'), + array(10), + array(20), + array(10.0), + array(20.0), + ); + } + + /** + * @dataProvider getInvalidValues + */ + public function testInvalidValues($value) + { + $this->context->expects($this->once()) + ->method('addViolation'); + + $constraint = new Range(array('min' => 10, 'max' => 20)); + $this->validator->validate($value, $constraint); + } + + public function getInvalidValues() + { + return array( + array(9.999999), + array(20.000001), + array('9.999999'), + array('20.000001'), + array(new \stdClass()), + ); + } + + public function testMinMessageIsSet() + { + $constraint = new Range(array( + 'min' => 10, + 'max' => 20, + 'minMessage' => 'myMessage', + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => 9, + '{{ limit }}' => 10, + )); + + $this->validator->validate(9, $constraint); + } + + public function testMaxMessageIsSet() + { + $constraint = new Range(array( + 'min' => 10, + 'max' => 20, + 'maxMessage' => 'myMessage', + )); + + $this->context->expects($this->once()) + ->method('addViolation') + ->with('myMessage', array( + '{{ value }}' => 21, + '{{ limit }}' => 20, + )); + + $this->validator->validate(21, $constraint); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/SizeLengthValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/SizeLengthValidatorTest.php deleted file mode 100644 index eddf9475cf59..000000000000 --- a/src/Symfony/Component/Validator/Tests/Constraints/SizeLengthValidatorTest.php +++ /dev/null @@ -1,172 +0,0 @@ - -* -* For the full copyright and license information, please view the LICENSE -* file that was distributed with this source code. -*/ - -namespace Symfony\Component\Validator\Tests\Constraints; - -use Symfony\Component\Validator\Constraints\SizeLength; -use Symfony\Component\Validator\Constraints\SizeLengthValidator; - -class SizeLengthValidatorTest extends \PHPUnit_Framework_TestCase -{ - protected $context; - protected $validator; - - protected function setUp() - { - $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false); - $this->validator = new SizeLengthValidator(); - $this->validator->initialize($this->context); - } - - protected function tearDown() - { - $this->context = null; - $this->validator = null; - } - - public function testNullIsValid() - { - $this->context->expects($this->never()) - ->method('addViolation'); - - $this->validator->validate(null, new SizeLength(array('min' => 6, 'max' => 10))); - } - - public function testEmptyStringIsValid() - { - $this->context->expects($this->never()) - ->method('addViolation'); - - $this->validator->validate('', new SizeLength(array('min' => 6, 'max' => 10))); - } - - /** - * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException - */ - public function testExpectsStringCompatibleType() - { - $this->validator->validate(new \stdClass(), new SizeLength(array('min' => 6, 'max' => 10))); - } - - /** - * @dataProvider getValidValues - */ - public function testValidValues($value, $mbOnly = false) - { - if ($mbOnly && !function_exists('mb_strlen')) { - return $this->markTestSkipped('mb_strlen does not exist'); - } - - $this->context->expects($this->never()) - ->method('addViolation'); - - $constraint = new SizeLength(array('min' => 6, 'max' => 10)); - $this->validator->validate($value, $constraint); - } - - public function getValidValues() - { - return array( - array(123456), - array(1234567890), - array('123456'), - array('1234567890'), - array('üüüüüü', true), - array('üüüüüüüüüü', true), - array('éééééé', true), - array('éééééééééé', true), - ); - } - - /** - * @dataProvider getInvalidValues - */ - public function testInvalidValues($value, $mbOnly = false) - { - if ($mbOnly && !function_exists('mb_strlen')) { - return $this->markTestSkipped('mb_strlen does not exist'); - } - - $this->context->expects($this->once()) - ->method('addViolation'); - - $constraint = new SizeLength(array('min' => 6, 'max' => 10)); - $this->validator->validate($value, $constraint); - } - - public function getInvalidValues() - { - return array( - array(12345), - array(12345678901), - array('12345'), - array('12345678901'), - array('üüüüü', true), - array('üüüüüüüüüüü', true), - array('ééééé', true), - array('ééééééééééé', true), - ); - } - - public function testMinMessageIsSet() - { - $constraint = new SizeLength(array( - 'min' => 5, - 'max' => 10, - 'minMessage' => 'myMessage', - )); - - $this->context->expects($this->once()) - ->method('addViolation') - ->with('myMessage', array( - '{{ value }}' => '1234', - '{{ limit }}' => 5, - ), null, 5); - - $this->validator->validate('1234', $constraint); - } - - public function testMaxMessageIsSet() - { - $constraint = new SizeLength(array( - 'min' => 5, - 'max' => 10, - 'maxMessage' => 'myMessage', - )); - - $this->context->expects($this->once()) - ->method('addViolation') - ->with('myMessage', array( - '{{ value }}' => '12345678901', - '{{ limit }}' => 10, - ), null, 10); - - $this->validator->validate('12345678901', $constraint); - } - - public function testExactMessageIsSet() - { - $constraint = new SizeLength(array( - 'min' => 5, - 'max' => 5, - 'exactMessage' => 'myMessage', - )); - - $this->context->expects($this->once()) - ->method('addViolation') - ->with('myMessage', array( - '{{ value }}' => '1234', - '{{ limit }}' => 5, - ), null, 5); - - $this->validator->validate('1234', $constraint); - } -} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/SizeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/SizeTest.php new file mode 100644 index 000000000000..4fb388ca4283 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/SizeTest.php @@ -0,0 +1,98 @@ +assertEquals($expected, $size->getMinMessage($type)); + } + + public function getMinMessageData() + { + $size = new Size(); + + return array( + array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringMinMessage')), + array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionMinMessage')), + array(array('minMessage' => 'Custom min message'), Size::TYPE_STRING, 'Custom min message'), + array(array('minMessage' => 'Custom min message'), Size::TYPE_COLLECTION, 'Custom min message'), + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetMinMessageWithInvalidType() + { + $size = new Size(); + $size->getMinMessage('foo'); + } + + /** + * @dataProvider getMaxMessageData + */ + public function testGetMaxMessage($options, $type, $expected) + { + $size = new Size($options); + $this->assertEquals($expected, $size->getMaxMessage($type)); + } + + public function getMaxMessageData() + { + $size = new Size(); + + return array( + array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringMaxMessage')), + array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionMaxMessage')), + array(array('maxMessage' => 'Custom max message'), Size::TYPE_STRING, 'Custom max message'), + array(array('maxMessage' => 'Custom max message'), Size::TYPE_COLLECTION, 'Custom max message'), + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetMaxMessageWithInvalidType() + { + $size = new Size(); + $size->getMaxMessage('foo'); + } + + /** + * @dataProvider getExactMessageData + */ + public function testGetExactMessage($options, $type, $expected) + { + $size = new Size($options); + $this->assertEquals($expected, $size->getExactMessage($type)); + } + + public function getExactMessageData() + { + $size = new Size(); + + return array( + array(array(), Size::TYPE_STRING, $this->readAttribute($size, 'stringExactMessage')), + array(array(), Size::TYPE_COLLECTION, $this->readAttribute($size, 'collectionExactMessage')), + array(array('exactMessage' => 'Custom exact message'), Size::TYPE_STRING, 'Custom exact message'), + array(array('exactMessage' => 'Custom exact message'), Size::TYPE_COLLECTION, 'Custom exact message'), + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetExactMessageWithInvalidType() + { + $size = new Size(); + $size->getExactMessage('foo'); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/SizeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/SizeValidatorTest.php index 5cda166f4847..24e9d19f2319 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/SizeValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/SizeValidatorTest.php @@ -14,6 +14,19 @@ use Symfony\Component\Validator\Constraints\Size; use Symfony\Component\Validator\Constraints\SizeValidator; +class SizeAmbiguous implements \Countable +{ + public function __toString() + { + return ''; + } + + public function count() + { + return 0; + } +} + class SizeValidatorTest extends \PHPUnit_Framework_TestCase { protected $context; @@ -28,94 +41,176 @@ protected function setUp() public function testNullIsValid() { - $this->context->expects($this->never()) - ->method('addViolation'); + $this->context->expects($this->never())->method('addViolation'); $this->validator->validate(null, new Size(array('min' => 10, 'max' => 20))); } + public function testNullIsValidAsAString() + { + $this->context->expects($this->never())->method('addViolation'); + + $this->validator->validate(null, new Size(array('type' => 'string', 'min' => 10, 'max' => 20))); + } + + public function testNullIsValidAsACollectionCollection() + { + $this->context->expects($this->never())->method('addViolation'); + + $this->validator->validate(null, new Size(array('type' => 'collection', 'min' => 10, 'max' => 20))); + } + + public function testEmptyStringIsValid() + { + $this->context->expects($this->never())->method('addViolation'); + + $this->validator->validate('', new Size(array('min' => 10, 'max' => 20))); + } + + public function testEmptyStringIsValidAsAString() + { + $this->context->expects($this->never())->method('addViolation'); + + $this->validator->validate('', new Size(array('type' => 'string', 'min' => 10, 'max' => 20))); + } + /** - * @dataProvider getValidValues + * @dataProvider getValidStringValues */ - public function testValidValues($value) + public function testValidStringValues($value, $mbOnly = false) { + if ($mbOnly && !function_exists('mb_strlen')) { + return $this->markTestSkipped('mb_strlen does not exist'); + } + $this->context->expects($this->never()) ->method('addViolation'); - $constraint = new Size(array('min' => 10, 'max' => 20)); + $constraint = new Size(array('min' => 6, 'max' => 10)); $this->validator->validate($value, $constraint); } - public function getValidValues() + public function getValidStringValues() { return array( - array(10.00001), - array(19.99999), - array('10.00001'), - array('19.99999'), - array(10), - array(20), - array(10.0), - array(20.0), + array(123456), + array(1234567890), + array('123456'), + array('1234567890'), + array('üüüüüü', true), + array('üüüüüüüüüü', true), + array('éééééé', true), + array('éééééééééé', true), ); } /** - * @dataProvider getInvalidValues + * @dataProvider getInvalidStringValues */ - public function testInvalidValues($value) + public function testInvalidStringValues($value, $mbOnly = false) { - $this->context->expects($this->once()) - ->method('addViolation'); + if ($mbOnly && !function_exists('mb_strlen')) { + return $this->markTestSkipped('mb_strlen does not exist'); + } - $constraint = new Size(array('min' => 10, 'max' => 20)); - $this->validator->validate($value, $constraint); + $this->context->expects($this->once())->method('addViolation'); + + $this->validator->validate($value, new Size(array('min' => 6, 'max' => 10))); + } + + public function getInvalidStringValues() + { + return array( + array(12345), + array(12345678901), + array('12345'), + array('12345678901'), + array('üüüüü', true), + array('üüüüüüüüüüü', true), + array('ééééé', true), + array('ééééééééééé', true), + ); } - public function getInvalidValues() + /** + * @dataProvider getValidCollectionValues + */ + public function testValidCollectionValue($value) { + $this->context->expects($this->never())->method('addViolation'); + + $this->validator->validate($value, new Size(array('min' => 10, 'max' => 20))); + } + + public function getValidCollectionValues() + { + $countable = $this->getMock('Countable'); + $countable->expects($this->any())->method('count')->will($this->returnValue(15)); + return array( - array(9.999999), - array(20.000001), - array('9.999999'), - array('20.000001'), - array(new \stdClass()), + array($countable), + array(range(1, 15)), ); } - public function testMinMessageIsSet() + /** + * @dataProvider getInvalidCollectionValues + */ + public function testInvalidCollectionValue($value) + { + $this->context->expects($this->once())->method('addViolation'); + + $this->validator->validate($value, new Size(array('min' => 10, 'max' => 20))); + } + + public function getInvalidCollectionValues() { - $constraint = new Size(array( - 'min' => 10, - 'max' => 20, - 'minMessage' => 'myMessage', - )); + $tooSmallCountable = $this->getMock('Countable'); + $tooSmallCountable->expects($this->any())->method('count')->will($this->returnValue(5)); - $this->context->expects($this->once()) - ->method('addViolation') - ->with('myMessage', array( - '{{ value }}' => 9, - '{{ limit }}' => 10, - )); + $tooBigCountable = $this->getmock('countable'); + $tooBigCountable->expects($this->any())->method('count')->will($this->returnValue(25)); - $this->validator->validate(9, $constraint); + return array( + array($tooSmallCountable), + array($tooBigCountable), + array(array()), + array(range(1, 5)), + array(range(1, 25)), + ); } - public function testMaxMessageIsSet() + /** + * @expectedException RuntimeException + */ + public function throwsAnExceptionWhenOnAmbiguousValue() + { + $this->validator->validate(new SizeAmbiguous(), new Size(array('min' => 10, 'max' => 20))); + } + + /** + * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsEitherStringOrCollectionCompatible() + { + $this->validator->validate(new \stdCLass(), new Size(array('min' => 10, 'max' => 20))); + } + + /** + * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsStringCompatibleType() { - $constraint = new Size(array( - 'min' => 10, - 'max' => 20, - 'maxMessage' => 'myMessage', - )); + $countable = $this->getMock('Countable'); - $this->context->expects($this->once()) - ->method('addViolation') - ->with('myMessage', array( - '{{ value }}' => 21, - '{{ limit }}' => 20, - )); + $this->validator->validate($countable, new Size(array('type' => 'string', 'min' => 6, 'max' => 10))); + } - $this->validator->validate(21, $constraint); + /** + * @expectedException Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testExpectsCollectionCompatibleType() + { + $this->validator->validate('string', new Size(array('type' => 'collection', 'min' => 6, 'max' => 10))); } }