123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- import XCTest
- @testable import Dynamic
- final class DynamicTests: XCTestCase {
- class override func setUp() {
- Dynamic.loggingEnabled = true
- // Logger.enabled = false
- }
- func testInit() {
- let className = "NSDateFormatter"
- let formatter1 = ObjC.NSDateFormatter()
- XCTAssertEqual(formatter1.asObject?.className, className, "Parameterless init")
- XCTAssert(formatter1.asObject is DateFormatter, "Parameterless init - Bridging")
- let formatter2 = ObjC.NSDateFormatter.`init`()
- XCTAssertEqual(formatter2.asObject?.className, className, "Parameterless init with explicit init")
- }
- func testInitWithParameters() {
- let uuidString = "68753A44-4D6F-1226-9C60-0050E4C00067"
- let className = "__NSConcreteUUID"
- let uuid1 = ObjC.NSUUID(UUIDString: uuidString)
- XCTAssertEqual(uuid1.asObject?.className, className, "Parameterized init")
- XCTAssertEqual(uuid1.UUIDString.asString, uuidString)
- let uuid2 = ObjC.NSUUID.initWithUUIDString(uuidString)
- XCTAssertEqual(uuid2.asObject?.className, className, "Parameterized init with explicit init")
- XCTAssertEqual(uuid2.UUIDString.asString, uuidString)
- }
- func testClassMethods() {
- let uuidClassName = "__NSConcreteUUID"
- let uuid = ObjC.NSUUID.UUID()
- XCTAssertEqual(uuid.asObject?.className, uuidClassName, "Class methods")
- let exceptionClassName = "NSException"
- let name = "Dummy"
- let reason = "Testing"
- let userInfo = ["Foo": "Bar"] as NSDictionary
- let exception = ObjC.NSException.exceptionWithName(name, reason: reason, userInfo: userInfo)
- XCTAssertEqual(exception.asObject?.className, exceptionClassName, "Class methods")
- XCTAssertEqual(exception.name.asString, name, "Properties passed to the constructor")
- XCTAssertEqual(exception.reason.asString, reason, "Properties passed to the constructor")
- XCTAssertEqual(exception.userInfo.asDictionary, userInfo, "Properties passed to the constructor")
- }
- func testProperties() {
- let host = "example.com"
- let urlString = "https://\(host)/"
- let urlComponents = ObjC.NSURLComponents.componentsWithString(urlString)
- XCTAssertEqual(urlComponents.host.asString, host, "Properties passed to the constructor")
- let host2 = "example2.com"
- urlComponents.host = host2
- XCTAssertEqual(urlComponents.host.asString, host2, "Setting properties")
- let queryItems = [NSURLQueryItem(name: "foo", value: "bar")] as NSArray
- urlComponents.queryItems = queryItems
- XCTAssertEqual(urlComponents.queryItems.asArray, queryItems, "Setting properties")
- XCTAssertEqual(urlComponents.URL, NSURL(string: "https://example2.com/?foo=bar"))
- let progress = ObjC.NSProgress.progressWithTotalUnitCount(100)
- progress.completedUnitCount = 50
- XCTAssertEqual(progress.fractionCompleted, 0.5, "Setting numeric properties")
- let queue = ObjC.NSOperationQueue()
- XCTAssertFalse(queue.isSuspended!)
- queue.isSuspended = true
- XCTAssertTrue(queue.isSuspended!, "Setting boolean properties with 'is' prefix")
- }
- func testBlocks() {
- // swiftlint:disable:next nesting
- typealias VoidBlock = @convention(block) () -> Void
- let closure1Called = expectation(description: "Closure 1")
- let progress = ObjC.NSProgress.progressWithTotalUnitCount(100)
- progress.cancellationHandler = {
- closure1Called.fulfill()
- } as VoidBlock
- progress.cancel()
- let closure2Called = expectation(description: "Closure 2")
- let operation = ObjC.NSBlockOperation.blockOperationWithBlock({
- closure2Called.fulfill()
- } as VoidBlock)
- ObjC.NSOperationQueue.mainQueue.addOperation(operation)
- waitForExpectations(timeout: 0.1, handler: nil)
- }
- func testExplicitUnwrapping() {
- // swiftlint:disable:next nesting
- struct NSOperatingSystemVersion {
- var majorVersion: Int
- var minorVersion: Int
- var patchVersion: Int
- }
- let processInfo1 = ProcessInfo.processInfo
- let processInfo2 = ObjC.NSProcessInfo.processInfo
- XCTAssertEqual(processInfo1.processIdentifier,
- processInfo2.processIdentifier.asInt32, "Int32")
- XCTAssertEqual(processInfo1.processorCount,
- processInfo2.processorCount.asInt, "Int")
- XCTAssertEqual(processInfo1.physicalMemory,
- processInfo2.physicalMemory.asUInt64, "UInt64")
- XCTAssertEqual(processInfo1.systemUptime,
- processInfo2.systemUptime.asDouble ?? 0, accuracy: 1, "Double")
- XCTAssertEqual(processInfo1.arguments as NSArray,
- processInfo2.arguments.asArray, "Array")
- XCTAssertEqual(processInfo1.arguments,
- processInfo2.arguments.asInferred(), "Array")
- XCTAssertEqual(processInfo1.environment as NSDictionary,
- processInfo2.environment, "Dictionary")
- XCTAssertEqual(processInfo1.processName,
- processInfo2.processName.asString, "String")
- let version1 = processInfo1.operatingSystemVersion
- let version2: NSOperatingSystemVersion? = processInfo2.operatingSystemVersion.asInferred()
- XCTAssertEqual(version1.majorVersion,
- version2?.majorVersion, "Struct")
- XCTAssertEqual(processInfo1.isOperatingSystemAtLeast(version1),
- processInfo2.isOperatingSystemAtLeastVersion(version2).asBool, "Bool")
- }
- func testImplicitUnwrapping() {
- // swiftlint:disable:next nesting
- struct NSOperatingSystemVersion {
- var majorVersion: Int
- var minorVersion: Int
- var patchVersion: Int
- }
- let processInfo1 = ProcessInfo.processInfo
- let processInfo2 = ObjC.NSProcessInfo.processInfo
- XCTAssertEqual(processInfo1.processIdentifier,
- processInfo2.processIdentifier, "Int32")
- XCTAssertEqual(processInfo1.processorCount,
- processInfo2.processorCount, "Int")
- XCTAssertEqual(processInfo1.physicalMemory,
- processInfo2.physicalMemory, "UInt64")
- XCTAssertEqual(processInfo1.systemUptime,
- processInfo2.systemUptime ?? 0, accuracy: 1, "Double")
- XCTAssertEqual(processInfo1.arguments,
- processInfo2.arguments, "Array")
- XCTAssertEqual(processInfo1.environment,
- processInfo2.environment, "Dictionary")
- XCTAssertEqual(processInfo1.processName,
- processInfo2.processName, "String")
- let version1 = processInfo1.operatingSystemVersion
- let version2: NSOperatingSystemVersion? = processInfo2.operatingSystemVersion
- XCTAssertEqual(version1.majorVersion,
- version2?.majorVersion, "Struct")
- XCTAssertEqual(processInfo1.isOperatingSystemAtLeast(version1),
- processInfo2.isOperatingSystemAtLeastVersion(version2), "Bool")
- let formatter1 = ObjC.NSDateFormatter()
- XCTAssertTrue(type(of: formatter1) == Dynamic.self, "Type should be Dynamic")
- let formatter2: NSObject? = ObjC.NSDateFormatter()
- XCTAssertEqual(formatter2?.className, "NSDateFormatter", "Value isn't unwrapped")
- let formatter3 = { () -> NSObject? in
- ObjC.NSDateFormatter()
- }()
- XCTAssertEqual(formatter3?.className, "NSDateFormatter", "Value isn't unwrapped")
- formatter1.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
- let date = ObjC.NSDate(timeIntervalSince1970: 1_600_000_000)
- let string: String? = formatter1.stringFromDate(date)
- let newDate: Date? = formatter1.dateFromString(string)
- XCTAssertEqual(date.asInferred(),
- newDate, "Value isn't unwrapped")
- XCTAssertEqual(date.asObject,
- formatter1.dateFromString(formatter1.stringFromDate(date)), "Value isn't unwrapped")
- }
- func testEdgeCases() {
- let error = ObjC.NSDateFormatter().invalidMethod()
- XCTAssertTrue(error.asObject is Error, "Calling non existing method should return error")
- XCTAssertTrue(error.isError, "isError should return true for errors")
- let errorChained = error.thisMethodCallHasNoEffect(123).randomProperty
- XCTAssertTrue(errorChained === error, "Calling methods and properties form error should return the same object")
- let null = ObjC.nil
- XCTAssertNil(null.asObject, "Wrapped nil should return nil")
- let nullChained = null.thisMethodCallHasNoEffect(123).randomProperty
- XCTAssertTrue(nullChained === null, "Calling methods and properties form <nil> should return the same object")
- let formatter = ObjC.NSDateFormatter()
- XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
- formatter.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
- XCTAssertNotEqual(formatter.stringFromDate(Date()), "", "Should NOT return an empty string")
- formatter.dateFormat = .nil
- XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
- formatter.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
- XCTAssertNotEqual(formatter.stringFromDate(Date()), "", "Should NOT return an empty string")
- formatter.dateFormat = nil as String? // or String?.none
- XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
- }
- func testAlternativeMethodNames() {
- let formatter = ObjC.NSDateFormatter()
- formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
- XCTAssertEqual(formatter.stringFromDate(Date()).asString,
- formatter.stringFrom(date: Date()), "Alternative methods should work as the original")
- XCTAssertEqual(formatter.stringFromDate(Date()).asString,
- formatter.string(fromDate: Date()), "Alternative methods should work as the original")
- let progress1 = ObjC.NSProgress.progressWithTotalUnitCount(99)
- let progress2 = ObjC.NSProgress.progress(withTotalUnitCount: 99)
- XCTAssertEqual(progress1.totalUnitCount.asInt,
- progress2.totalUnitCount.asInt, "Alternative methods should work as the original")
- }
- func testHiddenAPI() {
- do {
- let selector = NSSelectorFromString("lowercaseString")
- let target = NSString("ABC")
- let methodSignature: NSObject? = ObjC(target).methodSignatureForSelector(selector)
- let invocation = ObjC.NSInvocation.invocationWithMethodSignature(methodSignature)
- invocation.selector = selector
- invocation.invokeWithTarget(target)
- var result: NSString?
- _ = withUnsafeMutablePointer(to: &result) { pointer in
- invocation.getReturnValue(pointer)
- }
- XCTAssertEqual(result, "abc", "Can't use hidden API")
- if let string = result {
- _ = Unmanaged.passRetained(string).takeUnretainedValue()
- }
- }
- do {
- let selector = NSSelectorFromString("stringByPaddingToLength:withString:startingAtIndex:")
- let target = NSString("ABC")
- let methodSignature: NSObject? = ObjC(target).methodSignatureForSelector(selector)
- let invocation = ObjC.NSInvocation.invocationWithMethodSignature(methodSignature)
- invocation.selector = selector
- let length: Int = 6
- let padString = "0123" as NSString
- let index: Int = 1
- _ = withUnsafePointer(to: length) { pointer in
- invocation.setArgument(pointer, atIndex: 2)
- }
- _ = withUnsafePointer(to: padString) { pointer in
- invocation.setArgument(pointer, atIndex: 3)
- }
- _ = withUnsafePointer(to: index) { pointer in
- invocation.setArgument(pointer, atIndex: 4)
- }
- invocation.invokeWithTarget(target)
- var result: NSString?
- _ = withUnsafeMutablePointer(to: &result) { pointer in
- invocation.getReturnValue(pointer)
- }
- XCTAssertEqual(result, "ABC123", "Can't use hidden API")
- if let string = result {
- _ = Unmanaged.passRetained(string).takeUnretainedValue()
- }
- }
- }
- }
- extension NSObject {
- var className: String { String(describing: type(of: self)) }
- }
|