[Validator] Improved test coverage of the Traverse constraint

This commit is contained in:
Bernhard Schussek 2014-02-21 17:10:39 +01:00
parent 9ca61df923
commit 01ceeda376
7 changed files with 214 additions and 45 deletions

View File

@ -24,8 +24,6 @@ class Traverse extends Constraint
{
public $traverse = true;
public $deep = false;
public function __construct($options = null)
{
if (is_array($options) && array_key_exists('groups', $options)) {

View File

@ -25,6 +25,9 @@ class Valid extends Constraint
{
public $traverse = true;
/**
* @deprecated Deprecated as of version 2.5, to be removed in Symfony 3.0.
*/
public $deep = true;
public function __construct($options = null)
@ -38,11 +41,4 @@ class Valid extends Constraint
parent::__construct($options);
}
public function getDefaultOption()
{
// Traverse is extended for backwards compatibility reasons
// The parent class should be removed in 3.0
return null;
}
}

View File

@ -111,10 +111,8 @@ class LegacyExecutionContext extends ExecutionContext
public function validate($value, $subPath = '', $groups = null, $traverse = false, $deep = false)
{
if (is_array($value)) {
$constraint = new Traverse(array(
'traverse' => true,
'deep' => $deep,
));
// The $traverse flag is ignored for arrays
$constraint = new Valid(array('traverse' => true, 'deep' => $deep));
return $this
->getValidator()
@ -125,16 +123,13 @@ class LegacyExecutionContext extends ExecutionContext
}
if ($traverse && $value instanceof \Traversable) {
$constraints = array(
new Valid(),
new Traverse(array('traverse' => true, 'deep' => $deep)),
);
$constraint = new Valid(array('traverse' => true, 'deep' => $deep));
return $this
->getValidator()
->inContext($this)
->atPath($subPath)
->validate($value, $constraints, $groups)
->validate($value, $constraint, $groups)
;
}

View File

@ -201,20 +201,12 @@ class ClassMetadata extends ElementMetadata implements LegacyMetadataInterface,
}
if ($constraint instanceof Traverse) {
if (true === $constraint->traverse) {
if ($constraint->traverse) {
// If traverse is true, traversal should be explicitly enabled
$this->traversalStrategy = TraversalStrategy::TRAVERSE;
if (!$constraint->deep) {
$this->traversalStrategy |= TraversalStrategy::STOP_RECURSION;
}
} elseif (false === $constraint->traverse) {
} else {
// If traverse is false, traversal should be explicitly disabled
$this->traversalStrategy = TraversalStrategy::NONE;
} else {
// Else, traverse depending on the contextual information that
// is available during validation
$this->traversalStrategy = TraversalStrategy::IMPLICIT;
}
// The constraint is not added

View File

@ -12,8 +12,10 @@
namespace Symfony\Component\Validator\Mapping;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\Traverse;
use Symfony\Component\Validator\Constraints\Valid;
use Symfony\Component\Validator\Exception\BadMethodCallException;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\ValidationVisitorInterface;
/**
@ -103,9 +105,20 @@ class GenericMetadata implements MetadataInterface
* @param Constraint $constraint The constraint to add
*
* @return GenericMetadata This object
*
* @throws ConstraintDefinitionException When trying to add the
* {@link Traverse} constraint
*/
public function addConstraint(Constraint $constraint)
{
if ($constraint instanceof Traverse) {
throw new ConstraintDefinitionException(sprintf(
'The constraint "%s" can only be put on classes. Please use '.
'"Symfony\Component\Validator\Constraints\Valid" instead.',
get_class($constraint)
));
}
if ($constraint instanceof Valid) {
$this->cascadingStrategy = CascadingStrategy::CASCADE;

View File

@ -58,21 +58,6 @@ abstract class MemberMetadata extends ElementMetadata implements PropertyMetadat
));
}
// BC with Symfony < 2.5
if ($constraint instanceof Valid) {
if (true === $constraint->traverse) {
// Try to traverse cascaded objects, but ignore if they do not
// implement Traversable
$this->traversalStrategy = TraversalStrategy::IMPLICIT;
if (!$constraint->deep) {
$this->traversalStrategy |= TraversalStrategy::STOP_RECURSION;
}
} elseif (false === $constraint->traverse) {
$this->traversalStrategy = TraversalStrategy::NONE;
}
}
parent::addConstraint($constraint);
return $this;

View File

@ -328,18 +328,208 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest
$this->assertNull($violations[0]->getCode());
}
public function testTraversalEnabledOnClass()
{
$entity = new Entity();
$traversable = new \ArrayIterator(array('key' => $entity));
$callback = function ($value, ExecutionContextInterface $context) {
$context->addViolation('Message');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(true));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->metadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$violations = $this->validate($traversable, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(1, $violations);
}
public function testTraversalDisabledOnClass()
{
$test = $this;
$entity = new Entity();
$traversable = new \ArrayIterator(array('key' => $entity));
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
$test->fail('Should not be called');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(false));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->metadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$violations = $this->validate($traversable, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(0, $violations);
}
/**
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
*/
public function testExpectTraversableIfTraverseOnClass()
public function testExpectTraversableIfTraversalEnabledOnClass()
{
$entity = new Entity();
$this->metadata->addConstraint(new Traverse());
$this->metadata->addConstraint(new Traverse(true));
$this->validator->validate($entity);
}
public function testReferenceTraversalDisabledOnClass()
{
$test = $this;
$entity = new Entity();
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
$test->fail('Should not be called');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(false));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->referenceMetadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$this->metadata->addPropertyConstraint('reference', new Valid());
$violations = $this->validate($entity, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(0, $violations);
}
public function testReferenceTraversalEnabledOnReferenceDisabledOnClass()
{
$test = $this;
$entity = new Entity();
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
$test->fail('Should not be called');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(false));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->referenceMetadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$this->metadata->addPropertyConstraint('reference', new Valid(array(
'traverse' => true,
)));
$violations = $this->validate($entity, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(0, $violations);
}
public function testReferenceTraversalDisabledOnReferenceEnabledOnClass()
{
$test = $this;
$entity = new Entity();
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
$test->fail('Should not be called');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(true));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->referenceMetadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$this->metadata->addPropertyConstraint('reference', new Valid(array(
'traverse' => false,
)));
$violations = $this->validate($entity, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(0, $violations);
}
public function testReferenceTraversalRecursionEnabledOnReferenceTraversalEnabledOnClass()
{
$entity = new Entity();
$entity->reference = new \ArrayIterator(array(
2 => new \ArrayIterator(array('key' => new Reference())),
));
$callback = function ($value, ExecutionContextInterface $context) {
$context->addViolation('Message');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(true));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->referenceMetadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$this->metadata->addPropertyConstraint('reference', new Valid(array(
'deep' => true,
)));
$violations = $this->validate($entity, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(1, $violations);
}
public function testReferenceTraversalRecursionDisabledOnReferenceTraversalEnabledOnClass()
{
$test = $this;
$entity = new Entity();
$entity->reference = new \ArrayIterator(array(
2 => new \ArrayIterator(array('key' => new Reference())),
));
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
$test->fail('Should not be called');
};
$traversableMetadata = new ClassMetadata('ArrayIterator');
$traversableMetadata->addConstraint(new Traverse(true));
$this->metadataFactory->addMetadata($traversableMetadata);
$this->referenceMetadata->addConstraint(new Callback(array(
'callback' => $callback,
'groups' => 'Group',
)));
$this->metadata->addPropertyConstraint('reference', new Valid(array(
'deep' => false,
)));
$violations = $this->validate($entity, new Valid(), 'Group');
/** @var ConstraintViolationInterface[] $violations */
$this->assertCount(0, $violations);
}
public function testAddCustomizedViolation()
{
$entity = new Entity();