Security update to Drupal 8.4.6
[yaffs-website] / vendor / symfony / filesystem / Tests / FilesystemTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Filesystem\Tests;
13
14 /**
15  * Test class for Filesystem.
16  */
17 class FilesystemTest extends FilesystemTestCase
18 {
19     public function testCopyCreatesNewFile()
20     {
21         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
22         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
23
24         file_put_contents($sourceFilePath, 'SOURCE FILE');
25
26         $this->filesystem->copy($sourceFilePath, $targetFilePath);
27
28         $this->assertFileExists($targetFilePath);
29         $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
30     }
31
32     /**
33      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
34      */
35     public function testCopyFails()
36     {
37         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
38         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
39
40         $this->filesystem->copy($sourceFilePath, $targetFilePath);
41     }
42
43     /**
44      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
45      */
46     public function testCopyUnreadableFileFails()
47     {
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.');
51         }
52
53         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
54         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
55
56         file_put_contents($sourceFilePath, 'SOURCE FILE');
57
58         // make sure target cannot be read
59         $this->filesystem->chmod($sourceFilePath, 0222);
60
61         $this->filesystem->copy($sourceFilePath, $targetFilePath);
62     }
63
64     public function testCopyOverridesExistingFileIfModified()
65     {
66         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
67         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
68
69         file_put_contents($sourceFilePath, 'SOURCE FILE');
70         file_put_contents($targetFilePath, 'TARGET FILE');
71         touch($targetFilePath, time() - 1000);
72
73         $this->filesystem->copy($sourceFilePath, $targetFilePath);
74
75         $this->assertFileExists($targetFilePath);
76         $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
77     }
78
79     public function testCopyDoesNotOverrideExistingFileByDefault()
80     {
81         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
82         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
83
84         file_put_contents($sourceFilePath, 'SOURCE FILE');
85         file_put_contents($targetFilePath, 'TARGET FILE');
86
87         // make sure both files have the same modification time
88         $modificationTime = time() - 1000;
89         touch($sourceFilePath, $modificationTime);
90         touch($targetFilePath, $modificationTime);
91
92         $this->filesystem->copy($sourceFilePath, $targetFilePath);
93
94         $this->assertFileExists($targetFilePath);
95         $this->assertStringEqualsFile($targetFilePath, 'TARGET FILE');
96     }
97
98     public function testCopyOverridesExistingFileIfForced()
99     {
100         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
101         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
102
103         file_put_contents($sourceFilePath, 'SOURCE FILE');
104         file_put_contents($targetFilePath, 'TARGET FILE');
105
106         // make sure both files have the same modification time
107         $modificationTime = time() - 1000;
108         touch($sourceFilePath, $modificationTime);
109         touch($targetFilePath, $modificationTime);
110
111         $this->filesystem->copy($sourceFilePath, $targetFilePath, true);
112
113         $this->assertFileExists($targetFilePath);
114         $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
115     }
116
117     /**
118      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
119      */
120     public function testCopyWithOverrideWithReadOnlyTargetFails()
121     {
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.');
125         }
126
127         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
128         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
129
130         file_put_contents($sourceFilePath, 'SOURCE FILE');
131         file_put_contents($targetFilePath, 'TARGET FILE');
132
133         // make sure both files have the same modification time
134         $modificationTime = time() - 1000;
135         touch($sourceFilePath, $modificationTime);
136         touch($targetFilePath, $modificationTime);
137
138         // make sure target is read-only
139         $this->filesystem->chmod($targetFilePath, 0444);
140
141         $this->filesystem->copy($sourceFilePath, $targetFilePath, true);
142     }
143
144     public function testCopyCreatesTargetDirectoryIfItDoesNotExist()
145     {
146         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
147         $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
148         $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file';
149
150         file_put_contents($sourceFilePath, 'SOURCE FILE');
151
152         $this->filesystem->copy($sourceFilePath, $targetFilePath);
153
154         $this->assertTrue(is_dir($targetFileDirectory));
155         $this->assertFileExists($targetFilePath);
156         $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE');
157     }
158
159     /**
160      * @group network
161      */
162     public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy()
163     {
164         $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png';
165         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
166
167         file_put_contents($targetFilePath, 'TARGET FILE');
168
169         $this->filesystem->copy($sourceFilePath, $targetFilePath, false);
170
171         $this->assertFileExists($targetFilePath);
172         $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath));
173     }
174
175     public function testMkdirCreatesDirectoriesRecursively()
176     {
177         $directory = $this->workspace
178             .DIRECTORY_SEPARATOR.'directory'
179             .DIRECTORY_SEPARATOR.'sub_directory';
180
181         $this->filesystem->mkdir($directory);
182
183         $this->assertTrue(is_dir($directory));
184     }
185
186     public function testMkdirCreatesDirectoriesFromArray()
187     {
188         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
189         $directories = array(
190             $basePath.'1', $basePath.'2', $basePath.'3',
191         );
192
193         $this->filesystem->mkdir($directories);
194
195         $this->assertTrue(is_dir($basePath.'1'));
196         $this->assertTrue(is_dir($basePath.'2'));
197         $this->assertTrue(is_dir($basePath.'3'));
198     }
199
200     public function testMkdirCreatesDirectoriesFromTraversableObject()
201     {
202         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
203         $directories = new \ArrayObject(array(
204             $basePath.'1', $basePath.'2', $basePath.'3',
205         ));
206
207         $this->filesystem->mkdir($directories);
208
209         $this->assertTrue(is_dir($basePath.'1'));
210         $this->assertTrue(is_dir($basePath.'2'));
211         $this->assertTrue(is_dir($basePath.'3'));
212     }
213
214     /**
215      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
216      */
217     public function testMkdirCreatesDirectoriesFails()
218     {
219         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
220         $dir = $basePath.'2';
221
222         file_put_contents($dir, '');
223
224         $this->filesystem->mkdir($dir);
225     }
226
227     public function testTouchCreatesEmptyFile()
228     {
229         $file = $this->workspace.DIRECTORY_SEPARATOR.'1';
230
231         $this->filesystem->touch($file);
232
233         $this->assertFileExists($file);
234     }
235
236     /**
237      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
238      */
239     public function testTouchFails()
240     {
241         $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2';
242
243         $this->filesystem->touch($file);
244     }
245
246     public function testTouchCreatesEmptyFilesFromArray()
247     {
248         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
249         $files = array(
250             $basePath.'1', $basePath.'2', $basePath.'3',
251         );
252
253         $this->filesystem->touch($files);
254
255         $this->assertFileExists($basePath.'1');
256         $this->assertFileExists($basePath.'2');
257         $this->assertFileExists($basePath.'3');
258     }
259
260     public function testTouchCreatesEmptyFilesFromTraversableObject()
261     {
262         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
263         $files = new \ArrayObject(array(
264             $basePath.'1', $basePath.'2', $basePath.'3',
265         ));
266
267         $this->filesystem->touch($files);
268
269         $this->assertFileExists($basePath.'1');
270         $this->assertFileExists($basePath.'2');
271         $this->assertFileExists($basePath.'3');
272     }
273
274     public function testRemoveCleansFilesAndDirectoriesIteratively()
275     {
276         $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
277
278         mkdir($basePath);
279         mkdir($basePath.'dir');
280         touch($basePath.'file');
281
282         $this->filesystem->remove($basePath);
283
284         $this->assertFileNotExists($basePath);
285     }
286
287     public function testRemoveCleansArrayOfFilesAndDirectories()
288     {
289         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
290
291         mkdir($basePath.'dir');
292         touch($basePath.'file');
293
294         $files = array(
295             $basePath.'dir', $basePath.'file',
296         );
297
298         $this->filesystem->remove($files);
299
300         $this->assertFileNotExists($basePath.'dir');
301         $this->assertFileNotExists($basePath.'file');
302     }
303
304     public function testRemoveCleansTraversableObjectOfFilesAndDirectories()
305     {
306         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
307
308         mkdir($basePath.'dir');
309         touch($basePath.'file');
310
311         $files = new \ArrayObject(array(
312             $basePath.'dir', $basePath.'file',
313         ));
314
315         $this->filesystem->remove($files);
316
317         $this->assertFileNotExists($basePath.'dir');
318         $this->assertFileNotExists($basePath.'file');
319     }
320
321     public function testRemoveIgnoresNonExistingFiles()
322     {
323         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
324
325         mkdir($basePath.'dir');
326
327         $files = array(
328             $basePath.'dir', $basePath.'file',
329         );
330
331         $this->filesystem->remove($files);
332
333         $this->assertFileNotExists($basePath.'dir');
334     }
335
336     public function testRemoveCleansInvalidLinks()
337     {
338         $this->markAsSkippedIfSymlinkIsMissing();
339
340         $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
341
342         mkdir($basePath);
343         mkdir($basePath.'dir');
344         // create symlink to nonexistent file
345         @symlink($basePath.'file', $basePath.'file-link');
346
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'));
350
351         // create symlink to nonexistent dir
352         rmdir($basePath.'dir');
353         $this->assertFalse('\\' === DIRECTORY_SEPARATOR ? @readlink($basePath.'dir-link') : is_dir($basePath.'dir-link'));
354
355         $this->filesystem->remove($basePath);
356
357         $this->assertFileNotExists($basePath);
358     }
359
360     public function testFilesExists()
361     {
362         $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
363
364         mkdir($basePath);
365         touch($basePath.'file1');
366         mkdir($basePath.'folder');
367
368         $this->assertTrue($this->filesystem->exists($basePath.'file1'));
369         $this->assertTrue($this->filesystem->exists($basePath.'folder'));
370     }
371
372     /**
373      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
374      */
375     public function testFilesExistsFails()
376     {
377         if ('\\' !== DIRECTORY_SEPARATOR) {
378             $this->markTestSkipped('Long file names are an issue on Windows');
379         }
380         $basePath = $this->workspace.'\\directory\\';
381         $maxPathLength = PHP_MAXPATHLEN - 2;
382
383         $oldPath = getcwd();
384         mkdir($basePath);
385         chdir($basePath);
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
390         chdir($oldPath);
391         $this->filesystem->exists($path);
392     }
393
394     public function testFilesExistsTraversableObjectOfFilesAndDirectories()
395     {
396         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
397
398         mkdir($basePath.'dir');
399         touch($basePath.'file');
400
401         $files = new \ArrayObject(array(
402             $basePath.'dir', $basePath.'file',
403         ));
404
405         $this->assertTrue($this->filesystem->exists($files));
406     }
407
408     public function testFilesNotExistsTraversableObjectOfFilesAndDirectories()
409     {
410         $basePath = $this->workspace.DIRECTORY_SEPARATOR;
411
412         mkdir($basePath.'dir');
413         touch($basePath.'file');
414         touch($basePath.'file2');
415
416         $files = new \ArrayObject(array(
417             $basePath.'dir', $basePath.'file', $basePath.'file2',
418         ));
419
420         unlink($basePath.'file');
421
422         $this->assertFalse($this->filesystem->exists($files));
423     }
424
425     public function testInvalidFileNotExists()
426     {
427         $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR;
428
429         $this->assertFalse($this->filesystem->exists($basePath.time()));
430     }
431
432     public function testChmodChangesFileMode()
433     {
434         $this->markAsSkippedIfChmodIsMissing();
435
436         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
437         mkdir($dir);
438         $file = $dir.DIRECTORY_SEPARATOR.'file';
439         touch($file);
440
441         $this->filesystem->chmod($file, 0400);
442         $this->filesystem->chmod($dir, 0753);
443
444         $this->assertFilePermissions(753, $dir);
445         $this->assertFilePermissions(400, $file);
446     }
447
448     public function testChmodWithWrongModLeavesPreviousPermissionsUntouched()
449     {
450         $this->markAsSkippedIfChmodIsMissing();
451
452         if (defined('HHVM_VERSION')) {
453             $this->markTestSkipped('chmod() changes permissions even when passing invalid modes on HHVM');
454         }
455
456         $dir = $this->workspace.DIRECTORY_SEPARATOR.'file';
457         touch($dir);
458
459         $permissions = fileperms($dir);
460
461         $this->filesystem->chmod($dir, 'Wrongmode');
462
463         $this->assertSame($permissions, fileperms($dir));
464     }
465
466     public function testChmodRecursive()
467     {
468         $this->markAsSkippedIfChmodIsMissing();
469
470         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
471         mkdir($dir);
472         $file = $dir.DIRECTORY_SEPARATOR.'file';
473         touch($file);
474
475         $this->filesystem->chmod($file, 0400, 0000, true);
476         $this->filesystem->chmod($dir, 0753, 0000, true);
477
478         $this->assertFilePermissions(753, $dir);
479         $this->assertFilePermissions(753, $file);
480     }
481
482     public function testChmodAppliesUmask()
483     {
484         $this->markAsSkippedIfChmodIsMissing();
485
486         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
487         touch($file);
488
489         $this->filesystem->chmod($file, 0770, 0022);
490         $this->assertFilePermissions(750, $file);
491     }
492
493     public function testChmodChangesModeOfArrayOfFiles()
494     {
495         $this->markAsSkippedIfChmodIsMissing();
496
497         $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
498         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
499         $files = array($directory, $file);
500
501         mkdir($directory);
502         touch($file);
503
504         $this->filesystem->chmod($files, 0753);
505
506         $this->assertFilePermissions(753, $file);
507         $this->assertFilePermissions(753, $directory);
508     }
509
510     public function testChmodChangesModeOfTraversableFileObject()
511     {
512         $this->markAsSkippedIfChmodIsMissing();
513
514         $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
515         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
516         $files = new \ArrayObject(array($directory, $file));
517
518         mkdir($directory);
519         touch($file);
520
521         $this->filesystem->chmod($files, 0753);
522
523         $this->assertFilePermissions(753, $file);
524         $this->assertFilePermissions(753, $directory);
525     }
526
527     public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive()
528     {
529         $this->markAsSkippedIfChmodIsMissing();
530
531         $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory';
532         $subdirectory = $directory.DIRECTORY_SEPARATOR.'subdirectory';
533
534         mkdir($directory);
535         mkdir($subdirectory);
536         chmod($subdirectory, 0000);
537
538         $this->filesystem->chmod($directory, 0753, 0000, true);
539
540         $this->assertFilePermissions(753, $subdirectory);
541     }
542
543     public function testChown()
544     {
545         $this->markAsSkippedIfPosixIsMissing();
546
547         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
548         mkdir($dir);
549
550         $owner = $this->getFileOwner($dir);
551         $this->filesystem->chown($dir, $owner);
552
553         $this->assertSame($owner, $this->getFileOwner($dir));
554     }
555
556     public function testChownRecursive()
557     {
558         $this->markAsSkippedIfPosixIsMissing();
559
560         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
561         mkdir($dir);
562         $file = $dir.DIRECTORY_SEPARATOR.'file';
563         touch($file);
564
565         $owner = $this->getFileOwner($dir);
566         $this->filesystem->chown($dir, $owner, true);
567
568         $this->assertSame($owner, $this->getFileOwner($file));
569     }
570
571     public function testChownSymlink()
572     {
573         $this->markAsSkippedIfSymlinkIsMissing();
574
575         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
576         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
577
578         touch($file);
579
580         $this->filesystem->symlink($file, $link);
581
582         $owner = $this->getFileOwner($link);
583         $this->filesystem->chown($link, $owner);
584
585         $this->assertSame($owner, $this->getFileOwner($link));
586     }
587
588     /**
589      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
590      */
591     public function testChownSymlinkFails()
592     {
593         $this->markAsSkippedIfSymlinkIsMissing();
594
595         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
596         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
597
598         touch($file);
599
600         $this->filesystem->symlink($file, $link);
601
602         $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999));
603     }
604
605     /**
606      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
607      */
608     public function testChownFail()
609     {
610         $this->markAsSkippedIfPosixIsMissing();
611
612         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
613         mkdir($dir);
614
615         $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999));
616     }
617
618     public function testChgrp()
619     {
620         $this->markAsSkippedIfPosixIsMissing();
621
622         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
623         mkdir($dir);
624
625         $group = $this->getFileGroup($dir);
626         $this->filesystem->chgrp($dir, $group);
627
628         $this->assertSame($group, $this->getFileGroup($dir));
629     }
630
631     public function testChgrpRecursive()
632     {
633         $this->markAsSkippedIfPosixIsMissing();
634
635         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
636         mkdir($dir);
637         $file = $dir.DIRECTORY_SEPARATOR.'file';
638         touch($file);
639
640         $group = $this->getFileGroup($dir);
641         $this->filesystem->chgrp($dir, $group, true);
642
643         $this->assertSame($group, $this->getFileGroup($file));
644     }
645
646     public function testChgrpSymlink()
647     {
648         $this->markAsSkippedIfSymlinkIsMissing();
649
650         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
651         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
652
653         touch($file);
654
655         $this->filesystem->symlink($file, $link);
656
657         $group = $this->getFileGroup($link);
658         $this->filesystem->chgrp($link, $group);
659
660         $this->assertSame($group, $this->getFileGroup($link));
661     }
662
663     /**
664      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
665      */
666     public function testChgrpSymlinkFails()
667     {
668         $this->markAsSkippedIfSymlinkIsMissing();
669
670         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
671         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
672
673         touch($file);
674
675         $this->filesystem->symlink($file, $link);
676
677         $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999));
678     }
679
680     /**
681      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
682      */
683     public function testChgrpFail()
684     {
685         $this->markAsSkippedIfPosixIsMissing();
686
687         $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir';
688         mkdir($dir);
689
690         $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999));
691     }
692
693     public function testRename()
694     {
695         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
696         $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
697         touch($file);
698
699         $this->filesystem->rename($file, $newPath);
700
701         $this->assertFileNotExists($file);
702         $this->assertFileExists($newPath);
703     }
704
705     /**
706      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
707      */
708     public function testRenameThrowsExceptionIfTargetAlreadyExists()
709     {
710         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
711         $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
712
713         touch($file);
714         touch($newPath);
715
716         $this->filesystem->rename($file, $newPath);
717     }
718
719     public function testRenameOverwritesTheTargetIfItAlreadyExists()
720     {
721         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
722         $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
723
724         touch($file);
725         touch($newPath);
726
727         $this->filesystem->rename($file, $newPath, true);
728
729         $this->assertFileNotExists($file);
730         $this->assertFileExists($newPath);
731     }
732
733     /**
734      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
735      */
736     public function testRenameThrowsExceptionOnError()
737     {
738         $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid('fs_test_', true);
739         $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file';
740
741         $this->filesystem->rename($file, $newPath);
742     }
743
744     public function testSymlink()
745     {
746         if ('\\' === DIRECTORY_SEPARATOR) {
747             $this->markTestSkipped('Windows does not support creating "broken" symlinks');
748         }
749
750         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
751         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
752
753         // $file does not exists right now: creating "broken" links is a wanted feature
754         $this->filesystem->symlink($file, $link);
755
756         $this->assertTrue(is_link($link));
757
758         // Create the linked file AFTER creating the link
759         touch($file);
760
761         $this->assertEquals($file, readlink($link));
762     }
763
764     /**
765      * @depends testSymlink
766      */
767     public function testRemoveSymlink()
768     {
769         $this->markAsSkippedIfSymlinkIsMissing();
770
771         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
772
773         $this->filesystem->remove($link);
774
775         $this->assertFalse(is_link($link));
776         $this->assertFalse(is_file($link));
777         $this->assertFalse(is_dir($link));
778     }
779
780     public function testSymlinkIsOverwrittenIfPointsToDifferentTarget()
781     {
782         $this->markAsSkippedIfSymlinkIsMissing();
783
784         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
785         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
786
787         touch($file);
788         symlink($this->workspace, $link);
789
790         $this->filesystem->symlink($file, $link);
791
792         $this->assertTrue(is_link($link));
793         $this->assertEquals($file, readlink($link));
794     }
795
796     public function testSymlinkIsNotOverwrittenIfAlreadyCreated()
797     {
798         $this->markAsSkippedIfSymlinkIsMissing();
799
800         $file = $this->workspace.DIRECTORY_SEPARATOR.'file';
801         $link = $this->workspace.DIRECTORY_SEPARATOR.'link';
802
803         touch($file);
804         symlink($file, $link);
805
806         $this->filesystem->symlink($file, $link);
807
808         $this->assertTrue(is_link($link));
809         $this->assertEquals($file, readlink($link));
810     }
811
812     public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist()
813     {
814         $this->markAsSkippedIfSymlinkIsMissing();
815
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';
819
820         touch($file);
821
822         $this->filesystem->symlink($file, $link1);
823         $this->filesystem->symlink($file, $link2);
824
825         $this->assertTrue(is_link($link1));
826         $this->assertEquals($file, readlink($link1));
827         $this->assertTrue(is_link($link2));
828         $this->assertEquals($file, readlink($link2));
829     }
830
831     /**
832      * @dataProvider providePathsForMakePathRelative
833      */
834     public function testMakePathRelative($endPath, $startPath, $expectedPath)
835     {
836         $path = $this->filesystem->makePathRelative($endPath, $startPath);
837
838         $this->assertEquals($expectedPath, $path);
839     }
840
841     public function providePathsForMakePathRelative()
842     {
843         $paths = array(
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('../../../', '../../', '../'),
890             array('', '', './'),
891             array('', 'aa/', '../'),
892             array('aa/', '', 'aa/'),
893         );
894
895         if ('\\' === DIRECTORY_SEPARATOR) {
896             $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/');
897         }
898
899         return $paths;
900     }
901
902     public function testMirrorCopiesFilesAndDirectoriesRecursively()
903     {
904         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
905         $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR;
906         $file1 = $directory.'file1';
907         $file2 = $sourcePath.'file2';
908
909         mkdir($sourcePath);
910         mkdir($directory);
911         file_put_contents($file1, 'FILE1');
912         file_put_contents($file2, 'FILE2');
913
914         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
915
916         $this->filesystem->mirror($sourcePath, $targetPath);
917
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');
922
923         $this->filesystem->remove($file1);
924
925         $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false));
926         $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
927
928         $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
929         $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
930
931         file_put_contents($file1, 'FILE1');
932
933         $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true));
934         $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'));
935
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'));
940     }
941
942     public function testMirrorCreatesEmptyDirectory()
943     {
944         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
945
946         mkdir($sourcePath);
947
948         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
949
950         $this->filesystem->mirror($sourcePath, $targetPath);
951
952         $this->assertTrue(is_dir($targetPath));
953
954         $this->filesystem->remove($sourcePath);
955     }
956
957     public function testMirrorCopiesLinks()
958     {
959         $this->markAsSkippedIfSymlinkIsMissing();
960
961         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
962
963         mkdir($sourcePath);
964         file_put_contents($sourcePath.'file1', 'FILE1');
965         symlink($sourcePath.'file1', $sourcePath.'link1');
966
967         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
968
969         $this->filesystem->mirror($sourcePath, $targetPath);
970
971         $this->assertTrue(is_dir($targetPath));
972         $this->assertFileEquals($sourcePath.'file1', $targetPath.'link1');
973         $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1'));
974     }
975
976     public function testMirrorCopiesLinkedDirectoryContents()
977     {
978         $this->markAsSkippedIfSymlinkIsMissing(true);
979
980         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
981
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');
986
987         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
988
989         $this->filesystem->mirror($sourcePath, $targetPath);
990
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'));
994     }
995
996     public function testMirrorCopiesRelativeLinkedContents()
997     {
998         $this->markAsSkippedIfSymlinkIsMissing(true);
999
1000         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1001         $oldPath = getcwd();
1002
1003         mkdir($sourcePath.'nested/', 0777, true);
1004         file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1');
1005         // Note: Create relative symlink
1006         chdir($sourcePath);
1007         symlink('nested', 'link1');
1008
1009         chdir($oldPath);
1010
1011         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1012
1013         $this->filesystem->mirror($sourcePath, $targetPath);
1014
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'));
1019     }
1020
1021     public function testMirrorContentsWithSameNameAsSourceOrTargetWithoutDeleteOption()
1022     {
1023         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1024
1025         mkdir($sourcePath);
1026         touch($sourcePath.'source');
1027         touch($sourcePath.'target');
1028
1029         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1030
1031         $oldPath = getcwd();
1032         chdir($this->workspace);
1033
1034         $this->filesystem->mirror('source', $targetPath);
1035
1036         chdir($oldPath);
1037
1038         $this->assertTrue(is_dir($targetPath));
1039         $this->assertFileExists($targetPath.'source');
1040         $this->assertFileExists($targetPath.'target');
1041     }
1042
1043     public function testMirrorContentsWithSameNameAsSourceOrTargetWithDeleteOption()
1044     {
1045         $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR;
1046
1047         mkdir($sourcePath);
1048         touch($sourcePath.'source');
1049
1050         $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR;
1051
1052         mkdir($targetPath);
1053         touch($targetPath.'source');
1054         touch($targetPath.'target');
1055
1056         $oldPath = getcwd();
1057         chdir($this->workspace);
1058
1059         $this->filesystem->mirror('source', 'target', null, array('delete' => true));
1060
1061         chdir($oldPath);
1062
1063         $this->assertTrue(is_dir($targetPath));
1064         $this->assertFileExists($targetPath.'source');
1065         $this->assertFileNotExists($targetPath.'target');
1066     }
1067
1068     /**
1069      * @dataProvider providePathsForIsAbsolutePath
1070      */
1071     public function testIsAbsolutePath($path, $expectedResult)
1072     {
1073         $result = $this->filesystem->isAbsolutePath($path);
1074
1075         $this->assertEquals($expectedResult, $result);
1076     }
1077
1078     public function providePathsForIsAbsolutePath()
1079     {
1080         return array(
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),
1086             array('', false),
1087             array(null, false),
1088         );
1089     }
1090
1091     public function testTempnam()
1092     {
1093         $dirname = $this->workspace;
1094
1095         $filename = $this->filesystem->tempnam($dirname, 'foo');
1096
1097         $this->assertFileExists($filename);
1098     }
1099
1100     public function testTempnamWithFileScheme()
1101     {
1102         $scheme = 'file://';
1103         $dirname = $scheme.$this->workspace;
1104
1105         $filename = $this->filesystem->tempnam($dirname, 'foo');
1106
1107         $this->assertStringStartsWith($scheme, $filename);
1108         $this->assertFileExists($filename);
1109     }
1110
1111     public function testTempnamWithMockScheme()
1112     {
1113         stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream');
1114
1115         $scheme = 'mock://';
1116         $dirname = $scheme.$this->workspace;
1117
1118         $filename = $this->filesystem->tempnam($dirname, 'foo');
1119
1120         $this->assertStringStartsWith($scheme, $filename);
1121         $this->assertFileExists($filename);
1122     }
1123
1124     /**
1125      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1126      */
1127     public function testTempnamWithZlibSchemeFails()
1128     {
1129         $scheme = 'compress.zlib://';
1130         $dirname = $scheme.$this->workspace;
1131
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');
1134     }
1135
1136     public function testTempnamWithPHPTempSchemeFails()
1137     {
1138         $scheme = 'php://temp';
1139         $dirname = $scheme;
1140
1141         $filename = $this->filesystem->tempnam($dirname, 'bar');
1142
1143         $this->assertStringStartsWith($scheme, $filename);
1144
1145         // The php://temp stream deletes the file after close
1146         $this->assertFileNotExists($filename);
1147     }
1148
1149     /**
1150      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1151      */
1152     public function testTempnamWithPharSchemeFails()
1153     {
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.');
1157         }
1158
1159         $scheme = 'phar://';
1160         $dirname = $scheme.$this->workspace;
1161         $pharname = 'foo.phar';
1162
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');
1166     }
1167
1168     /**
1169      * @expectedException \Symfony\Component\Filesystem\Exception\IOException
1170      */
1171     public function testTempnamWithHTTPSchemeFails()
1172     {
1173         $scheme = 'http://';
1174         $dirname = $scheme.$this->workspace;
1175
1176         // The http:// scheme is read-only
1177         $this->filesystem->tempnam($dirname, 'bar');
1178     }
1179
1180     public function testTempnamOnUnwritableFallsBackToSysTmp()
1181     {
1182         $scheme = 'file://';
1183         $dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist';
1184
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);
1189
1190         // Tear down
1191         @unlink($filename);
1192     }
1193
1194     public function testDumpFile()
1195     {
1196         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1197
1198         $this->filesystem->dumpFile($filename, 'bar');
1199
1200         $this->assertFileExists($filename);
1201         $this->assertStringEqualsFile($filename, 'bar');
1202     }
1203
1204     /**
1205      * @group legacy
1206      */
1207     public function testDumpFileAndSetPermissions()
1208     {
1209         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1210
1211         $this->filesystem->dumpFile($filename, 'bar', 0753);
1212
1213         $this->assertFileExists($filename);
1214         $this->assertStringEqualsFile($filename, 'bar');
1215
1216         // skip mode check on Windows
1217         if ('\\' !== DIRECTORY_SEPARATOR) {
1218             $this->assertFilePermissions(753, $filename);
1219         }
1220     }
1221
1222     public function testDumpFileWithNullMode()
1223     {
1224         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1225
1226         $this->filesystem->dumpFile($filename, 'bar', null);
1227
1228         $this->assertFileExists($filename);
1229         $this->assertStringEqualsFile($filename, 'bar');
1230
1231         // skip mode check on Windows
1232         if ('\\' !== DIRECTORY_SEPARATOR) {
1233             $this->assertFilePermissions(600, $filename);
1234         }
1235     }
1236
1237     public function testDumpFileOverwritesAnExistingFile()
1238     {
1239         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
1240         file_put_contents($filename, 'FOO BAR');
1241
1242         $this->filesystem->dumpFile($filename, 'bar');
1243
1244         $this->assertFileExists($filename);
1245         $this->assertStringEqualsFile($filename, 'bar');
1246     }
1247
1248     public function testDumpFileWithFileScheme()
1249     {
1250         if (defined('HHVM_VERSION')) {
1251             $this->markTestSkipped('HHVM does not handle the file:// scheme correctly');
1252         }
1253
1254         $scheme = 'file://';
1255         $filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1256
1257         $this->filesystem->dumpFile($filename, 'bar', null);
1258
1259         $this->assertFileExists($filename);
1260         $this->assertSame('bar', file_get_contents($filename));
1261     }
1262
1263     public function testDumpFileWithZlibScheme()
1264     {
1265         $scheme = 'compress.zlib://';
1266         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
1267
1268         $this->filesystem->dumpFile($filename, 'bar', null);
1269
1270         // Zlib stat uses file:// wrapper so remove scheme
1271         $this->assertFileExists(str_replace($scheme, '', $filename));
1272         $this->assertSame('bar', file_get_contents($filename));
1273     }
1274
1275     public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile()
1276     {
1277         $this->markAsSkippedIfChmodIsMissing();
1278
1279         $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt';
1280         file_put_contents($filename, 'FOO BAR');
1281         chmod($filename, 0745);
1282
1283         $this->filesystem->dumpFile($filename, 'bar', null);
1284
1285         $this->assertFilePermissions(745, $filename);
1286     }
1287
1288     public function testCopyShouldKeepExecutionPermission()
1289     {
1290         $this->markAsSkippedIfChmodIsMissing();
1291
1292         $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
1293         $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file';
1294
1295         file_put_contents($sourceFilePath, 'SOURCE FILE');
1296         chmod($sourceFilePath, 0745);
1297
1298         $this->filesystem->copy($sourceFilePath, $targetFilePath);
1299
1300         $this->assertFilePermissions(767, $targetFilePath);
1301     }
1302 }