Yaffs site version 1.1
[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\Validation;
18
19 abstract class FileValidatorTest extends AbstractConstraintValidatorTest
20 {
21     protected $path;
22
23     protected $file;
24
25     protected function getApiVersion()
26     {
27         return Validation::API_VERSION_2_5;
28     }
29
30     protected function createValidator()
31     {
32         return new FileValidator();
33     }
34
35     protected function setUp()
36     {
37         parent::setUp();
38
39         $this->path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'FileValidatorTest';
40         $this->file = fopen($this->path, 'w');
41         fwrite($this->file, ' ', 1);
42     }
43
44     protected function tearDown()
45     {
46         parent::tearDown();
47
48         if (is_resource($this->file)) {
49             fclose($this->file);
50         }
51
52         if (file_exists($this->path)) {
53             unlink($this->path);
54         }
55
56         $this->path = null;
57         $this->file = null;
58     }
59
60     public function testNullIsValid()
61     {
62         $this->validator->validate(null, new File());
63
64         $this->assertNoViolation();
65     }
66
67     public function testEmptyStringIsValid()
68     {
69         $this->validator->validate('', new File());
70
71         $this->assertNoViolation();
72     }
73
74     /**
75      * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
76      */
77     public function testExpectsStringCompatibleTypeOrFile()
78     {
79         $this->validator->validate(new \stdClass(), new File());
80     }
81
82     public function testValidFile()
83     {
84         $this->validator->validate($this->path, new File());
85
86         $this->assertNoViolation();
87     }
88
89     public function testValidUploadedfile()
90     {
91         $file = new UploadedFile($this->path, 'originalName', null, null, null, true);
92         $this->validator->validate($file, new File());
93
94         $this->assertNoViolation();
95     }
96
97     public function provideMaxSizeExceededTests()
98     {
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         // -----------+--------------------------------------------------------
111
112         // As you see, we have two interesting borders:
113
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
116
117         // Analogous for kB/MB.
118
119         // Prior to Symfony 2.5, violation messages are always displayed in the
120         // same unit used to specify the limit.
121
122         // As of Symfony 2.5, the above logic is implemented.
123         return array(
124             // limit in bytes
125             array(1001, 1000, '1001', '1000', 'bytes'),
126             array(1004, 1000, '1004', '1000', 'bytes'),
127             array(1005, 1000, '1.01', '1', 'kB'),
128
129             array(1000001, 1000000, '1000001', '1000000', 'bytes'),
130             array(1004999, 1000000, '1005', '1000', 'kB'),
131             array(1005000, 1000000, '1.01', '1', 'MB'),
132
133             // limit in kB
134             array(1001, '1k', '1001', '1000', 'bytes'),
135             array(1004, '1k', '1004', '1000', 'bytes'),
136             array(1005, '1k', '1.01', '1', 'kB'),
137
138             array(1000001, '1000k', '1000001', '1000000', 'bytes'),
139             array(1004999, '1000k', '1005', '1000', 'kB'),
140             array(1005000, '1000k', '1.01', '1', 'MB'),
141
142             // limit in MB
143             array(1000001, '1M', '1000001', '1000000', 'bytes'),
144             array(1004999, '1M', '1005', '1000', 'kB'),
145             array(1005000, '1M', '1.01', '1', 'MB'),
146
147             // limit in KiB
148             array(1025, '1Ki', '1025', '1024', 'bytes'),
149             array(1029, '1Ki', '1029', '1024', 'bytes'),
150             array(1030, '1Ki', '1.01', '1', 'KiB'),
151
152             array(1048577, '1024Ki', '1048577', '1048576', 'bytes'),
153             array(1053818, '1024Ki', '1029.12', '1024', 'KiB'),
154             array(1053819, '1024Ki', '1.01', '1', 'MiB'),
155
156             // limit in MiB
157             array(1048577, '1Mi', '1048577', '1048576', 'bytes'),
158             array(1053818, '1Mi', '1029.12', '1024', 'KiB'),
159             array(1053819, '1Mi', '1.01', '1', 'MiB'),
160         );
161     }
162
163     /**
164      * @dataProvider provideMaxSizeExceededTests
165      */
166     public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
167     {
168         fseek($this->file, $bytesWritten - 1, SEEK_SET);
169         fwrite($this->file, '0');
170         fclose($this->file);
171
172         $constraint = new File(array(
173             'maxSize' => $limit,
174             'maxSizeMessage' => 'myMessage',
175         ));
176
177         $this->validator->validate($this->getFile($this->path), $constraint);
178
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)
185             ->assertRaised();
186     }
187
188     public function provideMaxSizeNotExceededTests()
189     {
190         return array(
191             // limit in bytes
192             array(1000, 1000),
193             array(1000000, 1000000),
194
195             // limit in kB
196             array(1000, '1k'),
197             array(1000000, '1000k'),
198
199             // limit in MB
200             array(1000000, '1M'),
201
202             // limit in KiB
203             array(1024, '1Ki'),
204             array(1048576, '1024Ki'),
205
206             // limit in MiB
207             array(1048576, '1Mi'),
208         );
209     }
210
211     /**
212      * @dataProvider provideMaxSizeNotExceededTests
213      */
214     public function testMaxSizeNotExceeded($bytesWritten, $limit)
215     {
216         fseek($this->file, $bytesWritten - 1, SEEK_SET);
217         fwrite($this->file, '0');
218         fclose($this->file);
219
220         $constraint = new File(array(
221             'maxSize' => $limit,
222             'maxSizeMessage' => 'myMessage',
223         ));
224
225         $this->validator->validate($this->getFile($this->path), $constraint);
226
227         $this->assertNoViolation();
228     }
229
230     /**
231      * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
232      */
233     public function testInvalidMaxSize()
234     {
235         $constraint = new File(array(
236             'maxSize' => '1abc',
237         ));
238
239         $this->validator->validate($this->path, $constraint);
240     }
241
242     public function provideBinaryFormatTests()
243     {
244         return array(
245             array(11, 10, null, '11', '10', 'bytes'),
246             array(11, 10, true, '11', '10', 'bytes'),
247             array(11, 10, false, '11', '10', 'bytes'),
248
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'),
253
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'),
257
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'),
261         );
262     }
263
264     /**
265      * @dataProvider provideBinaryFormatTests
266      */
267     public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix)
268     {
269         fseek($this->file, $bytesWritten - 1, SEEK_SET);
270         fwrite($this->file, '0');
271         fclose($this->file);
272
273         $constraint = new File(array(
274             'maxSize' => $limit,
275             'binaryFormat' => $binaryFormat,
276             'maxSizeMessage' => 'myMessage',
277         ));
278
279         $this->validator->validate($this->getFile($this->path), $constraint);
280
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)
287             ->assertRaised();
288     }
289
290     public function testValidMimeType()
291     {
292         $file = $this
293             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
294             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
295             ->getMock();
296         $file
297             ->expects($this->once())
298             ->method('getPathname')
299             ->will($this->returnValue($this->path));
300         $file
301             ->expects($this->once())
302             ->method('getMimeType')
303             ->will($this->returnValue('image/jpg'));
304
305         $constraint = new File(array(
306             'mimeTypes' => array('image/png', 'image/jpg'),
307         ));
308
309         $this->validator->validate($file, $constraint);
310
311         $this->assertNoViolation();
312     }
313
314     public function testValidWildcardMimeType()
315     {
316         $file = $this
317             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
318             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
319             ->getMock();
320         $file
321             ->expects($this->once())
322             ->method('getPathname')
323             ->will($this->returnValue($this->path));
324         $file
325             ->expects($this->once())
326             ->method('getMimeType')
327             ->will($this->returnValue('image/jpg'));
328
329         $constraint = new File(array(
330             'mimeTypes' => array('image/*'),
331         ));
332
333         $this->validator->validate($file, $constraint);
334
335         $this->assertNoViolation();
336     }
337
338     public function testInvalidMimeType()
339     {
340         $file = $this
341             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
342             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
343             ->getMock();
344         $file
345             ->expects($this->once())
346             ->method('getPathname')
347             ->will($this->returnValue($this->path));
348         $file
349             ->expects($this->once())
350             ->method('getMimeType')
351             ->will($this->returnValue('application/pdf'));
352
353         $constraint = new File(array(
354             'mimeTypes' => array('image/png', 'image/jpg'),
355             'mimeTypesMessage' => 'myMessage',
356         ));
357
358         $this->validator->validate($file, $constraint);
359
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)
365             ->assertRaised();
366     }
367
368     public function testInvalidWildcardMimeType()
369     {
370         $file = $this
371             ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
372             ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
373             ->getMock();
374         $file
375             ->expects($this->once())
376             ->method('getPathname')
377             ->will($this->returnValue($this->path));
378         $file
379             ->expects($this->once())
380             ->method('getMimeType')
381             ->will($this->returnValue('application/pdf'));
382
383         $constraint = new File(array(
384             'mimeTypes' => array('image/*', 'image/jpg'),
385             'mimeTypesMessage' => 'myMessage',
386         ));
387
388         $this->validator->validate($file, $constraint);
389
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)
395             ->assertRaised();
396     }
397
398     public function testDisallowEmpty()
399     {
400         ftruncate($this->file, 0);
401
402         $constraint = new File(array(
403             'disallowEmptyMessage' => 'myMessage',
404         ));
405
406         $this->validator->validate($this->getFile($this->path), $constraint);
407
408         $this->buildViolation('myMessage')
409             ->setParameter('{{ file }}', '"'.$this->path.'"')
410             ->setCode(File::EMPTY_ERROR)
411             ->assertRaised();
412     }
413
414     /**
415      * @dataProvider uploadedFileErrorProvider
416      */
417     public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
418     {
419         $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
420
421         $constraint = new File(array(
422             $message => 'myMessage',
423             'maxSize' => $maxSize,
424         ));
425
426         $this->validator->validate($file, $constraint);
427
428         $this->buildViolation('myMessage')
429             ->setParameters($params)
430             ->setCode($error)
431             ->assertRaised();
432     }
433
434     public function uploadedFileErrorProvider()
435     {
436         $tests = array(
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'),
443         );
444
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',
450             ));
451
452             // it should use the smaller limitation (maxSize option in this case)
453             $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
454                 '{{ limit }}' => 1,
455                 '{{ suffix }}' => 'bytes',
456             ), '1');
457
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',
463             ), '1000M');
464
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',
470             ), '100K');
471         }
472
473         return $tests;
474     }
475
476     abstract protected function getFile($filename);
477 }