@@ -35,7 +35,8 @@ class TestFileManager : XCTestCase {
35
35
( " test_homedirectoryForUser " , test_homedirectoryForUser) ,
36
36
( " test_temporaryDirectoryForUser " , test_temporaryDirectoryForUser) ,
37
37
( " test_creatingDirectoryWithShortIntermediatePath " , test_creatingDirectoryWithShortIntermediatePath) ,
38
- ( " test_mountedVolumeURLs " , test_mountedVolumeURLs)
38
+ ( " test_mountedVolumeURLs " , test_mountedVolumeURLs) ,
39
+ ( " test_contentsEqual " , test_contentsEqual)
39
40
]
40
41
}
41
42
@@ -690,4 +691,136 @@ class TestFileManager : XCTestCase {
690
691
XCTAssertTrue ( visibleVolumes. count < volumes. count)
691
692
#endif
692
693
}
694
+
695
+ func test_contentsEqual( ) {
696
+ let fm = FileManager . default
697
+ let tmpParentDirURL = URL ( fileURLWithPath: NSTemporaryDirectory ( ) + " test_contentsEqualdir " , isDirectory: true )
698
+ let testDir1 = tmpParentDirURL. appendingPathComponent ( " testDir1 " )
699
+ let testDir2 = tmpParentDirURL. appendingPathComponent ( " testDir2 " )
700
+ let testDir3 = testDir1. appendingPathComponent ( " subDir/anotherDir/extraDir/lastDir " )
701
+
702
+ defer { try ? fm. removeItem ( atPath: tmpParentDirURL. path) }
703
+
704
+ func testFileURL( _ name: String , _ ext: String ) -> URL ? {
705
+ guard let url = testBundle ( ) . url ( forResource: name, withExtension: ext) else {
706
+ XCTFail ( " Cant open \( name) . \( ext) " )
707
+ return nil
708
+ }
709
+ return url
710
+ }
711
+
712
+ guard let testFile1URL = testFileURL ( " NSStringTestData " , " txt " ) else { return }
713
+ guard let testFile2URL = testFileURL ( " NSURLTestData " , " plist " ) else { return }
714
+ guard let testFile3URL = testFileURL ( " NSString-UTF32-BE-data " , " txt " ) else { return }
715
+ guard let testFile4URL = testFileURL ( " NSString-UTF32-LE-data " , " txt " ) else { return }
716
+ let symlink = testDir1. appendingPathComponent ( " testlink " ) . path
717
+
718
+ // Setup test directories
719
+ do {
720
+ // Clean out and leftover test data
721
+ try ? fm. removeItem ( atPath: tmpParentDirURL. path)
722
+
723
+ // testDir1
724
+ try fm. createDirectory ( atPath: testDir1. path, withIntermediateDirectories: true )
725
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " null1 " ) . path, withDestinationPath: " /dev/null " )
726
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " zero1 " ) . path, withDestinationPath: " /dev/zero " )
727
+ try " foo " . write ( toFile: testDir1. appendingPathComponent ( " foo.txt " ) . path, atomically: false , encoding: . ascii)
728
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " foo1 " ) . path, withDestinationPath: " foo.txt " )
729
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " bar2 " ) . path, withDestinationPath: " foo1 " )
730
+ let unreadable = testDir1. appendingPathComponent ( " unreadable_file " ) . path
731
+ try " unreadable " . write ( toFile: unreadable, atomically: false , encoding: . ascii)
732
+ try fm. setAttributes ( [ . posixPermissions: NSNumber ( value: 0 ) ] , ofItemAtPath: unreadable)
733
+ try Data ( ) . write ( to: testDir1. appendingPathComponent ( " empty_file " ) )
734
+ try fm. createSymbolicLink ( atPath: symlink, withDestinationPath: testFile1URL. path)
735
+
736
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " thisDir " ) . path, withDestinationPath: " . " )
737
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " parentDir " ) . path, withDestinationPath: " .. " )
738
+ try fm. createSymbolicLink ( atPath: testDir1. appendingPathComponent ( " rootDir " ) . path, withDestinationPath: " / " )
739
+
740
+ // testDir2
741
+ try fm. createDirectory ( atPath: testDir2. path, withIntermediateDirectories: true )
742
+ try fm. createSymbolicLink ( atPath: testDir2. appendingPathComponent ( " bar2 " ) . path, withDestinationPath: " foo1 " )
743
+ try fm. createSymbolicLink ( atPath: testDir2. appendingPathComponent ( " foo2 " ) . path, withDestinationPath: " ../testDir1/foo.txt " )
744
+
745
+ // testDir3
746
+ try fm. createDirectory ( atPath: testDir3. path, withIntermediateDirectories: true )
747
+ try fm. createSymbolicLink ( atPath: testDir3. appendingPathComponent ( " bar2 " ) . path, withDestinationPath: " foo1 " )
748
+ try fm. createSymbolicLink ( atPath: testDir3. appendingPathComponent ( " foo2 " ) . path, withDestinationPath: " ../testDir1/foo.txt " )
749
+ } catch {
750
+ XCTFail ( String ( describing: error) )
751
+ }
752
+
753
+ XCTAssertTrue ( fm. contentsEqual ( atPath: " /dev/null " , andPath: " /dev/null " ) )
754
+ XCTAssertTrue ( fm. contentsEqual ( atPath: " /dev/urandom " , andPath: " /dev/urandom " ) )
755
+ XCTAssertFalse ( fm. contentsEqual ( atPath: " /dev/null " , andPath: " /dev/zero " ) )
756
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " null1 " ) . path, andPath: " /dev/null " ) )
757
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " zero " ) . path, andPath: " /dev/zero " ) )
758
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " foo.txt " ) . path, andPath: testDir1. appendingPathComponent ( " foo1 " ) . path) )
759
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " foo.txt " ) . path, andPath: testDir1. appendingPathComponent ( " foo2 " ) . path) )
760
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " bar2 " ) . path, andPath: testDir2. appendingPathComponent ( " bar2 " ) . path) )
761
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. appendingPathComponent ( " foo1 " ) . path, andPath: testDir2. appendingPathComponent ( " foo2 " ) . path) )
762
+ XCTAssertFalse ( fm. contentsEqual ( atPath: " /non_existant_file " , andPath: " /non_existant_file " ) )
763
+
764
+ let emptyFile = testDir1. appendingPathComponent ( " empty_file " )
765
+ XCTAssertFalse ( fm. contentsEqual ( atPath: emptyFile. path, andPath: " /dev/null " ) )
766
+ XCTAssertFalse ( fm. contentsEqual ( atPath: emptyFile. path, andPath: testDir1. appendingPathComponent ( " null1 " ) . path) )
767
+ XCTAssertFalse ( fm. contentsEqual ( atPath: emptyFile. path, andPath: testDir1. appendingPathComponent ( " unreadable_file " ) . path) )
768
+
769
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testFile1URL. path, andPath: testFile1URL. path) )
770
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testFile1URL. path, andPath: testFile2URL. path) )
771
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testFile3URL. path, andPath: testFile4URL. path) )
772
+ XCTAssertFalse ( fm. contentsEqual ( atPath: symlink, andPath: testFile1URL. path) )
773
+
774
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir1. path) )
775
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testDir2. path, andPath: testDir3. path) )
776
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir2. path) )
777
+
778
+ // Copy everything in testDir1 to testDir2 to make them equal
779
+ do {
780
+ for entry in try fm. subpathsOfDirectory ( atPath: testDir1. path) {
781
+ // Skip entries that already exist
782
+ if entry == " bar2 " || entry == " unreadable_file " {
783
+ continue
784
+ }
785
+ let srcPath = testDir1. appendingPathComponent ( entry) . path
786
+ let dstPath = testDir2. appendingPathComponent ( entry) . path
787
+ if let attrs = try ? fm. attributesOfItem ( atPath: srcPath) ,
788
+ let fileType = attrs [ . type] as? FileAttributeType , fileType == . typeDirectory {
789
+ try fm. createDirectory ( atPath: dstPath, withIntermediateDirectories: false , attributes: nil )
790
+ } else {
791
+ try fm. copyItem ( atPath: srcPath, toPath: dstPath)
792
+ }
793
+ }
794
+ } catch {
795
+ XCTFail ( " Failed to copy \( testDir1. path) to \( testDir2. path) , \( error) " )
796
+ return
797
+ }
798
+ // This will still fail due to unreadable files and a file in testDir2 not in testDir1
799
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir2. path) )
800
+ do {
801
+ try fm. copyItem ( atPath: testDir2. appendingPathComponent ( " foo2 " ) . path, toPath: testDir1. appendingPathComponent ( " foo2 " ) . path)
802
+ try fm. removeItem ( atPath: testDir1. appendingPathComponent ( " unreadable_file " ) . path)
803
+ } catch {
804
+ XCTFail ( String ( describing: error) )
805
+ return
806
+ }
807
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir2. path) )
808
+
809
+ let dataFile1 = testDir1. appendingPathComponent ( " dataFile " )
810
+ let dataFile2 = testDir2. appendingPathComponent ( " dataFile " )
811
+ do {
812
+ try Data ( count: 100_000 ) . write ( to: dataFile1)
813
+ try fm. copyItem ( atPath: dataFile1. path, toPath: dataFile2. path)
814
+ } catch {
815
+ XCTFail ( " Could not create test data files: \( error) " )
816
+ return
817
+ }
818
+ XCTAssertTrue ( fm. contentsEqual ( atPath: dataFile1. path, andPath: dataFile2. path) )
819
+ XCTAssertTrue ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir2. path) )
820
+ var data = Data ( count: 100_000 )
821
+ data [ 99_999 ] = 1
822
+ try ? data. write ( to: dataFile1)
823
+ XCTAssertFalse ( fm. contentsEqual ( atPath: dataFile1. path, andPath: dataFile2. path) )
824
+ XCTAssertFalse ( fm. contentsEqual ( atPath: testDir1. path, andPath: testDir2. path) )
825
+ }
693
826
}
0 commit comments