Skip to content

Commit 452388d

Browse files
committed
Fix Up FileManager Tests on Windows
- Windows directory enumerator now doesn't look in a directory it returns until nextObject is called again - Fixed some path handling platform issues - Properly calculate level
1 parent 7e67c02 commit 452388d

File tree

4 files changed

+74
-23
lines changed

4 files changed

+74
-23
lines changed

Foundation/FileManager+Win32.swift

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -652,14 +652,15 @@ extension FileManager {
652652
var _options : FileManager.DirectoryEnumerationOptions
653653
var _errorHandler : ((URL, Error) -> Bool)?
654654
var _stack: [URL]
655-
var _current: URL?
655+
var _current: URL
656656
var _rootDepth : Int
657657

658658
init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) {
659659
_options = options
660660
_errorHandler = errorHandler
661-
_stack = [url]
661+
_stack = []
662662
_rootDepth = url.pathComponents.count
663+
_current = url
663664
}
664665

665666
override func nextObject() -> Any? {
@@ -689,27 +690,52 @@ extension FileManager {
689690
} while FindNextFileW(h, &ffd)
690691
return files
691692
}
692-
while let url = _stack.popLast() {
693-
if url.hasDirectoryPath && !_options.contains(.skipsSubdirectoryDescendants) {
694-
guard let dirContents = contentsOfDir(directory: url)?.reversed() else {
695-
if let handler = _errorHandler {
696-
let dirFSR = url.withUnsafeFileSystemRepresentation { $0.flatMap { fsr in String(utf8String: fsr) } }
697-
let keepGoing = handler(URL(fileURLWithPath: dirFSR ?? ""),
698-
_NSErrorWithWindowsError(GetLastError(), reading: true))
699-
if !keepGoing { return nil }
700-
}
701-
continue
702-
}
703-
_stack.append(contentsOf: dirContents)
693+
694+
func findFirstValidFile() -> URL? {
695+
while let url = _stack.popLast() {
696+
let dirFSR = url.withUnsafeFileSystemRepresentation { $0.flatMap { fsr in String(utf8String: fsr) } } ?? ""
697+
if !FileManager.default.fileExists(atPath: dirFSR, isDirectory: nil) {
698+
if let handler = _errorHandler {
699+
let keepGoing = handler(URL(fileURLWithPath: dirFSR),
700+
_NSErrorWithWindowsError(GetLastError(), reading: true))
701+
if !keepGoing { return nil }
702+
}
703+
continue
704704
}
705+
705706
_current = url
706-
return url
707+
return _current
708+
}
709+
return nil
707710
}
708-
return nil
711+
712+
var isDir: ObjCBool = false
713+
let dirFSR = _current.withUnsafeFileSystemRepresentation { $0.flatMap { fsr in String(utf8String: fsr) } } ?? ""
714+
guard FileManager.default.fileExists(atPath: dirFSR, isDirectory: &isDir) else {
715+
if let handler = _errorHandler {
716+
let keepGoing = handler(URL(fileURLWithPath: dirFSR),
717+
_NSErrorWithWindowsError(GetLastError(), reading: true))
718+
return keepGoing ? findFirstValidFile() : nil
719+
}
720+
return nil
721+
}
722+
723+
if isDir.boolValue && !_options.contains(.skipsSubdirectoryDescendants) {
724+
if let dirContents = contentsOfDir(directory: _current)?.reversed() {
725+
_stack.append(contentsOf: dirContents)
726+
} else {
727+
if let handler = _errorHandler {
728+
let keepGoing = handler(URL(fileURLWithPath: dirFSR),
729+
_NSErrorWithWindowsError(GetLastError(), reading: true))
730+
if !keepGoing { return nil }
731+
}
732+
}
733+
}
734+
return findFirstValidFile()
709735
}
710736

711737
override var level: Int {
712-
return _rootDepth - (_current?.pathComponents.count ?? _rootDepth)
738+
return _current.pathComponents.count - _rootDepth
713739
}
714740

715741
override func skipDescendants() {
@@ -733,8 +759,7 @@ extension FileManager.NSPathDirectoryEnumerator {
733759
return nil
734760
}
735761

736-
var relativePath = UnsafeMutableBufferPointer<WCHAR>.allocate(capacity: Int(MAX_PATH))
737-
defer { relativePath.deallocate() }
762+
var relativePath: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH))
738763
func withURLCString<Result>(url: URL, _ f: (UnsafePointer<WCHAR>) -> Result?) -> Result? {
739764
return url.withUnsafeFileSystemRepresentation { fsr in
740765
(fsr.flatMap { String(utf8String: $0) })?.withCString(encodedAs: UTF16.self) { f($0) }
@@ -747,12 +772,14 @@ extension FileManager.NSPathDirectoryEnumerator {
747772
guard fromAttrs != INVALID_FILE_ATTRIBUTES, toAttrs != INVALID_FILE_ATTRIBUTES else {
748773
return false
749774
}
750-
return PathRelativePathToW(relativePath.baseAddress, pszFrom, fromAttrs, pszTo, toAttrs)
775+
return PathRelativePathToW(&relativePath, pszFrom, fromAttrs, pszTo, toAttrs)
751776
}
752-
}) == true, let (path, _) = String.decodeCString(relativePath.baseAddress, as: UTF16.self) else {
777+
}) == true, let (path, _) = String.decodeCString(&relativePath, as: UTF16.self) else {
753778
return nil
754779
}
755-
_currentItemPath = path
780+
// PathRelativePathToW always prepends a ".\" to the path. Drop it to
781+
// match the POSIX implementation
782+
_currentItemPath = path.dropFirst(2)
756783
return _currentItemPath
757784
}
758785

