Skip to content

Commit

Permalink
Ensure directory URL enumerator error handler block is invoked with n…
Browse files Browse the repository at this point in the history
…on-nil URL
  • Loading branch information
kperryua committed Oct 26, 2016
1 parent 87aaec4 commit 6f83a82
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 0 deletions.
3 changes: 3 additions & 0 deletions apinotes/Foundation.apinotes
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,9 @@ Classes:
- Selector: 'URLForPublishingUbiquitousItemAtURL:expirationDate:error:'
SwiftName: url(forPublishingUbiquitousItemAt:expiration:)
MethodKind: Instance
- Selector: 'enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:'
MethodKind: Instance
Availability: nonswift
- Name: NSFileVersion
Methods:
- Selector: 'versionOfItemAtURL:forPersistentIdentifier:'
Expand Down
19 changes: 19 additions & 0 deletions stdlib/public/SDK/Foundation/FileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ internal func NS_Swift_NSFileManager_replaceItemAtURL_withItemAtURL_backupItemNa
_ options: FileManager.ItemReplacementOptions,
_ error: NSErrorPointer) -> NSURL?

@_silgen_name("NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler")
internal func NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(
_ self_ : AnyObject,
_ url: AnyObject,
_ keys: NSArray?,
_ options: FileManager.DirectoryEnumerationOptions,
_ errorHandler: @escaping @convention(block) (NSURL, NSError) -> Bool) -> FileManager.DirectoryEnumerator?

extension FileManager {
/*
Expand All @@ -45,4 +52,16 @@ extension FileManager {
}
throw error!
}

@available(OSX 10.6, iOS 4.0, *)
public func enumerator(at url: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions = [], errorHandler handler: ((URL, Error) -> Bool)? = nil) -> FileManager.DirectoryEnumerator? {
return NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(self, url as NSURL, keys as NSArray?, mask, { (url, error) in
var errorResult = true;
if let h = handler {
errorResult = h(url as URL, error)
}
return errorResult
})
}

}
28 changes: 28 additions & 0 deletions stdlib/public/SDK/Foundation/FileManagerThunks.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,31 @@
[backupItemName release];
return success ? [result retain] : nil;
}

extern /*"C"*/ NS_RETURNS_RETAINED id
NS_Swift_NSFileManager_enumeratorAt_includingPropertiesForKeys_options_errorHandler(
NSFileManager *NS_RELEASES_ARGUMENT _Nonnull self_,
NSURL *NS_RELEASES_ARGUMENT _Nonnull url,
NSArray *NS_RELEASES_ARGUMENT _Nullable keys,
NSUInteger options,
BOOL (^_Nonnull errorHandler)(NSURL * _Nonnull url, NSError * _Nonnull error) ) {

NSDirectoryEnumerator *result = [self_ enumeratorAtURL:url
includingPropertiesForKeys:keys
options:(NSDirectoryEnumerationOptions)options
errorHandler:^(NSURL * url, NSError *error) {

NSURL *realURL = url ?: error.userInfo[NSURLErrorKey];
if (!realURL) {
NSString *path = error.userInfo[NSFilePathErrorKey];
realURL = [NSURL fileURLWithPath:path];
}
return errorHandler(realURL, error);

}];
[self_ release];
[url release];
[keys release];
[errorHandler release];
return [result retain];
}
56 changes: 56 additions & 0 deletions test/stdlib/TestFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,68 @@ class TestFileManager : TestFileManagerSuper {
expectTrue(threw, "Should have thrown")

}

func testDirectoryEnumerator_error() {
let fm = FileManager.default
let nonexistantURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistant")

var invoked = false
let e = fm.enumerator(at: nonexistantURL, includingPropertiesForKeys: []) { (url, err) in
invoked = true
expectEqual(nonexistantURL, url)
expectEqual((err as NSError).code, NSFileReadNoSuchFileError)
return true
}

let url = e?.nextObject()
expectTrue(invoked)
expectTrue(url == nil)

}

func testDirectoryEnumerator_error_noHandler() {
let fm = FileManager.default
let nonexistantURL = URL(fileURLWithPath: "\(NSTemporaryDirectory())/nonexistant")

let e = fm.enumerator(at: nonexistantURL, includingPropertiesForKeys: [])
let url = e?.nextObject()
expectTrue(url == nil)

}

func testDirectoryEnumerator_simple() {
let fm = FileManager.default
let dirPath = (NSTemporaryDirectory() as NSString).appendingPathComponent(NSUUID().uuidString)
try! fm.createDirectory(atPath: dirPath, withIntermediateDirectories: true, attributes: nil)
defer { try! FileManager.default.removeItem(atPath: dirPath) }

let item1 = URL(fileURLWithPath: "\(dirPath)/1", isDirectory: false)
let item2 = URL(fileURLWithPath: "\(dirPath)/2", isDirectory: false)

try! Data().write(to: item1)
try! Data().write(to: item2)

let e = fm.enumerator(at: URL(fileURLWithPath: dirPath, isDirectory: true), includingPropertiesForKeys: [])
let result1 = e?.nextObject()
let result2 = e?.nextObject()
let result3 = e?.nextObject()

// Avoid potential symlink discrepancy between the result and the original URL
expectEqual((result1! as! URL).lastPathComponent, item1.lastPathComponent)
expectEqual((result2! as! URL).lastPathComponent, item2.lastPathComponent)
expectTrue(result3 == nil)

}

}

#if !FOUNDATION_XCTEST
var FMTests = TestSuite("TestFileManager")
FMTests.test("testReplaceItem") { TestFileManager().testReplaceItem() }
FMTests.test("testReplaceItem_error") { TestFileManager().testReplaceItem_error() }
FMTests.test("testDirectoryEnumerator_error") { TestFileManager().testDirectoryEnumerator_error() }
FMTests.test("testDirectoryEnumerator_error_noHandler") { TestFileManager().testDirectoryEnumerator_error_noHandler() }
FMTests.test("testDirectoryEnumerator_simple") { TestFileManager().testDirectoryEnumerator_simple() }

runAllTests()
#endif
Expand Down

0 comments on commit 6f83a82

Please sign in to comment.