2c0c48637f6430b0bca150bc8e405ac3bd814d33
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Resource / Driver / LocalDriverTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Resource\Driver;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use org\bovigo\vfs\vfsStream;
18 use org\bovigo\vfs\vfsStreamWrapper;
19 use TYPO3\CMS\Components\TestingFramework\Core\FileStreamWrapper;
20 use TYPO3\CMS\Core\Resource\Exception\InvalidFileNameException;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Testcase for the local storage driver class of the TYPO3 VFS
25 */
26 class LocalDriverTest extends \TYPO3\CMS\Core\Tests\Unit\Resource\BaseTestCase
27 {
28 /**
29 * @var \TYPO3\CMS\Core\Resource\Driver\LocalDriver
30 */
31 protected $localDriver = null;
32
33 /**
34 * @var array A backup of registered singleton instances
35 */
36 protected $singletonInstances = [];
37
38 /**
39 * @var array
40 */
41 protected $testDirs = [];
42
43 /**
44 * @var string
45 */
46 protected $iso88591GreaterThan127 = '';
47
48 /**
49 * @var string
50 */
51 protected $utf8Latin1Supplement = '';
52
53 /**
54 * @var string
55 */
56 protected $utf8Latin1ExtendedA = '';
57
58 /**
59 * Tear down
60 */
61 protected function tearDown()
62 {
63 foreach ($this->testDirs as $dir) {
64 chmod($dir, 0777);
65 \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir($dir, true);
66 }
67 parent::tearDown();
68 }
69
70 /**
71 * Creates a "real" directory for doing tests. This is necessary because some file system properties (e.g. permissions)
72 * cannot be reflected by vfsStream, and some methods (like touch()) don't work there either.
73 *
74 * Created directories are automatically destroyed during tearDown()
75 *
76 * @return string
77 */
78 protected function createRealTestdir()
79 {
80 $basedir = PATH_site . 'typo3temp/var/tests/' . $this->getUniqueId('fal-test-');
81 mkdir($basedir);
82 $this->testDirs[] = $basedir;
83 return $basedir;
84 }
85
86 /**
87 * Create a "real" directory together with a driver configured
88 * for this directory.
89 *
90 * @return array With path to base directory and driver
91 */
92 protected function prepareRealTestEnvironment()
93 {
94 $basedir = $this->createRealTestdir();
95 $subject = $this->createDriver([
96 'basePath' => $basedir
97 ]);
98 return [$basedir, $subject];
99 }
100
101 /**
102 * Creates a mocked driver object as test subject, optionally using a given mount object.
103 *
104 * IMPORTANT: Call this only after setting up the virtual file system (with the addTo* methods)!
105 *
106 * @param array $driverConfiguration
107 * @param array $mockedDriverMethods
108 * @return \TYPO3\CMS\Core\Resource\Driver\LocalDriver
109 */
110 protected function createDriver($driverConfiguration = [], $mockedDriverMethods = [])
111 {
112 // it's important to do that here, so vfsContents could have been set before
113 if (!isset($driverConfiguration['basePath'])) {
114 $this->initializeVfs();
115 $driverConfiguration['basePath'] = $this->getMountRootUrl();
116 }
117 /** @var \TYPO3\CMS\Core\Resource\Driver\LocalDriver $driver */
118 $mockedDriverMethods[] = 'isPathValid';
119 $driver = $this->getAccessibleMock(\TYPO3\CMS\Core\Resource\Driver\LocalDriver::class, $mockedDriverMethods, [$driverConfiguration]);
120 $driver->expects($this->any())
121 ->method('isPathValid')
122 ->will(
123 $this->returnValue(true)
124 );
125
126 $driver->setStorageUid(5);
127 $driver->processConfiguration();
128 $driver->initialize();
129 return $driver;
130 }
131
132 /**
133 * @test
134 */
135 public function calculatedBasePathRelativeIsSane()
136 {
137 $subject = $this->createDriver();
138
139 // This would cause problems if you fill "/fileadmin/" into the base path field of a sys_file_storage record and select "relative" as path type
140 $relativeDriverConfiguration = [
141 'pathType' => 'relative',
142 'basePath' => '/typo3temp/var/tests/',
143 ];
144 $basePath = $subject->_call('calculateBasePath', $relativeDriverConfiguration);
145
146 $this->assertNotContains('//', $basePath);
147 }
148
149 /**
150 * @test
151 */
152 public function calculatedBasePathAbsoluteIsSane()
153 {
154 $subject = $this->createDriver();
155
156 // This test checks if "/../" are properly filtered out (i.e. from "Base path" field of sys_file_storage)
157 $relativeDriverConfiguration = [
158 'basePath' => PATH_site . 'typo3temp/var/tests/../../../typo3temp/var/tests/',
159 ];
160 $basePath = $subject->_call('calculateBasePath', $relativeDriverConfiguration);
161
162 $this->assertNotContains('/../', $basePath);
163 }
164
165 /**
166 * @test
167 */
168 public function createFolderRecursiveSanitizesFilename()
169 {
170 /** @var \TYPO3\CMS\Core\Resource\Driver\LocalDriver|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Components\TestingFramework\Core\AccessibleObjectInterface $driver */
171 $driver = $this->createDriver([], ['sanitizeFilename']);
172 $driver->expects($this->exactly(2))
173 ->method('sanitizeFileName')
174 ->will(
175 $this->returnValue('sanitized')
176 );
177 $driver->createFolder('newFolder/andSubfolder', '/', true);
178 $this->assertFileExists($this->getUrlInMount('/sanitized/sanitized/'));
179 }
180
181 /**
182 * @test
183 */
184 public function determineBaseUrlUrlEncodesUriParts()
185 {
186 /** @var \TYPO3\CMS\Core\Resource\Driver\LocalDriver|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Components\TestingFramework\Core\AccessibleObjectInterface $driver */
187 $driver = $this->getAccessibleMock(\TYPO3\CMS\Core\Resource\Driver\LocalDriver::class, ['hasCapability'], [], '', false);
188 $driver->expects($this->once())
189 ->method('hasCapability')
190 ->with(\TYPO3\CMS\Core\Resource\ResourceStorage::CAPABILITY_PUBLIC)
191 ->will(
192 $this->returnValue(true)
193 );
194 $driver->_set('absoluteBasePath', PATH_site . 'un encö/ded %path/');
195 $driver->_call('determineBaseUrl');
196 $baseUri = $driver->_get('baseUri');
197 $this->assertEquals(rawurlencode('un encö') . '/' . rawurlencode('ded %path') . '/', $baseUri);
198 }
199
200 /**
201 * @test
202 */
203 public function getDefaultFolderReturnsFolderForUserUploadPath()
204 {
205 $subject = $this->createDriver();
206 $folderIdentifier = $subject->getDefaultFolder();
207 $this->assertEquals('/user_upload/', $folderIdentifier);
208 }
209
210 /**
211 * @test
212 */
213 public function defaultLevelFolderFolderIsCreatedIfItDoesntExist()
214 {
215 $subject = $this->createDriver();
216 $this->assertFileExists($this->getUrlInMount($subject->getDefaultFolder()));
217 }
218
219 /**
220 * @test
221 */
222 public function getFolderInFolderReturnsCorrectFolderObject()
223 {
224 $this->addToMount([
225 'someDir' => [
226 'someSubdir' => []
227 ]
228 ]);
229 $subject = $this->createDriver();
230 $folder = $subject->getFolderInFolder('someSubdir', '/someDir/');
231 $this->assertEquals('/someDir/someSubdir/', $folder);
232 }
233
234 /**
235 * @test
236 */
237 public function createFolderCreatesFolderOnDisk()
238 {
239 $this->addToMount(['some' => ['folder' => []]]);
240 $subject = $this->createDriver();
241 $subject->createFolder('path', '/some/folder/');
242 $this->assertFileExists($this->getUrlInMount('/some/folder/'));
243 $this->assertFileExists($this->getUrlInMount('/some/folder/path'));
244 }
245
246 /**
247 * @test
248 */
249 public function createFolderReturnsFolderObject()
250 {
251 $this->addToMount(['some' => ['folder' => []]]);
252 $subject = $this->createDriver();
253 $createdFolder = $subject->createFolder('path', '/some/folder/');
254 $this->assertEquals('/some/folder/path/', $createdFolder);
255 }
256
257 public static function createFolderSanitizesFolderNameBeforeCreationDataProvider()
258 {
259 return [
260 'folder name with NULL character' => [
261 'some' . chr(0) . 'Folder',
262 'some_Folder'
263 ],
264 'folder name with directory part' => [
265 '../someFolder',
266 '.._someFolder'
267 ]
268 ];
269 }
270
271 /**
272 * @test
273 * @dataProvider createFolderSanitizesFolderNameBeforeCreationDataProvider
274 */
275 public function createFolderSanitizesFolderNameBeforeCreation($newFolderName, $expectedFolderName)
276 {
277 $this->addToMount(['some' => ['folder' => []]]);
278 $subject = $this->createDriver();
279 $subject->createFolder($newFolderName, '/some/folder/');
280 $this->assertFileExists($this->getUrlInMount('/some/folder/' . $expectedFolderName));
281 }
282
283 /**
284 * @test
285 */
286 public function basePathIsNormalizedWithTrailingSlash()
287 {
288 $subject = $this->createDriver();
289 $this->assertEquals('/', substr($subject->_call('getAbsoluteBasePath'), -1));
290 }
291
292 /**
293 * @test
294 */
295 public function noSecondSlashIsAddedIfBasePathAlreadyHasTrailingSlash()
296 {
297 $subject = $this->createDriver();
298 $this->assertNotEquals('/', substr($subject->_call('getAbsoluteBasePath'), -2, 1));
299 }
300
301 public function getSpecificFileInformationDataProvider()
302 {
303 return [
304 'size' => [
305 'expectedValue' => filesize(__DIR__ . '/Fixtures/Dummy.html'),
306 'propertyName' => 'size'
307 ],
308 'atime' => [
309 'expectedValue' => 'WILL_BE_REPLACED_BY_VFS_TIME',
310 'propertyName' => 'atime'
311 ],
312 'mtime' => [
313 'expectedValue' => 'WILL_BE_REPLACED_BY_VFS_TIME',
314 'propertyName' => 'mtime'
315 ],
316 'ctime' => [
317 'expectedValue' => 'WILL_BE_REPLACED_BY_VFS_TIME',
318 'propertyName' => 'ctime'
319 ],
320 'name' => [
321 'expectedValue' => 'Dummy.html',
322 'propertyName' => 'name'
323 ],
324 'mimetype' => [
325 'expectedValue' => 'text/html',
326 'propertyName' => 'mimetype'
327 ],
328 'identifier' => [
329 'expectedValue' => '/Dummy.html',
330 'propertyName' => 'identifier'
331 ],
332 'storage' => [
333 'expectedValue' => 5,
334 'propertyName' => 'storage'
335 ],
336 'identifier_hash' => [
337 'expectedValue' => 'b11efa5d7c0556a65c6aa261343b9807cac993bc',
338 'propertyName' => 'identifier_hash'
339 ],
340 'folder_hash' => [
341 'expectedValue' => '42099b4af021e53fd8fd4e056c2568d7c2e3ffa8',
342 'propertyName' => 'folder_hash'
343 ]
344 ];
345 }
346
347 /**
348 * @test
349 * @dataProvider getSpecificFileInformationDataProvider
350 */
351 public function getSpecificFileInformationReturnsRequestedFileInformation($expectedValue, $property)
352 {
353 $root = vfsStream::setup();
354 $subFolder = vfsStream::newDirectory('fileadmin');
355 $root->addChild($subFolder);
356 // Load fixture files and folders from disk
357 $directory = vfsStream::copyFromFileSystem(__DIR__ . '/Fixtures/', $subFolder, 1024*1024);
358 if (in_array($property, ['mtime', 'ctime', 'atime'])) {
359 $expectedValue = $directory->getChild('Dummy.html')->filemtime();
360 }
361 FileStreamWrapper::init(PATH_site);
362 FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin', false);
363
364 $subject = $this->createDriver(['basePath' => PATH_site . 'fileadmin']);
365 $this->assertSame(
366 $expectedValue,
367 $subject->getSpecificFileInformation(PATH_site . 'fileadmin/Dummy.html', '/', $property)
368 );
369
370 FileStreamWrapper::destroy();
371 }
372
373 /**
374 * @test
375 */
376 public function getAbsolutePathReturnsCorrectPath()
377 {
378 $this->addToMount([
379 'someFolder' => [
380 'file1.ext' => 'asdfg'
381 ]
382 ]);
383 $subject = $this->createDriver();
384 $path = $subject->_call('getAbsolutePath', '/someFolder/file1.ext');
385 $this->assertTrue(file_exists($path));
386 $this->assertEquals($this->getUrlInMount('/someFolder/file1.ext'), $path);
387 }
388
389 /**
390 * @test
391 */
392 public function addFileMovesFileToCorrectLocation()
393 {
394 $this->addToMount(['targetFolder' => []]);
395 $this->addToVfs([
396 'sourceFolder' => [
397 'file' => 'asdf'
398 ]
399 ]);
400 $subject = $this->createDriver(
401 [],
402 ['getMimeTypeOfFile']
403 );
404 $this->assertTrue(file_exists($this->getUrl('sourceFolder/file')));
405 $subject->addFile($this->getUrl('sourceFolder/file'), '/targetFolder/', 'file');
406 $this->assertTrue(file_exists($this->getUrlInMount('/targetFolder/file')));
407 }
408
409 /**
410 * @test
411 */
412 public function addFileUsesFilenameIfGiven()
413 {
414 $this->addToMount(['targetFolder' => []]);
415 $this->addToVfs([
416 'sourceFolder' => [
417 'file' => 'asdf'
418 ]
419 ]);
420 $subject = $this->createDriver(
421 [],
422 ['getMimeTypeOfFile']
423 );
424 $this->assertTrue(file_exists($this->getUrl('sourceFolder/file')));
425 $subject->addFile($this->getUrl('sourceFolder/file'), '/targetFolder/', 'targetFile');
426 $this->assertTrue(file_exists($this->getUrlInMount('/targetFolder/targetFile')));
427 }
428
429 /**
430 * @test
431 */
432 public function addFileFailsIfFileIsInDriverStorage()
433 {
434 $this->expectException(\InvalidArgumentException::class);
435 $this->expectExceptionCode(1314778269);
436 $this->addToMount([
437 'targetFolder' => [
438 'file' => 'asdf'
439 ]
440 ]);
441 $subject = $this->createDriver();
442 $subject->addFile($this->getUrlInMount('/targetFolder/file'), '/targetFolder/', 'file');
443 }
444
445 /**
446 * @test
447 */
448 public function addFileReturnsFileIdentifier()
449 {
450 $this->addToMount(['targetFolder' => []]);
451 $this->addToVfs([
452 'sourceFolder' => [
453 'file' => 'asdf'
454 ]
455 ]);
456 $subject = $this->createDriver(
457 [],
458 ['getMimeTypeOfFile']
459 );
460 $this->assertTrue(file_exists($this->getUrl('sourceFolder/file')));
461 $fileIdentifier = $subject->addFile($this->getUrl('sourceFolder/file'), '/targetFolder/', 'file');
462 $this->assertEquals('file', basename($fileIdentifier));
463 $this->assertEquals('/targetFolder/file', $fileIdentifier);
464 }
465
466 /**
467 * @test
468 */
469 public function existenceChecksWorkForFilesAndFolders()
470 {
471 $this->addToMount([
472 'file' => 'asdf',
473 'folder' => []
474 ]);
475 $subject = $this->createDriver();
476 // Using slashes at the beginning of paths because they will be stored in the DB this way.
477 $this->assertTrue($subject->fileExists('/file'));
478 $this->assertTrue($subject->folderExists('/folder/'));
479 $this->assertFalse($subject->fileExists('/nonexistingFile'));
480 $this->assertFalse($subject->folderExists('/nonexistingFolder/'));
481 }
482
483 /**
484 * @test
485 */
486 public function existenceChecksInFolderWorkForFilesAndFolders()
487 {
488 $this->addToMount([
489 'subfolder' => [
490 'file' => 'asdf',
491 'folder' => []
492 ]
493 ]);
494 $subject = $this->createDriver();
495 $this->assertTrue($subject->fileExistsInFolder('file', '/subfolder/'));
496 $this->assertTrue($subject->folderExistsInFolder('folder', '/subfolder/'));
497 $this->assertFalse($subject->fileExistsInFolder('nonexistingFile', '/subfolder/'));
498 $this->assertFalse($subject->folderExistsInFolder('nonexistingFolder', '/subfolder/'));
499 }
500
501 /**
502 * @test
503 */
504 public function getPublicUrlReturnsCorrectUriForConfiguredBaseUri()
505 {
506 $baseUri = 'http://example.org/foobar/' . $this->getUniqueId();
507 $this->addToMount([
508 'file.ext' => 'asdf',
509 'subfolder' => [
510 'file2.ext' => 'asdf'
511 ]
512 ]);
513 $subject = $this->createDriver([
514 'baseUri' => $baseUri
515 ]);
516 $this->assertEquals($baseUri . '/file.ext', $subject->getPublicUrl('/file.ext'));
517 $this->assertEquals($baseUri . '/subfolder/file2.ext', $subject->getPublicUrl('/subfolder/file2.ext'));
518 }
519
520 /**
521 * Data provider for getPublicUrlReturnsValidUrlContainingSpecialCharacters().
522 *
523 * @return array
524 */
525 public function getPublicUrlReturnsValidUrlContainingSpecialCharacters_dataProvider()
526 {
527 return [
528 ['/single file with some special chars äüö!.txt'],
529 ['/on subfolder/with special chars äüö!.ext'],
530 ['/who names a file like !"§$%&()=?*+~"#\'´`<>-.ext'],
531 ['no leading slash !"§$%&()=?*+~#\'"´`"<>-.txt']
532 ];
533 }
534
535 /**
536 * @test
537 * @dataProvider getPublicUrlReturnsValidUrlContainingSpecialCharacters_dataProvider
538 */
539 public function getPublicUrlReturnsValidUrlContainingSpecialCharacters($fileIdentifier)
540 {
541 $baseUri = 'http://example.org/foobar/' . $this->getUniqueId();
542 $subject = $this->createDriver([
543 'baseUri' => $baseUri
544 ]);
545 $publicUrl = $subject->getPublicUrl($fileIdentifier);
546 $this->assertTrue(GeneralUtility::isValidUrl($publicUrl), 'getPublicUrl did not return a valid URL:' . $publicUrl);
547 }
548
549 /**
550 * @test
551 */
552 public function fileContentsCanBeWrittenAndRead()
553 {
554 $fileContents = 'asdf';
555 $this->addToMount([
556 'file.ext' => $fileContents
557 ]);
558 $subject = $this->createDriver();
559 $this->assertEquals($fileContents, $subject->getFileContents('/file.ext'), 'File contents could not be read');
560 $newFileContents = 'asdfgh';
561 $subject->setFileContents('/file.ext', $newFileContents);
562 $this->assertEquals($newFileContents, $subject->getFileContents('/file.ext'), 'New file contents could not be read.');
563 }
564
565 /**
566 * @test
567 */
568 public function setFileContentsReturnsNumberOfBytesWrittenToFile()
569 {
570 $fileContents = 'asdf';
571 $this->addToMount([
572 'file.ext' => $fileContents
573 ]);
574 $subject = $this->createDriver();
575 $newFileContents = 'asdfgh';
576 $bytesWritten = $subject->setFileContents('/file.ext', $newFileContents);
577 $this->assertEquals(strlen($newFileContents), $bytesWritten);
578 }
579
580 /**
581 * @test
582 * @see http://phpmagazin.de/vfsStream-1.1.0-nutzt-PHP-5.4-M%C3%B6glichkeiten-064406.html
583 */
584 public function newFilesCanBeCreated()
585 {
586 $subject = $this->createDriver();
587 $subject->createFile('testfile.txt', '/');
588 $this->assertTrue($subject->fileExists('/testfile.txt'));
589 }
590
591 /**
592 * @test
593 * @see http://phpmagazin.de/vfsStream-1.1.0-nutzt-PHP-5.4-M%C3%B6glichkeiten-064406.html
594 */
595 public function createdFilesAreEmpty()
596 {
597 $subject = $this->createDriver();
598 $subject->createFile('testfile.txt', '/');
599 $this->assertTrue($subject->fileExists('/testfile.txt'));
600 $fileData = $subject->getFileContents('/testfile.txt');
601 $this->assertEquals(0, strlen($fileData));
602 }
603
604 /**
605 * @test
606 */
607 public function createFileFixesPermissionsOnCreatedFile()
608 {
609 if (TYPO3_OS == 'WIN') {
610 $this->markTestSkipped('createdFilesHaveCorrectRights() tests not available on Windows');
611 }
612
613 // No one will use this as his default file create mask so we hopefully don't get any false positives
614 $testpattern = '0646';
615 $GLOBALS['TYPO3_CONF_VARS']['SYS']['fileCreateMask'] = $testpattern;
616
617 $this->addToMount(
618 [
619 'someDir' => []
620 ]
621 );
622 /** @var $subject \TYPO3\CMS\Core\Resource\Driver\LocalDriver */
623 list($basedir, $subject) = $this->prepareRealTestEnvironment();
624 mkdir($basedir . '/someDir');
625 $subject->createFile('testfile.txt', '/someDir');
626 $this->assertEquals($testpattern, decoct(fileperms($basedir . '/someDir/testfile.txt') & 0777));
627 }
628
629 /**********************************
630 * File and directory listing
631 **********************************/
632 /**
633 * @test
634 */
635 public function getFileReturnsCorrectIdentifier()
636 {
637 $root = vfsStream::setup();
638 $subFolder = vfsStream::newDirectory('fileadmin');
639 $root->addChild($subFolder);
640 // Load fixture files and folders from disk
641 vfsStream::copyFromFileSystem(__DIR__ . '/Fixtures/', $subFolder, 1024*1024);
642 FileStreamWrapper::init(PATH_site);
643 FileStreamWrapper::registerOverlayPath('fileadmin/', 'vfs://root/fileadmin/', false);
644
645 $subject = $this->createDriver(['basePath' => PATH_site . 'fileadmin']);
646
647 $subdirFileInfo = $subject->getFileInfoByIdentifier('Dummy.html');
648 $this->assertEquals('/Dummy.html', $subdirFileInfo['identifier']);
649 $rootFileInfo = $subject->getFileInfoByIdentifier('LocalDriverFilenameFilter.php');
650 $this->assertEquals('/LocalDriverFilenameFilter.php', $rootFileInfo['identifier']);
651
652 FileStreamWrapper::destroy();
653 }
654
655 /**
656 * @test
657 */
658 public function getFileThrowsExceptionIfFileDoesNotExist()
659 {
660 $this->expectException(\InvalidArgumentException::class);
661 $this->expectExceptionCode(1314516809);
662 $subject = $this->createDriver();
663 $subject->getFileInfoByIdentifier('/some/file/at/a/random/path');
664 }
665
666 /**
667 * @test
668 */
669 public function getFilesInFolderReturnsEmptyArrayForEmptyDirectory()
670 {
671 $subject = $this->createDriver();
672 $fileList = $subject->getFilesInFolder('/');
673 $this->assertEmpty($fileList);
674 }
675
676 /**
677 * @test
678 */
679 public function getFileListReturnsAllFilesInDirectory()
680 {
681 $dirStructure = [
682 'aDir' => [],
683 'file1' => 'asdfg',
684 'file2' => 'fdsa'
685 ];
686 $this->addToMount($dirStructure);
687 $subject = $this->createDriver(
688 [],
689 // Mocked because finfo() can not deal with vfs streams and throws warnings
690 ['getMimeTypeOfFile']
691 );
692 $fileList = $subject->getFilesInFolder('/');
693 $this->assertEquals(['/file1', '/file2'], array_keys($fileList));
694 }
695
696 /**
697 * @test
698 */
699 public function getFileListReturnsAllFilesInSubdirectoryIfRecursiveParameterIsSet()
700 {
701 $dirStructure = [
702 'aDir' => [
703 'file3' => 'asdfgh',
704 'subdir' => [
705 'file4' => 'asklfjklasjkl'
706 ]
707 ],
708 'file1' => 'asdfg',
709 'file2' => 'fdsa'
710 ];
711 $this->addToMount($dirStructure);
712 $subject = $this->createDriver(
713 [],
714 // Mocked because finfo() can not deal with vfs streams and throws warnings
715 ['getMimeTypeOfFile']
716 );
717 $fileList = $subject->getFilesInFolder('/', 0, 0, true);
718 $this->assertEquals(['/file1', '/file2', '/aDir/file3', '/aDir/subdir/file4'], array_keys($fileList));
719 }
720
721 /**
722 * @test
723 */
724 public function getFileListFailsIfDirectoryDoesNotExist()
725 {
726 $this->expectException(\InvalidArgumentException::class);
727 $this->expectExceptionCode(1314349666);
728 $this->addToMount(['somefile' => '']);
729 $subject = $this->createDriver();
730 $subject->getFilesInFolder('somedir/');
731 }
732
733 /**
734 * @test
735 */
736 public function getFileInFolderCallsConfiguredCallbackFunctionWithGivenItemName()
737 {
738 $dirStructure = [
739 'file2' => 'fdsa'
740 ];
741 // register static callback to self
742 $callback = [
743 [
744 get_class($this),
745 'callbackStaticTestFunction'
746 ]
747 ];
748 $this->addToMount($dirStructure);
749 $subject = $this->createDriver();
750 // the callback function will throw an exception used to check if it was called with correct $itemName
751 $this->expectException(\InvalidArgumentException::class);
752 $this->expectExceptionCode(1336159604);
753 $subject->getFilesInFolder('/', 0, 0, false, $callback);
754 }
755
756 /**
757 * Static callback function used to test if the filter callbacks work
758 * As it is static we are using an exception to test if it is really called and works
759 *
760 * @static
761 * @param string $itemName
762 * @throws \InvalidArgumentException
763 * @see getFileListCallsConfiguredCallbackFunction
764 */
765 public static function callbackStaticTestFunction($itemName)
766 {
767 if ($itemName === 'file2') {
768 throw new \InvalidArgumentException('$itemName', 1336159604);
769 }
770 }
771
772 /**
773 * @test
774 */
775 public function getFileListFiltersItemsWithGivenFilterMethods()
776 {
777 $dirStructure = [
778 'fileA' => 'asdfg',
779 'fileB' => 'fdsa'
780 ];
781 $this->addToMount($dirStructure);
782 $subject = $this->createDriver(
783 [],
784 // Mocked because finfo() can not deal with vfs streams and throws warnings
785 ['getMimeTypeOfFile']
786 );
787 $filterCallbacks = [
788 [
789 \TYPO3\CMS\Core\Tests\Unit\Resource\Driver\Fixtures\LocalDriverFilenameFilter::class,
790 'filterFilename',
791 ],
792 ];
793 $fileList = $subject->getFilesInFolder('/', 0, 0, false, $filterCallbacks);
794 $this->assertNotContains('/fileA', array_keys($fileList));
795 }
796
797 /**
798 * @test
799 */
800 public function getFolderListReturnsAllDirectoriesInDirectory()
801 {
802 $dirStructure = [
803 'dir1' => [],
804 'dir2' => [],
805 'file' => 'asdfg'
806 ];
807 $this->addToMount($dirStructure);
808 $subject = $this->createDriver();
809 $fileList = $subject->getFoldersInFolder('/');
810 $this->assertEquals(['/dir1/', '/dir2/'], array_keys($fileList));
811 }
812
813 /**
814 * @test
815 */
816 public function getFolderListReturnsHiddenFoldersByDefault()
817 {
818 $dirStructure = [
819 '.someHiddenDir' => [],
820 'aDir' => [],
821 'file1' => ''
822 ];
823 $this->addToMount($dirStructure);
824 $subject = $this->createDriver();
825
826 $fileList = $subject->getFoldersInFolder('/');
827
828 $this->assertEquals(['/.someHiddenDir/', '/aDir/'], array_keys($fileList));
829 }
830
831 /**
832 * Checks if the folder names . and .. are ignored when listing subdirectories
833 *
834 * @test
835 */
836 public function getFolderListLeavesOutNavigationalEntries()
837 {
838 // we have to add .. and . manually, as these are not included in vfsStream directory listings (as opposed
839 // to normal filelistings)
840 $this->addToMount([
841 '..' => [],
842 '.' => []
843 ]);
844 $subject = $this->createDriver();
845 $fileList = $subject->getFoldersInFolder('/');
846 $this->assertEmpty($fileList);
847 }
848
849 /**
850 * @test
851 */
852 public function getFolderListFiltersItemsWithGivenFilterMethods()
853 {
854 $dirStructure = [
855 'folderA' => [],
856 'folderB' => []
857 ];
858 $this->addToMount($dirStructure);
859 $subject = $this->createDriver();
860 $filterCallbacks = [
861 [
862 \TYPO3\CMS\Core\Tests\Unit\Resource\Driver\Fixtures\LocalDriverFilenameFilter::class,
863 'filterFilename',
864 ],
865 ];
866 $folderList = $subject->getFoldersInFolder('/', 0, 0, $filterCallbacks);
867 $this->assertNotContains('folderA', array_keys($folderList));
868 }
869
870 /**
871 * @test
872 */
873 public function getFolderListFailsIfDirectoryDoesNotExist()
874 {
875 $this->expectException(\InvalidArgumentException::class);
876 $this->expectExceptionCode(1314349666);
877 $subject = $this->createDriver();
878 vfsStream::create([$this->basedir => ['somefile' => '']]);
879 $subject->getFoldersInFolder('somedir/');
880 }
881
882 /**
883 * @test
884 */
885 public function hashReturnsCorrectHashes()
886 {
887 $contents = '68b329da9893e34099c7d8ad5cb9c940';
888 $expectedMd5Hash = '8c67dbaf0ba22f2e7fbc26413b86051b';
889 $expectedSha1Hash = 'a60cd808ba7a0bcfa37fa7f3fb5998e1b8dbcd9d';
890 $this->addToMount(['hashFile' => $contents]);
891 $subject = $this->createDriver();
892 $this->assertEquals($expectedSha1Hash, $subject->hash('/hashFile', 'sha1'));
893 $this->assertEquals($expectedMd5Hash, $subject->hash('/hashFile', 'md5'));
894 }
895
896 /**
897 * @test
898 */
899 public function hashingWithUnsupportedAlgorithmFails()
900 {
901 $this->expectException(\InvalidArgumentException::class);
902 $this->expectExceptionCode(1304964032);
903 $subject = $this->createDriver();
904 $subject->hash('/hashFile', $this->getUniqueId());
905 }
906
907 /**
908 * @test
909 * @covers TYPO3\CMS\Core\Resource\Driver\LocalDriver::getFileForLocalProcessing
910 */
911 public function getFileForLocalProcessingCreatesCopyOfFileByDefault()
912 {
913 $fileContents = 'asdfgh';
914 $this->addToMount([
915 'someDir' => [
916 'someFile' => $fileContents
917 ]
918 ]);
919 $subject = $this->createDriver([], ['copyFileToTemporaryPath']);
920 $subject->expects($this->once())->method('copyFileToTemporaryPath');
921 $subject->getFileForLocalProcessing('/someDir/someFile');
922 }
923
924 /**
925 * @test
926 */
927 public function getFileForLocalProcessingReturnsOriginalFilepathForReadonlyAccess()
928 {
929 $fileContents = 'asdfgh';
930 $this->addToMount([
931 'someDir' => [
932 'someFile' => $fileContents
933 ]
934 ]);
935 $subject = $this->createDriver();
936 $filePath = $subject->getFileForLocalProcessing('/someDir/someFile', false);
937 $this->assertEquals($filePath, $this->getUrlInMount('someDir/someFile'));
938 }
939
940 /**
941 * @test
942 */
943 public function filesCanBeCopiedToATemporaryPath()
944 {
945 $fileContents = 'asdfgh';
946 $this->addToMount([
947 'someDir' => [
948 'someFile' => $fileContents
949 ]
950 ]);
951 $subject = $this->createDriver();
952 $filePath = GeneralUtility::fixWindowsFilePath($subject->_call('copyFileToTemporaryPath', '/someDir/someFile'));
953 $this->testFilesToDelete[] = $filePath;
954 $this->assertContains('/typo3temp/var/transient/', $filePath);
955 $this->assertEquals($fileContents, file_get_contents($filePath));
956 }
957
958 /**
959 * @test
960 */
961 public function permissionsAreCorrectlyRetrievedForAllowedFile()
962 {
963 /** @var $subject \TYPO3\CMS\Core\Resource\Driver\LocalDriver */
964 list($basedir, $subject) = $this->prepareRealTestEnvironment();
965 touch($basedir . '/someFile');
966 chmod($basedir . '/someFile', 448);
967 clearstatcache();
968 $this->assertEquals(['r' => true, 'w' => true], $subject->getPermissions('/someFile'));
969 }
970
971 /**
972 * @test
973 */
974 public function permissionsAreCorrectlyRetrievedForForbiddenFile()
975 {
976 if (function_exists('posix_getegid') && posix_getegid() === 0) {
977 $this->markTestSkipped('Test skipped if run on linux as root');
978 } elseif (TYPO3_OS === 'WIN') {
979 $this->markTestSkipped('Test skipped if run on Windows system');
980 }
981 /** @var $subject \TYPO3\CMS\Core\Resource\Driver\LocalDriver */
982 list($basedir, $subject) = $this->prepareRealTestEnvironment();
983 touch($basedir . '/someForbiddenFile');
984 chmod($basedir . '/someForbiddenFile', 0);
985 clearstatcache();
986 $this->assertEquals(['r' => false, 'w' => false], $subject->getPermissions('/someForbiddenFile'));
987 }
988
989 /**
990 * @test
991 */
992 public function permissionsAreCorrectlyRetrievedForAllowedFolder()
993 {
994 /** @var $subject \TYPO3\CMS\Core\Resource\Driver\LocalDriver */
995 list($basedir, $subject) = $this->prepareRealTestEnvironment();
996 mkdir($basedir . '/someFolder');
997 chmod($basedir . '/someFolder', 448);
998 clearstatcache();
999 $this->assertEquals(['r' => true, 'w' => true], $subject->getPermissions('/someFolder'));
1000 }
1001
1002 /**
1003 * @test
1004 */
1005 public function permissionsAreCorrectlyRetrievedForForbiddenFolder()
1006 {
1007 if (function_exists('posix_getegid') && posix_getegid() === 0) {
1008 $this->markTestSkipped('Test skipped if run on linux as root');
1009 } elseif (TYPO3_OS === 'WIN') {
1010 $this->markTestSkipped('Test skipped if run on Windows system');
1011 }
1012 /** @var $subject \TYPO3\CMS\Core\Resource\Driver\LocalDriver */
1013 list($basedir, $subject) = $this->prepareRealTestEnvironment();
1014 mkdir($basedir . '/someForbiddenFolder');
1015 chmod($basedir . '/someForbiddenFolder', 0);
1016 clearstatcache();
1017 $result = $subject->getPermissions('/someForbiddenFolder');
1018 // Change permissions back to writable, so the sub-folder can be removed in tearDown
1019 chmod($basedir . '/someForbiddenFolder', 0777);
1020 $this->assertEquals(['r' => false, 'w' => false], $result);
1021 }
1022
1023 /**
1024 * Dataprovider for getFilePermissionsReturnsCorrectPermissionsForFilesNotOwnedByCurrentUser test
1025 *
1026 * @return array group, filemode and expected result
1027 */
1028 public function getFilePermissionsReturnsCorrectPermissionsForFilesNotOwnedByCurrentUser_dataProvider()
1029 {
1030 $data = [];
1031 // On some OS, the posix_* functions do not exits
1032 if (function_exists('posix_getgid')) {
1033 $data = [
1034 'current group, readable/writable' => [
1035 posix_getgid(),
1036 48,
1037 ['r' => true, 'w' => true]
1038 ],
1039 'current group, readable/not writable' => [
1040 posix_getgid(),
1041 32,
1042 ['r' => true, 'w' => false]
1043 ],
1044 'current group, not readable/not writable' => [
1045 posix_getgid(),
1046 0,
1047 ['r' => false, 'w' => false]
1048 ]
1049 ];
1050 }
1051 $data = array_merge_recursive($data, [
1052 'arbitrary group, readable/writable' => [
1053 vfsStream::GROUP_USER_1,
1054 6,
1055 ['r' => true, 'w' => true]
1056 ],
1057 'arbitrary group, readable/not writable' => [
1058 vfsStream::GROUP_USER_1,
1059 436,
1060 ['r' => true, 'w' => false]
1061 ],
1062 'arbitrary group, not readable/not writable' => [
1063 vfsStream::GROUP_USER_1,
1064 432,
1065 ['r' => false, 'w' => false]
1066 ]
1067 ]);
1068 return $data;
1069 }
1070
1071 /**
1072 * @test
1073 * @dataProvider getFilePermissionsReturnsCorrectPermissionsForFilesNotOwnedByCurrentUser_dataProvider
1074 */
1075 public function getFilePermissionsReturnsCorrectPermissionsForFilesNotOwnedByCurrentUser($group, $permissions, $expectedResult)
1076 {
1077 if (TYPO3_OS === 'WIN') {
1078 $this->markTestSkipped('Test skipped if run on Windows system');
1079 }
1080 $this->addToMount([
1081 'testfile' => 'asdfg'
1082 ]);
1083 $subject = $this->createDriver();
1084 /** @var $fileObject vfsStreamContent */
1085 $fileObject = vfsStreamWrapper::getRoot()->getChild($this->mountDir)->getChild('testfile');
1086 // just use an "arbitrary" user here - it is only important that
1087 $fileObject->chown(vfsStream::OWNER_USER_1);
1088 $fileObject->chgrp($group);
1089 $fileObject->chmod($permissions);
1090 $this->assertEquals($expectedResult, $subject->getPermissions('/testfile'));
1091 }
1092
1093 /**
1094 * @test
1095 */
1096 public function isWithinRecognizesFilesWithinFolderAndInOtherFolders()
1097 {
1098 $subject = $this->createDriver();
1099 $this->assertTrue($subject->isWithin('/someFolder/', '/someFolder/test.jpg'));
1100 $this->assertTrue($subject->isWithin('/someFolder/', '/someFolder/subFolder/test.jpg'));
1101 $this->assertFalse($subject->isWithin('/someFolder/', '/someFolderWithALongName/test.jpg'));
1102 }
1103
1104 /**
1105 * @test
1106 */
1107 public function isWithinAcceptsFileAndFolderObjectsAsContent()
1108 {
1109 $subject = $this->createDriver();
1110 $this->assertTrue($subject->isWithin('/someFolder/', '/someFolder/test.jpg'));
1111 $this->assertTrue($subject->isWithin('/someFolder/', '/someFolder/subfolder/'));
1112 }
1113
1114 /**********************************
1115 * Copy/move file
1116 **********************************/
1117
1118 /**
1119 * @test
1120 */
1121 public function filesCanBeCopiedWithinStorage()
1122 {
1123 $fileContents = $this->getUniqueId();
1124 $this->addToMount([
1125 'someFile' => $fileContents,
1126 'targetFolder' => []
1127 ]);
1128 $subject = $this->createDriver(
1129 [],
1130 ['getMimeTypeOfFile']
1131 );
1132 $subject->copyFileWithinStorage('/someFile', '/targetFolder/', 'someFile');
1133 $this->assertFileEquals($this->getUrlInMount('/someFile'), $this->getUrlInMount('/targetFolder/someFile'));
1134 }
1135
1136 /**
1137 * @test
1138 */
1139 public function filesCanBeMovedWithinStorage()
1140 {
1141 $fileContents = $this->getUniqueId();
1142 $this->addToMount([
1143 'targetFolder' => [],
1144 'someFile' => $fileContents
1145 ]);
1146 $subject = $this->createDriver();
1147 $newIdentifier = $subject->moveFileWithinStorage('/someFile', '/targetFolder/', 'file');
1148 $this->assertEquals($fileContents, file_get_contents($this->getUrlInMount('/targetFolder/file')));
1149 $this->assertFileNotExists($this->getUrlInMount('/someFile'));
1150 $this->assertEquals('/targetFolder/file', $newIdentifier);
1151 }
1152
1153 /**
1154 * @test
1155 */
1156 public function fileMetadataIsChangedAfterMovingFile()
1157 {
1158 $fileContents = $this->getUniqueId();
1159 $this->addToMount([
1160 'targetFolder' => [],
1161 'someFile' => $fileContents
1162 ]);
1163 $subject = $this->createDriver(
1164 [],
1165 // Mocked because finfo() can not deal with vfs streams and throws warnings
1166 ['getMimeTypeOfFile']
1167 );
1168 $newIdentifier = $subject->moveFileWithinStorage('/someFile', '/targetFolder/', 'file');
1169 $fileMetadata = $subject->getFileInfoByIdentifier($newIdentifier);
1170 $this->assertEquals($newIdentifier, $fileMetadata['identifier']);
1171 }
1172
1173 public function renamingFiles_dataProvider()
1174 {
1175 return [
1176 'file in subfolder' => [
1177 [
1178 'targetFolder' => ['file' => '']
1179 ],
1180 '/targetFolder/file',
1181 'newFile',
1182 '/targetFolder/newFile'
1183 ],
1184 'file in rootfolder' => [
1185 [
1186 'fileInRoot' => ''
1187 ],
1188 '/fileInRoot',
1189 'newFile',
1190 '/newFile'
1191 ]
1192 ];
1193 }
1194
1195 /**
1196 * @test
1197 * @dataProvider renamingFiles_dataProvider
1198 */
1199 public function renamingFilesChangesFilenameOnDisk(array $filesystemStructure, $oldFileIdentifier, $newFileName, $expectedNewIdentifier)
1200 {
1201 $this->addToMount($filesystemStructure);
1202 $subject = $this->createDriver();
1203 $newIdentifier = $subject->renameFile($oldFileIdentifier, $newFileName);
1204 $this->assertFalse($subject->fileExists($oldFileIdentifier));
1205 $this->assertTrue($subject->fileExists($newIdentifier));
1206 $this->assertEquals($expectedNewIdentifier, $newIdentifier);
1207 }
1208
1209 /**
1210 * @test
1211 */
1212 public function renamingFilesFailsIfTargetFileExists()
1213 {
1214 $this->expectException(\TYPO3\CMS\Core\Resource\Exception\ExistingTargetFileNameException::class);
1215 $this->expectExceptionCode(1320291063);
1216 $this->addToMount([
1217 'targetFolder' => ['file' => '', 'newFile' => '']
1218 ]);
1219 $subject = $this->createDriver();
1220 $subject->renameFile('/targetFolder/file', 'newFile');
1221 }
1222
1223 /**
1224 * We use this data provider for testing move methods because there are some issues with the
1225 *
1226 * @return array
1227 */
1228 public function renamingFolders_dataProvider()
1229 {
1230 return [
1231 'folder in root folder' => [
1232 [
1233 'someFolder' => []
1234 ],
1235 '/someFolder/',
1236 'newFolder',
1237 '/newFolder/'
1238 ],
1239 'file in subfolder' => [
1240 [
1241 'subfolder' => [
1242 'someFolder' => []
1243 ]
1244 ],
1245 '/subfolder/someFolder/',
1246 'newFolder',
1247 '/subfolder/newFolder/'
1248 ]
1249 ];
1250 }
1251
1252 /**
1253 * @test
1254 * @dataProvider renamingFolders_dataProvider
1255 */
1256 public function renamingFoldersChangesFolderNameOnDisk(array $filesystemStructure, $oldFolderIdentifier, $newFolderName, $expectedNewIdentifier)
1257 {
1258 $this->addToMount($filesystemStructure);
1259 $subject = $this->createDriver();
1260 $mapping = $subject->renameFolder($oldFolderIdentifier, $newFolderName);
1261 $this->assertFalse($subject->folderExists($oldFolderIdentifier));
1262 $this->assertTrue($subject->folderExists($expectedNewIdentifier));
1263 $this->assertEquals($expectedNewIdentifier, $mapping[$oldFolderIdentifier]);
1264 }
1265
1266 /**
1267 * @test
1268 */
1269 public function renameFolderReturnsCorrectMappingInformationForAllFiles()
1270 {
1271 $fileContents = 'asdfg';
1272 $this->addToMount([
1273 'sourceFolder' => [
1274 'subFolder' => ['file' => $fileContents],
1275 'file2' => 'asdfg'
1276 ]
1277 ]);
1278 $subject = $this->createDriver();
1279 $mappingInformation = $subject->renameFolder('/sourceFolder/', 'newFolder');
1280 $this->isTrue(is_array($mappingInformation));
1281 $this->assertEquals('/newFolder/', $mappingInformation['/sourceFolder/']);
1282 $this->assertEquals('/newFolder/file2', $mappingInformation['/sourceFolder/file2']);
1283 $this->assertEquals('/newFolder/subFolder/file', $mappingInformation['/sourceFolder/subFolder/file']);
1284 $this->assertEquals('/newFolder/subFolder/', $mappingInformation['/sourceFolder/subFolder/']);
1285 }
1286
1287 /**
1288 * @test
1289 */
1290 public function renameFolderRevertsRenamingIfFilenameMapCannotBeCreated()
1291 {
1292 $this->expectException(\RuntimeException::class);
1293 $this->expectExceptionCode(1334160746);
1294 $this->addToMount([
1295 'sourceFolder' => [
1296 'file' => 'asdfg'
1297 ]
1298 ]);
1299 $subject = $this->createDriver([], ['createIdentifierMap']);
1300 $subject->expects($this->atLeastOnce())->method('createIdentifierMap')->will(
1301 $this->throwException(
1302 new \TYPO3\CMS\Core\Resource\Exception\FileOperationErrorException('testing', 1476045666)
1303 )
1304 );
1305 $subject->renameFolder('/sourceFolder/', 'newFolder');
1306 $this->assertFileExists($this->getUrlInMount('/sourceFolder/file'));
1307 }
1308
1309 /**
1310 * @test
1311 */
1312 public function isFolderEmptyReturnsTrueForEmptyFolder()
1313 {
1314 // This also prepares the next few tests, so add more info than required for this test
1315 $this->addToMount([
1316 'emptyFolder' => []
1317 ]);
1318 $subject = $this->createDriver();
1319 $this->assertTrue($subject->isFolderEmpty('/emptyFolder/'));
1320 return $subject;
1321 }
1322
1323 /**
1324 * @test
1325 */
1326 public function isFolderEmptyReturnsFalseIfFolderHasFile()
1327 {
1328 $this->addToMount([
1329 'folderWithFile' => [
1330 'someFile' => ''
1331 ]
1332 ]);
1333 $subject = $this->createDriver();
1334 $this->assertFalse($subject->isFolderEmpty('/folderWithFile/'));
1335 }
1336
1337 /**
1338 * @test
1339 */
1340 public function isFolderEmptyReturnsFalseIfFolderHasSubfolder()
1341 {
1342 $this->addToMount([
1343 'folderWithSubfolder' => [
1344 'someFolder' => []
1345 ]
1346 ]);
1347 $subject = $this->createDriver();
1348 $this->assertFalse($subject->isFolderEmpty('/folderWithSubfolder/'));
1349 }
1350
1351 /**********************************
1352 * Copy/move folder
1353 **********************************/
1354 /**
1355 * @test
1356 */
1357 public function foldersCanBeMovedWithinStorage()
1358 {
1359 $fileContents = $this->getUniqueId();
1360 $this->addToMount([
1361 'sourceFolder' => [
1362 'file' => $fileContents,
1363 ],
1364 'targetFolder' => [],
1365 ]);
1366 $subject = $this->createDriver();
1367 /** @var \TYPO3\CMS\Core\Resource\Driver\LocalDriver $subject */
1368 $subject->moveFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'someFolder');
1369 $this->assertTrue(file_exists($this->getUrlInMount('/targetFolder/someFolder/')));
1370 $this->assertEquals($fileContents, file_get_contents($this->getUrlInMount('/targetFolder/someFolder/file')));
1371 $this->assertFileNotExists($this->getUrlInMount('/sourceFolder'));
1372 }
1373
1374 /**
1375 * @test
1376 */
1377 public function moveFolderWithinStorageReturnsCorrectMappingInformationForAllFiles()
1378 {
1379 $fileContents = 'asdfg';
1380 $this->addToMount([
1381 'targetFolder' => [],
1382 'sourceFolder' => [
1383 'subFolder' => ['file' => $fileContents],
1384 'file' => 'asdfg'
1385 ]
1386 ]);
1387 $subject = $this->createDriver();
1388 $mappingInformation = $subject->moveFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'sourceFolder');
1389 $this->assertEquals('/targetFolder/sourceFolder/file', $mappingInformation['/sourceFolder/file']);
1390 $this->assertEquals('/targetFolder/sourceFolder/subFolder/file', $mappingInformation['/sourceFolder/subFolder/file']);
1391 $this->assertEquals('/targetFolder/sourceFolder/subFolder/', $mappingInformation['/sourceFolder/subFolder/']);
1392 }
1393
1394 /**
1395 * @test
1396 */
1397 public function folderCanBeRenamedWhenMoving()
1398 {
1399 $this->addToMount([
1400 'sourceFolder' => [
1401 'file' => $this->getUniqueId(),
1402 ],
1403 'targetFolder' => [],
1404 ]);
1405 $subject = $this->createDriver();
1406 $subject->moveFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'newFolder');
1407 $this->assertTrue(file_exists($this->getUrlInMount('/targetFolder/newFolder/')));
1408 }
1409
1410 /**
1411 * @test
1412 */
1413 public function copyFolderWithinStorageCopiesSingleFileToNewFolderName()
1414 {
1415 $this->addToMount([
1416 'sourceFolder' => [
1417 'file' => $this->getUniqueId(),
1418 ],
1419 'targetFolder' => [],
1420 ]);
1421 $subject = $this->createDriver();
1422 $subject->copyFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'newFolderName');
1423 $this->assertTrue(is_file($this->getUrlInMount('/targetFolder/newFolderName/file')));
1424 }
1425
1426 /**
1427 * @test
1428 */
1429 public function copyFolderWithinStorageCopiesSingleSubFolderToNewFolderName()
1430 {
1431 list($basePath, $subject) = $this->prepareRealTestEnvironment();
1432 GeneralUtility::mkdir_deep($basePath, '/sourceFolder/subFolder');
1433 GeneralUtility::mkdir_deep($basePath, '/targetFolder');
1434
1435 $subject->copyFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'newFolderName');
1436 $this->isTrue(is_dir($basePath . '/targetFolder/newFolderName/subFolder'));
1437 }
1438
1439 /**
1440 * @test
1441 */
1442 public function copyFolderWithinStorageCopiesFileInSingleSubFolderToNewFolderName()
1443 {
1444 list($basePath, $subject) = $this->prepareRealTestEnvironment();
1445 GeneralUtility::mkdir_deep($basePath, '/sourceFolder/subFolder');
1446 GeneralUtility::mkdir_deep($basePath, '/targetFolder');
1447 file_put_contents($basePath . '/sourceFolder/subFolder/file', $this->getUniqueId());
1448 GeneralUtility::fixPermissions($basePath . '/sourceFolder/subFolder/file');
1449
1450 $subject->copyFolderWithinStorage('/sourceFolder/', '/targetFolder/', 'newFolderName');
1451 $this->assertTrue(is_file($basePath . '/targetFolder/newFolderName/subFolder/file'));
1452 }
1453
1454 ///////////////////////
1455 // Tests concerning sanitizeFileName
1456 ///////////////////////
1457
1458 /**
1459 * Set up data for sanitizeFileName tests
1460 */
1461 public function setUpCharacterStrings()
1462 {
1463 // Generate string containing all characters for the iso8859-1 charset, charcode greater than 127
1464 $this->iso88591GreaterThan127 = '';
1465 for ($i = 0xA0; $i <= 0xFF; $i++) {
1466 $this->iso88591GreaterThan127 .= chr($i);
1467 }
1468
1469 // Generate string containing all characters for the utf-8 Latin-1 Supplement (U+0080 to U+00FF)
1470 // without U+0080 to U+009F: control characters
1471 // Based on http://www.utf8-chartable.de/unicode-utf8-table.pl
1472 $this->utf8Latin1Supplement = '';
1473 for ($i = 0xA0; $i <= 0xBF; $i++) {
1474 $this->utf8Latin1Supplement .= chr(0xC2) . chr($i);
1475 }
1476 for ($i = 0x80; $i <= 0xBF; $i++) {
1477 $this->utf8Latin1Supplement .= chr(0xC3) . chr($i);
1478 }
1479
1480 // Generate string containing all characters for the utf-8 Latin-1 Extended-A (U+0100 to U+017F)
1481 $this->utf8Latin1ExtendedA = '';
1482 for ($i = 0x80; $i <= 0xBF; $i++) {
1483 $this->utf8Latin1ExtendedA .= chr(0xC4) . chr($i);
1484 }
1485 for ($i = 0x80; $i <= 0xBF; $i++) {
1486 $this->utf8Latin1ExtendedA .= chr(0xC5) . chr($i);
1487 }
1488 }
1489
1490 /**
1491 * Data provider for sanitizeFileNameUTF8FilesystemDataProvider
1492 *
1493 * Every array splits into:
1494 * - String value fileName
1495 * - String value charset (none = '', utf-8, latin1, etc.)
1496 * - Expected result (cleaned fileName)
1497 *
1498 * @return array
1499 */
1500 public function sanitizeFileNameUTF8FilesystemDataProvider()
1501 {
1502 $this->setUpCharacterStrings();
1503 return [
1504 // Characters ordered by ASCII table
1505 'allowed characters utf-8 (ASCII part)' => [
1506 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz',
1507 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
1508 ],
1509 // Characters ordered by ASCII table (except for space-character, because space-character ist trimmed)
1510 'replace special characters with _ (not allowed characters) utf-8 (ASCII part)' => [
1511 '! "#$%&\'()*+,/:;<=>?[\\]^`{|}~',
1512 '_____________________________'
1513 ],
1514 'utf-8 (Latin-1 Supplement)' => [
1515 $this->utf8Latin1Supplement,
1516 '________________________________ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'
1517 ],
1518 'trim leading and tailing spaces utf-8' => [
1519 ' test.txt ',
1520 'test.txt'
1521 ],
1522 'remove tailing dot' => [
1523 'test.txt.',
1524 'test.txt'
1525 ],
1526 ];
1527 }
1528
1529 /**
1530 * @test
1531 * @dataProvider sanitizeFileNameUTF8FilesystemDataProvider
1532 */
1533 public function sanitizeFileNameUTF8Filesystem($fileName, $expectedResult)
1534 {
1535 $GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem'] = 1;
1536 $this->assertEquals(
1537 $expectedResult,
1538 $this->createDriver()->sanitizeFileName($fileName)
1539 );
1540 }
1541
1542 /**
1543 * Data provider for sanitizeFileNameNonUTF8Filesystem
1544 *
1545 * Every array splits into:
1546 * - String value fileName
1547 * - String value charset (none = '', utf-8, latin1, etc.)
1548 * - Expected result (cleaned fileName)
1549 *
1550 * @return array
1551 */
1552 public function sanitizeFileNameNonUTF8FilesystemDataProvider()
1553 {
1554 $this->setUpCharacterStrings();
1555 return [
1556 // Characters ordered by ASCII table
1557 'allowed characters iso-8859-1' => [
1558 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz',
1559 'iso-8859-1',
1560 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
1561 ],
1562 // Characters ordered by ASCII table
1563 'allowed characters utf-8' => [
1564 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz',
1565 'utf-8',
1566 '-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'
1567 ],
1568 // Characters ordered by ASCII table (except for space-character, because space-character ist trimmed)
1569 'replace special characters with _ (not allowed characters) iso-8859-1' => [
1570 '! "#$%&\'()*+,/:;<=>?[\\]^`{|}~',
1571 'iso-8859-1',
1572 '_____________________________'
1573 ],
1574 // Characters ordered by ASCII table (except for space-character, because space-character ist trimmed)
1575 'replace special characters with _ (not allowed characters) utf-8' => [
1576 '! "#$%&\'()*+,/:;<=>?[\\]^`{|}~',
1577 'utf-8',
1578 '_____________________________'
1579 ],
1580 'iso-8859-1 (code > 127)' => [
1581 // http://de.wikipedia.org/wiki/ISO_8859-1
1582 // chr(0xA0) = NBSP (no-break space) => gets trimmed
1583 $this->iso88591GreaterThan127,
1584 'iso-8859-1',
1585 '_centpound_yen____c_a_____R_____-23_u___1o__1_41_23_4_AAAAAEAAAECEEEEIIIIDNOOOOOExOEUUUUEYTHssaaaaaeaaaeceeeeiiiidnoooooe_oeuuuueythy'
1586 ],
1587 'utf-8 (Latin-1 Supplement)' => [
1588 // chr(0xC2) . chr(0x0A) = NBSP (no-break space) => gets trimmed
1589 $this->utf8Latin1Supplement,
1590 'utf-8',
1591 '_centpound__yen______c_a_______R_______-23__u_____1o__1_41_23_4_AAAAAEAAAECEEEEIIIIDNOOOOOExOEUUUUEYTHssaaaaaeaaaeceeeeiiiidnoooooe_oeuuuueythy'
1592 ],
1593 'utf-8 (Latin-1 Extended A)' => [
1594 $this->utf8Latin1ExtendedA,
1595 'utf-8',
1596 'AaAaAaCcCcCcCcDdDdEeEeEeEeEeGgGgGgGgHhHhIiIiIiIiIiIJijJjKk__LlLlLlL_l_LlNnNnNn_n____OOooOoOoOEoeRrRrRrSsSsSsSsTtTtTtUuUuUuUuUuUuWwYyYZzZzZzs'
1597 ],
1598 'trim leading and tailing spaces iso-8859-1' => [
1599 ' test.txt ',
1600 'iso-8859-1',
1601 'test.txt'
1602 ],
1603 'trim leading and tailing spaces utf-8' => [
1604 ' test.txt ',
1605 'utf-8',
1606 'test.txt'
1607 ],
1608 'remove tailing dot iso-8859-1' => [
1609 'test.txt.',
1610 'iso-8859-1',
1611 'test.txt'
1612 ],
1613 'remove tailing dot utf-8' => [
1614 'test.txt.',
1615 'utf-8',
1616 'test.txt'
1617 ],
1618 ];
1619 }
1620
1621 /**
1622 * @test
1623 * @dataProvider sanitizeFileNameNonUTF8FilesystemDataProvider
1624 */
1625 public function sanitizeFileNameNonUTF8Filesystem($fileName, $charset, $expectedResult)
1626 {
1627 $GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem'] = 0;
1628 $this->assertEquals(
1629 $expectedResult,
1630 $this->createDriver()->sanitizeFileName($fileName, $charset)
1631 );
1632 }
1633
1634 /**
1635 * @test
1636 */
1637 public function sanitizeFileNameThrowsExceptionOnInvalidFileName()
1638 {
1639 $this->expectException(InvalidFileNameException::class);
1640 $this->expectExceptionCode(1320288991);
1641
1642 $GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem'] = 1;
1643 $this->createDriver()->sanitizeFileName('');
1644 }
1645
1646 /**
1647 * @test
1648 */
1649 public function applyFilterMethodsToDirectoryItemCallsFilterMethodIfClosure()
1650 {
1651 $this->expectException(\Exception::class);
1652 $this->expectExceptionCode(1463073434);
1653 $closure = function () {
1654 throw new \Exception('I was called!', 1463073434);
1655 };
1656
1657 $filterMethods = [
1658 $closure,
1659 ];
1660
1661 $this->createDriver()->_call('applyFilterMethodsToDirectoryItem', $filterMethods, '', '', '');
1662 }
1663
1664 /**
1665 * @test
1666 */
1667 public function applyFilterMethodsToDirectoryItemCallsFilterMethodIfName()
1668 {
1669 $dummyObject = $this
1670 ->getMockBuilder('\TYPO3\CMS\Core\Resource\Driver\LocalDriver')
1671 ->setMethods(['dummy'])
1672 ->disableOriginalConstructor()
1673 ->getMock();
1674 $method = [
1675 $dummyObject,
1676 'dummy',
1677 ];
1678 $dummyObject->expects($this->once())->method('dummy');
1679 $filterMethods = [
1680 $method,
1681 ];
1682 $this->createDriver()->_call('applyFilterMethodsToDirectoryItem', $filterMethods, '', '', '');
1683 }
1684 }