Security update for Core, with self-updated composer
[yaffs-website] / vendor / symfony / validator / Tests / Constraints / FileValidatorTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Validator\Tests\Constraints;
13
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;
18
19 abstract class FileValidatorTest extends ConstraintValidatorTestCase
20 {
21     protected $path;
22
23     protected $file;
24
25     protected function createValidator()
26     {
27         return new FileValidator();
28     }
29
30     protected function setUp()
31     {
32         parent::setUp();
33
34         $this->path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'FileValidatorTest';
35         $this->file = fopen($this->path, 'w');
36         fwrite($this->file, ' ', 1);
37     }
38
39     protected function tearDown()
40     {
41         parent::tearDown();
42
43         if (is_resource($this->file)) {
44             fclose($this->file);
45         }
46
47         if (file_exists($this->path)) {
48             unlink($this->path);
49         }
50
51         $this->path = null;
52         $this->file = null;
53     }
54
55     public function testNullIsValid()
56     {
57         $this->validator->validate(null, new File());
58
59         $this->assertNoViolation();
60     }
61
62     public function testEmptyStringIsValid()
63     {
64         $this->validator->validate('', new File());
65
66         $this->assertNoViolation();
67     }
68
69     /**
70      * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
71      */
72     public function testExpectsStringCompatibleTypeOrFile()
73     {
74         $this->validator->validate(new \stdClass(), new File());
75     }
76
77     public function testValidFile()
78     {
79         $this->validator->validate($this->path, new File());
80
81         $this->assertNoViolation();
82     }
83
84     public function testValidUploadedfile()
85     {
86         $file = new UploadedFile($this->path, 'originalName', null, null, null, true);
87         $this->validator->validate($file, new File());
88
89         $this->assertNoViolation();
90     }
91
92     public function provideMaxSizeExceededTests()
93     {
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         // -----------+--------------------------------------------------------
106
107         // As you see, we have two interesting borders:
108
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
111
112         // Analogous for kB/MB.
113
114         // Prior to Symfony 2.5, violation messages are always displayed in the
115         // same unit used to specify the limit.
116
117         // As of Symfony 2.5, the above logic is implemented.
118         return array(
119             // limit in bytes
120             array(1001, 1000, '1001', '1000', 'bytes'),
121             array(1004, 1000, '1004', '1000', 'bytes'),
122             array(1005, 1000, '1.01', '1', 'kB'),
123
124             array(1000001, 1000000, '1000001', '1000000', 'bytes'),
125             array(1004999, 1000000, '1005', '1000', 'kB'),
126             array(1005000, 1000000, '1.01', '1', 'MB'),
127
128             // limit in kB
129             array(1001, '1k', '1001', '1000', 'bytes'),
130             array(1004, '1k', '1004', '1000', 'bytes'),
131             array(1005, '1k', '1.01', '1', 'kB'),
132
133             array(1000001, '1000k', '1000001', '1000000', 'bytes'),
134             array(1004999, '1000k', '1005', '1000', 'kB'),
135             array(1005000, '1000k', '1.01', '1', 'MB'),
136
137             // limit in MB
138             array(1000001, '1M', '1000001', '1000000', 'bytes'),
139             array(1004999, '1M', '1005', '1000', 'kB'),
140             array(1005000, '1M', '1.01', '1', 'MB'),
141
142             // limit in KiB
143             array(1025, '1Ki', '1025', '1024', 'bytes'),
144             array(1029, '1Ki', '1029', '1024', 'bytes'),
145             array(1030, '1Ki', '1.01', '1', 'KiB'),
146
147             array(1048577, '1024Ki', '1048577', '1048576', 'bytes'),
148             array(1053818, '1024Ki', '1029.12', '1024', 'KiB'),
149             array(1053819, '1024Ki', '1.01', '1', 'MiB'),
150
151             // limit in MiB
152             array(1048577, '1Mi', '1048577', '1048576', 'bytes'),
153             array(1053818, '1Mi', '1029.12', '1024', 'KiB'),
154             array(1053819, '1Mi', '1.01', '1', 'MiB'),
155         );
156     }
157
158     /**
159      * @dataProvider provideMaxSizeExceededTests
160      */
161     public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
162     {
163         fseek($this->file, $bytesWritten - 1, SEEK_SET);
164         fwrite($this->file, '0');
165         fclose($this->file);
166
167         $constraint = new File(array(
168             'maxSize' => $limit,
169             'maxSizeMessage' => 'myMessage',
170         ));
171
172         $this->validator->validate($this->getFile($this->path), $constraint);
173
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)
180             ->assertRaised();
181     }
182
183     public function provideMaxSizeNotExceededTests()
184     {
185         return array(
186             // limit in bytes
187             array(1000, 1000),
188             array(1000000, 1000000),
189
190             // limit in kB
191             array(1000, '1k'),
192             array(1000000, '1000k'),
193
194             // limit in MB
195             array(1000000, '1M'),
196
197             // limit in KiB
198             array(1024, '1Ki'),
199             array(1048576, '1024Ki'),
200
201             // limit in MiB
202             array(1048576, '1Mi'),
203         );
204     }
205
206     /**
207      * @dataProvider provideMaxSizeNotExceededTests
208      */
209     public function testMaxSizeNotExceeded($bytesWritten, $limit)
210     {
211         fseek($this->file, $bytesWritten - 1, SEEK_SET);
212         fwrite($this->file, '0');
213         fclose($this->file);
214
215         $constraint = new File(array(
216             'maxSize' => $limit,
217             'maxSizeMessage' => 'myMessage',
218         ));
219
220         $this->validator->validate($this->getFile($this->path), $constraint);
221
222         $this->assertNoViolation();
223     }
224
225     /**
226      * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
227      */
228     public function testInvalidMaxSize()
229     {
230         $constraint = new File(array(
231             'maxSize' => '1abc',
232         ));
233
234         $this->validator->validate($this->path, $constraint);
235     }
236
237     public function provideBinaryFormatTests()
238     {
239         return array(
240             array(11, 10, null, '11', '10', 'bytes'),
241             array(11, 10, true, '11', '10', 'bytes'),
242             array(11, 10, false, '11', '10', 'bytes'),
243
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'),
248
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'),
252
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'),
256         );
257     }
258
259     /**
260      * @dataProvider provideBinaryFormatTests
261      */
262     public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix)
263     {
264         fseek($this->file, $bytesWritten - 1, SEEK_SET);
265         fwrite($this->file, '0');
266         fclose($this->file);
267
268         $constraint = new File(array(
269             'maxSize' => $limit,
270             'binaryFormat' => $binaryFormat,
271             'maxSizeMessage' => 'myMessage',
272         ));
273
274         $this->validator->validate($this->getFile($this->path), $constraint);
275
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)
282             ->assertRaised();
283     }
284
285     public function testValidMimeType()
286     {
287         $file = $this
288             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
289             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
290             ->getMock();
291         $file
292             ->expects($this->once())
293             ->method('getPathname')
294             ->will($this->returnValue($this->path));
295         $file
296             ->expects($this->once())
297             ->method('getMimeType')
298             ->will($this->returnValue('image/jpg'));
299
300         $constraint = new File(array(
301             'mimeTypes' => array('image/png', 'image/jpg'),
302         ));
303
304         $this->validator->validate($file, $constraint);
305
306         $this->assertNoViolation();
307     }
308
309     public function testValidWildcardMimeType()
310     {
311         $file = $this
312             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
313             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
314             ->getMock();
315         $file
316             ->expects($this->once())
317             ->method('getPathname')
318             ->will($this->returnValue($this->path));
319         $file
320             ->expects($this->once())
321             ->method('getMimeType')
322             ->will($this->returnValue('image/jpg'));
323
324         $constraint = new File(array(
325             'mimeTypes' => array('image/*'),
326         ));
327
328         $this->validator->validate($file, $constraint);
329
330         $this->assertNoViolation();
331     }
332
333     public function testInvalidMimeType()
334     {
335         $file = $this
336             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
337             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
338             ->getMock();
339         $file
340             ->expects($this->once())
341             ->method('getPathname')
342             ->will($this->returnValue($this->path));
343         $file
344             ->expects($this->once())
345             ->method('getMimeType')
346             ->will($this->returnValue('application/pdf'));
347
348         $constraint = new File(array(
349             'mimeTypes' => array('image/png', 'image/jpg'),
350             'mimeTypesMessage' => 'myMessage',
351         ));
352
353         $this->validator->validate($file, $constraint);
354
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)
360             ->assertRaised();
361     }
362
363     public function testInvalidWildcardMimeType()
364     {
365         $file = $this
366             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
367             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
368             ->getMock();
369         $file
370             ->expects($this->once())
371             ->method('getPathname')
372             ->will($this->returnValue($this->path));
373         $file
374             ->expects($this->once())
375             ->method('getMimeType')
376             ->will($this->returnValue('application/pdf'));
377
378         $constraint = new File(array(
379             'mimeTypes' => array('image/*', 'image/jpg'),
380             'mimeTypesMessage' => 'myMessage',
381         ));
382
383         $this->validator->validate($file, $constraint);
384
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)
390             ->assertRaised();
391     }
392
393     public function testDisallowEmpty()
394     {
395         ftruncate($this->file, 0);
396
397         $constraint = new File(array(
398             'disallowEmptyMessage' => 'myMessage',
399         ));
400
401         $this->validator->validate($this->getFile($this->path), $constraint);
402
403         $this->buildViolation('myMessage')
404             ->setParameter('{{ file }}', '"'.$this->path.'"')
405             ->setCode(File::EMPTY_ERROR)
406             ->assertRaised();
407     }
408
409     /**
410      * @dataProvider uploadedFileErrorProvider
411      */
412     public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
413     {
414         $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
415
416         $constraint = new File(array(
417             $message => 'myMessage',
418             'maxSize' => $maxSize,
419         ));
420
421         $this->validator->validate($file, $constraint);
422
423         $this->buildViolation('myMessage')
424             ->setParameters($params)
425             ->setCode($error)
426             ->assertRaised();
427     }
428
429     public function uploadedFileErrorProvider()
430     {
431         $tests = array(
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'),
438         );
439
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',
445             ));
446
447             // it should use the smaller limitation (maxSize option in this case)
448             $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
449                 '{{ limit }}' => 1,
450                 '{{ suffix }}' => 'bytes',
451             ), '1');
452
453             // it correctly parses the maxSize option and not only uses simple string comparison
454             // 1000M should be bigger than the ini value
455             $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
456                 '{{ limit }}' => UploadedFile::getMaxFilesize() / 1048576,
457                 '{{ suffix }}' => 'MiB',
458             ), '1000M');
459
460             // it correctly parses the maxSize option and not only uses simple string comparison
461             // 1000M should be bigger than the ini value
462             $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
463                 '{{ limit }}' => '0.1',
464                 '{{ suffix }}' => 'MB',
465             ), '100K');
466         }
467
468         return $tests;
469     }
470
471     abstract protected function getFile($filename);
472 }