4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Validator\Tests\Constraints;
14 use Symfony\Component\HttpFoundation\File\UploadedFile;
15 use Symfony\Component\Validator\Constraints\File;
16 use Symfony\Component\Validator\Constraints\FileValidator;
17 use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
19 abstract class FileValidatorTest extends ConstraintValidatorTestCase
25 protected function createValidator()
27 return new FileValidator();
30 protected function setUp()
34 $this->path = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'FileValidatorTest';
35 $this->file = fopen($this->path, 'w');
36 fwrite($this->file, ' ', 1);
39 protected function tearDown()
43 if (\is_resource($this->file)) {
47 if (file_exists($this->path)) {
55 public function testNullIsValid()
57 $this->validator->validate(null, new File());
59 $this->assertNoViolation();
62 public function testEmptyStringIsValid()
64 $this->validator->validate('', new File());
66 $this->assertNoViolation();
70 * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
72 public function testExpectsStringCompatibleTypeOrFile()
74 $this->validator->validate(new \stdClass(), new File());
77 public function testValidFile()
79 $this->validator->validate($this->path, new File());
81 $this->assertNoViolation();
84 public function testValidUploadedfile()
86 $file = new UploadedFile($this->path, 'originalName', null, null, null, true);
87 $this->validator->validate($file, new File());
89 $this->assertNoViolation();
92 public function provideMaxSizeExceededTests()
94 // We have various interesting limit - size combinations to test.
95 // Assume a limit of 1000 bytes (1 kB). Then the following table
96 // lists the violation messages for different file sizes:
97 // -----------+--------------------------------------------------------
98 // Size | Violation Message
99 // -----------+--------------------------------------------------------
100 // 1000 bytes | No violation
101 // 1001 bytes | "Size of 1001 bytes exceeded limit of 1000 bytes"
102 // 1004 bytes | "Size of 1004 bytes exceeded limit of 1000 bytes"
103 // | NOT: "Size of 1 kB exceeded limit of 1 kB"
104 // 1005 bytes | "Size of 1.01 kB exceeded limit of 1 kB"
105 // -----------+--------------------------------------------------------
107 // As you see, we have two interesting borders:
109 // 1000/1001 - The border as of which a violation occurs
110 // 1004/1005 - The border as of which the message can be rounded to kB
112 // Analogous for kB/MB.
114 // Prior to Symfony 2.5, violation messages are always displayed in the
115 // same unit used to specify the limit.
117 // As of Symfony 2.5, the above logic is implemented.
120 array(1001, 1000, '1001', '1000', 'bytes'),
121 array(1004, 1000, '1004', '1000', 'bytes'),
122 array(1005, 1000, '1.01', '1', 'kB'),
124 array(1000001, 1000000, '1000001', '1000000', 'bytes'),
125 array(1004999, 1000000, '1005', '1000', 'kB'),
126 array(1005000, 1000000, '1.01', '1', 'MB'),
129 array(1001, '1k', '1001', '1000', 'bytes'),
130 array(1004, '1k', '1004', '1000', 'bytes'),
131 array(1005, '1k', '1.01', '1', 'kB'),
133 array(1000001, '1000k', '1000001', '1000000', 'bytes'),
134 array(1004999, '1000k', '1005', '1000', 'kB'),
135 array(1005000, '1000k', '1.01', '1', 'MB'),
138 array(1000001, '1M', '1000001', '1000000', 'bytes'),
139 array(1004999, '1M', '1005', '1000', 'kB'),
140 array(1005000, '1M', '1.01', '1', 'MB'),
143 array(1025, '1Ki', '1025', '1024', 'bytes'),
144 array(1029, '1Ki', '1029', '1024', 'bytes'),
145 array(1030, '1Ki', '1.01', '1', 'KiB'),
147 array(1048577, '1024Ki', '1048577', '1048576', 'bytes'),
148 array(1053818, '1024Ki', '1029.12', '1024', 'KiB'),
149 array(1053819, '1024Ki', '1.01', '1', 'MiB'),
152 array(1048577, '1Mi', '1048577', '1048576', 'bytes'),
153 array(1053818, '1Mi', '1029.12', '1024', 'KiB'),
154 array(1053819, '1Mi', '1.01', '1', 'MiB'),
159 * @dataProvider provideMaxSizeExceededTests
161 public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
163 fseek($this->file, $bytesWritten - 1, SEEK_SET);
164 fwrite($this->file, '0');
167 $constraint = new File(array(
169 'maxSizeMessage' => 'myMessage',
172 $this->validator->validate($this->getFile($this->path), $constraint);
174 $this->buildViolation('myMessage')
175 ->setParameter('{{ limit }}', $limitAsString)
176 ->setParameter('{{ size }}', $sizeAsString)
177 ->setParameter('{{ suffix }}', $suffix)
178 ->setParameter('{{ file }}', '"'.$this->path.'"')
179 ->setCode(File::TOO_LARGE_ERROR)
183 public function provideMaxSizeNotExceededTests()
188 array(1000000, 1000000),
192 array(1000000, '1000k'),
195 array(1000000, '1M'),
199 array(1048576, '1024Ki'),
202 array(1048576, '1Mi'),
207 * @dataProvider provideMaxSizeNotExceededTests
209 public function testMaxSizeNotExceeded($bytesWritten, $limit)
211 fseek($this->file, $bytesWritten - 1, SEEK_SET);
212 fwrite($this->file, '0');
215 $constraint = new File(array(
217 'maxSizeMessage' => 'myMessage',
220 $this->validator->validate($this->getFile($this->path), $constraint);
222 $this->assertNoViolation();
226 * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
228 public function testInvalidMaxSize()
230 $constraint = new File(array(
234 $this->validator->validate($this->path, $constraint);
237 public function provideBinaryFormatTests()
240 array(11, 10, null, '11', '10', 'bytes'),
241 array(11, 10, true, '11', '10', 'bytes'),
242 array(11, 10, false, '11', '10', 'bytes'),
244 // round(size) == 1.01kB, limit == 1kB
245 array(ceil(1000 * 1.01), 1000, null, '1.01', '1', 'kB'),
246 array(ceil(1000 * 1.01), '1k', null, '1.01', '1', 'kB'),
247 array(ceil(1024 * 1.01), '1Ki', null, '1.01', '1', 'KiB'),
249 array(ceil(1024 * 1.01), 1024, true, '1.01', '1', 'KiB'),
250 array(ceil(1024 * 1.01 * 1000), '1024k', true, '1010', '1000', 'KiB'),
251 array(ceil(1024 * 1.01), '1Ki', true, '1.01', '1', 'KiB'),
253 array(ceil(1000 * 1.01), 1000, false, '1.01', '1', 'kB'),
254 array(ceil(1000 * 1.01), '1k', false, '1.01', '1', 'kB'),
255 array(ceil(1024 * 1.01 * 10), '10Ki', false, '10.34', '10.24', 'kB'),
260 * @dataProvider provideBinaryFormatTests
262 public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix)
264 fseek($this->file, $bytesWritten - 1, SEEK_SET);
265 fwrite($this->file, '0');
268 $constraint = new File(array(
270 'binaryFormat' => $binaryFormat,
271 'maxSizeMessage' => 'myMessage',
274 $this->validator->validate($this->getFile($this->path), $constraint);
276 $this->buildViolation('myMessage')
277 ->setParameter('{{ limit }}', $limitAsString)
278 ->setParameter('{{ size }}', $sizeAsString)
279 ->setParameter('{{ suffix }}', $suffix)
280 ->setParameter('{{ file }}', '"'.$this->path.'"')
281 ->setCode(File::TOO_LARGE_ERROR)
285 public function testValidMimeType()
288 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
289 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
292 ->expects($this->once())
293 ->method('getPathname')
294 ->will($this->returnValue($this->path));
296 ->expects($this->once())
297 ->method('getMimeType')
298 ->will($this->returnValue('image/jpg'));
300 $constraint = new File(array(
301 'mimeTypes' => array('image/png', 'image/jpg'),
304 $this->validator->validate($file, $constraint);
306 $this->assertNoViolation();
309 public function testValidWildcardMimeType()
312 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
313 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
316 ->expects($this->once())
317 ->method('getPathname')
318 ->will($this->returnValue($this->path));
320 ->expects($this->once())
321 ->method('getMimeType')
322 ->will($this->returnValue('image/jpg'));
324 $constraint = new File(array(
325 'mimeTypes' => array('image/*'),
328 $this->validator->validate($file, $constraint);
330 $this->assertNoViolation();
333 public function testInvalidMimeType()
336 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
337 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
340 ->expects($this->once())
341 ->method('getPathname')
342 ->will($this->returnValue($this->path));
344 ->expects($this->once())
345 ->method('getMimeType')
346 ->will($this->returnValue('application/pdf'));
348 $constraint = new File(array(
349 'mimeTypes' => array('image/png', 'image/jpg'),
350 'mimeTypesMessage' => 'myMessage',
353 $this->validator->validate($file, $constraint);
355 $this->buildViolation('myMessage')
356 ->setParameter('{{ type }}', '"application/pdf"')
357 ->setParameter('{{ types }}', '"image/png", "image/jpg"')
358 ->setParameter('{{ file }}', '"'.$this->path.'"')
359 ->setCode(File::INVALID_MIME_TYPE_ERROR)
363 public function testInvalidWildcardMimeType()
366 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
367 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
370 ->expects($this->once())
371 ->method('getPathname')
372 ->will($this->returnValue($this->path));
374 ->expects($this->once())
375 ->method('getMimeType')
376 ->will($this->returnValue('application/pdf'));
378 $constraint = new File(array(
379 'mimeTypes' => array('image/*', 'image/jpg'),
380 'mimeTypesMessage' => 'myMessage',
383 $this->validator->validate($file, $constraint);
385 $this->buildViolation('myMessage')
386 ->setParameter('{{ type }}', '"application/pdf"')
387 ->setParameter('{{ types }}', '"image/*", "image/jpg"')
388 ->setParameter('{{ file }}', '"'.$this->path.'"')
389 ->setCode(File::INVALID_MIME_TYPE_ERROR)
393 public function testDisallowEmpty()
395 ftruncate($this->file, 0);
397 $constraint = new File(array(
398 'disallowEmptyMessage' => 'myMessage',
401 $this->validator->validate($this->getFile($this->path), $constraint);
403 $this->buildViolation('myMessage')
404 ->setParameter('{{ file }}', '"'.$this->path.'"')
405 ->setCode(File::EMPTY_ERROR)
410 * @dataProvider uploadedFileErrorProvider
412 public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
414 $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
416 $constraint = new File(array(
417 $message => 'myMessage',
418 'maxSize' => $maxSize,
421 $this->validator->validate($file, $constraint);
423 $this->buildViolation('myMessage')
424 ->setParameters($params)
429 public function uploadedFileErrorProvider()
432 array(UPLOAD_ERR_FORM_SIZE, 'uploadFormSizeErrorMessage'),
433 array(UPLOAD_ERR_PARTIAL, 'uploadPartialErrorMessage'),
434 array(UPLOAD_ERR_NO_FILE, 'uploadNoFileErrorMessage'),
435 array(UPLOAD_ERR_NO_TMP_DIR, 'uploadNoTmpDirErrorMessage'),
436 array(UPLOAD_ERR_CANT_WRITE, 'uploadCantWriteErrorMessage'),
437 array(UPLOAD_ERR_EXTENSION, 'uploadExtensionErrorMessage'),
440 if (class_exists('Symfony\Component\HttpFoundation\File\UploadedFile')) {
441 // when no maxSize is specified on constraint, it should use the ini value
442 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
443 '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576,
444 '{{ suffix }}' => 'MiB',
447 // it should use the smaller limitation (maxSize option in this case)
448 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
450 '{{ suffix }}' => 'bytes',
453 // access FileValidator::factorizeSizes() private method to format max file size
454 $reflection = new \ReflectionClass(\get_class(new FileValidator()));
455 $method = $reflection->getMethod('factorizeSizes');
456 $method->setAccessible(true);
457 list($sizeAsString, $limit, $suffix) = $method->invokeArgs(new FileValidator(), array(0, UploadedFile::getMaxFilesize(), false));
459 // it correctly parses the maxSize option and not only uses simple string comparison
460 // 1000M should be bigger than the ini value
461 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
462 '{{ limit }}' => $limit,
463 '{{ suffix }}' => $suffix,
466 // it correctly parses the maxSize option and not only uses simple string comparison
467 // 1000M should be bigger than the ini value
468 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
469 '{{ limit }}' => '0.1',
470 '{{ suffix }}' => 'MB',
477 abstract protected function getFile($filename);