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\Validation;
19 abstract class FileValidatorTest extends AbstractConstraintValidatorTest
25 protected function getApiVersion()
27 return Validation::API_VERSION_2_5;
30 protected function createValidator()
32 return new FileValidator();
35 protected function setUp()
39 $this->path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'FileValidatorTest';
40 $this->file = fopen($this->path, 'w');
41 fwrite($this->file, ' ', 1);
44 protected function tearDown()
48 if (is_resource($this->file)) {
52 if (file_exists($this->path)) {
60 public function testNullIsValid()
62 $this->validator->validate(null, new File());
64 $this->assertNoViolation();
67 public function testEmptyStringIsValid()
69 $this->validator->validate('', new File());
71 $this->assertNoViolation();
75 * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
77 public function testExpectsStringCompatibleTypeOrFile()
79 $this->validator->validate(new \stdClass(), new File());
82 public function testValidFile()
84 $this->validator->validate($this->path, new File());
86 $this->assertNoViolation();
89 public function testValidUploadedfile()
91 $file = new UploadedFile($this->path, 'originalName', null, null, null, true);
92 $this->validator->validate($file, new File());
94 $this->assertNoViolation();
97 public function provideMaxSizeExceededTests()
99 // We have various interesting limit - size combinations to test.
100 // Assume a limit of 1000 bytes (1 kB). Then the following table
101 // lists the violation messages for different file sizes:
102 // -----------+--------------------------------------------------------
103 // Size | Violation Message
104 // -----------+--------------------------------------------------------
105 // 1000 bytes | No violation
106 // 1001 bytes | "Size of 1001 bytes exceeded limit of 1000 bytes"
107 // 1004 bytes | "Size of 1004 bytes exceeded limit of 1000 bytes"
108 // | NOT: "Size of 1 kB exceeded limit of 1 kB"
109 // 1005 bytes | "Size of 1.01 kB exceeded limit of 1 kB"
110 // -----------+--------------------------------------------------------
112 // As you see, we have two interesting borders:
114 // 1000/1001 - The border as of which a violation occurs
115 // 1004/1005 - The border as of which the message can be rounded to kB
117 // Analogous for kB/MB.
119 // Prior to Symfony 2.5, violation messages are always displayed in the
120 // same unit used to specify the limit.
122 // As of Symfony 2.5, the above logic is implemented.
125 array(1001, 1000, '1001', '1000', 'bytes'),
126 array(1004, 1000, '1004', '1000', 'bytes'),
127 array(1005, 1000, '1.01', '1', 'kB'),
129 array(1000001, 1000000, '1000001', '1000000', 'bytes'),
130 array(1004999, 1000000, '1005', '1000', 'kB'),
131 array(1005000, 1000000, '1.01', '1', 'MB'),
134 array(1001, '1k', '1001', '1000', 'bytes'),
135 array(1004, '1k', '1004', '1000', 'bytes'),
136 array(1005, '1k', '1.01', '1', 'kB'),
138 array(1000001, '1000k', '1000001', '1000000', 'bytes'),
139 array(1004999, '1000k', '1005', '1000', 'kB'),
140 array(1005000, '1000k', '1.01', '1', 'MB'),
143 array(1000001, '1M', '1000001', '1000000', 'bytes'),
144 array(1004999, '1M', '1005', '1000', 'kB'),
145 array(1005000, '1M', '1.01', '1', 'MB'),
148 array(1025, '1Ki', '1025', '1024', 'bytes'),
149 array(1029, '1Ki', '1029', '1024', 'bytes'),
150 array(1030, '1Ki', '1.01', '1', 'KiB'),
152 array(1048577, '1024Ki', '1048577', '1048576', 'bytes'),
153 array(1053818, '1024Ki', '1029.12', '1024', 'KiB'),
154 array(1053819, '1024Ki', '1.01', '1', 'MiB'),
157 array(1048577, '1Mi', '1048577', '1048576', 'bytes'),
158 array(1053818, '1Mi', '1029.12', '1024', 'KiB'),
159 array(1053819, '1Mi', '1.01', '1', 'MiB'),
164 * @dataProvider provideMaxSizeExceededTests
166 public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
168 fseek($this->file, $bytesWritten - 1, SEEK_SET);
169 fwrite($this->file, '0');
172 $constraint = new File(array(
174 'maxSizeMessage' => 'myMessage',
177 $this->validator->validate($this->getFile($this->path), $constraint);
179 $this->buildViolation('myMessage')
180 ->setParameter('{{ limit }}', $limitAsString)
181 ->setParameter('{{ size }}', $sizeAsString)
182 ->setParameter('{{ suffix }}', $suffix)
183 ->setParameter('{{ file }}', '"'.$this->path.'"')
184 ->setCode(File::TOO_LARGE_ERROR)
188 public function provideMaxSizeNotExceededTests()
193 array(1000000, 1000000),
197 array(1000000, '1000k'),
200 array(1000000, '1M'),
204 array(1048576, '1024Ki'),
207 array(1048576, '1Mi'),
212 * @dataProvider provideMaxSizeNotExceededTests
214 public function testMaxSizeNotExceeded($bytesWritten, $limit)
216 fseek($this->file, $bytesWritten - 1, SEEK_SET);
217 fwrite($this->file, '0');
220 $constraint = new File(array(
222 'maxSizeMessage' => 'myMessage',
225 $this->validator->validate($this->getFile($this->path), $constraint);
227 $this->assertNoViolation();
231 * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
233 public function testInvalidMaxSize()
235 $constraint = new File(array(
239 $this->validator->validate($this->path, $constraint);
242 public function provideBinaryFormatTests()
245 array(11, 10, null, '11', '10', 'bytes'),
246 array(11, 10, true, '11', '10', 'bytes'),
247 array(11, 10, false, '11', '10', 'bytes'),
249 // round(size) == 1.01kB, limit == 1kB
250 array(ceil(1000 * 1.01), 1000, null, '1.01', '1', 'kB'),
251 array(ceil(1000 * 1.01), '1k', null, '1.01', '1', 'kB'),
252 array(ceil(1024 * 1.01), '1Ki', null, '1.01', '1', 'KiB'),
254 array(ceil(1024 * 1.01), 1024, true, '1.01', '1', 'KiB'),
255 array(ceil(1024 * 1.01 * 1000), '1024k', true, '1010', '1000', 'KiB'),
256 array(ceil(1024 * 1.01), '1Ki', true, '1.01', '1', 'KiB'),
258 array(ceil(1000 * 1.01), 1000, false, '1.01', '1', 'kB'),
259 array(ceil(1000 * 1.01), '1k', false, '1.01', '1', 'kB'),
260 array(ceil(1024 * 1.01 * 10), '10Ki', false, '10.34', '10.24', 'kB'),
265 * @dataProvider provideBinaryFormatTests
267 public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix)
269 fseek($this->file, $bytesWritten - 1, SEEK_SET);
270 fwrite($this->file, '0');
273 $constraint = new File(array(
275 'binaryFormat' => $binaryFormat,
276 'maxSizeMessage' => 'myMessage',
279 $this->validator->validate($this->getFile($this->path), $constraint);
281 $this->buildViolation('myMessage')
282 ->setParameter('{{ limit }}', $limitAsString)
283 ->setParameter('{{ size }}', $sizeAsString)
284 ->setParameter('{{ suffix }}', $suffix)
285 ->setParameter('{{ file }}', '"'.$this->path.'"')
286 ->setCode(File::TOO_LARGE_ERROR)
290 public function testValidMimeType()
293 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
294 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
297 ->expects($this->once())
298 ->method('getPathname')
299 ->will($this->returnValue($this->path));
301 ->expects($this->once())
302 ->method('getMimeType')
303 ->will($this->returnValue('image/jpg'));
305 $constraint = new File(array(
306 'mimeTypes' => array('image/png', 'image/jpg'),
309 $this->validator->validate($file, $constraint);
311 $this->assertNoViolation();
314 public function testValidWildcardMimeType()
317 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
318 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
321 ->expects($this->once())
322 ->method('getPathname')
323 ->will($this->returnValue($this->path));
325 ->expects($this->once())
326 ->method('getMimeType')
327 ->will($this->returnValue('image/jpg'));
329 $constraint = new File(array(
330 'mimeTypes' => array('image/*'),
333 $this->validator->validate($file, $constraint);
335 $this->assertNoViolation();
338 public function testInvalidMimeType()
341 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
342 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
345 ->expects($this->once())
346 ->method('getPathname')
347 ->will($this->returnValue($this->path));
349 ->expects($this->once())
350 ->method('getMimeType')
351 ->will($this->returnValue('application/pdf'));
353 $constraint = new File(array(
354 'mimeTypes' => array('image/png', 'image/jpg'),
355 'mimeTypesMessage' => 'myMessage',
358 $this->validator->validate($file, $constraint);
360 $this->buildViolation('myMessage')
361 ->setParameter('{{ type }}', '"application/pdf"')
362 ->setParameter('{{ types }}', '"image/png", "image/jpg"')
363 ->setParameter('{{ file }}', '"'.$this->path.'"')
364 ->setCode(File::INVALID_MIME_TYPE_ERROR)
368 public function testInvalidWildcardMimeType()
371 ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
372 ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
375 ->expects($this->once())
376 ->method('getPathname')
377 ->will($this->returnValue($this->path));
379 ->expects($this->once())
380 ->method('getMimeType')
381 ->will($this->returnValue('application/pdf'));
383 $constraint = new File(array(
384 'mimeTypes' => array('image/*', 'image/jpg'),
385 'mimeTypesMessage' => 'myMessage',
388 $this->validator->validate($file, $constraint);
390 $this->buildViolation('myMessage')
391 ->setParameter('{{ type }}', '"application/pdf"')
392 ->setParameter('{{ types }}', '"image/*", "image/jpg"')
393 ->setParameter('{{ file }}', '"'.$this->path.'"')
394 ->setCode(File::INVALID_MIME_TYPE_ERROR)
398 public function testDisallowEmpty()
400 ftruncate($this->file, 0);
402 $constraint = new File(array(
403 'disallowEmptyMessage' => 'myMessage',
406 $this->validator->validate($this->getFile($this->path), $constraint);
408 $this->buildViolation('myMessage')
409 ->setParameter('{{ file }}', '"'.$this->path.'"')
410 ->setCode(File::EMPTY_ERROR)
415 * @dataProvider uploadedFileErrorProvider
417 public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
419 $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
421 $constraint = new File(array(
422 $message => 'myMessage',
423 'maxSize' => $maxSize,
426 $this->validator->validate($file, $constraint);
428 $this->buildViolation('myMessage')
429 ->setParameters($params)
434 public function uploadedFileErrorProvider()
437 array(UPLOAD_ERR_FORM_SIZE, 'uploadFormSizeErrorMessage'),
438 array(UPLOAD_ERR_PARTIAL, 'uploadPartialErrorMessage'),
439 array(UPLOAD_ERR_NO_FILE, 'uploadNoFileErrorMessage'),
440 array(UPLOAD_ERR_NO_TMP_DIR, 'uploadNoTmpDirErrorMessage'),
441 array(UPLOAD_ERR_CANT_WRITE, 'uploadCantWriteErrorMessage'),
442 array(UPLOAD_ERR_EXTENSION, 'uploadExtensionErrorMessage'),
445 if (class_exists('Symfony\Component\HttpFoundation\File\UploadedFile')) {
446 // when no maxSize is specified on constraint, it should use the ini value
447 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
448 '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576,
449 '{{ suffix }}' => 'MiB',
452 // it should use the smaller limitation (maxSize option in this case)
453 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
455 '{{ suffix }}' => 'bytes',
458 // it correctly parses the maxSize option and not only uses simple string comparison
459 // 1000M should be bigger than the ini value
460 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
461 '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576,
462 '{{ suffix }}' => 'MiB',
465 // it correctly parses the maxSize option and not only uses simple string comparison
466 // 1000M should be bigger than the ini value
467 $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
468 '{{ limit }}' => '0.1',
469 '{{ suffix }}' => 'MB',
476 abstract protected function getFile($filename);