| // RUN: %empty-directory(%t) |
| // |
| // RUN: %gyb %s -o %t/BridgeIdAsAny.swift |
| // RUN: %target-build-swift -g -module-name a %t/BridgeIdAsAny.swift -o %t.out |
| // RUN: %target-run %t.out |
| // REQUIRES: executable_test |
| // |
| // REQUIRES: objc_interop |
| |
| import StdlibUnittest |
| import Foundation |
| |
| var BridgeAnything = TestSuite("BridgeAnything") |
| |
| func wantonlyWrapInAny<T>(_ x: T) -> Any { |
| return x |
| } |
| |
| // Professional runtime testers on a closed course. Do not attempt at home. |
| extension LifetimeTracked: Error {} |
| extension String: Error {} |
| |
| struct KnownUnbridged: Equatable, Hashable { |
| var x, y: LifetimeTracked |
| |
| init() { |
| x = LifetimeTracked(17) |
| y = LifetimeTracked(38) |
| } |
| |
| public static func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool { |
| return a.x === b.x && a.y === b.y |
| } |
| |
| public var hashValue: Int { |
| return x.hashValue ^ y.hashValue |
| } |
| } |
| |
| struct KnownUnbridgedWithDescription: CustomStringConvertible, |
| CustomDebugStringConvertible { |
| var x, y: LifetimeTracked |
| |
| init() { |
| x = LifetimeTracked(17) |
| y = LifetimeTracked(38) |
| } |
| |
| var description: String { |
| return "\(x)\(y), baby, hey" |
| } |
| |
| var debugDescription: String { |
| return "KnownUnbridgedWithDescription(\"\(x)\(y)\" /* baby, hey */)" |
| } |
| } |
| |
| func bridgedObjectPreservesIdentity(original: LifetimeTracked, |
| bridged: AnyObject) { |
| expectTrue(original === bridged) |
| } |
| |
| func stringBridgesToEqualNSString(original: String, |
| bridged: AnyObject) { |
| expectTrue(bridged.isEqual(to: original)) |
| } |
| |
| func boxedTypeRoundTripsThroughDynamicCasting(original: KnownUnbridged, |
| bridged: AnyObject) { |
| direct: do { |
| guard let bridgedAndCast = bridged as? KnownUnbridged else { |
| expectUnreachable() |
| break direct |
| } |
| expectEqual(original, bridgedAndCast) |
| } |
| |
| let bridgedAny: Any = bridged |
| any: do { |
| guard let bridgedAndCastAny = bridgedAny as? KnownUnbridged else { |
| expectUnreachable() |
| break any |
| } |
| expectEqual(original, bridgedAndCastAny) |
| } |
| |
| anyInAny: do { |
| let bridgedAnyInAny = wantonlyWrapInAny(bridgedAny) |
| guard let bridgedAndCastAnyInAny = bridgedAnyInAny as? KnownUnbridged else { |
| expectUnreachable() |
| break anyInAny |
| } |
| expectEqual(original, bridgedAndCastAnyInAny) |
| } |
| |
| // Failed casts should fail, and hopefully shouldn't leak or corrupt memory |
| // either. |
| expectEqual(bridged as? Int, nil) |
| expectEqual(bridged as? String, nil) |
| } |
| |
| func tupleCanBeDynamicallyCast(original: (Int, String), |
| bridged: AnyObject) { |
| expectTrue(original == (bridged as! (Int, String))) |
| } |
| func metatypeCanBeDynamicallyCast(original: Int.Type, |
| bridged: AnyObject) { |
| expectTrue(original == (bridged as! Int.Type)) |
| expectTrue(original == (bridged as! Any.Type)) |
| } |
| func metatypeCanBeDynamicallyCast(original: CFString.Type, |
| bridged: AnyObject) { |
| expectTrue(original == (bridged as! CFString.Type)) |
| expectTrue(original == (bridged as! Any.Type)) |
| } |
| func metatypeCanBeDynamicallyCastGenerically<T>(original: T.Type, |
| bridged: AnyObject) { |
| expectTrue(original == (bridged as! T.Type)) |
| expectTrue(original == (bridged as! Any.Type)) |
| } |
| |
| |
| func guineaPigFunction() -> Int { |
| return 1738 |
| } |
| |
| func functionCanBeDynamicallyCast(original: () -> Int, |
| bridged: AnyObject) { |
| expectEqual(original(), (bridged as! () -> Int)()) |
| expectEqual(original(), try! (bridged as! () throws -> Int)()) |
| } |
| |
| func classMetatypePreservesIdentity<T: AnyObject>(original: T.Type, |
| bridged: AnyObject) { |
| expectTrue(original as AnyObject === bridged) |
| expectTrue(original as AnyObject.Type as AnyObject === bridged) |
| expectTrue(original as Any.Type as AnyObject === bridged) |
| } |
| |
| func classMetatypePreservesIdentityGenerically<T>(original: T.Type, |
| bridged: AnyObject) { |
| expectTrue(original as AnyObject === bridged) |
| expectTrue(original as Any.Type as AnyObject === bridged) |
| } |
| |
| func protocolObjectPreservesIdentity(original: NSCopying.Protocol, |
| bridged: AnyObject) { |
| let proto: Protocol = original |
| expectTrue(proto === bridged) |
| } |
| |
| enum MyError: Error { |
| case a, e, i, o, u |
| } |
| |
| func errorValueTypeBridgesToNSError(original: MyError, bridged: AnyObject) { |
| let bridgedNSError = bridged as! NSError |
| expectEqual(bridgedNSError.domain, original._domain) |
| expectEqual(bridgedNSError.code, original._code) |
| |
| let unbridgedError = bridged as! MyError |
| expectEqual(original, unbridgedError) |
| |
| let unbridgedError2 = bridgedNSError as! MyError |
| expectEqual(original, unbridgedError2) |
| } |
| |
| protocol P {} |
| |
| // We want to exhaustively check all paths through the bridging and dynamic |
| // casting infrastructure, so expand out test cases that wrap the different |
| // interesting bridging cases in different kinds of existential container. |
| %{ |
| testCases = [ |
| # testName type valueExpr testFunc conformsToError conformsToHashable |
| ("classes", "LifetimeTracked", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True), |
| ("strings", "String", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True), |
| ("unbridged type", "KnownUnbridged", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", False, True), |
| ("tuple", "(Int, String)", '(1, "2")', "tupleCanBeDynamicallyCast", False, False), |
| ("metatype", "Int.Type", 'Int.self', "metatypeCanBeDynamicallyCast", False, False), |
| ("generic metatype", "Int.Type", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False, False), |
| ("CF metatype", "CFString.Type", 'CFString.self', "metatypeCanBeDynamicallyCast", False, False), |
| ("generic CF metatype", "CFString.Type", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False, False), |
| ("class metatype", "LifetimeTracked.Type", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False, False), |
| ("objc metatype", "NSObject.Type", 'NSObject.self', "classMetatypePreservesIdentity", False, False), |
| ("protocol", "P.Protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False, False), |
| ("objc protocol", "NSCopying.Protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False, False), |
| ("objc protocol composition", "(NSCopying & NSCoding).Protocol", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False, False), |
| ("mixed protocol composition", "(NSCopying & P).Protocol", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False, False), |
| ("generic class metatype", "LifetimeTracked.Type", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False, False), |
| ("generic objc metatype", "NSObject.Type", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False, False), |
| ("function", "() -> Int", 'guineaPigFunction', "functionCanBeDynamicallyCast", False, False), |
| ("error type", "MyError", 'MyError.e', "errorValueTypeBridgesToNSError", True, True), |
| ] |
| }% |
| |
| % for testName, type, valueExpr, testFunc, conformsToError, conformsToHashable in testCases: |
| BridgeAnything.test("${testName}") { |
| autoreleasepool { |
| let x: ${type} = ${valueExpr} |
| ${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(x)) |
| ${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(x)) |
| |
| // Bridge an array containing x. |
| let xInArray = [x] |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) |
| if (x as? NSObject) != nil { |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0]) |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0]) |
| } |
| |
| // Bridge a dictionary containing x as a value. |
| let xInDictValue = ["key" : x] |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!) |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!) |
| if (x as? NSObject) != nil { |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!) |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!) |
| } |
| |
| % if conformsToHashable: |
| // Bridge a dictionary containing x as a key. |
| let xInDictKey = [x : "value"] as [AnyHashable: String] |
| // FIXME: need a way to express `AnyObject & Hashable`. |
| // The NSObject version below can't test class LifetimeTracked. |
| // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!) |
| // ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!) |
| if (x as? NSObject) != nil { |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!) |
| ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!) |
| } |
| |
| % end |
| |
| let xInAny: Any = x |
| let xInAnyBridged = _bridgeAnythingToObjectiveC(xInAny) |
| let xInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInAny) |
| ${testFunc}(original: x, bridged: xInAnyBridged) |
| ${testFunc}(original: x, bridged: xInAnyBridged2) |
| |
| let xInAnyInAny = wantonlyWrapInAny(xInAny) |
| let xInAnyInAnyBridged = _bridgeAnythingToObjectiveC(xInAnyInAny) |
| let xInAnyInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInAnyInAny) |
| ${testFunc}(original: x, bridged: xInAnyInAnyBridged) |
| ${testFunc}(original: x, bridged: xInAnyInAnyBridged2) |
| |
| % if conformsToError: |
| let xInError: Error = x |
| let xInErrorBridged = _bridgeAnythingToObjectiveC(xInError) |
| let xInErrorBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInError) |
| |
| ${testFunc}(original: x, bridged: xInErrorBridged) |
| ${testFunc}(original: x, bridged: xInErrorBridged2) |
| |
| let xInErrorInAny = wantonlyWrapInAny(xInError) |
| let xInErrorInAnyBridged = _bridgeAnythingToObjectiveC(xInErrorInAny) |
| let xInErrorInAnyBridged2 = _bridgeAnythingNonVerbatimToObjectiveC(xInErrorInAny) |
| ${testFunc}(original: x, bridged: xInErrorInAnyBridged) |
| ${testFunc}(original: x, bridged: xInErrorInAnyBridged) |
| % end |
| |
| % if conformsToHashable: |
| // Check that we get an equal value if we bridge cast back. |
| let xFromAny = xInAnyBridged as! ${type} |
| expectEqual(x, xFromAny) |
| let xFromAny2 = xInAnyBridged2 as! ${type} |
| expectEqual(x, xFromAny2) |
| let xInAnyBridgedInAny = wantonlyWrapInAny(xInAnyBridged) |
| let xFromAnyBridgedInAny = xInAnyBridgedInAny as! ${type} |
| expectEqual(x, xFromAnyBridgedInAny) |
| |
| let xFromAnyInAny = xInAnyInAnyBridged as! ${type} |
| expectEqual(x, xFromAnyInAny) |
| let xFromAnyInAny2 = xInAnyInAnyBridged2 as! ${type} |
| expectEqual(x, xFromAnyInAny2) |
| let xInAnyInAnyBridgedInAny = wantonlyWrapInAny(xInAnyInAnyBridged) |
| let xFromAnyInAnyBridgedInAny = xInAnyInAnyBridgedInAny as! ${type} |
| expectEqual(x, xFromAnyInAnyBridgedInAny) |
| |
| % if conformsToError: |
| let xFromError = xInErrorBridged as! ${type} |
| expectEqual(x, xFromError) |
| let xFromError2 = xInErrorBridged2 as! ${type} |
| expectEqual(x, xFromError2) |
| let xInErrorBridgedInAny = wantonlyWrapInAny(xInErrorBridged) |
| let xFromErrorBridgedInAny = xInErrorBridgedInAny as! ${type} |
| expectEqual(x, xFromErrorBridgedInAny) |
| |
| let xFromErrorInAny = xInErrorInAnyBridged as! ${type} |
| expectEqual(x, xFromErrorInAny) |
| let xFromErrorInAny2 = xInErrorInAnyBridged2 as! ${type} |
| expectEqual(x, xFromErrorInAny2) |
| let xInErrorInAnyBridgedInAny = wantonlyWrapInAny(xInErrorInAnyBridged) |
| let xFromErrorInAnyBridgedInAny = xInErrorInAnyBridgedInAny as! ${type} |
| expectEqual(x, xFromErrorInAnyBridgedInAny) |
| % end |
| % end |
| } |
| } |
| % end |
| |
| BridgeAnything.test("description of boxed values") { |
| for x in [KnownUnbridged(), KnownUnbridgedWithDescription()] as [Any] { |
| let summary = String(reflecting: x) |
| expectEqual(summary, _bridgeAnythingToObjectiveC(x).description) |
| expectEqual(summary, _bridgeAnythingToObjectiveC(x).debugDescription) |
| } |
| } |
| |
| BridgeAnything.test("SwiftValue(mixed values)/Hashable") { |
| var boxedXs: [NSObject] = [] |
| % for wrapped in ['OpaqueValue', 'MinimalHashableValue']: |
| do { |
| let xs = (0..<5).flatMap { |
| [ ${wrapped}($0, identity: 0), |
| ${wrapped}($0, identity: 1) ] |
| } |
| boxedXs.append( |
| contentsOf: xs.map { |
| _bridgeAnythingToObjectiveC($0) as! NSObject |
| }) |
| } |
| % end |
| |
| boxedXs.append( |
| contentsOf: [0, 0, 1, 1, 2, 2, 3, 3, 4, 4].map { |
| _bridgeAnythingToObjectiveC($0) as! NSObject |
| }) |
| |
| boxedXs.append( |
| contentsOf: ["a", "a", "b", "b", "c", "c", "d", "d", "e", "e"].map { |
| _bridgeAnythingToObjectiveC($0) as! NSObject |
| }) |
| |
| func equalityOracle(_ lhs: Int, rhs: Int) -> Bool { |
| if (0..<10).contains(lhs) || (0..<10).contains(rhs) { |
| return lhs == rhs |
| } |
| return lhs / 2 == rhs / 2 |
| } |
| checkHashable(boxedXs, equalityOracle: equalityOracle) |
| } |
| |
| runAllTests() |