mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
getErrorDomainStringForObjC() now includes the parent types in the error domain string. Its implementation does not support generic types or private discriminators, but those types can’t be PrintAsObjC’d anyway, so we should never see them.
771 lines
23 KiB
Swift
771 lines
23 KiB
Swift
// RUN: %empty-directory(%t)
|
|
// RUN: %target-build-swift -o %t/ErrorBridged -DPTR_SIZE_%target-ptrsize -module-name main %s
|
|
// RUN: %target-run %t/ErrorBridged
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: objc_interop
|
|
|
|
import StdlibUnittest
|
|
|
|
|
|
import Foundation
|
|
import CoreLocation
|
|
import Darwin
|
|
|
|
var ErrorBridgingTests = TestSuite("ErrorBridging")
|
|
|
|
var NoisyErrorLifeCount = 0
|
|
var NoisyErrorDeathCount = 0
|
|
var CanaryHandle = 0
|
|
|
|
protocol OtherProtocol {
|
|
var otherProperty: String { get }
|
|
}
|
|
|
|
protocol OtherClassProtocol : class {
|
|
var otherClassProperty: String { get }
|
|
}
|
|
|
|
class NoisyError : Error, OtherProtocol, OtherClassProtocol {
|
|
init() { NoisyErrorLifeCount += 1 }
|
|
deinit { NoisyErrorDeathCount += 1 }
|
|
|
|
let _domain = "NoisyError"
|
|
let _code = 123
|
|
|
|
let otherProperty = "otherProperty"
|
|
let otherClassProperty = "otherClassProperty"
|
|
}
|
|
|
|
@objc enum EnumError : Int, Error {
|
|
case BadError = 9000
|
|
case ReallyBadError = 9001
|
|
}
|
|
|
|
ErrorBridgingTests.test("NSError") {
|
|
NoisyErrorLifeCount = 0
|
|
NoisyErrorDeathCount = 0
|
|
autoreleasepool {
|
|
let ns = NSError(domain: "SomeDomain", code: 321, userInfo: nil)
|
|
|
|
objc_setAssociatedObject(ns, &CanaryHandle, NoisyError(),
|
|
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
|
|
let e: Error = ns
|
|
expectEqual(e._domain, "SomeDomain")
|
|
expectEqual(e._code, 321)
|
|
|
|
let ns2 = e as NSError
|
|
expectTrue(ns === ns2)
|
|
expectEqual(ns2._domain, "SomeDomain")
|
|
expectEqual(ns2._code, 321)
|
|
}
|
|
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
|
|
}
|
|
|
|
ErrorBridgingTests.test("NSCopying") {
|
|
autoreleasepool {
|
|
let orig = EnumError.ReallyBadError as NSError
|
|
let copy = orig.copy() as! NSError
|
|
expectEqual(orig, copy)
|
|
}
|
|
}
|
|
|
|
// Gated on the availability of NSKeyedArchiver.archivedData(withRootObject:).
|
|
@available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)
|
|
func archiveAndUnarchiveObject<T: NSCoding>(
|
|
_ object: T
|
|
) -> T?
|
|
where T: NSObject {
|
|
let unarchiver = NSKeyedUnarchiver(forReadingWith:
|
|
NSKeyedArchiver.archivedData(withRootObject: object)
|
|
)
|
|
unarchiver.requiresSecureCoding = true
|
|
return unarchiver.decodeObject(of: T.self, forKey: "root")
|
|
}
|
|
ErrorBridgingTests.test("NSCoding") {
|
|
if #available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
autoreleasepool {
|
|
let orig = EnumError.ReallyBadError as NSError
|
|
let unarchived = archiveAndUnarchiveObject(orig)!
|
|
expectEqual(orig, unarchived)
|
|
expectTrue(type(of: unarchived) == NSError.self)
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorBridgingTests.test("NSError-to-enum bridging") {
|
|
NoisyErrorLifeCount = 0
|
|
NoisyErrorDeathCount = 0
|
|
let testURL = URL(string: "https://swift.org")!
|
|
|
|
autoreleasepool {
|
|
let underlyingError = CocoaError(.fileLocking)
|
|
as Error as NSError
|
|
let ns = NSError(domain: NSCocoaErrorDomain,
|
|
code: NSFileNoSuchFileError,
|
|
userInfo: [
|
|
NSFilePathErrorKey: "/dev/null",
|
|
NSStringEncodingErrorKey: /*ASCII=*/1,
|
|
NSUnderlyingErrorKey: underlyingError,
|
|
NSURLErrorKey: testURL
|
|
])
|
|
|
|
objc_setAssociatedObject(ns, &CanaryHandle, NoisyError(),
|
|
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
|
|
let e: Error = ns
|
|
|
|
let cocoaCode: Int?
|
|
switch e {
|
|
case let x as CocoaError:
|
|
cocoaCode = x._code
|
|
expectTrue(x.isFileError)
|
|
expectEqual(x.code, .fileNoSuchFile)
|
|
default:
|
|
cocoaCode = nil
|
|
}
|
|
|
|
expectEqual(NSFileNoSuchFileError, cocoaCode)
|
|
|
|
let cocoaCode2: Int? = (ns as? CocoaError)?._code
|
|
expectEqual(NSFileNoSuchFileError, cocoaCode2)
|
|
|
|
let isNoSuchFileError: Bool
|
|
switch e {
|
|
case CocoaError.fileNoSuchFile:
|
|
isNoSuchFileError = true
|
|
default:
|
|
isNoSuchFileError = false
|
|
}
|
|
|
|
expectTrue(isNoSuchFileError)
|
|
|
|
// Check the contents of the error.
|
|
let cocoaError = e as! CocoaError
|
|
expectEqual("/dev/null", cocoaError.filePath)
|
|
expectEqual(String.Encoding.ascii, cocoaError.stringEncoding)
|
|
expectEqual(underlyingError, cocoaError.underlying.map { $0 as NSError })
|
|
expectEqual(testURL, cocoaError.url)
|
|
|
|
// URLError domain
|
|
let nsURL = NSError(domain: NSURLErrorDomain,
|
|
code: NSURLErrorBadURL,
|
|
userInfo: [NSURLErrorFailingURLErrorKey: testURL])
|
|
let eURL: Error = nsURL
|
|
let isBadURLError: Bool
|
|
switch eURL {
|
|
case URLError.badURL:
|
|
isBadURLError = true
|
|
default:
|
|
isBadURLError = false
|
|
}
|
|
|
|
expectTrue(isBadURLError)
|
|
|
|
let urlError = eURL as! URLError
|
|
expectEqual(testURL, urlError.failingURL)
|
|
expectNil(urlError.failureURLPeerTrust)
|
|
|
|
// CoreLocation error domain
|
|
let nsCL = NSError(domain: kCLErrorDomain,
|
|
code: CLError.headingFailure.rawValue,
|
|
userInfo: [NSURLErrorKey: testURL])
|
|
let eCL: Error = nsCL
|
|
let isHeadingFailure: Bool
|
|
switch eCL {
|
|
case CLError.headingFailure:
|
|
isHeadingFailure = true
|
|
default:
|
|
isHeadingFailure = false
|
|
}
|
|
|
|
let isCLError: Bool
|
|
switch eCL {
|
|
case let error as CLError:
|
|
isCLError = true
|
|
expectEqual(testURL, (error as NSError).userInfo[NSURLErrorKey] as? URL)
|
|
expectEqual(testURL, error.userInfo[NSURLErrorKey] as? URL)
|
|
default:
|
|
isCLError = false
|
|
}
|
|
|
|
expectTrue(isCLError)
|
|
|
|
// NSPOSIXError domain
|
|
let nsPOSIX = NSError(domain: NSPOSIXErrorDomain,
|
|
code: Int(EDEADLK),
|
|
userInfo: [:])
|
|
let ePOSIX: Error = nsPOSIX
|
|
let isDeadlock: Bool
|
|
switch ePOSIX {
|
|
case POSIXError.EDEADLK:
|
|
isDeadlock = true
|
|
default:
|
|
isDeadlock = false
|
|
}
|
|
|
|
expectTrue(isDeadlock)
|
|
|
|
// NSMachError domain
|
|
let nsMach = NSError(domain: NSMachErrorDomain,
|
|
code: Int(KERN_MEMORY_FAILURE),
|
|
userInfo: [:])
|
|
let eMach: Error = nsMach
|
|
let isMemoryFailure: Bool
|
|
switch eMach {
|
|
case MachError.memoryFailure:
|
|
isMemoryFailure = true
|
|
default:
|
|
isMemoryFailure = false
|
|
}
|
|
|
|
expectTrue(isMemoryFailure)
|
|
}
|
|
|
|
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
|
|
}
|
|
|
|
func opaqueUpcastToAny<T>(_ x: T) -> Any {
|
|
return x
|
|
}
|
|
|
|
struct StructError: Error {
|
|
var _domain: String { return "StructError" }
|
|
var _code: Int { return 4812 }
|
|
}
|
|
|
|
ErrorBridgingTests.test("Error-to-NSError bridging") {
|
|
NoisyErrorLifeCount = 0
|
|
NoisyErrorDeathCount = 0
|
|
autoreleasepool {
|
|
let e: Error = NoisyError()
|
|
let ns = e as NSError
|
|
let ns2 = e as NSError
|
|
expectTrue(ns === ns2)
|
|
expectEqual(ns._domain, "NoisyError")
|
|
expectEqual(ns._code, 123)
|
|
|
|
let e3: Error = ns
|
|
expectEqual(e3._domain, "NoisyError")
|
|
expectEqual(e3._code, 123)
|
|
let ns3 = e3 as NSError
|
|
expectTrue(ns === ns3)
|
|
|
|
let nativeNS = NSError(domain: NSCocoaErrorDomain,
|
|
code: NSFileNoSuchFileError,
|
|
userInfo: nil)
|
|
|
|
objc_setAssociatedObject(ns, &CanaryHandle, NoisyError(),
|
|
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
|
|
let nativeE: Error = nativeNS
|
|
let nativeNS2 = nativeE as NSError
|
|
expectTrue(nativeNS === nativeNS2)
|
|
expectEqual(nativeNS2._domain, NSCocoaErrorDomain)
|
|
expectEqual(nativeNS2._code, NSFileNoSuchFileError)
|
|
|
|
let any: Any = NoisyError()
|
|
let ns4 = any as! NSError
|
|
expectEqual(ns4._domain, "NoisyError")
|
|
expectEqual(ns4._code, 123)
|
|
|
|
let ao: AnyObject = NoisyError()
|
|
let ns5 = ao as! NSError
|
|
expectEqual(ns5._domain, "NoisyError")
|
|
expectEqual(ns5._code, 123)
|
|
|
|
let any2: Any = StructError()
|
|
let ns6 = any2 as! NSError
|
|
expectEqual(ns6._domain, "StructError")
|
|
expectEqual(ns6._code, 4812)
|
|
}
|
|
expectEqual(NoisyErrorDeathCount, NoisyErrorLifeCount)
|
|
}
|
|
|
|
ErrorBridgingTests.test("NSError-to-error bridging in bridged container") {
|
|
autoreleasepool {
|
|
let error = NSError(domain: "domain", code: 42, userInfo: nil)
|
|
let nsdictionary = ["error": error] as NSDictionary
|
|
let dictionary = nsdictionary as? Dictionary<String, Error>
|
|
expectNotNil(dictionary)
|
|
expectEqual(error, dictionary?["error"] as NSError?)
|
|
}
|
|
}
|
|
|
|
ErrorBridgingTests.test("enum-to-NSError round trip") {
|
|
autoreleasepool {
|
|
// Emulate throwing an error from Objective-C.
|
|
func throwNSError(_ error: EnumError) throws {
|
|
throw NSError(domain: "main.EnumError", code: error.rawValue,
|
|
userInfo: [:])
|
|
}
|
|
|
|
var caughtError: Bool
|
|
|
|
caughtError = false
|
|
do {
|
|
try throwNSError(.BadError)
|
|
expectUnreachable()
|
|
} catch let error as EnumError {
|
|
expectEqual(.BadError, error)
|
|
caughtError = true
|
|
} catch _ {
|
|
expectUnreachable()
|
|
}
|
|
expectTrue(caughtError)
|
|
|
|
caughtError = false
|
|
do {
|
|
try throwNSError(.ReallyBadError)
|
|
expectUnreachable()
|
|
} catch EnumError.BadError {
|
|
expectUnreachable()
|
|
} catch EnumError.ReallyBadError {
|
|
caughtError = true
|
|
} catch _ {
|
|
expectUnreachable()
|
|
}
|
|
expectTrue(caughtError)
|
|
}
|
|
}
|
|
|
|
class SomeNSErrorSubclass: NSError {}
|
|
|
|
|
|
ErrorBridgingTests.test("Thrown NSError identity is preserved") {
|
|
do {
|
|
let e = NSError(domain: "ClericalError", code: 219,
|
|
userInfo: ["yeah": "yeah"])
|
|
do {
|
|
throw e
|
|
} catch let e2 as NSError {
|
|
expectTrue(e === e2)
|
|
expectTrue(e2.userInfo["yeah"] as? String == "yeah")
|
|
} catch {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
|
|
do {
|
|
let f = SomeNSErrorSubclass(domain: "ClericalError", code: 219,
|
|
userInfo: ["yeah": "yeah"])
|
|
do {
|
|
throw f
|
|
} catch let f2 as NSError {
|
|
expectTrue(f === f2)
|
|
expectTrue(f2.userInfo["yeah"] as? String == "yeah")
|
|
} catch {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check errors customized via protocol.
|
|
struct MyCustomizedError : Error {
|
|
let code: Int
|
|
}
|
|
|
|
extension MyCustomizedError : LocalizedError {
|
|
var errorDescription: String? {
|
|
return NSLocalizedString("something went horribly wrong", comment: "")
|
|
}
|
|
|
|
var failureReason: String? {
|
|
return NSLocalizedString("because someone wrote 'throw'", comment: "")
|
|
}
|
|
|
|
var recoverySuggestion: String? {
|
|
return NSLocalizedString("delete the 'throw'", comment: "")
|
|
}
|
|
|
|
var helpAnchor: String? {
|
|
return NSLocalizedString("there is no help when writing tests", comment: "")
|
|
}
|
|
}
|
|
|
|
extension MyCustomizedError : CustomNSError {
|
|
static var errorDomain: String {
|
|
return "custom"
|
|
}
|
|
|
|
/// The error code within the given domain.
|
|
var errorCode: Int {
|
|
return code
|
|
}
|
|
|
|
/// The user-info dictionary.
|
|
var errorUserInfo: [String : Any] {
|
|
return [NSURLErrorKey : URL(string: "https://swift.org")!]
|
|
}
|
|
}
|
|
|
|
extension MyCustomizedError : RecoverableError {
|
|
var recoveryOptions: [String] {
|
|
return ["Delete 'throw'", "Disable the test" ]
|
|
}
|
|
|
|
func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool {
|
|
return recoveryOptionIndex == 0
|
|
}
|
|
}
|
|
|
|
/// An error type that provides localization and recovery, but doesn't
|
|
/// customize NSError directly.
|
|
enum MySwiftCustomizedError : Error {
|
|
case failed
|
|
static var errorDescriptionCount = 0
|
|
}
|
|
|
|
extension MySwiftCustomizedError : LocalizedError {
|
|
var errorDescription: String? {
|
|
MySwiftCustomizedError.errorDescriptionCount =
|
|
MySwiftCustomizedError.errorDescriptionCount + 1
|
|
return NSLocalizedString("something went horribly wrong", comment: "")
|
|
}
|
|
|
|
var failureReason: String? {
|
|
return NSLocalizedString("because someone wrote 'throw'", comment: "")
|
|
}
|
|
|
|
var recoverySuggestion: String? {
|
|
return NSLocalizedString("delete the 'throw'", comment: "")
|
|
}
|
|
|
|
var helpAnchor: String? {
|
|
return NSLocalizedString("there is no help when writing tests", comment: "")
|
|
}
|
|
}
|
|
|
|
extension MySwiftCustomizedError : RecoverableError {
|
|
var recoveryOptions: [String] {
|
|
return ["Delete 'throw'", "Disable the test" ]
|
|
}
|
|
|
|
func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool {
|
|
return recoveryOptionIndex == 0
|
|
}
|
|
}
|
|
|
|
/// Fake definition of the informal protocol
|
|
/// "NSErrorRecoveryAttempting" that we use to poke at the object
|
|
/// produced for a RecoverableError.
|
|
@objc protocol FakeNSErrorRecoveryAttempting {
|
|
@objc(attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo:)
|
|
func attemptRecovery(fromError nsError: Error,
|
|
optionIndex recoveryOptionIndex: Int,
|
|
delegate: AnyObject?,
|
|
didRecoverSelector: Selector,
|
|
contextInfo: UnsafeMutableRawPointer?)
|
|
|
|
@objc(attemptRecoveryFromError:optionIndex:)
|
|
func attemptRecovery(fromError nsError: Error,
|
|
optionIndex recoveryOptionIndex: Int) -> Bool
|
|
}
|
|
|
|
class RecoveryDelegate {
|
|
let expectedSuccess: Bool
|
|
let expectedContextInfo: UnsafeMutableRawPointer?
|
|
var called = false
|
|
|
|
init(expectedSuccess: Bool,
|
|
expectedContextInfo: UnsafeMutableRawPointer?) {
|
|
self.expectedSuccess = expectedSuccess
|
|
self.expectedContextInfo = expectedContextInfo
|
|
}
|
|
|
|
@objc func recover(success: Bool, contextInfo: UnsafeMutableRawPointer?) {
|
|
expectEqual(expectedSuccess, success)
|
|
expectEqual(expectedContextInfo, contextInfo)
|
|
called = true
|
|
}
|
|
}
|
|
|
|
/// Helper for testing a customized error.
|
|
func testCustomizedError(error: Error, nsError: NSError) {
|
|
// LocalizedError
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectNil(nsError.userInfo[NSLocalizedDescriptionKey])
|
|
expectNil(nsError.userInfo[NSLocalizedFailureReasonErrorKey])
|
|
expectNil(nsError.userInfo[NSLocalizedRecoverySuggestionErrorKey])
|
|
expectNil(nsError.userInfo[NSHelpAnchorErrorKey])
|
|
} else {
|
|
expectEqual("something went horribly wrong",
|
|
nsError.userInfo[NSLocalizedDescriptionKey] as? String)
|
|
expectEqual("because someone wrote 'throw'",
|
|
nsError.userInfo[NSLocalizedFailureReasonErrorKey] as? String)
|
|
expectEqual("delete the 'throw'",
|
|
nsError.userInfo[NSLocalizedRecoverySuggestionErrorKey] as? String)
|
|
expectEqual("there is no help when writing tests",
|
|
nsError.userInfo[NSHelpAnchorErrorKey] as? String)
|
|
}
|
|
expectEqual("something went horribly wrong", error.localizedDescription)
|
|
expectEqual("something went horribly wrong", nsError.localizedDescription)
|
|
expectEqual("because someone wrote 'throw'", nsError.localizedFailureReason)
|
|
expectEqual("delete the 'throw'", nsError.localizedRecoverySuggestion)
|
|
expectEqual("there is no help when writing tests", nsError.helpAnchor)
|
|
|
|
// RecoverableError
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectNil(nsError.userInfo[NSLocalizedRecoveryOptionsErrorKey])
|
|
} else {
|
|
expectEqual(["Delete 'throw'", "Disable the test" ],
|
|
nsError.userInfo[NSLocalizedRecoveryOptionsErrorKey] as? [String])
|
|
}
|
|
expectEqual(["Delete 'throw'", "Disable the test" ],
|
|
nsError.localizedRecoveryOptions)
|
|
|
|
// Directly recover.
|
|
let ctx = UnsafeMutableRawPointer(bitPattern:0x1234567)
|
|
let attempter: AnyObject
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectNil(nsError.userInfo[NSRecoveryAttempterErrorKey])
|
|
attempter = nsError.recoveryAttempter! as AnyObject
|
|
} else {
|
|
attempter = nsError.userInfo[NSRecoveryAttempterErrorKey]! as AnyObject
|
|
}
|
|
expectEqual(attempter.attemptRecovery(fromError: nsError, optionIndex: 0), true)
|
|
expectEqual(attempter.attemptRecovery(fromError: nsError, optionIndex: 1), false)
|
|
|
|
// Recover through delegate.
|
|
let rd1 = RecoveryDelegate(expectedSuccess: true, expectedContextInfo: ctx)
|
|
expectEqual(false, rd1.called)
|
|
attempter.attemptRecovery(
|
|
fromError: nsError,
|
|
optionIndex: 0,
|
|
delegate: rd1,
|
|
didRecoverSelector: #selector(RecoveryDelegate.recover(success:contextInfo:)),
|
|
contextInfo: ctx)
|
|
expectEqual(true, rd1.called)
|
|
|
|
let rd2 = RecoveryDelegate(expectedSuccess: false, expectedContextInfo: nil)
|
|
expectEqual(false, rd2.called)
|
|
attempter.attemptRecovery(
|
|
fromError: nsError,
|
|
optionIndex: 1,
|
|
delegate: rd2,
|
|
didRecoverSelector: #selector(RecoveryDelegate.recover(success:contextInfo:)),
|
|
contextInfo: nil)
|
|
expectEqual(true, rd2.called)
|
|
}
|
|
|
|
ErrorBridgingTests.test("Customizing NSError via protocols") {
|
|
let error = MyCustomizedError(code: 12345)
|
|
let nsError = error as NSError
|
|
|
|
// CustomNSError
|
|
expectEqual("custom", nsError.domain)
|
|
expectEqual(12345, nsError.code)
|
|
expectEqual(URL(string: "https://swift.org"),
|
|
nsError.userInfo[NSURLErrorKey] as? URL)
|
|
|
|
testCustomizedError(error: error, nsError: nsError)
|
|
}
|
|
|
|
ErrorBridgingTests.test("Customizing localization/recovery via protocols") {
|
|
let error = MySwiftCustomizedError.failed
|
|
let nsError = error as NSError
|
|
testCustomizedError(error: error, nsError: nsError)
|
|
}
|
|
|
|
ErrorBridgingTests.test("Customizing localization/recovery laziness") {
|
|
let countBefore = MySwiftCustomizedError.errorDescriptionCount
|
|
let error = MySwiftCustomizedError.failed
|
|
let nsError = error as NSError
|
|
|
|
// RecoverableError
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectNil(nsError.userInfo[NSLocalizedRecoveryOptionsErrorKey])
|
|
} else {
|
|
expectEqual(["Delete 'throw'", "Disable the test" ],
|
|
nsError.userInfo[NSLocalizedRecoveryOptionsErrorKey] as? [String])
|
|
}
|
|
expectEqual(["Delete 'throw'", "Disable the test" ], nsError.localizedRecoveryOptions)
|
|
|
|
// None of the operations above should affect the count
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectEqual(countBefore, MySwiftCustomizedError.errorDescriptionCount)
|
|
}
|
|
|
|
// This one does affect the count.
|
|
expectEqual("something went horribly wrong", error.localizedDescription)
|
|
|
|
// Check that we did get a call to errorDescription.
|
|
if #available(OSX 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *) {
|
|
expectEqual(countBefore+1, MySwiftCustomizedError.errorDescriptionCount)
|
|
}
|
|
}
|
|
|
|
enum DefaultCustomizedError1 : CustomNSError {
|
|
case bad
|
|
case worse
|
|
}
|
|
|
|
enum DefaultCustomizedError2 : Int, CustomNSError {
|
|
case bad = 7
|
|
case worse = 13
|
|
}
|
|
|
|
enum DefaultCustomizedError3 : UInt, CustomNSError {
|
|
case bad = 9
|
|
case worse = 115
|
|
|
|
#if PTR_SIZE_64
|
|
case dreadful = 0xFFFFFFFFFFFFFFFF
|
|
#elseif PTR_SIZE_32
|
|
case dreadful = 0xFFFFFFFF
|
|
#else
|
|
#error ("Unknown pointer size")
|
|
#endif
|
|
|
|
static var errorDomain: String {
|
|
return "customized3"
|
|
}
|
|
}
|
|
|
|
enum DefaultCustomizedParent {
|
|
enum ChildError: CustomNSError {
|
|
case foo
|
|
}
|
|
}
|
|
|
|
ErrorBridgingTests.test("Default-customized via CustomNSError") {
|
|
expectEqual(1, (DefaultCustomizedError1.worse as NSError).code)
|
|
expectEqual(13, (DefaultCustomizedError2.worse as NSError).code)
|
|
expectEqual(115, (DefaultCustomizedError3.worse as NSError).code)
|
|
expectEqual(-1, (DefaultCustomizedError3.dreadful as NSError).code)
|
|
expectEqual("main.DefaultCustomizedError1", (DefaultCustomizedError1.worse as NSError).domain)
|
|
expectEqual("customized3", (DefaultCustomizedError3.worse as NSError).domain)
|
|
expectEqual("main.DefaultCustomizedParent.ChildError", (DefaultCustomizedParent.ChildError.foo as NSError).domain)
|
|
}
|
|
|
|
class MyNSError : NSError { }
|
|
|
|
ErrorBridgingTests.test("NSError subclass identity") {
|
|
let myNSError: Error = MyNSError(domain: "MyNSError", code: 0, userInfo: [:])
|
|
let nsError = myNSError as NSError
|
|
expectTrue(type(of: nsError) == MyNSError.self)
|
|
}
|
|
|
|
ErrorBridgingTests.test("Wrapped NSError identity") {
|
|
let nsError = NSError(domain: NSCocoaErrorDomain,
|
|
code: NSFileNoSuchFileError,
|
|
userInfo: [
|
|
NSFilePathErrorKey : "/dev/null",
|
|
NSStringEncodingErrorKey : /*ASCII=*/1,
|
|
])
|
|
|
|
let error: Error = nsError
|
|
let nsError2: NSError = error as NSError
|
|
expectTrue(nsError === nsError2)
|
|
|
|
// Extracting the NSError via the runtime.
|
|
let cocoaErrorAny: Any = error as! CocoaError
|
|
let nsError3: NSError = cocoaErrorAny as! NSError
|
|
expectTrue(nsError === nsError3)
|
|
|
|
if let cocoaErrorAny2: Any = error as? CocoaError {
|
|
let nsError4: NSError = cocoaErrorAny2 as! NSError
|
|
expectTrue(nsError === nsError4)
|
|
} else {
|
|
expectUnreachable()
|
|
}
|
|
|
|
// Extracting the NSError via direct call.
|
|
let cocoaError = error as! CocoaError
|
|
let nsError5: NSError = cocoaError as NSError
|
|
expectTrue(nsError === nsError5)
|
|
|
|
if error is CocoaError {
|
|
let nsError6: NSError = cocoaError as NSError
|
|
expectTrue(nsError === nsError6)
|
|
} else {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
|
|
extension Error {
|
|
func asNSError() -> NSError {
|
|
return self as NSError
|
|
}
|
|
}
|
|
|
|
func unconditionalCast<T>(_ x: Any, to: T.Type) -> T {
|
|
return x as! T
|
|
}
|
|
|
|
func conditionalCast<T>(_ x: Any, to: T.Type) -> T? {
|
|
return x as? T
|
|
}
|
|
|
|
// SR-1562
|
|
ErrorBridgingTests.test("Error archetype identity") {
|
|
let myError = NSError(domain: "myErrorDomain", code: 0,
|
|
userInfo: [ "one" : 1 ])
|
|
expectTrue(myError === myError.asNSError())
|
|
|
|
expectTrue(unconditionalCast(myError, to: Error.self) as NSError
|
|
=== myError)
|
|
expectTrue(conditionalCast(myError, to: Error.self)! as NSError
|
|
=== myError)
|
|
|
|
let nsError = NSError(domain: NSCocoaErrorDomain,
|
|
code: NSFileNoSuchFileError,
|
|
userInfo: [
|
|
NSFilePathErrorKey : "/dev/null",
|
|
NSStringEncodingErrorKey : /*ASCII=*/1,
|
|
])
|
|
let cocoaError = nsError as Error as! CocoaError
|
|
expectTrue(cocoaError.asNSError() === nsError)
|
|
expectTrue(unconditionalCast(cocoaError, to: Error.self) as NSError
|
|
=== nsError)
|
|
expectTrue(conditionalCast(cocoaError, to: Error.self)! as NSError
|
|
=== nsError)
|
|
}
|
|
|
|
// SR-9389
|
|
class ParentA: NSObject {
|
|
@objc(ParentAError) enum Error: Int, Swift.Error {
|
|
case failed
|
|
}
|
|
}
|
|
class ParentB: NSObject {
|
|
@objc(ParentBError) enum Error: Int, Swift.Error {
|
|
case failed
|
|
}
|
|
}
|
|
private class NonPrintAsObjCClass: NSObject {
|
|
@objc enum Error: Int, Swift.Error {
|
|
case foo
|
|
}
|
|
}
|
|
@objc private enum NonPrintAsObjCError: Int, Error {
|
|
case bar
|
|
}
|
|
|
|
ErrorBridgingTests.test("@objc error domains for nested types") {
|
|
// Domain strings should correspond to Swift types, including parent types.
|
|
expectEqual(ParentA.Error.failed._domain, "main.ParentA.Error")
|
|
expectEqual(ParentB.Error.failed._domain, "main.ParentB.Error")
|
|
|
|
func makeNSError(like error: Error) -> NSError {
|
|
return NSError(domain: error._domain, code: error._code)
|
|
}
|
|
|
|
// NSErrors corresponding to Error types with the same name but nested in
|
|
// different enclosing types should not be castable to the wrong error type.
|
|
expectTrue(makeNSError(like: ParentA.Error.failed) is ParentA.Error)
|
|
expectFalse(makeNSError(like: ParentA.Error.failed) is ParentB.Error)
|
|
expectFalse(makeNSError(like: ParentB.Error.failed) is ParentA.Error)
|
|
expectTrue(makeNSError(like: ParentB.Error.failed) is ParentB.Error)
|
|
|
|
// If an @objc enum error is not eligible for PrintAsObjC, we should treat it
|
|
// as though it inherited the default implementation, which calls
|
|
// String(reflecting:).
|
|
expectEqual(NonPrintAsObjCClass.Error.foo._domain,
|
|
String(reflecting: NonPrintAsObjCClass.Error.self))
|
|
expectEqual(NonPrintAsObjCError.bar._domain,
|
|
String(reflecting: NonPrintAsObjCError.self))
|
|
}
|
|
|
|
runAllTests()
|