diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php index f6b919331fae..593c5115ffd3 100644 --- a/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AbstractComparisonValidator.php @@ -42,7 +42,7 @@ public function validate($value, Constraint $constraint) /** * Returns a string representation of the type of the value. * - * @param mixed $value + * @param mixed $value * * @return string */ @@ -54,7 +54,7 @@ private function valueToType($value) /** * Returns a string representation of the value. * - * @param mixed $value + * @param mixed $value * * @return string */ @@ -74,8 +74,8 @@ private function valueToString($value) /** * Compares the two given values to find if their relationship is valid * - * @param mixed $value1 The first value to compare - * @param mixed $value2 The second value to compare + * @param mixed $value1 The first value to compare + * @param mixed $value2 The second value to compare * * @return Boolean true if the relationship is valid, false otherwise */ diff --git a/src/Symfony/Component/Validator/Constraints/AbstractComposite.php b/src/Symfony/Component/Validator/Constraints/AbstractComposite.php new file mode 100644 index 000000000000..fc000fa5f166 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/AbstractComposite.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\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Exception\ConstraintDefinitionException; + +/** + * @Annotation + * + * @author Marc Morales Valldepérez + * @author Marc Morera Merino + */ +abstract class AbstractComposite extends Constraint +{ + + /** + * @var array + * + * Set of constraints + */ + public $constraints = array(); + + /** + * {@inheritDoc} + */ + public function __construct($options = null) + { + parent::__construct($options); + + if (!is_array($this->constraints)) { + $this->constraints = array($this->constraints); + } + + // We consider explicid groups are defined if are not default one + $areExplicitGroupsDefined = ( $this->groups != array(self::DEFAULT_GROUP)); + + // Each constraint contained + foreach ($this->constraints as $constraint) { + if (!$constraint instanceof Constraint) { + throw new ConstraintDefinitionException(sprintf('The value %s is not an instance of Constraint in constraint %s', $constraint, __CLASS__)); + } + + if ($constraint instanceof Valid) { + throw new ConstraintDefinitionException(sprintf('The constraint Valid cannot be nested inside constraint %s. You can only declare the Valid constraint directly on a field or method.', __CLASS__)); + } + + // If explicid groups are defined + if ($areExplicitGroupsDefined) { + /** + * If constraint has explicid groups defined + * + * In that case, the groups of the nested constraint need to be + * a subset of the groups of the outer constraint. + */ + if ($constraint->groups !== array(self::DEFAULT_GROUP)) { + + // If are not a subset + if ($constraint->groups != array_intersect($constraint->groups, $this->groups)) { + throw new ConstraintDefinitionException(sprintf('The groups defined in Constraint %s must be a subset of the groups defined in the Constraint %s', $constraint, __CLASS__)); + } + + // Otherwise, we add all defined groups here + } else { + foreach ($this->groups as $group) { + $constraint->addImplicitGroupName($group); + } + } + + /** + * Otherwise, we merge current groups with constraint + */ + } else { + $this->groups = array_unique(array_merge($this->groups, $constraint->groups)); + } + } + } + + /** + * Adds the given group if this constraint is in the Default group. + * + * Also propagate same method to nested Constraints. + * + * @param string $group + * + * @api + */ + public function addImplicitGroupName($group) + { + parent::addImplicitGroupName($group); + + foreach ($this->constraints as $constraint) { + $constraint->addImplicitGroupName($group); + } + } + + /** + * {@inheritDoc} + */ + public function getDefaultOption() + { + return 'constraints'; + } + + /** + * {@inheritDoc} + */ + public function getRequiredOptions() + { + return array('constraints'); + } +} diff --git a/src/Symfony/Component/Validator/Constraints/All.php b/src/Symfony/Component/Validator/Constraints/All.php index 537168625ca0..35efec4e29aa 100644 --- a/src/Symfony/Component/Validator/Constraints/All.php +++ b/src/Symfony/Component/Validator/Constraints/All.php @@ -20,6 +20,9 @@ * @author Bernhard Schussek * * @api + * + * @deprecated Deprecated in 2.5, to be removed in 3.0. Use + * {@link \Symfony\Component\Validator\Constraints\Each} instead. */ class All extends Constraint { @@ -56,4 +59,5 @@ public function getRequiredOptions() { return array('constraints'); } + } diff --git a/src/Symfony/Component/Validator/Constraints/AllValidator.php b/src/Symfony/Component/Validator/Constraints/AllValidator.php index c38f19a6669c..087a6f62896f 100644 --- a/src/Symfony/Component/Validator/Constraints/AllValidator.php +++ b/src/Symfony/Component/Validator/Constraints/AllValidator.php @@ -19,6 +19,9 @@ * @author Bernhard Schussek * * @api + * + * @deprecated Deprecated in 2.5, to be removed in 3.0. Use + * {@link \Symfony\Component\Validator\Constraints\EachValidator} instead. */ class AllValidator extends ConstraintValidator { diff --git a/src/Symfony/Component/Validator/Constraints/Each.php b/src/Symfony/Component/Validator/Constraints/Each.php new file mode 100644 index 000000000000..a7a775b20902 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/Each.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +/** + * @Annotation + * + * @author Marc Morera Merino + * @author Marc Morales Valldepérez * + */ +class Each extends AbstractComposite +{ +} diff --git a/src/Symfony/Component/Validator/Constraints/EachValidator.php b/src/Symfony/Component/Validator/Constraints/EachValidator.php new file mode 100644 index 000000000000..3be1350fea29 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/EachValidator.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class EachValidator extends ConstraintValidator +{ + + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Traversable) { + throw new UnexpectedTypeException($value, 'array or Traversable'); + } + + $group = $this->context->getGroup(); + + foreach ($value as $key => $element) { + foreach ($constraint->constraints as $constr) { + $this->context->validateValue($element, $constr, '[' . $key . ']', $group); + } + } + } +} diff --git a/src/Symfony/Component/Validator/Constraints/None.php b/src/Symfony/Component/Validator/Constraints/None.php new file mode 100644 index 000000000000..63cf1713dcc5 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/None.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +/** + * @Annotation + * + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class None extends AbstractComposite +{ + + /** + * @var string + * + * Message for notice Violation + */ + public $message = 'None of this collection should pass validation.'; +} diff --git a/src/Symfony/Component/Validator/Constraints/NoneValidator.php b/src/Symfony/Component/Validator/Constraints/NoneValidator.php new file mode 100644 index 000000000000..37084ddb4321 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/NoneValidator.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class NoneValidator extends ConstraintValidator +{ + + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Traversable) { + throw new UnexpectedTypeException($value, 'array or Traversable'); + } + + $group = $this->context->getGroup(); + + $totalIterations = count($value) * count($constraint->constraints); + + foreach ($value as $key => $element) { + foreach ($constraint->constraints as $constr) { + $this->context->validateValue($element, $constr, '[' . $key . ']', $group); + } + } + + $constraintsSuccess = $totalIterations - (int) $this->context->getViolations()->count(); + + //We clear all violations as just current Validator should add real Violations + $this->context->clearViolations(); + + if ($constraintsSuccess > 0) { + $this->context->addViolation($constraint->message); + } + } +} diff --git a/src/Symfony/Component/Validator/Constraints/Some.php b/src/Symfony/Component/Validator/Constraints/Some.php new file mode 100644 index 000000000000..038da3249bf2 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/Some.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Exception\MissingOptionsException; + +/** + * @Annotation + * + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class Some extends AbstractComposite +{ + + /** + * @var string + * + * Message for notice Min Violation + */ + public $minMessage = 'At least {{ limit }} element of this collection should pass validation.|At least {{ limit }} elements or more of this collection should pass validation.'; + + /** + * @var string + * + * Message for notice Max Violation + */ + public $maxMessage = '{{ limit }} or less element of this collection should pass validation.'; + + /** + * @var string + * + * Message for notice Exactly Violation + */ + public $exactlyMessage = 'Exactly {{ limit }} element of this collection should pass validation.|Exactly {{ limit }} elements of this collection should pass validation'; + + /** + * @var int + * + * Min number of Succeds expected + */ + public $min; + + /** + * @var int + * + * Max number of Succeds expected + */ + public $max; + + /** + * @var int + * + * Exactly number of Succeds expected + */ + public $exactly; + + /** + * {@inheritDoc} + */ + public function __construct($options = null) + { + parent::__construct($options); + + if ( (isset($this->min) || isset($this->max)) && isset($this->exactly)) { + throw new MissingOptionsException(sprintf('"min" or "max" and "exactly" must not be given at the same time: %s', __CLASS__), array('min', 'max', 'exactly')); + } + + if (!isset($this->min) && !isset($this->exactly)) { + $this->min = 1; + } + + if ( isset($this->max) && ($this->min > $this->max)) { + throw new MissingOptionsException(sprintf('"min" must not be given great than "max": %s', __CLASS__), array('min', 'max')); + } + } +} diff --git a/src/Symfony/Component/Validator/Constraints/SomeValidator.php b/src/Symfony/Component/Validator/Constraints/SomeValidator.php new file mode 100644 index 000000000000..6de02535739b --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/SomeValidator.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class SomeValidator extends ConstraintValidator +{ + + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Traversable) { + throw new UnexpectedTypeException($value, 'array or Traversable'); + } + + $group = $this->context->getGroup(); + + $totalIterations = count($value) * count($constraint->constraints); + + foreach ($value as $key => $element) { + foreach ($constraint->constraints as $constr) { + $this->context->validateValue($element, $constr, '[' . $key . ']', $group); + } + } + + $constraintsSuccess = $totalIterations - (int) $this->context->getViolations()->count(); + + // We clear all violations as just current Validator should add real Violations + $this->context->clearViolations(); + + if (isset($constraint->exactly) && $constraintsSuccess != $constraint->exactly) { + + $this->context->addViolation($constraint->exactlyMessage, array( + '{{ limit }}' => $constraint->exactly, + ), null, true); + + return; + } + + if (isset($constraint->min) && $constraintsSuccess < $constraint->min) { + $this->context->addViolation($constraint->minMessage, array( + '{{ limit }}' => $constraint->min, + )); + + return; + } + + if (isset($constraint->max) && $constraintsSuccess > $constraint->max) { + $this->context->addViolation($constraint->maxMessage, array( + '{{ limit }}' => $constraint->max, + ), null, true); + } + } +} diff --git a/src/Symfony/Component/Validator/Constraints/Unique.php b/src/Symfony/Component/Validator/Constraints/Unique.php new file mode 100644 index 000000000000..22120e8e63e2 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/Unique.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; + +/** + * @Annotation + * + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class Unique extends Constraint +{ + + /** + * @var string + * + * Message for notice Exactly Violation + */ + public $message = 'This collection has repeated elements'; +} diff --git a/src/Symfony/Component/Validator/Constraints/UniqueValidator.php b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php new file mode 100644 index 000000000000..c9dda8f2cc58 --- /dev/null +++ b/src/Symfony/Component/Validator/Constraints/UniqueValidator.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Constraints; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class UniqueValidator extends ConstraintValidator +{ + + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint) + { + if (null === $value) { + return; + } + + if (!is_array($value) && !$value instanceof \Traversable) { + throw new UnexpectedTypeException($value, 'array or Traversable'); + } + + if ($this->findRepeated($value)) { + + $this->context->addViolation($constraint->message, $params=array()); + } + } + + /** + * Given a set of iterable elements, just checks if all elements are once + * + * @param Mixed $elements Elements to check + * + * @return boolean Elements in collection are once + */ + private function findRepeated($elements) + { + $arrayUnique = array(); + + foreach ($elements as $element) { + + $arrayUnique[serialize($element)] = $element; + } + + return (count($arrayUnique) < count($elements)); + } +} diff --git a/src/Symfony/Component/Validator/ExecutionContext.php b/src/Symfony/Component/Validator/ExecutionContext.php index 31a959187e35..9e9dc5c2bdc1 100644 --- a/src/Symfony/Component/Validator/ExecutionContext.php +++ b/src/Symfony/Component/Validator/ExecutionContext.php @@ -140,6 +140,18 @@ public function getViolations() return $this->globalContext->getViolations(); } + /** + * Clears violation stack + * + * @return ExecutionContext self Object + */ + public function clearViolations() + { + $this->globalContext->clearViolations(); + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractCompositeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractCompositeTest.php new file mode 100644 index 000000000000..5862dd77dff2 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractCompositeTest.php @@ -0,0 +1,202 @@ + + * + * 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\AbstractComposite; +use Symfony\Component\Validator\Constraint; + +/** + * @author Marc Morales Valldepérez + * @author Marc Morera Merino + */ +class AbstractCompositeTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var Constraint + * + * Constraint + */ + private $simpleConstraint; + + /** + * Setup method + */ + public function setUp() + { + parent::setUp(); + + $this->simpleConstraint = $this + ->getMockBuilder('Symfony\Component\Validator\Constraint') + ->disableOriginalConstructor() + ->setMethods(null) + ->getMock(); + } + + /** + * Collection groups: none + * Constraint groups: none + * + * Collection groups result: [Default] + * Constraint groups result: [Default] + */ + public function testEmptyGroups() + { + $composite = $this + ->getMockBuilder('Symfony\Component\Validator\Constraints\AbstractComposite') + ->setConstructorArgs(array( + 'constraints' => array( + $this->simpleConstraint + ), + )) + ->setMethods(null) + ->getMock(); + + $this->assertEquals(array_values($composite->groups), array( + $composite::DEFAULT_GROUP, + )); + + $constraint = $this->simpleConstraint; + $this->assertEquals(array_values($this->simpleConstraint->groups), array( + $constraint::DEFAULT_GROUP, + )); + } + + /** + * Collection groups: [Default, Group1] + * Constraint groups: none + * + * Collection groups result: [Default, Group1] + * Constraint groups result: [Default, Group1] + */ + public function testCollectionGroups() + { + $composite = $this + ->getMockBuilder('Symfony\Component\Validator\Constraints\AbstractComposite') + ->setConstructorArgs(array( + array( + 'constraints' => array( + $this->simpleConstraint + ), + 'groups' => array( + 'Default', + 'Group1', + ), + ) + )) + ->setMethods(null) + ->getMock(); + + $this->assertEquals(array_values($composite->groups), array( + $composite::DEFAULT_GROUP, + 'Group1' + )); + + $constraint = $this->simpleConstraint; + $this->assertEquals(array_values($this->simpleConstraint->groups), array( + $constraint::DEFAULT_GROUP, + 'Group1' + )); + } + + /** + * Collection groups: none + * Constraint groups: [Default, Group1] + * + * Collection groups result: [Default, Group1] + * Constraint groups result: [Default, Group1] + */ + public function testConstraintsGroups() + { + $this->simpleConstraint = $this + ->getMockBuilder('Symfony\Component\Validator\Constraint') + ->setConstructorArgs(array( + array( + 'groups' => array( + 'Default', + 'Group1', + ), + ) + )) + ->setMethods(null) + ->getMock(); + + $composite = $this + ->getMockBuilder('Symfony\Component\Validator\Constraints\AbstractComposite') + ->setConstructorArgs(array( + array( + 'constraints' => array( + $this->simpleConstraint + ), + ) + )) + ->setMethods(null) + ->getMock(); + + $this->assertEquals(array_values($composite->groups), array( + $composite::DEFAULT_GROUP, + 'Group1' + )); + + $constraint = $this->simpleConstraint; + $this->assertEquals(array_values($this->simpleConstraint->groups), array( + $constraint::DEFAULT_GROUP, + 'Group1' + )); + } + + /** + * Collection groups: none + * Constraint groups: [Default, Group1] + * + * Collection groups result: [Default, Group1] + * Constraint groups result: [Default, Group1] + */ + public function testBothGroups() + { + $this->simpleConstraint = $this + ->getMockBuilder('Symfony\Component\Validator\Constraint') + ->setConstructorArgs(array( + array( + 'groups' => array( + 'Default', + 'Group1', + ), + ) + )) + ->setMethods(null) + ->getMock(); + + $composite = $this + ->getMockBuilder('Symfony\Component\Validator\Constraints\AbstractComposite') + ->setConstructorArgs(array( + array( + 'constraints' => array( + $this->simpleConstraint + ), + ) + )) + ->setMethods(null) + ->getMock(); + + $this->assertEquals(array_values($composite->groups), array( + $composite::DEFAULT_GROUP, + 'Group1' + )); + + $constraint = $this->simpleConstraint; + $this->assertEquals(array_values($this->simpleConstraint->groups), array( + $constraint::DEFAULT_GROUP, + 'Group1' + )); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EachTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EachTest.php new file mode 100644 index 000000000000..95abb18da149 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/EachTest.php @@ -0,0 +1,42 @@ + + * + * 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\Each; +use Symfony\Component\Validator\Constraints\Valid; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class EachTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectNonConstraints() + { + new Each(array( + 'foo', + )); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectValidConstraint() + { + new Each(array( + new Valid(), + )); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EachValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EachValidatorTest.php new file mode 100644 index 000000000000..9cf95037b1f7 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/EachValidatorTest.php @@ -0,0 +1,116 @@ + + * + * 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\ExecutionContext; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\NotNull; +use Symfony\Component\Validator\Constraints\Each; +use Symfony\Component\Validator\Constraints\EachValidator; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class EachValidatorTest 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 EachValidator(); + $this->validator->initialize($this->context); + + $this->context->expects($this->any()) + ->method('getGroup') + ->will($this->returnValue('MyGroup')); + } + + protected function tearDown() + { + $this->validator = null; + $this->context = null; + } + + public function testNullIsValid() + { + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate(null, new Each(new Range(array('min' => 4)))); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testThrowsExceptionIfNotTraversable() + { + $this->validator->validate('foo.barbar', new Each(new Range(array('min' => 4)))); + } + + /** + * @dataProvider getValidArguments + */ + public function testWalkSingleConstraint($array) + { + $constraint = new Range(array('min' => 4)); + + $i = 1; + + foreach ($array as $key => $value) { + $this->context->expects($this->at($i++)) + ->method('validateValue') + ->with($value, $constraint, '['.$key.']', 'MyGroup'); + } + + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($array, new Each($constraint)); + } + + /** + * @dataProvider getValidArguments + */ + public function testWalkMultipleConstraints($array) + { + $constraint1 = new Range(array('min' => 4)); + $constraint2 = new NotNull(); + + $constraints = array($constraint1, $constraint2); + $i = 1; + + foreach ($array as $key => $value) { + $this->context->expects($this->at($i++)) + ->method('validateValue') + ->with($value, $constraint1, '['.$key.']', 'MyGroup'); + $this->context->expects($this->at($i++)) + ->method('validateValue') + ->with($value, $constraint2, '['.$key.']', 'MyGroup'); + } + + $this->context->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate($array, new Each($constraints)); + } + + public function getValidArguments() + { + return array( + array(array(5, 6, 7)), + array(new \ArrayObject(array(5, 6, 7))), + ); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NoneTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NoneTest.php new file mode 100644 index 000000000000..38282fa48c97 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/NoneTest.php @@ -0,0 +1,42 @@ + + * + * 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\None; +use Symfony\Component\Validator\Constraints\Valid; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class NoneTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectNonConstraints() + { + new None(array( + 'foo', + )); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectValidConstraint() + { + new None(array( + new Valid(), + )); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/NoneValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/NoneValidatorTest.php new file mode 100644 index 000000000000..0d652db0da52 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/NoneValidatorTest.php @@ -0,0 +1,273 @@ + + * + * 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\ExecutionContext; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\None; +use Symfony\Component\Validator\Constraints\NoneValidator; +use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; +use Symfony\Component\Validator\ValidationVisitor; +use Symfony\Component\Validator\DefaultTranslator; +use Symfony\Component\Validator\ConstraintValidatorFactory; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Tests\Fixtures\EntityCollection; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class NoneValidatorTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var ExecutionContext + * + * Context mockup + */ + protected $context; + + /** + * @var SomeValidator + * + * Validator instance + */ + protected $validator; + + /** + * Set up method + */ + protected function setUp() + { + + $this->context = $this + ->getMockBuilder('Symfony\Component\Validator\ExecutionContext') + ->disableOriginalConstructor() + ->setMethods(array()) + ->getMock(); + + $this->validator = new NoneValidator(); + $this->validator->initialize($this->context); + } + + /** + * Tear down method + */ + protected function tearDown() + { + $this->validator = null; + $this->context = null; + } + + /** + * Tests that if null, just valid + */ + public function testNullIsValid() + { + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate( + null, + new None( + array( + 'constraints' => array( + new Range(array('min' => 10)) + ), + ) + ) + ); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testThrowsExceptionIfNotTraversable() + { + $this->validator->validate('foo.barbar', new None(new Range(array('min' => 4)))); + } + + /** + * Validates success + * + * @dataProvider getValidArguments + */ + public function testSuccessValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(6)); + + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 8)); + $constraint2 = new Range(array('min' => 9)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new None( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + ) + ) + ); + } + + /** + * Validates not success + * + * @dataProvider getValidArguments + */ + public function testNotSuccessValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new None( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + ) + ) + ); + } + + /** + * Adds validateValue assertions + */ + protected function setValidateValueAssertions($array, $constraint1, $constraint2) + { + $iteration = 1; + + foreach ($array as $key => $value) { + + $this + ->context + ->expects($this->at($iteration++)) + ->method('validateValue') + ->with($value, $constraint1, '['.$key.']'); + + $this + ->context + ->expects($this->at($iteration++)) + ->method('validateValue') + ->with($value, $constraint2, '['.$key.']'); + } + } + + /** + * Functional test, validating None constraint + * + * Using exactly + */ + public function testFunctionalSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collection', new None( + array( + 'constraints' => array( + new Range(array('min' => 4)), + new Range(array('min' => 5)), + new Range(array('min' => 6)), + ), + ) + ) + ); + $metadataFactory->addMetadata($metadata); + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(0, $visitor->getViolations()); + } + + /** + * Functional test, not validating None constraint + * + * Using exactly + */ + public function testFunctionalNotSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collection', new None( + array( + 'constraints' => array( + new Range(array('min' => 1)), + new Range(array('min' => 2)), + new Range(array('min' => 3)), + ), + ) + ) + ); + $metadataFactory->addMetadata($metadata); + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(1, $visitor->getViolations()); + } + + /** + * Data provider + */ + public function getValidArguments() + { + return array( + array(array(5, 6, 7)), + array(new \ArrayObject(array(5, 6, 7))), + ); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/SomeTest.php b/src/Symfony/Component/Validator/Tests/Constraints/SomeTest.php new file mode 100644 index 000000000000..6624e1245ee9 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/SomeTest.php @@ -0,0 +1,42 @@ + + * + * 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\Some; +use Symfony\Component\Validator\Constraints\Valid; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class SomeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectNonConstraints() + { + new Some(array( + 'foo', + )); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException + */ + public function testRejectValidConstraint() + { + new Some(array( + new Valid(), + )); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/SomeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/SomeValidatorTest.php new file mode 100644 index 000000000000..24a294ba7115 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/SomeValidatorTest.php @@ -0,0 +1,675 @@ + + * + * 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\ExecutionContext; +use Symfony\Component\Validator\Constraints\Range; +use Symfony\Component\Validator\Constraints\Some; +use Symfony\Component\Validator\Constraints\SomeValidator; +use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; +use Symfony\Component\Validator\ValidationVisitor; +use Symfony\Component\Validator\DefaultTranslator; +use Symfony\Component\Validator\ConstraintValidatorFactory; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Tests\Fixtures\EntityCollection; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class SomeValidatorTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var ExecutionContext + * + * Context mockup + */ + protected $context; + + /** + * @var SomeValidator + * + * Validator instance + */ + protected $validator; + + /** + * Set up method + */ + protected function setUp() + { + + $this->context = $this + ->getMockBuilder('Symfony\Component\Validator\ExecutionContext') + ->disableOriginalConstructor() + ->setMethods(array()) + ->getMock(); + + $this->validator = new SomeValidator(); + $this->validator->initialize($this->context); + } + + /** + * Tear down method + */ + protected function tearDown() + { + $this->validator = null; + $this->context = null; + } + + /** + * Tests that if null, just valid + */ + public function testNullIsValid() + { + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'min' => 1 + ) + ) + ); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testThrowsExceptionIfNotTraversable() + { + $this->validator->validate('foo.barbar', new Some(new Range(array('min' => 4)))); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\MissingOptionsException + */ + public function testThrowsExceptionMinAndExactly() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'min' => 1, + 'exactly' => 2 + ) + ) + ); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\MissingOptionsException + */ + public function testThrowsExceptionMaxAndExactly() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'max' => 1, + 'exactly' => 2 + ) + ) + ); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\MissingOptionsException + */ + public function testThrowsExceptionMinGreatThanMax() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'min' => 3, + 'max' => 1 + ) + ) + ); + } + + /** + * Testing when min and max are defined + */ + public function testMinAndMax() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'min'=>1, + 'max'=>10, + ) + ) + ); + } + + /** + * Testing when min, max and exactly are defined + * + * @expectedException \Symfony\Component\Validator\Exception\MissingOptionsException + */ + public function testMinAndMaxAndExactly() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'min'=>1, + 'max'=>10, + 'exactly'=>10, + ) + ) + ); + } + + /** + * Testing when just max is defined + */ + public function testMax() + { + $this->validator->validate( + null, + new Some( + array( + 'constraints' => array( + new Range(array('min' => 4)) + ), + 'max'=>10, + ) + ) + ); + } + + /** + * Validates success min + * + * @dataProvider getValidArguments + */ + public function testSuccessMinValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'min' => 3 + ) + )); + } + + /** + * Not validates success min + * + * @dataProvider getValidArguments + */ + public function testNotSuccessMinValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'min' => 5 + ) + ) + ); + } + + /** + * Validates success min + * + * @dataProvider getValidArguments + */ + public function testSuccessMinMaxValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'min' => 2, + 'max' => 4, + ) + ) + ); + } + + /** + * Validates not success min + * + * @dataProvider getValidArguments + */ + public function testNotSuccessMinMaxValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'min' => 1, + 'max' => 3, + ) + ) + ); + } + + /** + * Validates success max + * + * @dataProvider getValidArguments + */ + public function testSuccessMaxValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'max' => 5, + ) + ) + ); + } + + /** + * Validates not success max + * + * @dataProvider getValidArguments + */ + public function testNotSuccessMaxValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'max' => 2, + ) + ) + ); + } + + /** + * Validates success exactly + * + * @dataProvider getValidArguments + */ + public function testSuccessExactlyValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->never()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'exactly' => 4, + ) + ) + ); + } + + /** + * Validates not success exactly + * + * @dataProvider getValidArguments + */ + public function testNotSuccessExactlyValidate($array) + { + $constraintViolationList = $this + ->getMockBuilder('Symfony\Component\Validator\ConstraintViolationList') + ->disableOriginalConstructor() + ->setMethods(array('count')) + ->getMock(); + + $constraintViolationList + ->expects($this->once()) + ->method('count') + ->will($this->returnValue(2)); + + $this->context + ->expects($this->once()) + ->method('addViolation'); + + $this->context + ->expects($this->once()) + ->method('getViolations') + ->will($this->returnValue($constraintViolationList)); + + $constraint1 = new Range(array('min' => 2)); + $constraint2 = new Range(array('min' => 7)); + + $this->setValidateValueAssertions($array, $constraint1, $constraint2); + + $this->validator->validate( + $array, + new Some( + array( + 'constraints' => array( + $constraint1, + $constraint2, + ), + 'exactly' => 3, + ) + ) + ); + } + + /** + * Functional test, validating Some constraint + * + * Using exactly + */ + public function testFunctionalSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collection', new Some( + array( + 'constraints' => array( + new Range(array('min' => 2)), + new Range(array('min' => 3)), + new Range(array('min' => 4)), + new Range(array('min' => 5)) + ), + 'exactly' => 3, + ) + ) + ); + $metadataFactory->addMetadata($metadata); + + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(0, $visitor->getViolations()); + } + + /** + * Functional test, not validating Some constraint + * + * Using exactly + */ + public function testFunctionalNotSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collection', new Some( + array( + 'constraints' => array( + new Range(array('min' => 2)), + new Range(array('min' => 3)), + new Range(array('min' => 4)), + new Range(array('min' => 5)) + ), + 'exactly' => 1, + ) + ) + ); + $metadataFactory->addMetadata($metadata); + + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(1, $visitor->getViolations()); + } + + /** + * Adds validateValue assertions + */ + protected function setValidateValueAssertions($array, $constraint1, $constraint2) + { + $iteration = 1; + + foreach ($array as $key => $value) { + + $this + ->context + ->expects($this->at($iteration++)) + ->method('validateValue') + ->with($value, $constraint1, '['.$key.']'); + + $this + ->context + ->expects($this->at($iteration++)) + ->method('validateValue') + ->with($value, $constraint2, '['.$key.']'); + } + } + + /** + * Data provider + */ + public function getValidArguments() + { + return array( + array(array(5, 6, 7)), + array(new \ArrayObject(array(5, 6, 7))), + ); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UniqueTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UniqueTest.php new file mode 100644 index 000000000000..a929288c771e --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/UniqueTest.php @@ -0,0 +1,27 @@ + + * + * 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\Unique; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class UniqueTest extends \PHPUnit_Framework_TestCase +{ + + public function testSuccess() + { + new Unique(); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php new file mode 100644 index 000000000000..5e3e7a764491 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/UniqueValidatorTest.php @@ -0,0 +1,155 @@ + + * + * 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\ExecutionContext; +use Symfony\Component\Validator\Constraints\Unique; +use Symfony\Component\Validator\Constraints\UniqueValidator; +use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; +use Symfony\Component\Validator\ValidationVisitor; +use Symfony\Component\Validator\DefaultTranslator; +use Symfony\Component\Validator\ConstraintValidatorFactory; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\Tests\Fixtures\EntityCollection; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class UniqueValidatorTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var ExecutionContext + * + * Context mockup + */ + protected $context; + + /** + * @var SomeValidator + * + * Validator instance + */ + protected $validator; + + /** + * Set up method + */ + protected function setUp() + { + $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false); + $this->validator = new UniqueValidator(); + $this->validator->initialize($this->context); + } + + /** + * Tear down method + */ + protected function tearDown() + { + $this->validator = null; + $this->context = null; + } + + /** + * Tests that if null, just valid + */ + public function testNullIsValid() + { + $this + ->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate( + null, + new Unique() + ); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException + */ + public function testThrowsExceptionIfNotTraversable() + { + $this->validator->validate('foo.barbar', new Unique()); + } + + /** + * Test not validate + */ + + public function testUniqueNotSuccess() + { + $this + ->context + ->expects($this->once()) + ->method('addViolation'); + + $this->validator->validate( + array( + array(1, 1, 1), + array(1, 1, 1) + ), + new Unique() + ); + + } + + /** + * Test validate + */ + public function testUniqueSuccess() + { + $this + ->context + ->expects($this->never()) + ->method('addViolation'); + + $this->validator->validate( + array( + array(1, 1, 1), + array(2, 2, 2) + ), + new Unique() + ); + } + + /** + * Functional test, validating satisfactorily Unique constraint + */ + public function testFunctionalSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collection', new Unique()); + $metadataFactory->addMetadata($metadata); + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(0, $visitor->getViolations()); + } + + /** + * Functional test, validating unsatisfactorily Unique constraint + */ + public function testFunctionalNotSuccessExactly() + { + $metadataFactory = new FakeMetadataFactory(); + $visitor = new ValidationVisitor('Root', $metadataFactory, new ConstraintValidatorFactory(), new DefaultTranslator()); + $metadata = new ClassMetadata('Symfony\Component\Validator\Tests\Fixtures\EntityCollection'); + $metadata->addPropertyConstraint('collectionNotUnique', new Unique()); + $metadataFactory->addMetadata($metadata); + $visitor->validate(new EntityCollection(), 'Default', ''); + $this->assertCount(1, $visitor->getViolations()); + } +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/EntityCollection.php b/src/Symfony/Component/Validator/Tests/Fixtures/EntityCollection.php new file mode 100644 index 000000000000..ac3b79f97be2 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Fixtures/EntityCollection.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Fixtures; + +use Symfony\Component\Validator\Constraints as Assert; + +/** + * @author Marc Morera Merino + * @author Marc Morales Valldepérez + */ +class EntityCollection +{ + /** + * + */ + protected $collection = array(1,2,3); + + /** + * + */ + protected $collectionNotUnique = array(1,2,3,1,2); +} diff --git a/src/Symfony/Component/Validator/ValidationVisitor.php b/src/Symfony/Component/Validator/ValidationVisitor.php index ddff8adc6038..23bd633f830b 100644 --- a/src/Symfony/Component/Validator/ValidationVisitor.php +++ b/src/Symfony/Component/Validator/ValidationVisitor.php @@ -163,6 +163,18 @@ public function validate($value, $group, $propertyPath, $traverse = false, $deep } } + /** + * Clears violation stack + * + * @return ValidationVisitor self Object + */ + public function clearViolations() + { + $this->violations = new ConstraintViolationList(); + + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/composer.json b/src/Symfony/Component/Validator/composer.json index af14eabdb63a..09a592326d71 100644 --- a/src/Symfony/Component/Validator/composer.json +++ b/src/Symfony/Component/Validator/composer.json @@ -26,7 +26,8 @@ "symfony/yaml": "~2.0", "symfony/config": "~2.2", "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0" + "doctrine/cache": "~1.0", + "symfony/expression-language": "2.5.*@dev" }, "suggest": { "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",