PageRenderTime 34ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/Validator/Tests/Constraints/FileValidatorTest.php

https://github.com/fmagnan/symfony
PHP | 456 lines | 344 code | 77 blank | 35 comment | 3 complexity | ab2e0e0baa9fa46b6b553c9f41756b01 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator\Tests\Constraints;
  11. use Symfony\Component\HttpFoundation\File\UploadedFile;
  12. use Symfony\Component\Validator\Constraints\File;
  13. use Symfony\Component\Validator\Constraints\FileValidator;
  14. abstract class FileValidatorTest extends \PHPUnit_Framework_TestCase
  15. {
  16. protected $context;
  17. protected $validator;
  18. protected $path;
  19. protected $file;
  20. protected function setUp()
  21. {
  22. $this->context = $this->getMock('Symfony\Component\Validator\ExecutionContext', array(), array(), '', false);
  23. $this->validator = new FileValidator();
  24. $this->validator->initialize($this->context);
  25. $this->path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'FileValidatorTest';
  26. $this->file = fopen($this->path, 'w');
  27. fwrite($this->file, ' ', 1);
  28. }
  29. protected function tearDown()
  30. {
  31. if (is_resource($this->file)) {
  32. fclose($this->file);
  33. }
  34. if (file_exists($this->path)) {
  35. unlink($this->path);
  36. }
  37. $this->context = null;
  38. $this->validator = null;
  39. $this->path = null;
  40. $this->file = null;
  41. }
  42. public function testNullIsValid()
  43. {
  44. $this->context->expects($this->never())
  45. ->method('addViolation');
  46. $this->validator->validate(null, new File());
  47. }
  48. public function testEmptyStringIsValid()
  49. {
  50. $this->context->expects($this->never())
  51. ->method('addViolation');
  52. $this->validator->validate('', new File());
  53. }
  54. /**
  55. * @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
  56. */
  57. public function testExpectsStringCompatibleTypeOrFile()
  58. {
  59. $this->validator->validate(new \stdClass(), new File());
  60. }
  61. public function testValidFile()
  62. {
  63. $this->context->expects($this->never())
  64. ->method('addViolation');
  65. $this->validator->validate($this->path, new File());
  66. }
  67. public function testValidUploadedfile()
  68. {
  69. $this->context->expects($this->never())
  70. ->method('addViolation');
  71. $file = new UploadedFile($this->path, 'originalName', null, null, null, true);
  72. $this->validator->validate($file, new File());
  73. }
  74. public function provideMaxSizeExceededTests()
  75. {
  76. return array(
  77. array(11, 10, '11', '10', 'bytes'),
  78. array(ceil(1.005*1000), ceil(1.005*1000) - 1, '1005', '1004', 'bytes'),
  79. array(ceil(1.005*1000*1000), ceil(1.005*1000*1000) - 1, '1005000', '1004999', 'bytes'),
  80. // round(size) == 1.01kB, limit == 1kB
  81. array(ceil(1.005*1000), 1000, '1.01', '1', 'kB'),
  82. array(ceil(1.005*1000), '1k', '1.01', '1', 'kB'),
  83. array(ceil(1.005*1024), '1Ki', '1.01', '1', 'KiB'),
  84. // round(size) == 1kB, limit == 1kB -> use bytes
  85. array(ceil(1.004*1000), 1000, '1004', '1000', 'bytes'),
  86. array(ceil(1.004*1000), '1k', '1004', '1000', 'bytes'),
  87. array(ceil(1.004*1024), '1Ki', '1029', '1024', 'bytes'),
  88. array(1000 + 1, 1000, '1001', '1000', 'bytes'),
  89. array(1000 + 1, '1k', '1001', '1000', 'bytes'),
  90. array(1024 + 1, '1Ki', '1025', '1024', 'bytes'),
  91. // round(size) == 1.01MB, limit == 1MB
  92. array(ceil(1.005*1000*1000), 1000*1000, '1.01', '1', 'MB'),
  93. array(ceil(1.005*1000*1000), '1000k', '1.01', '1', 'MB'),
  94. array(ceil(1.005*1000*1000), '1M', '1.01', '1', 'MB'),
  95. array(ceil(1.005*1024*1024), '1024Ki', '1.01', '1', 'MiB'),
  96. array(ceil(1.005*1024*1024), '1Mi', '1.01', '1', 'MiB'),
  97. // round(size) == 1MB, limit == 1MB -> use kB
  98. array(ceil(1.004*1000*1000), 1000*1000, '1004', '1000', 'kB'),
  99. array(ceil(1.004*1000*1000), '1000k', '1004', '1000', 'kB'),
  100. array(ceil(1.004*1000*1000), '1M', '1004', '1000', 'kB'),
  101. array(ceil(1.004*1024*1024), '1024Ki', '1028.1', '1024', 'KiB'),
  102. array(ceil(1.004*1024*1024), '1Mi', '1028.1', '1024', 'KiB'),
  103. array(1000*1000 + 1, 1000*1000, '1000001', '1000000', 'bytes'),
  104. array(1000*1000 + 1, '1000k', '1000001', '1000000', 'bytes'),
  105. array(1000*1000 + 1, '1M', '1000001', '1000000', 'bytes'),
  106. array(1024*1024 + 1, '1024Ki', '1048577', '1048576', 'bytes'),
  107. array(1024*1024 + 1, '1Mi', '1048577', '1048576', 'bytes'),
  108. );
  109. }
  110. /**
  111. * @dataProvider provideMaxSizeExceededTests
  112. */
  113. public function testMaxSizeExceeded($bytesWritten, $limit, $sizeAsString, $limitAsString, $suffix)
  114. {
  115. fseek($this->file, $bytesWritten-1, SEEK_SET);
  116. fwrite($this->file, '0');
  117. fclose($this->file);
  118. $constraint = new File(array(
  119. 'maxSize' => $limit,
  120. 'maxSizeMessage' => 'myMessage',
  121. ));
  122. $this->context->expects($this->once())
  123. ->method('addViolation')
  124. ->with('myMessage', array(
  125. '{{ limit }}' => $limitAsString,
  126. '{{ size }}' => $sizeAsString,
  127. '{{ suffix }}' => $suffix,
  128. '{{ file }}' => $this->path,
  129. ));
  130. $this->validator->validate($this->getFile($this->path), $constraint);
  131. }
  132. public function provideMaxSizeNotExceededTests()
  133. {
  134. return array(
  135. array(10, 10),
  136. array(9, 10),
  137. array(1000, '1k'),
  138. array(1000 - 1, '1k'),
  139. array(1024, '1Ki'),
  140. array(1024 - 1, '1Ki'),
  141. array(1000*1000, '1M'),
  142. array(1000*1000 - 1, '1M'),
  143. array(1024*1024, '1Mi'),
  144. array(1024*1024 - 1, '1Mi'),
  145. );
  146. }
  147. /**
  148. * @dataProvider provideMaxSizeNotExceededTests
  149. */
  150. public function testMaxSizeNotExceeded($bytesWritten, $limit)
  151. {
  152. fseek($this->file, $bytesWritten-1, SEEK_SET);
  153. fwrite($this->file, '0');
  154. fclose($this->file);
  155. $constraint = new File(array(
  156. 'maxSize' => $limit,
  157. 'maxSizeMessage' => 'myMessage',
  158. ));
  159. $this->context->expects($this->never())
  160. ->method('addViolation');
  161. $this->validator->validate($this->getFile($this->path), $constraint);
  162. }
  163. /**
  164. * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
  165. */
  166. public function testInvalidMaxSize()
  167. {
  168. $constraint = new File(array(
  169. 'maxSize' => '1abc',
  170. ));
  171. $this->validator->validate($this->path, $constraint);
  172. }
  173. public function provideBinaryFormatTests()
  174. {
  175. return array(
  176. array(11, 10, null, '11', '10', 'bytes'),
  177. array(11, 10, true, '11', '10', 'bytes'),
  178. array(11, 10, false, '11', '10', 'bytes'),
  179. // round(size) == 1.01kB, limit == 1kB
  180. array(ceil(1000*1.01), 1000, null, '1.01', '1', 'kB'),
  181. array(ceil(1000*1.01), '1k', null, '1.01', '1', 'kB'),
  182. array(ceil(1024*1.01), '1Ki', null, '1.01', '1', 'KiB'),
  183. array(ceil(1024*1.01), 1024, true, '1.01', '1', 'KiB'),
  184. array(ceil(1024*1.01*1000), '1024k', true, '1010', '1000', 'KiB'),
  185. array(ceil(1024*1.01), '1Ki', true, '1.01', '1', 'KiB'),
  186. array(ceil(1000*1.01), 1000, false, '1.01', '1', 'kB'),
  187. array(ceil(1000*1.01), '1k', false, '1.01', '1', 'kB'),
  188. array(ceil(1024*1.01*10), '10Ki', false, '10.34', '10.24', 'kB'),
  189. );
  190. }
  191. /**
  192. * @dataProvider provideBinaryFormatTests
  193. */
  194. public function testBinaryFormat($bytesWritten, $limit, $binaryFormat, $sizeAsString, $limitAsString, $suffix)
  195. {
  196. fseek($this->file, $bytesWritten-1, SEEK_SET);
  197. fwrite($this->file, '0');
  198. fclose($this->file);
  199. $constraint = new File(array(
  200. 'maxSize' => $limit,
  201. 'binaryFormat' => $binaryFormat,
  202. 'maxSizeMessage' => 'myMessage',
  203. ));
  204. $this->context->expects($this->once())
  205. ->method('addViolation')
  206. ->with('myMessage', array(
  207. '{{ limit }}' => $limitAsString,
  208. '{{ size }}' => $sizeAsString,
  209. '{{ suffix }}' => $suffix,
  210. '{{ file }}' => $this->path,
  211. ));
  212. $this->validator->validate($this->getFile($this->path), $constraint);
  213. }
  214. public function testValidMimeType()
  215. {
  216. $file = $this
  217. ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
  218. ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
  219. ->getMock()
  220. ;
  221. $file
  222. ->expects($this->once())
  223. ->method('getPathname')
  224. ->will($this->returnValue($this->path))
  225. ;
  226. $file
  227. ->expects($this->once())
  228. ->method('getMimeType')
  229. ->will($this->returnValue('image/jpg'))
  230. ;
  231. $this->context->expects($this->never())
  232. ->method('addViolation');
  233. $constraint = new File(array(
  234. 'mimeTypes' => array('image/png', 'image/jpg'),
  235. ));
  236. $this->validator->validate($file, $constraint);
  237. }
  238. public function testValidWildcardMimeType()
  239. {
  240. $file = $this
  241. ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
  242. ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
  243. ->getMock()
  244. ;
  245. $file
  246. ->expects($this->once())
  247. ->method('getPathname')
  248. ->will($this->returnValue($this->path))
  249. ;
  250. $file
  251. ->expects($this->once())
  252. ->method('getMimeType')
  253. ->will($this->returnValue('image/jpg'))
  254. ;
  255. $this->context->expects($this->never())
  256. ->method('addViolation');
  257. $constraint = new File(array(
  258. 'mimeTypes' => array('image/*'),
  259. ));
  260. $this->validator->validate($file, $constraint);
  261. }
  262. public function testInvalidMimeType()
  263. {
  264. $file = $this
  265. ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
  266. ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
  267. ->getMock()
  268. ;
  269. $file
  270. ->expects($this->once())
  271. ->method('getPathname')
  272. ->will($this->returnValue($this->path))
  273. ;
  274. $file
  275. ->expects($this->once())
  276. ->method('getMimeType')
  277. ->will($this->returnValue('application/pdf'))
  278. ;
  279. $constraint = new File(array(
  280. 'mimeTypes' => array('image/png', 'image/jpg'),
  281. 'mimeTypesMessage' => 'myMessage',
  282. ));
  283. $this->context->expects($this->once())
  284. ->method('addViolation')
  285. ->with('myMessage', array(
  286. '{{ type }}' => '"application/pdf"',
  287. '{{ types }}' => '"image/png", "image/jpg"',
  288. '{{ file }}' => $this->path,
  289. ));
  290. $this->validator->validate($file, $constraint);
  291. }
  292. public function testInvalidWildcardMimeType()
  293. {
  294. $file = $this
  295. ->getMockBuilder('Symfony\Component\HttpFoundation\File\File')
  296. ->setConstructorArgs(array(__DIR__.'/Fixtures/foo'))
  297. ->getMock()
  298. ;
  299. $file
  300. ->expects($this->once())
  301. ->method('getPathname')
  302. ->will($this->returnValue($this->path))
  303. ;
  304. $file
  305. ->expects($this->once())
  306. ->method('getMimeType')
  307. ->will($this->returnValue('application/pdf'))
  308. ;
  309. $constraint = new File(array(
  310. 'mimeTypes' => array('image/*', 'image/jpg'),
  311. 'mimeTypesMessage' => 'myMessage',
  312. ));
  313. $this->context->expects($this->once())
  314. ->method('addViolation')
  315. ->with('myMessage', array(
  316. '{{ type }}' => '"application/pdf"',
  317. '{{ types }}' => '"image/*", "image/jpg"',
  318. '{{ file }}' => $this->path,
  319. ));
  320. $this->validator->validate($file, $constraint);
  321. }
  322. public function testDisallowEmpty()
  323. {
  324. ftruncate($this->file, 0);
  325. $constraint = new File(array(
  326. 'disallowEmptyMessage' => 'myMessage',
  327. ));
  328. $this->context->expects($this->once())
  329. ->method('addViolation')
  330. ->with('myMessage');
  331. $this->validator->validate($this->getFile($this->path), $constraint);
  332. }
  333. /**
  334. * @dataProvider uploadedFileErrorProvider
  335. */
  336. public function testUploadedFileError($error, $message, array $params = array(), $maxSize = null)
  337. {
  338. $file = new UploadedFile('/path/to/file', 'originalName', 'mime', 0, $error);
  339. $constraint = new File(array(
  340. $message => 'myMessage',
  341. 'maxSize' => $maxSize
  342. ));
  343. $this->context->expects($this->once())
  344. ->method('addViolation')
  345. ->with('myMessage', $params);
  346. $this->validator->validate($file, $constraint);
  347. }
  348. public function uploadedFileErrorProvider()
  349. {
  350. $tests = array(
  351. array(UPLOAD_ERR_FORM_SIZE, 'uploadFormSizeErrorMessage'),
  352. array(UPLOAD_ERR_PARTIAL, 'uploadPartialErrorMessage'),
  353. array(UPLOAD_ERR_NO_FILE, 'uploadNoFileErrorMessage'),
  354. array(UPLOAD_ERR_NO_TMP_DIR, 'uploadNoTmpDirErrorMessage'),
  355. array(UPLOAD_ERR_CANT_WRITE, 'uploadCantWriteErrorMessage'),
  356. array(UPLOAD_ERR_EXTENSION, 'uploadExtensionErrorMessage'),
  357. );
  358. if (class_exists('Symfony\Component\HttpFoundation\File\UploadedFile')) {
  359. // when no maxSize is specified on constraint, it should use the ini value
  360. $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
  361. '{{ limit }}' => UploadedFile::getMaxFilesize(),
  362. '{{ suffix }}' => 'bytes',
  363. ));
  364. // it should use the smaller limitation (maxSize option in this case)
  365. $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
  366. '{{ limit }}' => 1,
  367. '{{ suffix }}' => 'bytes',
  368. ), '1');
  369. // it correctly parses the maxSize option and not only uses simple string comparison
  370. // 1000M should be bigger than the ini value
  371. $tests[] = array(UPLOAD_ERR_INI_SIZE, 'uploadIniSizeErrorMessage', array(
  372. '{{ limit }}' => UploadedFile::getMaxFilesize(),
  373. '{{ suffix }}' => 'bytes',
  374. ), '1000M');
  375. }
  376. return $tests;
  377. }
  378. abstract protected function getFile($filename);
  379. }