442 lines
13 KiB
PHP
442 lines
13 KiB
PHP
|
<?php
|
||
|
|
||
|
/*
|
||
|
* This file is part of the Symfony package.
|
||
|
*
|
||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||
|
*
|
||
|
* For the full copyright and license information, please view the LICENSE
|
||
|
* file that was distributed with this source code.
|
||
|
*/
|
||
|
|
||
|
namespace Symfony\Component\Validator\Tests\Constraints;
|
||
|
|
||
|
use PHPUnit\Framework\Assert;
|
||
|
use PHPUnit\Framework\TestCase;
|
||
|
use Symfony\Component\Validator\Constraint;
|
||
|
use Symfony\Component\Validator\Constraints\NotNull;
|
||
|
use Symfony\Component\Validator\ConstraintValidatorInterface;
|
||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||
|
use Symfony\Component\Validator\Context\ExecutionContext;
|
||
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||
|
use Symfony\Component\Validator\Context\LegacyExecutionContext;
|
||
|
use Symfony\Component\Validator\ExecutionContextInterface as LegacyExecutionContextInterface;
|
||
|
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||
|
use Symfony\Component\Validator\Mapping\PropertyMetadata;
|
||
|
use Symfony\Component\Validator\Validation;
|
||
|
|
||
|
/**
|
||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||
|
*/
|
||
|
abstract class AbstractConstraintValidatorTest extends TestCase
|
||
|
{
|
||
|
/**
|
||
|
* @var ExecutionContextInterface
|
||
|
*/
|
||
|
protected $context;
|
||
|
|
||
|
/**
|
||
|
* @var ConstraintValidatorInterface
|
||
|
*/
|
||
|
protected $validator;
|
||
|
|
||
|
protected $group;
|
||
|
protected $metadata;
|
||
|
protected $object;
|
||
|
protected $value;
|
||
|
protected $root;
|
||
|
protected $propertyPath;
|
||
|
protected $constraint;
|
||
|
protected $defaultTimezone;
|
||
|
|
||
|
protected function setUp()
|
||
|
{
|
||
|
$this->group = 'MyGroup';
|
||
|
$this->metadata = null;
|
||
|
$this->object = null;
|
||
|
$this->value = 'InvalidValue';
|
||
|
$this->root = 'root';
|
||
|
$this->propertyPath = 'property.path';
|
||
|
|
||
|
// Initialize the context with some constraint so that we can
|
||
|
// successfully build a violation.
|
||
|
$this->constraint = new NotNull();
|
||
|
|
||
|
$this->context = $this->createContext();
|
||
|
$this->validator = $this->createValidator();
|
||
|
$this->validator->initialize($this->context);
|
||
|
|
||
|
\Locale::setDefault('en');
|
||
|
|
||
|
$this->setDefaultTimezone('UTC');
|
||
|
}
|
||
|
|
||
|
protected function tearDown()
|
||
|
{
|
||
|
$this->restoreDefaultTimezone();
|
||
|
}
|
||
|
|
||
|
protected function setDefaultTimezone($defaultTimezone)
|
||
|
{
|
||
|
// Make sure this method can not be called twice before calling
|
||
|
// also restoreDefaultTimezone()
|
||
|
if (null === $this->defaultTimezone) {
|
||
|
$this->defaultTimezone = date_default_timezone_get();
|
||
|
date_default_timezone_set($defaultTimezone);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function restoreDefaultTimezone()
|
||
|
{
|
||
|
if (null !== $this->defaultTimezone) {
|
||
|
date_default_timezone_set($this->defaultTimezone);
|
||
|
$this->defaultTimezone = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected function createContext()
|
||
|
{
|
||
|
$translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock();
|
||
|
$validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
|
||
|
$contextualValidator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ContextualValidatorInterface')->getMock();
|
||
|
|
||
|
switch ($this->getApiVersion()) {
|
||
|
case Validation::API_VERSION_2_5:
|
||
|
$context = new ExecutionContext(
|
||
|
$validator,
|
||
|
$this->root,
|
||
|
$translator
|
||
|
);
|
||
|
break;
|
||
|
case Validation::API_VERSION_2_4:
|
||
|
case Validation::API_VERSION_2_5_BC:
|
||
|
$context = new LegacyExecutionContext(
|
||
|
$validator,
|
||
|
$this->root,
|
||
|
$this->getMockBuilder('Symfony\Component\Validator\MetadataFactoryInterface')->getMock(),
|
||
|
$translator
|
||
|
);
|
||
|
break;
|
||
|
default:
|
||
|
throw new \RuntimeException('Invalid API version');
|
||
|
}
|
||
|
|
||
|
$context->setGroup($this->group);
|
||
|
$context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
||
|
$context->setConstraint($this->constraint);
|
||
|
|
||
|
$validator->expects($this->any())
|
||
|
->method('inContext')
|
||
|
->with($context)
|
||
|
->will($this->returnValue($contextualValidator));
|
||
|
|
||
|
return $context;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param mixed $message
|
||
|
* @param array $parameters
|
||
|
* @param string $propertyPath
|
||
|
* @param string $invalidValue
|
||
|
* @param null $plural
|
||
|
* @param null $code
|
||
|
*
|
||
|
* @return ConstraintViolation
|
||
|
*
|
||
|
* @deprecated to be removed in Symfony 3.0. Use {@link buildViolation()} instead.
|
||
|
*/
|
||
|
protected function createViolation($message, array $parameters = array(), $propertyPath = 'property.path', $invalidValue = 'InvalidValue', $plural = null, $code = null)
|
||
|
{
|
||
|
return new ConstraintViolation(
|
||
|
null,
|
||
|
$message,
|
||
|
$parameters,
|
||
|
$this->root,
|
||
|
$propertyPath,
|
||
|
$invalidValue,
|
||
|
$plural,
|
||
|
$code,
|
||
|
$this->constraint
|
||
|
);
|
||
|
}
|
||
|
|
||
|
protected function setGroup($group)
|
||
|
{
|
||
|
$this->group = $group;
|
||
|
$this->context->setGroup($group);
|
||
|
}
|
||
|
|
||
|
protected function setObject($object)
|
||
|
{
|
||
|
$this->object = $object;
|
||
|
$this->metadata = is_object($object)
|
||
|
? new ClassMetadata(get_class($object))
|
||
|
: null;
|
||
|
|
||
|
$this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
||
|
}
|
||
|
|
||
|
protected function setProperty($object, $property)
|
||
|
{
|
||
|
$this->object = $object;
|
||
|
$this->metadata = is_object($object)
|
||
|
? new PropertyMetadata(get_class($object), $property)
|
||
|
: null;
|
||
|
|
||
|
$this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
||
|
}
|
||
|
|
||
|
protected function setValue($value)
|
||
|
{
|
||
|
$this->value = $value;
|
||
|
$this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
||
|
}
|
||
|
|
||
|
protected function setRoot($root)
|
||
|
{
|
||
|
$this->root = $root;
|
||
|
$this->context = $this->createContext();
|
||
|
$this->validator->initialize($this->context);
|
||
|
}
|
||
|
|
||
|
protected function setPropertyPath($propertyPath)
|
||
|
{
|
||
|
$this->propertyPath = $propertyPath;
|
||
|
$this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
||
|
}
|
||
|
|
||
|
protected function expectNoValidate()
|
||
|
{
|
||
|
$validator = $this->context->getValidator()->inContext($this->context);
|
||
|
$validator->expects($this->never())
|
||
|
->method('atPath');
|
||
|
$validator->expects($this->never())
|
||
|
->method('validate');
|
||
|
}
|
||
|
|
||
|
protected function expectValidateAt($i, $propertyPath, $value, $group)
|
||
|
{
|
||
|
$validator = $this->context->getValidator()->inContext($this->context);
|
||
|
$validator->expects($this->at(2 * $i))
|
||
|
->method('atPath')
|
||
|
->with($propertyPath)
|
||
|
->will($this->returnValue($validator));
|
||
|
$validator->expects($this->at(2 * $i + 1))
|
||
|
->method('validate')
|
||
|
->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
|
||
|
}
|
||
|
|
||
|
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
|
||
|
{
|
||
|
$contextualValidator = $this->context->getValidator()->inContext($this->context);
|
||
|
$contextualValidator->expects($this->at(2 * $i))
|
||
|
->method('atPath')
|
||
|
->with($propertyPath)
|
||
|
->will($this->returnValue($contextualValidator));
|
||
|
$contextualValidator->expects($this->at(2 * $i + 1))
|
||
|
->method('validate')
|
||
|
->with($value, $constraints, $group);
|
||
|
}
|
||
|
|
||
|
protected function assertNoViolation()
|
||
|
{
|
||
|
$this->assertSame(0, $violationsCount = count($this->context->getViolations()), sprintf('0 violation expected. Got %u.', $violationsCount));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param mixed $message
|
||
|
* @param array $parameters
|
||
|
* @param string $propertyPath
|
||
|
* @param string $invalidValue
|
||
|
* @param null $plural
|
||
|
* @param null $code
|
||
|
*
|
||
|
* @deprecated To be removed in Symfony 3.0. Use
|
||
|
* {@link buildViolation()} instead.
|
||
|
*/
|
||
|
protected function assertViolation($message, array $parameters = array(), $propertyPath = 'property.path', $invalidValue = 'InvalidValue', $plural = null, $code = null)
|
||
|
{
|
||
|
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.3 and will be removed in 3.0. Use the buildViolation() method instead.', E_USER_DEPRECATED);
|
||
|
|
||
|
$this->buildViolation($message)
|
||
|
->setParameters($parameters)
|
||
|
->atPath($propertyPath)
|
||
|
->setInvalidValue($invalidValue)
|
||
|
->setCode($code)
|
||
|
->setPlural($plural)
|
||
|
->assertRaised();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param array $expected
|
||
|
*
|
||
|
* @deprecated To be removed in Symfony 3.0. Use
|
||
|
* {@link buildViolation()} instead.
|
||
|
*/
|
||
|
protected function assertViolations(array $expected)
|
||
|
{
|
||
|
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.3 and will be removed in 3.0. Use the buildViolation() method instead.', E_USER_DEPRECATED);
|
||
|
|
||
|
$violations = $this->context->getViolations();
|
||
|
|
||
|
$this->assertCount(count($expected), $violations);
|
||
|
|
||
|
$i = 0;
|
||
|
|
||
|
foreach ($expected as $violation) {
|
||
|
$this->assertEquals($violation, $violations[$i++]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $message
|
||
|
*
|
||
|
* @return ConstraintViolationAssertion
|
||
|
*/
|
||
|
protected function buildViolation($message)
|
||
|
{
|
||
|
return new ConstraintViolationAssertion($this->context, $message, $this->constraint);
|
||
|
}
|
||
|
|
||
|
protected function getApiVersion()
|
||
|
{
|
||
|
return Validation::API_VERSION_2_5;
|
||
|
}
|
||
|
|
||
|
abstract protected function createValidator();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal
|
||
|
*/
|
||
|
class ConstraintViolationAssertion
|
||
|
{
|
||
|
/**
|
||
|
* @var LegacyExecutionContextInterface
|
||
|
*/
|
||
|
private $context;
|
||
|
|
||
|
/**
|
||
|
* @var ConstraintViolationAssertion[]
|
||
|
*/
|
||
|
private $assertions;
|
||
|
|
||
|
private $message;
|
||
|
private $parameters = array();
|
||
|
private $invalidValue = 'InvalidValue';
|
||
|
private $propertyPath = 'property.path';
|
||
|
private $translationDomain;
|
||
|
private $plural;
|
||
|
private $code;
|
||
|
private $constraint;
|
||
|
private $cause;
|
||
|
|
||
|
public function __construct(LegacyExecutionContextInterface $context, $message, Constraint $constraint = null, array $assertions = array())
|
||
|
{
|
||
|
$this->context = $context;
|
||
|
$this->message = $message;
|
||
|
$this->constraint = $constraint;
|
||
|
$this->assertions = $assertions;
|
||
|
}
|
||
|
|
||
|
public function atPath($path)
|
||
|
{
|
||
|
$this->propertyPath = $path;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setParameter($key, $value)
|
||
|
{
|
||
|
$this->parameters[$key] = $value;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setParameters(array $parameters)
|
||
|
{
|
||
|
$this->parameters = $parameters;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setTranslationDomain($translationDomain)
|
||
|
{
|
||
|
$this->translationDomain = $translationDomain;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setInvalidValue($invalidValue)
|
||
|
{
|
||
|
$this->invalidValue = $invalidValue;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setPlural($number)
|
||
|
{
|
||
|
$this->plural = $number;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setCode($code)
|
||
|
{
|
||
|
$this->code = $code;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function setCause($cause)
|
||
|
{
|
||
|
$this->cause = $cause;
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function buildNextViolation($message)
|
||
|
{
|
||
|
$assertions = $this->assertions;
|
||
|
$assertions[] = $this;
|
||
|
|
||
|
return new self($this->context, $message, $this->constraint, $assertions);
|
||
|
}
|
||
|
|
||
|
public function assertRaised()
|
||
|
{
|
||
|
$expected = array();
|
||
|
foreach ($this->assertions as $assertion) {
|
||
|
$expected[] = $assertion->getViolation();
|
||
|
}
|
||
|
$expected[] = $this->getViolation();
|
||
|
|
||
|
$violations = iterator_to_array($this->context->getViolations());
|
||
|
|
||
|
Assert::assertSame($expectedCount = count($expected), $violationsCount = count($violations), sprintf('%u violation(s) expected. Got %u.', $expectedCount, $violationsCount));
|
||
|
|
||
|
reset($violations);
|
||
|
|
||
|
foreach ($expected as $violation) {
|
||
|
Assert::assertEquals($violation, current($violations));
|
||
|
next($violations);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function getViolation()
|
||
|
{
|
||
|
return new ConstraintViolation(
|
||
|
null,
|
||
|
$this->message,
|
||
|
$this->parameters,
|
||
|
$this->context->getRoot(),
|
||
|
$this->propertyPath,
|
||
|
$this->invalidValue,
|
||
|
$this->plural,
|
||
|
$this->code,
|
||
|
$this->constraint,
|
||
|
$this->cause
|
||
|
);
|
||
|
}
|
||
|
}
|