Foundation/NSPathUtilities.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ extension String {
118118
}
119119

120120
internal var absolutePath: Bool {
121+
#if os(Windows)
122+
return !withCString(encodedAs: UTF16.self, PathIsRelativeW)
123+
#else
121124
return hasPrefix("~") || hasPrefix("/")
125+
#endif
122126
}
123127

124128
internal func _stringByAppendingPathComponent(_ str: String, doneAppending : Bool = true) -> String {
@@ -345,7 +349,11 @@ extension NSString {
345349

346350
let automount = "/var/automount"
347351
resolved = resolved._tryToRemovePathPrefix(automount) ?? resolved
352+
#if os(Windows)
353+
return String(resolved.map { $0 == "\\" ? "/" : $0 })
354+
#else
348355
return resolved
356+
#endif
349357
}
350358

351359
public var resolvingSymlinksInPath: String {

Foundation/NSURL.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ private func _standardizedPath(_ path: String) -> String {
3232
if !path.absolutePath {
3333
return path._nsObject.standardizingPath
3434
}
35+
#if os(Windows)
36+
return String(path.map({$0 == "/" ? "\\" : $0}))
37+
#else
3538
return path
39+
#endif
3640
}
3741

3842
internal func _pathComponents(_ path: String?) -> [String]? {
@@ -986,7 +990,7 @@ extension NSURL {
986990
}
987991

988992
let absolutePath: String
989-
if selfPath.hasPrefix("/") {
993+
if selfPath.absolutePath {
990994
absolutePath = selfPath
991995
} else {
992996
let workingDir = FileManager.default.currentDirectoryPath

TestFoundation/TestFileManager.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,13 +447,25 @@ class TestFileManager : XCTestCase {
447447
var foundItems = Set<String>()
448448
while let item = e.nextObject() as? String {
449449
foundItems.insert(item)
450+
#if os(Windows)
451+
if item == "item" {
452+
item1FileAttributes = e.fileAttributes
453+
} else if item == "path2\\item" {
454+
item2FileAttributes = e.fileAttributes
455+
}
456+
#else
450457
if item == "item" {
451458
item1FileAttributes = e.fileAttributes
452459
} else if item == "path2/item" {
453460
item2FileAttributes = e.fileAttributes
454461
}
462+
#endif
455463
}
464+
#if os(Windows)
465+
XCTAssertEqual(foundItems, Set(["item", "path2", "path2\\item"]))
466+
#else
456467
XCTAssertEqual(foundItems, Set(["item", "path2", "path2/item"]))
468+
#endif
457469
} else {
458470
XCTFail()
459471
}

0 commit comments

Comments
 (0)