Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Validator\Test;
|
Chris@0
|
13
|
Chris@0
|
14 use PHPUnit\Framework\Assert;
|
Chris@0
|
15 use PHPUnit\Framework\TestCase;
|
Chris@0
|
16 use Symfony\Component\Validator\Constraint;
|
Chris@0
|
17 use Symfony\Component\Validator\Constraints\NotNull;
|
Chris@0
|
18 use Symfony\Component\Validator\ConstraintValidatorInterface;
|
Chris@0
|
19 use Symfony\Component\Validator\ConstraintViolation;
|
Chris@0
|
20 use Symfony\Component\Validator\Context\ExecutionContext;
|
Chris@0
|
21 use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
Chris@0
|
22 use Symfony\Component\Validator\Mapping\ClassMetadata;
|
Chris@0
|
23 use Symfony\Component\Validator\Mapping\PropertyMetadata;
|
Chris@0
|
24
|
Chris@0
|
25 /**
|
Chris@0
|
26 * A test case to ease testing Constraint Validators.
|
Chris@0
|
27 *
|
Chris@0
|
28 * @author Bernhard Schussek <bschussek@gmail.com>
|
Chris@0
|
29 */
|
Chris@0
|
30 abstract class ConstraintValidatorTestCase extends TestCase
|
Chris@0
|
31 {
|
Chris@0
|
32 /**
|
Chris@0
|
33 * @var ExecutionContextInterface
|
Chris@0
|
34 */
|
Chris@0
|
35 protected $context;
|
Chris@0
|
36
|
Chris@0
|
37 /**
|
Chris@0
|
38 * @var ConstraintValidatorInterface
|
Chris@0
|
39 */
|
Chris@0
|
40 protected $validator;
|
Chris@0
|
41
|
Chris@0
|
42 protected $group;
|
Chris@0
|
43 protected $metadata;
|
Chris@0
|
44 protected $object;
|
Chris@0
|
45 protected $value;
|
Chris@0
|
46 protected $root;
|
Chris@0
|
47 protected $propertyPath;
|
Chris@0
|
48 protected $constraint;
|
Chris@0
|
49 protected $defaultTimezone;
|
Chris@0
|
50
|
Chris@0
|
51 protected function setUp()
|
Chris@0
|
52 {
|
Chris@0
|
53 $this->group = 'MyGroup';
|
Chris@0
|
54 $this->metadata = null;
|
Chris@0
|
55 $this->object = null;
|
Chris@0
|
56 $this->value = 'InvalidValue';
|
Chris@0
|
57 $this->root = 'root';
|
Chris@0
|
58 $this->propertyPath = 'property.path';
|
Chris@0
|
59
|
Chris@0
|
60 // Initialize the context with some constraint so that we can
|
Chris@0
|
61 // successfully build a violation.
|
Chris@0
|
62 $this->constraint = new NotNull();
|
Chris@0
|
63
|
Chris@0
|
64 $this->context = $this->createContext();
|
Chris@0
|
65 $this->validator = $this->createValidator();
|
Chris@0
|
66 $this->validator->initialize($this->context);
|
Chris@0
|
67
|
Chris@0
|
68 \Locale::setDefault('en');
|
Chris@0
|
69
|
Chris@0
|
70 $this->setDefaultTimezone('UTC');
|
Chris@0
|
71 }
|
Chris@0
|
72
|
Chris@0
|
73 protected function tearDown()
|
Chris@0
|
74 {
|
Chris@0
|
75 $this->restoreDefaultTimezone();
|
Chris@0
|
76 }
|
Chris@0
|
77
|
Chris@0
|
78 protected function setDefaultTimezone($defaultTimezone)
|
Chris@0
|
79 {
|
Chris@0
|
80 // Make sure this method can not be called twice before calling
|
Chris@0
|
81 // also restoreDefaultTimezone()
|
Chris@0
|
82 if (null === $this->defaultTimezone) {
|
Chris@0
|
83 $this->defaultTimezone = date_default_timezone_get();
|
Chris@0
|
84 date_default_timezone_set($defaultTimezone);
|
Chris@0
|
85 }
|
Chris@0
|
86 }
|
Chris@0
|
87
|
Chris@0
|
88 protected function restoreDefaultTimezone()
|
Chris@0
|
89 {
|
Chris@0
|
90 if (null !== $this->defaultTimezone) {
|
Chris@0
|
91 date_default_timezone_set($this->defaultTimezone);
|
Chris@0
|
92 $this->defaultTimezone = null;
|
Chris@0
|
93 }
|
Chris@0
|
94 }
|
Chris@0
|
95
|
Chris@0
|
96 protected function createContext()
|
Chris@0
|
97 {
|
Chris@0
|
98 $translator = $this->getMockBuilder('Symfony\Component\Translation\TranslatorInterface')->getMock();
|
Chris@0
|
99 $validator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ValidatorInterface')->getMock();
|
Chris@0
|
100 $contextualValidator = $this->getMockBuilder('Symfony\Component\Validator\Validator\ContextualValidatorInterface')->getMock();
|
Chris@0
|
101
|
Chris@0
|
102 $context = new ExecutionContext($validator, $this->root, $translator);
|
Chris@0
|
103 $context->setGroup($this->group);
|
Chris@0
|
104 $context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
Chris@0
|
105 $context->setConstraint($this->constraint);
|
Chris@0
|
106
|
Chris@0
|
107 $validator->expects($this->any())
|
Chris@0
|
108 ->method('inContext')
|
Chris@0
|
109 ->with($context)
|
Chris@0
|
110 ->will($this->returnValue($contextualValidator));
|
Chris@0
|
111
|
Chris@0
|
112 return $context;
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@0
|
115 protected function setGroup($group)
|
Chris@0
|
116 {
|
Chris@0
|
117 $this->group = $group;
|
Chris@0
|
118 $this->context->setGroup($group);
|
Chris@0
|
119 }
|
Chris@0
|
120
|
Chris@0
|
121 protected function setObject($object)
|
Chris@0
|
122 {
|
Chris@0
|
123 $this->object = $object;
|
Chris@0
|
124 $this->metadata = is_object($object)
|
Chris@0
|
125 ? new ClassMetadata(get_class($object))
|
Chris@0
|
126 : null;
|
Chris@0
|
127
|
Chris@0
|
128 $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 protected function setProperty($object, $property)
|
Chris@0
|
132 {
|
Chris@0
|
133 $this->object = $object;
|
Chris@0
|
134 $this->metadata = is_object($object)
|
Chris@0
|
135 ? new PropertyMetadata(get_class($object), $property)
|
Chris@0
|
136 : null;
|
Chris@0
|
137
|
Chris@0
|
138 $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
Chris@0
|
139 }
|
Chris@0
|
140
|
Chris@0
|
141 protected function setValue($value)
|
Chris@0
|
142 {
|
Chris@0
|
143 $this->value = $value;
|
Chris@0
|
144 $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 protected function setRoot($root)
|
Chris@0
|
148 {
|
Chris@0
|
149 $this->root = $root;
|
Chris@0
|
150 $this->context = $this->createContext();
|
Chris@0
|
151 $this->validator->initialize($this->context);
|
Chris@0
|
152 }
|
Chris@0
|
153
|
Chris@0
|
154 protected function setPropertyPath($propertyPath)
|
Chris@0
|
155 {
|
Chris@0
|
156 $this->propertyPath = $propertyPath;
|
Chris@0
|
157 $this->context->setNode($this->value, $this->object, $this->metadata, $this->propertyPath);
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 protected function expectNoValidate()
|
Chris@0
|
161 {
|
Chris@0
|
162 $validator = $this->context->getValidator()->inContext($this->context);
|
Chris@0
|
163 $validator->expects($this->never())
|
Chris@0
|
164 ->method('atPath');
|
Chris@0
|
165 $validator->expects($this->never())
|
Chris@0
|
166 ->method('validate');
|
Chris@0
|
167 }
|
Chris@0
|
168
|
Chris@0
|
169 protected function expectValidateAt($i, $propertyPath, $value, $group)
|
Chris@0
|
170 {
|
Chris@0
|
171 $validator = $this->context->getValidator()->inContext($this->context);
|
Chris@0
|
172 $validator->expects($this->at(2 * $i))
|
Chris@0
|
173 ->method('atPath')
|
Chris@0
|
174 ->with($propertyPath)
|
Chris@0
|
175 ->will($this->returnValue($validator));
|
Chris@0
|
176 $validator->expects($this->at(2 * $i + 1))
|
Chris@0
|
177 ->method('validate')
|
Chris@0
|
178 ->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
|
Chris@0
|
179 }
|
Chris@0
|
180
|
Chris@0
|
181 protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
|
Chris@0
|
182 {
|
Chris@0
|
183 $contextualValidator = $this->context->getValidator()->inContext($this->context);
|
Chris@0
|
184 $contextualValidator->expects($this->at(2 * $i))
|
Chris@0
|
185 ->method('atPath')
|
Chris@0
|
186 ->with($propertyPath)
|
Chris@0
|
187 ->will($this->returnValue($contextualValidator));
|
Chris@0
|
188 $contextualValidator->expects($this->at(2 * $i + 1))
|
Chris@0
|
189 ->method('validate')
|
Chris@0
|
190 ->with($value, $constraints, $group);
|
Chris@0
|
191 }
|
Chris@0
|
192
|
Chris@0
|
193 protected function assertNoViolation()
|
Chris@0
|
194 {
|
Chris@0
|
195 $this->assertSame(0, $violationsCount = count($this->context->getViolations()), sprintf('0 violation expected. Got %u.', $violationsCount));
|
Chris@0
|
196 }
|
Chris@0
|
197
|
Chris@0
|
198 /**
|
Chris@0
|
199 * @param $message
|
Chris@0
|
200 *
|
Chris@0
|
201 * @return ConstraintViolationAssertion
|
Chris@0
|
202 */
|
Chris@0
|
203 protected function buildViolation($message)
|
Chris@0
|
204 {
|
Chris@0
|
205 return new ConstraintViolationAssertion($this->context, $message, $this->constraint);
|
Chris@0
|
206 }
|
Chris@0
|
207
|
Chris@0
|
208 abstract protected function createValidator();
|
Chris@0
|
209 }
|
Chris@0
|
210
|
Chris@0
|
211 /**
|
Chris@0
|
212 * @internal
|
Chris@0
|
213 */
|
Chris@0
|
214 class ConstraintViolationAssertion
|
Chris@0
|
215 {
|
Chris@0
|
216 /**
|
Chris@0
|
217 * @var ExecutionContextInterface
|
Chris@0
|
218 */
|
Chris@0
|
219 private $context;
|
Chris@0
|
220
|
Chris@0
|
221 /**
|
Chris@0
|
222 * @var ConstraintViolationAssertion[]
|
Chris@0
|
223 */
|
Chris@0
|
224 private $assertions;
|
Chris@0
|
225
|
Chris@0
|
226 private $message;
|
Chris@0
|
227 private $parameters = array();
|
Chris@0
|
228 private $invalidValue = 'InvalidValue';
|
Chris@0
|
229 private $propertyPath = 'property.path';
|
Chris@0
|
230 private $plural;
|
Chris@0
|
231 private $code;
|
Chris@0
|
232 private $constraint;
|
Chris@0
|
233 private $cause;
|
Chris@0
|
234
|
Chris@0
|
235 public function __construct(ExecutionContextInterface $context, $message, Constraint $constraint = null, array $assertions = array())
|
Chris@0
|
236 {
|
Chris@0
|
237 $this->context = $context;
|
Chris@0
|
238 $this->message = $message;
|
Chris@0
|
239 $this->constraint = $constraint;
|
Chris@0
|
240 $this->assertions = $assertions;
|
Chris@0
|
241 }
|
Chris@0
|
242
|
Chris@0
|
243 public function atPath($path)
|
Chris@0
|
244 {
|
Chris@0
|
245 $this->propertyPath = $path;
|
Chris@0
|
246
|
Chris@0
|
247 return $this;
|
Chris@0
|
248 }
|
Chris@0
|
249
|
Chris@0
|
250 public function setParameter($key, $value)
|
Chris@0
|
251 {
|
Chris@0
|
252 $this->parameters[$key] = $value;
|
Chris@0
|
253
|
Chris@0
|
254 return $this;
|
Chris@0
|
255 }
|
Chris@0
|
256
|
Chris@0
|
257 public function setParameters(array $parameters)
|
Chris@0
|
258 {
|
Chris@0
|
259 $this->parameters = $parameters;
|
Chris@0
|
260
|
Chris@0
|
261 return $this;
|
Chris@0
|
262 }
|
Chris@0
|
263
|
Chris@0
|
264 public function setTranslationDomain($translationDomain)
|
Chris@0
|
265 {
|
Chris@0
|
266 // no-op for BC
|
Chris@0
|
267
|
Chris@0
|
268 return $this;
|
Chris@0
|
269 }
|
Chris@0
|
270
|
Chris@0
|
271 public function setInvalidValue($invalidValue)
|
Chris@0
|
272 {
|
Chris@0
|
273 $this->invalidValue = $invalidValue;
|
Chris@0
|
274
|
Chris@0
|
275 return $this;
|
Chris@0
|
276 }
|
Chris@0
|
277
|
Chris@0
|
278 public function setPlural($number)
|
Chris@0
|
279 {
|
Chris@0
|
280 $this->plural = $number;
|
Chris@0
|
281
|
Chris@0
|
282 return $this;
|
Chris@0
|
283 }
|
Chris@0
|
284
|
Chris@0
|
285 public function setCode($code)
|
Chris@0
|
286 {
|
Chris@0
|
287 $this->code = $code;
|
Chris@0
|
288
|
Chris@0
|
289 return $this;
|
Chris@0
|
290 }
|
Chris@0
|
291
|
Chris@0
|
292 public function setCause($cause)
|
Chris@0
|
293 {
|
Chris@0
|
294 $this->cause = $cause;
|
Chris@0
|
295
|
Chris@0
|
296 return $this;
|
Chris@0
|
297 }
|
Chris@0
|
298
|
Chris@0
|
299 public function buildNextViolation($message)
|
Chris@0
|
300 {
|
Chris@0
|
301 $assertions = $this->assertions;
|
Chris@0
|
302 $assertions[] = $this;
|
Chris@0
|
303
|
Chris@0
|
304 return new self($this->context, $message, $this->constraint, $assertions);
|
Chris@0
|
305 }
|
Chris@0
|
306
|
Chris@0
|
307 public function assertRaised()
|
Chris@0
|
308 {
|
Chris@0
|
309 $expected = array();
|
Chris@0
|
310 foreach ($this->assertions as $assertion) {
|
Chris@0
|
311 $expected[] = $assertion->getViolation();
|
Chris@0
|
312 }
|
Chris@0
|
313 $expected[] = $this->getViolation();
|
Chris@0
|
314
|
Chris@0
|
315 $violations = iterator_to_array($this->context->getViolations());
|
Chris@0
|
316
|
Chris@0
|
317 Assert::assertSame($expectedCount = count($expected), $violationsCount = count($violations), sprintf('%u violation(s) expected. Got %u.', $expectedCount, $violationsCount));
|
Chris@0
|
318
|
Chris@0
|
319 reset($violations);
|
Chris@0
|
320
|
Chris@0
|
321 foreach ($expected as $violation) {
|
Chris@0
|
322 Assert::assertEquals($violation, current($violations));
|
Chris@0
|
323 next($violations);
|
Chris@0
|
324 }
|
Chris@0
|
325 }
|
Chris@0
|
326
|
Chris@0
|
327 private function getViolation()
|
Chris@0
|
328 {
|
Chris@0
|
329 return new ConstraintViolation(
|
Chris@0
|
330 null,
|
Chris@0
|
331 $this->message,
|
Chris@0
|
332 $this->parameters,
|
Chris@0
|
333 $this->context->getRoot(),
|
Chris@0
|
334 $this->propertyPath,
|
Chris@0
|
335 $this->invalidValue,
|
Chris@0
|
336 $this->plural,
|
Chris@0
|
337 $this->code,
|
Chris@0
|
338 $this->constraint,
|
Chris@0
|
339 $this->cause
|
Chris@0
|
340 );
|
Chris@0
|
341 }
|
Chris@0
|
342 }
|