4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Filesystem\Tests;
15 * Test class for Filesystem.
17 class FilesystemTest extends FilesystemTestCase
19 public function testCopyCreatesNewFile()
21 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
22 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
24 file_put_contents($sourceFilePath, 'SOURCE FILE');
26 $this->filesystem->copy($sourceFilePath, $targetFilePath);
28 $this->assertFileExists($targetFilePath);
29 $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
33 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
35 public function testCopyFails()
37 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
38 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
40 $this->filesystem->copy($sourceFilePath, $targetFilePath);
44 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
46 public function testCopyUnreadableFileFails()
48 // skip test on Windows; PHP can't easily set file as unreadable on Windows
49 if ('\\' === DIRECTORY_SEPARATOR) {
50 $this->markTestSkipped('This test cannot run on Windows.');
53 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
54 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
56 file_put_contents($sourceFilePath, 'SOURCE FILE');
58 // make sure target cannot be read
59 $this->filesystem->chmod($sourceFilePath, 0222);
61 $this->filesystem->copy($sourceFilePath, $targetFilePath);
64 public function testCopyOverridesExistingFileIfModified()
66 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
67 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
69 file_put_contents($sourceFilePath, 'SOURCE FILE');
70 file_put_contents($targetFilePath, 'TARGET FILE');
71 touch($targetFilePath, time() - 1000);
73 $this->filesystem->copy($sourceFilePath, $targetFilePath);
75 $this->assertFileExists($targetFilePath);
76 $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
79 public function testCopyDoesNotOverrideExistingFileByDefault()
81 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
84 file_put_contents($sourceFilePath, 'SOURCE FILE');
85 file_put_contents($targetFilePath, 'TARGET FILE');
87 // make sure both files have the same modification time
88 $modificationTime = time() - 1000;
89 touch($sourceFilePath, $modificationTime);
90 touch($targetFilePath, $modificationTime);
92 $this->filesystem->copy($sourceFilePath, $targetFilePath);
94 $this->assertFileExists($targetFilePath);
95 $this->assertStringEqualsFile($targetFilePath, 'TARGET FILE');
98 public function testCopyOverridesExistingFileIfForced()
100 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
101 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
103 file_put_contents($sourceFilePath, 'SOURCE FILE');
104 file_put_contents($targetFilePath, 'TARGET FILE');
106 // make sure both files have the same modification time
107 $modificationTime = time() - 1000;
108 touch($sourceFilePath, $modificationTime);
109 touch($targetFilePath, $modificationTime);
111 $this->filesystem->copy($sourceFilePath, $targetFilePath, true);
113 $this->assertFileExists($targetFilePath);
114 $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
118 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
120 public function testCopyWithOverrideWithReadOnlyTargetFails()
122 // skip test on Windows; PHP can't easily set file as unwritable on Windows
123 if ('\\' === DIRECTORY_SEPARATOR) {
124 $this->markTestSkipped('This test cannot run on Windows.');
127 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
128 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
130 file_put_contents($sourceFilePath, 'SOURCE FILE');
131 file_put_contents($targetFilePath, 'TARGET FILE');
133 // make sure both files have the same modification time
134 $modificationTime = time() - 1000;
135 touch($sourceFilePath, $modificationTime);
136 touch($targetFilePath, $modificationTime);
138 // make sure target is read-only
139 $this->filesystem->chmod($targetFilePath, 0444);
141 $this->filesystem->copy($sourceFilePath, $targetFilePath, true);
144 public function testCopyCreatesTargetDirectoryIfItDoesNotExist()
146 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
147 $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
148 $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file';
150 file_put_contents($sourceFilePath, 'SOURCE FILE');
152 $this->filesystem->copy($sourceFilePath, $targetFilePath);
154 $this->assertTrue(is_dir($targetFileDirectory));
155 $this->assertFileExists($targetFilePath);
156 $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
162 public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy()
164 $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png';
165 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
167 file_put_contents($targetFilePath, 'TARGET FILE');
169 $this->filesystem->copy($sourceFilePath, $targetFilePath, false);
171 $this->assertFileExists($targetFilePath);
172 $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath));
175 public function testMkdirCreatesDirectoriesRecursively()
177 $directory = $this->workspace
178 .DIRECTORY_SEPARATOR.'directory'
179 .DIRECTORY_SEPARATOR.'sub_directory';
181 $this->filesystem->mkdir($directory);
183 $this->assertTrue(is_dir($directory));
186 public function testMkdirCreatesDirectoriesFromArray()
188 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
189 $directories = array(
190 $basePath.'1', $basePath.'2', $basePath.'3',
193 $this->filesystem->mkdir($directories);
195 $this->assertTrue(is_dir($basePath.'1'));
196 $this->assertTrue(is_dir($basePath.'2'));
197 $this->assertTrue(is_dir($basePath.'3'));
200 public function testMkdirCreatesDirectoriesFromTraversableObject()
202 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
203 $directories = new \ArrayObject(array(
204 $basePath.'1', $basePath.'2', $basePath.'3',
207 $this->filesystem->mkdir($directories);
209 $this->assertTrue(is_dir($basePath.'1'));
210 $this->assertTrue(is_dir($basePath.'2'));
211 $this->assertTrue(is_dir($basePath.'3'));
215 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
217 public function testMkdirCreatesDirectoriesFails()
219 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
220 $dir = $basePath.'2';
222 file_put_contents($dir, '');
224 $this->filesystem->mkdir($dir);
227 public function testTouchCreatesEmptyFile()
229 $file = $this->workspace.DIRECTORY_SEPARATOR.'1';
231 $this->filesystem->touch($file);
233 $this->assertFileExists($file);
237 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
239 public function testTouchFails()
241 $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2';
243 $this->filesystem->touch($file);
246 public function testTouchCreatesEmptyFilesFromArray()
248 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
250 $basePath.'1', $basePath.'2', $basePath.'3',
253 $this->filesystem->touch($files);
255 $this->assertFileExists($basePath.'1');
256 $this->assertFileExists($basePath.'2');
257 $this->assertFileExists($basePath.'3');
260 public function testTouchCreatesEmptyFilesFromTraversableObject()
262 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
263 $files = new \ArrayObject(array(
264 $basePath.'1', $basePath.'2', $basePath.'3',
267 $this->filesystem->touch($files);
269 $this->assertFileExists($basePath.'1');
270 $this->assertFileExists($basePath.'2');
271 $this->assertFileExists($basePath.'3');
274 public function testRemoveCleansFilesAndDirectoriesIteratively()
276 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
279 mkdir($basePath.'dir');
280 touch($basePath.'file');
282 $this->filesystem->remove($basePath);
284 $this->assertFileNotExists($basePath);
287 public function testRemoveCleansArrayOfFilesAndDirectories()
289 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
291 mkdir($basePath.'dir');
292 touch($basePath.'file');
295 $basePath.'dir', $basePath.'file',
298 $this->filesystem->remove($files);
300 $this->assertFileNotExists($basePath.'dir');
301 $this->assertFileNotExists($basePath.'file');
304 public function testRemoveCleansTraversableObjectOfFilesAndDirectories()
306 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
308 mkdir($basePath.'dir');
309 touch($basePath.'file');
311 $files = new \ArrayObject(array(
312 $basePath.'dir', $basePath.'file',
315 $this->filesystem->remove($files);
317 $this->assertFileNotExists($basePath.'dir');
318 $this->assertFileNotExists($basePath.'file');
321 public function testRemoveIgnoresNonExistingFiles()
323 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
325 mkdir($basePath.'dir');
328 $basePath.'dir', $basePath.'file',
331 $this->filesystem->remove($files);
333 $this->assertFileNotExists($basePath.'dir');
336 public function testRemoveCleansInvalidLinks()
338 $this->markAsSkippedIfSymlinkIsMissing();
340 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
343 mkdir($basePath.'dir');
344 // create symlink to nonexistent file
345 @symlink($basePath.'file', $basePath.'file-link');
347 // create symlink to dir using trailing forward slash
348 $this->filesystem->symlink($basePath.'dir/', $basePath.'dir-link');
349 $this->assertTrue(is_dir($basePath.'dir-link'));
351 // create symlink to nonexistent dir
352 rmdir($basePath.'dir');
353 $this->assertFalse('\\' === DIRECTORY_SEPARATOR ? @readlink($basePath.'dir-link') : is_dir($basePath.'dir-link'));
355 $this->filesystem->remove($basePath);
357 $this->assertFileNotExists($basePath);
360 public function testFilesExists()
362 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
365 touch($basePath.'file1');
366 mkdir($basePath.'folder');
368 $this->assertTrue($this->filesystem->exists($basePath.'file1'));
369 $this->assertTrue($this->filesystem->exists($basePath.'folder'));
373 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
375 public function testFilesExistsFails()
377 if ('\\' !== DIRECTORY_SEPARATOR) {
378 $this->markTestSkipped('Long file names are an issue on Windows');
380 $basePath = $this->workspace.'\\directory\\';
381 $maxPathLength = PHP_MAXPATHLEN - 2;
386 $file = str_repeat('T', $maxPathLength - strlen($basePath) + 1);
387 $path = $basePath.$file;
388 exec('TYPE NUL >>'.$file); // equivalent of touch, we can not use the php touch() here because it suffers from the same limitation
389 $this->longPathNamesWindows[] = $path; // save this so we can clean up later
391 $this->filesystem->exists($path);
394 public function testFilesExistsTraversableObjectOfFilesAndDirectories()
396 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
398 mkdir($basePath.'dir');
399 touch($basePath.'file');
401 $files = new \ArrayObject(array(
402 $basePath.'dir', $basePath.'file',
405 $this->assertTrue($this->filesystem->exists($files));
408 public function testFilesNotExistsTraversableObjectOfFilesAndDirectories()
410 $basePath = $this->workspace.DIRECTORY_SEPARATOR;
412 mkdir($basePath.'dir');
413 touch($basePath.'file');
414 touch($basePath.'file2');
416 $files = new \ArrayObject(array(
417 $basePath.'dir', $basePath.'file', $basePath.'file2',
420 unlink($basePath.'file');
422 $this->assertFalse($this->filesystem->exists($files));
425 public function testInvalidFileNotExists()
427 $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
429 $this->assertFalse($this->filesystem->exists($basePath.time()));
432 public function testChmodChangesFileMode()
434 $this->markAsSkippedIfChmodIsMissing();
436 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
438 $file = $dir.DIRECTORY_SEPARATOR.'file';
441 $this->filesystem->chmod($file, 0400);
442 $this->filesystem->chmod($dir, 0753);
444 $this->assertFilePermissions(753, $dir);
445 $this->assertFilePermissions(400, $file);
448 public function testChmodWithWrongModLeavesPreviousPermissionsUntouched()
450 $this->markAsSkippedIfChmodIsMissing();
452 if (defined('HHVM_VERSION')) {
453 $this->markTestSkipped('chmod() changes permissions even when passing invalid modes on HHVM');
456 $dir = $this->workspace.DIRECTORY_SEPARATOR.'file';
459 $permissions = fileperms($dir);
461 $this->filesystem->chmod($dir, 'Wrongmode');
463 $this->assertSame($permissions, fileperms($dir));
466 public function testChmodRecursive()
468 $this->markAsSkippedIfChmodIsMissing();
470 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
472 $file = $dir.DIRECTORY_SEPARATOR.'file';
475 $this->filesystem->chmod($file, 0400, 0000, true);
476 $this->filesystem->chmod($dir, 0753, 0000, true);
478 $this->assertFilePermissions(753, $dir);
479 $this->assertFilePermissions(753, $file);
482 public function testChmodAppliesUmask()
484 $this->markAsSkippedIfChmodIsMissing();
486 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
489 $this->filesystem->chmod($file, 0770, 0022);
490 $this->assertFilePermissions(750, $file);
493 public function testChmodChangesModeOfArrayOfFiles()
495 $this->markAsSkippedIfChmodIsMissing();
497 $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
498 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
499 $files = array($directory, $file);
504 $this->filesystem->chmod($files, 0753);
506 $this->assertFilePermissions(753, $file);
507 $this->assertFilePermissions(753, $directory);
510 public function testChmodChangesModeOfTraversableFileObject()
512 $this->markAsSkippedIfChmodIsMissing();
514 $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
515 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
516 $files = new \ArrayObject(array($directory, $file));
521 $this->filesystem->chmod($files, 0753);
523 $this->assertFilePermissions(753, $file);
524 $this->assertFilePermissions(753, $directory);
527 public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive()
529 $this->markAsSkippedIfChmodIsMissing();
531 $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
532 $subdirectory = $directory.DIRECTORY_SEPARATOR.'subdirectory';
535 mkdir($subdirectory);
536 chmod($subdirectory, 0000);
538 $this->filesystem->chmod($directory, 0753, 0000, true);
540 $this->assertFilePermissions(753, $subdirectory);
543 public function testChown()
545 $this->markAsSkippedIfPosixIsMissing();
547 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
550 $owner = $this->getFileOwner($dir);
551 $this->filesystem->chown($dir, $owner);
553 $this->assertSame($owner, $this->getFileOwner($dir));
556 public function testChownRecursive()
558 $this->markAsSkippedIfPosixIsMissing();
560 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
562 $file = $dir.DIRECTORY_SEPARATOR.'file';
565 $owner = $this->getFileOwner($dir);
566 $this->filesystem->chown($dir, $owner, true);
568 $this->assertSame($owner, $this->getFileOwner($file));
571 public function testChownSymlink()
573 $this->markAsSkippedIfSymlinkIsMissing();
575 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
576 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
580 $this->filesystem->symlink($file, $link);
582 $owner = $this->getFileOwner($link);
583 $this->filesystem->chown($link, $owner);
585 $this->assertSame($owner, $this->getFileOwner($link));
589 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
591 public function testChownSymlinkFails()
593 $this->markAsSkippedIfSymlinkIsMissing();
595 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
596 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
600 $this->filesystem->symlink($file, $link);
602 $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999));
606 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
608 public function testChownFail()
610 $this->markAsSkippedIfPosixIsMissing();
612 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
615 $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
618 public function testChgrp()
620 $this->markAsSkippedIfPosixIsMissing();
622 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
625 $group = $this->getFileGroup($dir);
626 $this->filesystem->chgrp($dir, $group);
628 $this->assertSame($group, $this->getFileGroup($dir));
631 public function testChgrpRecursive()
633 $this->markAsSkippedIfPosixIsMissing();
635 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
637 $file = $dir.DIRECTORY_SEPARATOR.'file';
640 $group = $this->getFileGroup($dir);
641 $this->filesystem->chgrp($dir, $group, true);
643 $this->assertSame($group, $this->getFileGroup($file));
646 public function testChgrpSymlink()
648 $this->markAsSkippedIfSymlinkIsMissing();
650 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
651 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
655 $this->filesystem->symlink($file, $link);
657 $group = $this->getFileGroup($link);
658 $this->filesystem->chgrp($link, $group);
660 $this->assertSame($group, $this->getFileGroup($link));
664 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
666 public function testChgrpSymlinkFails()
668 $this->markAsSkippedIfSymlinkIsMissing();
670 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
671 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
675 $this->filesystem->symlink($file, $link);
677 $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999));
681 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
683 public function testChgrpFail()
685 $this->markAsSkippedIfPosixIsMissing();
687 $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
690 $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999));
693 public function testRename()
695 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
696 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
699 $this->filesystem->rename($file, $newPath);
701 $this->assertFileNotExists($file);
702 $this->assertFileExists($newPath);
706 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
708 public function testRenameThrowsExceptionIfTargetAlreadyExists()
710 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
711 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
716 $this->filesystem->rename($file, $newPath);
719 public function testRenameOverwritesTheTargetIfItAlreadyExists()
721 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
722 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
727 $this->filesystem->rename($file, $newPath, true);
729 $this->assertFileNotExists($file);
730 $this->assertFileExists($newPath);
734 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
736 public function testRenameThrowsExceptionOnError()
738 $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid('fs_test_', true);
739 $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
741 $this->filesystem->rename($file, $newPath);
744 public function testSymlink()
746 if ('\\' === DIRECTORY_SEPARATOR) {
747 $this->markTestSkipped('Windows does not support creating "broken" symlinks');
750 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
751 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
753 // $file does not exists right now: creating "broken" links is a wanted feature
754 $this->filesystem->symlink($file, $link);
756 $this->assertTrue(is_link($link));
758 // Create the linked file AFTER creating the link
761 $this->assertEquals($file, readlink($link));
765 * @depends testSymlink
767 public function testRemoveSymlink()
769 $this->markAsSkippedIfSymlinkIsMissing();
771 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
773 $this->filesystem->remove($link);
775 $this->assertFalse(is_link($link));
776 $this->assertFalse(is_file($link));
777 $this->assertFalse(is_dir($link));
780 public function testSymlinkIsOverwrittenIfPointsToDifferentTarget()
782 $this->markAsSkippedIfSymlinkIsMissing();
784 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
785 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
788 symlink($this->workspace, $link);
790 $this->filesystem->symlink($file, $link);
792 $this->assertTrue(is_link($link));
793 $this->assertEquals($file, readlink($link));
796 public function testSymlinkIsNotOverwrittenIfAlreadyCreated()
798 $this->markAsSkippedIfSymlinkIsMissing();
800 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
801 $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
804 symlink($file, $link);
806 $this->filesystem->symlink($file, $link);
808 $this->assertTrue(is_link($link));
809 $this->assertEquals($file, readlink($link));
812 public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist()
814 $this->markAsSkippedIfSymlinkIsMissing();
816 $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
817 $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link';
818 $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link';
822 $this->filesystem->symlink($file, $link1);
823 $this->filesystem->symlink($file, $link2);
825 $this->assertTrue(is_link($link1));
826 $this->assertEquals($file, readlink($link1));
827 $this->assertTrue(is_link($link2));
828 $this->assertEquals($file, readlink($link2));
832 * @dataProvider providePathsForMakePathRelative
834 public function testMakePathRelative($endPath, $startPath, $expectedPath)
836 $path = $this->filesystem->makePathRelative($endPath, $startPath);
838 $this->assertEquals($expectedPath, $path);
841 public function providePathsForMakePathRelative()
844 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'),
845 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'),
846 array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'),
847 array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'),
848 array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'),
849 array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'),
850 array('usr/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'),
851 array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'),
852 array('/aa/bb', '/aa/bb', './'),
853 array('/aa/bb', '/aa/bb/', './'),
854 array('/aa/bb/', '/aa/bb', './'),
855 array('/aa/bb/', '/aa/bb/', './'),
856 array('/aa/bb/cc', '/aa/bb/cc/dd', '../'),
857 array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'),
858 array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'),
859 array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'),
860 array('/aa/bb/cc', '/aa', 'bb/cc/'),
861 array('/aa/bb/cc', '/aa/', 'bb/cc/'),
862 array('/aa/bb/cc/', '/aa', 'bb/cc/'),
863 array('/aa/bb/cc/', '/aa/', 'bb/cc/'),
864 array('/a/aab/bb', '/a/aa', '../aab/bb/'),
865 array('/a/aab/bb', '/a/aa/', '../aab/bb/'),
866 array('/a/aab/bb/', '/a/aa', '../aab/bb/'),
867 array('/a/aab/bb/', '/a/aa/', '../aab/bb/'),
868 array('/a/aab/bb/', '/', 'a/aab/bb/'),
869 array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'),
870 array('/aab/bb', '/aa', '../aab/bb/'),
871 array('/aab', '/aa', '../aab/'),
872 array('/aa/bb/cc', '/aa/dd/..', 'bb/cc/'),
873 array('/aa/../bb/cc', '/aa/dd/..', '../bb/cc/'),
874 array('/aa/bb/../../cc', '/aa/../dd/..', 'cc/'),
875 array('/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'),
876 array('/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'),
877 array('C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'),
878 array('c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'),
879 array('C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'),
880 array('C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'),
881 array('C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'),
882 array('aa/bb', 'aa/cc', '../bb/'),
883 array('aa/cc', 'bb/cc', '../../aa/cc/'),
884 array('aa/bb', 'aa/./cc', '../bb/'),
885 array('aa/./bb', 'aa/cc', '../bb/'),
886 array('aa/./bb', 'aa/./cc', '../bb/'),
887 array('../../', '../../', './'),
888 array('../aa/bb/', 'aa/bb/', '../../../aa/bb/'),
889 array('../../../', '../../', '../'),
891 array('', 'aa/', '../'),
892 array('aa/', '', 'aa/'),
895 if ('\\' === DIRECTORY_SEPARATOR) {
896 $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/');
902 public function testMirrorCopiesFilesAndDirectoriesRecursively()
904 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
905 $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR;
906 $file1 = $directory.'file1';
907 $file2 = $sourcePath.'file2';
911 file_put_contents($file1, 'FILE1');
912 file_put_contents($file2, 'FILE2');
914 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
916 $this->filesystem->mirror($sourcePath, $targetPath);
918 $this->assertTrue(is_dir($targetPath));
919 $this->assertTrue(is_dir($targetPath.'directory'));
920 $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1');
921 $this->assertFileEquals($file2, $targetPath.'file2');
923 $this->filesystem->remove($file1);
925 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false));
926 $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
928 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
929 $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
931 file_put_contents($file1, 'FILE1');
933 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
934 $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
936 $this->filesystem->remove($directory);
937 $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
938 $this->assertFalse($this->filesystem->exists($targetPath.'directory'));
939 $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
942 public function testMirrorCreatesEmptyDirectory()
944 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
948 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
950 $this->filesystem->mirror($sourcePath, $targetPath);
952 $this->assertTrue(is_dir($targetPath));
954 $this->filesystem->remove($sourcePath);
957 public function testMirrorCopiesLinks()
959 $this->markAsSkippedIfSymlinkIsMissing();
961 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
964 file_put_contents($sourcePath.'file1', 'FILE1');
965 symlink($sourcePath.'file1', $sourcePath.'link1');
967 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
969 $this->filesystem->mirror($sourcePath, $targetPath);
971 $this->assertTrue(is_dir($targetPath));
972 $this->assertFileEquals($sourcePath.'file1', $targetPath.'link1');
973 $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
976 public function testMirrorCopiesLinkedDirectoryContents()
978 $this->markAsSkippedIfSymlinkIsMissing(true);
980 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
982 mkdir($sourcePath.'nested/', 0777, true);
983 file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1');
984 // Note: We symlink directory, not file
985 symlink($sourcePath.'nested', $sourcePath.'link1');
987 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
989 $this->filesystem->mirror($sourcePath, $targetPath);
991 $this->assertTrue(is_dir($targetPath));
992 $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt');
993 $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
996 public function testMirrorCopiesRelativeLinkedContents()
998 $this->markAsSkippedIfSymlinkIsMissing(true);
1000 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1001 $oldPath = getcwd();
1003 mkdir($sourcePath.'nested/', 0777, true);
1004 file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1');
1005 // Note: Create relative symlink
1007 symlink('nested', 'link1');
1011 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1013 $this->filesystem->mirror($sourcePath, $targetPath);
1015 $this->assertTrue(is_dir($targetPath));
1016 $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt');
1017 $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
1018 $this->assertEquals('\\' === DIRECTORY_SEPARATOR ? realpath($sourcePath.'\nested') : 'nested', readlink($targetPath.DIRECTORY_SEPARATOR.'link1'));
1021 public function testMirrorContentsWithSameNameAsSourceOrTargetWithoutDeleteOption()
1023 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1026 touch($sourcePath.'source');
1027 touch($sourcePath.'target');
1029 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1031 $oldPath = getcwd();
1032 chdir($this->workspace);
1034 $this->filesystem->mirror('source', $targetPath);
1038 $this->assertTrue(is_dir($targetPath));
1039 $this->assertFileExists($targetPath.'source');
1040 $this->assertFileExists($targetPath.'target');
1043 public function testMirrorContentsWithSameNameAsSourceOrTargetWithDeleteOption()
1045 $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1048 touch($sourcePath.'source');
1050 $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1053 touch($targetPath.'source');
1054 touch($targetPath.'target');
1056 $oldPath = getcwd();
1057 chdir($this->workspace);
1059 $this->filesystem->mirror('source', 'target', null, array('delete' => true));
1063 $this->assertTrue(is_dir($targetPath));
1064 $this->assertFileExists($targetPath.'source');
1065 $this->assertFileNotExists($targetPath.'target');
1069 * @dataProvider providePathsForIsAbsolutePath
1071 public function testIsAbsolutePath($path, $expectedResult)
1073 $result = $this->filesystem->isAbsolutePath($path);
1075 $this->assertEquals($expectedResult, $result);
1078 public function providePathsForIsAbsolutePath()
1081 array('/var/lib', true),
1082 array('c:\\\\var\\lib', true),
1083 array('\\var\\lib', true),
1084 array('var/lib', false),
1085 array('../var/lib', false),
1091 public function testTempnam()
1093 $dirname = $this->workspace;
1095 $filename = $this->filesystem->tempnam($dirname, 'foo');
1097 $this->assertFileExists($filename);
1100 public function testTempnamWithFileScheme()
1102 $scheme = 'file://';
1103 $dirname = $scheme.$this->workspace;
1105 $filename = $this->filesystem->tempnam($dirname, 'foo');
1107 $this->assertStringStartsWith($scheme, $filename);
1108 $this->assertFileExists($filename);
1111 public function testTempnamWithMockScheme()
1113 stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream');
1115 $scheme = 'mock://';
1116 $dirname = $scheme.$this->workspace;
1118 $filename = $this->filesystem->tempnam($dirname, 'foo');
1120 $this->assertStringStartsWith($scheme, $filename);
1121 $this->assertFileExists($filename);
1125 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1127 public function testTempnamWithZlibSchemeFails()
1129 $scheme = 'compress.zlib://';
1130 $dirname = $scheme.$this->workspace;
1132 // The compress.zlib:// stream does not support mode x: creates the file, errors "failed to open stream: operation failed" and returns false
1133 $this->filesystem->tempnam($dirname, 'bar');
1136 public function testTempnamWithPHPTempSchemeFails()
1138 $scheme = 'php://temp';
1141 $filename = $this->filesystem->tempnam($dirname, 'bar');
1143 $this->assertStringStartsWith($scheme, $filename);
1145 // The php://temp stream deletes the file after close
1146 $this->assertFileNotExists($filename);
1150 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1152 public function testTempnamWithPharSchemeFails()
1154 // Skip test if Phar disabled phar.readonly must be 0 in php.ini
1155 if (!\Phar::canWrite()) {
1156 $this->markTestSkipped('This test cannot run when phar.readonly is 1.');
1159 $scheme = 'phar://';
1160 $dirname = $scheme.$this->workspace;
1161 $pharname = 'foo.phar';
1163 new \Phar($this->workspace.'/'.$pharname, 0, $pharname);
1164 // The phar:// stream does not support mode x: fails to create file, errors "failed to open stream: phar error: "$filename" is not a file in phar "$pharname"" and returns false
1165 $this->filesystem->tempnam($dirname, $pharname.'/bar');
1169 * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1171 public function testTempnamWithHTTPSchemeFails()
1173 $scheme = 'http://';
1174 $dirname = $scheme.$this->workspace;
1176 // The http:// scheme is read-only
1177 $this->filesystem->tempnam($dirname, 'bar');
1180 public function testTempnamOnUnwritableFallsBackToSysTmp()
1182 $scheme = 'file://';
1183 $dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist';
1185 $filename = $this->filesystem->tempnam($dirname, 'bar');
1186 $realTempDir = realpath(sys_get_temp_dir());
1187 $this->assertStringStartsWith(rtrim($scheme.$realTempDir, DIRECTORY_SEPARATOR), $filename);
1188 $this->assertFileExists($filename);
1194 public function testDumpFile()
1196 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1198 $this->filesystem->dumpFile($filename, 'bar');
1200 $this->assertFileExists($filename);
1201 $this->assertStringEqualsFile($filename, 'bar');
1207 public function testDumpFileAndSetPermissions()
1209 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1211 $this->filesystem->dumpFile($filename, 'bar', 0753);
1213 $this->assertFileExists($filename);
1214 $this->assertStringEqualsFile($filename, 'bar');
1216 // skip mode check on Windows
1217 if ('\\' !== DIRECTORY_SEPARATOR) {
1218 $this->assertFilePermissions(753, $filename);
1222 public function testDumpFileWithNullMode()
1224 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1226 $this->filesystem->dumpFile($filename, 'bar', null);
1228 $this->assertFileExists($filename);
1229 $this->assertStringEqualsFile($filename, 'bar');
1231 // skip mode check on Windows
1232 if ('\\' !== DIRECTORY_SEPARATOR) {
1233 $this->assertFilePermissions(600, $filename);
1237 public function testDumpFileOverwritesAnExistingFile()
1239 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
1240 file_put_contents($filename, 'FOO BAR');
1242 $this->filesystem->dumpFile($filename, 'bar');
1244 $this->assertFileExists($filename);
1245 $this->assertStringEqualsFile($filename, 'bar');
1248 public function testDumpFileWithFileScheme()
1250 if (defined('HHVM_VERSION')) {
1251 $this->markTestSkipped('HHVM does not handle the file:// scheme correctly');
1254 $scheme = 'file://';
1255 $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1257 $this->filesystem->dumpFile($filename, 'bar', null);
1259 $this->assertFileExists($filename);
1260 $this->assertSame('bar', file_get_contents($filename));
1263 public function testDumpFileWithZlibScheme()
1265 $scheme = 'compress.zlib://';
1266 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1268 $this->filesystem->dumpFile($filename, 'bar', null);
1270 // Zlib stat uses file:// wrapper so remove scheme
1271 $this->assertFileExists(str_replace($scheme, '', $filename));
1272 $this->assertSame('bar', file_get_contents($filename));
1275 public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile()
1277 $this->markAsSkippedIfChmodIsMissing();
1279 $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
1280 file_put_contents($filename, 'FOO BAR');
1281 chmod($filename, 0745);
1283 $this->filesystem->dumpFile($filename, 'bar', null);
1285 $this->assertFilePermissions(745, $filename);
1288 public function testCopyShouldKeepExecutionPermission()
1290 $this->markAsSkippedIfChmodIsMissing();
1292 $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
1293 $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
1295 file_put_contents($sourceFilePath, 'SOURCE FILE');
1296 chmod($sourceFilePath, 0745);
1298 $this->filesystem->copy($sourceFilePath, $targetFilePath);
1300 $this->assertFilePermissions(767, $targetFilePath);