DynamicTests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import XCTest
  2. @testable import Dynamic
  3. final class DynamicTests: XCTestCase {
  4. class override func setUp() {
  5. Dynamic.loggingEnabled = true
  6. // Logger.enabled = false
  7. }
  8. func testInit() {
  9. let className = "NSDateFormatter"
  10. let formatter1 = ObjC.NSDateFormatter()
  11. XCTAssertEqual(formatter1.asObject?.className, className, "Parameterless init")
  12. XCTAssert(formatter1.asObject is DateFormatter, "Parameterless init - Bridging")
  13. let formatter2 = ObjC.NSDateFormatter.`init`()
  14. XCTAssertEqual(formatter2.asObject?.className, className, "Parameterless init with explicit init")
  15. }
  16. func testInitWithParameters() {
  17. let uuidString = "68753A44-4D6F-1226-9C60-0050E4C00067"
  18. let className = "__NSConcreteUUID"
  19. let uuid1 = ObjC.NSUUID(UUIDString: uuidString)
  20. XCTAssertEqual(uuid1.asObject?.className, className, "Parameterized init")
  21. XCTAssertEqual(uuid1.UUIDString.asString, uuidString)
  22. let uuid2 = ObjC.NSUUID.initWithUUIDString(uuidString)
  23. XCTAssertEqual(uuid2.asObject?.className, className, "Parameterized init with explicit init")
  24. XCTAssertEqual(uuid2.UUIDString.asString, uuidString)
  25. }
  26. func testClassMethods() {
  27. let uuidClassName = "__NSConcreteUUID"
  28. let uuid = ObjC.NSUUID.UUID()
  29. XCTAssertEqual(uuid.asObject?.className, uuidClassName, "Class methods")
  30. let exceptionClassName = "NSException"
  31. let name = "Dummy"
  32. let reason = "Testing"
  33. let userInfo = ["Foo": "Bar"] as NSDictionary
  34. let exception = ObjC.NSException.exceptionWithName(name, reason: reason, userInfo: userInfo)
  35. XCTAssertEqual(exception.asObject?.className, exceptionClassName, "Class methods")
  36. XCTAssertEqual(exception.name.asString, name, "Properties passed to the constructor")
  37. XCTAssertEqual(exception.reason.asString, reason, "Properties passed to the constructor")
  38. XCTAssertEqual(exception.userInfo.asDictionary, userInfo, "Properties passed to the constructor")
  39. }
  40. func testProperties() {
  41. let host = "example.com"
  42. let urlString = "https://\(host)/"
  43. let urlComponents = ObjC.NSURLComponents.componentsWithString(urlString)
  44. XCTAssertEqual(urlComponents.host.asString, host, "Properties passed to the constructor")
  45. let host2 = "example2.com"
  46. urlComponents.host = host2
  47. XCTAssertEqual(urlComponents.host.asString, host2, "Setting properties")
  48. let queryItems = [NSURLQueryItem(name: "foo", value: "bar")] as NSArray
  49. urlComponents.queryItems = queryItems
  50. XCTAssertEqual(urlComponents.queryItems.asArray, queryItems, "Setting properties")
  51. XCTAssertEqual(urlComponents.URL, NSURL(string: "https://example2.com/?foo=bar"))
  52. let progress = ObjC.NSProgress.progressWithTotalUnitCount(100)
  53. progress.completedUnitCount = 50
  54. XCTAssertEqual(progress.fractionCompleted, 0.5, "Setting numeric properties")
  55. let queue = ObjC.NSOperationQueue()
  56. XCTAssertFalse(queue.isSuspended!)
  57. queue.isSuspended = true
  58. XCTAssertTrue(queue.isSuspended!, "Setting boolean properties with 'is' prefix")
  59. }
  60. func testBlocks() {
  61. // swiftlint:disable:next nesting
  62. typealias VoidBlock = @convention(block) () -> Void
  63. let closure1Called = expectation(description: "Closure 1")
  64. let progress = ObjC.NSProgress.progressWithTotalUnitCount(100)
  65. progress.cancellationHandler = {
  66. closure1Called.fulfill()
  67. } as VoidBlock
  68. progress.cancel()
  69. let closure2Called = expectation(description: "Closure 2")
  70. let operation = ObjC.NSBlockOperation.blockOperationWithBlock({
  71. closure2Called.fulfill()
  72. } as VoidBlock)
  73. ObjC.NSOperationQueue.mainQueue.addOperation(operation)
  74. waitForExpectations(timeout: 0.1, handler: nil)
  75. }
  76. func testExplicitUnwrapping() {
  77. // swiftlint:disable:next nesting
  78. struct NSOperatingSystemVersion {
  79. var majorVersion: Int
  80. var minorVersion: Int
  81. var patchVersion: Int
  82. }
  83. let processInfo1 = ProcessInfo.processInfo
  84. let processInfo2 = ObjC.NSProcessInfo.processInfo
  85. XCTAssertEqual(processInfo1.processIdentifier,
  86. processInfo2.processIdentifier.asInt32, "Int32")
  87. XCTAssertEqual(processInfo1.processorCount,
  88. processInfo2.processorCount.asInt, "Int")
  89. XCTAssertEqual(processInfo1.physicalMemory,
  90. processInfo2.physicalMemory.asUInt64, "UInt64")
  91. XCTAssertEqual(processInfo1.systemUptime,
  92. processInfo2.systemUptime.asDouble ?? 0, accuracy: 1, "Double")
  93. XCTAssertEqual(processInfo1.arguments as NSArray,
  94. processInfo2.arguments.asArray, "Array")
  95. XCTAssertEqual(processInfo1.arguments,
  96. processInfo2.arguments.asInferred(), "Array")
  97. XCTAssertEqual(processInfo1.environment as NSDictionary,
  98. processInfo2.environment, "Dictionary")
  99. XCTAssertEqual(processInfo1.processName,
  100. processInfo2.processName.asString, "String")
  101. let version1 = processInfo1.operatingSystemVersion
  102. let version2: NSOperatingSystemVersion? = processInfo2.operatingSystemVersion.asInferred()
  103. XCTAssertEqual(version1.majorVersion,
  104. version2?.majorVersion, "Struct")
  105. XCTAssertEqual(processInfo1.isOperatingSystemAtLeast(version1),
  106. processInfo2.isOperatingSystemAtLeastVersion(version2).asBool, "Bool")
  107. }
  108. func testImplicitUnwrapping() {
  109. // swiftlint:disable:next nesting
  110. struct NSOperatingSystemVersion {
  111. var majorVersion: Int
  112. var minorVersion: Int
  113. var patchVersion: Int
  114. }
  115. let processInfo1 = ProcessInfo.processInfo
  116. let processInfo2 = ObjC.NSProcessInfo.processInfo
  117. XCTAssertEqual(processInfo1.processIdentifier,
  118. processInfo2.processIdentifier, "Int32")
  119. XCTAssertEqual(processInfo1.processorCount,
  120. processInfo2.processorCount, "Int")
  121. XCTAssertEqual(processInfo1.physicalMemory,
  122. processInfo2.physicalMemory, "UInt64")
  123. XCTAssertEqual(processInfo1.systemUptime,
  124. processInfo2.systemUptime ?? 0, accuracy: 1, "Double")
  125. XCTAssertEqual(processInfo1.arguments,
  126. processInfo2.arguments, "Array")
  127. XCTAssertEqual(processInfo1.environment,
  128. processInfo2.environment, "Dictionary")
  129. XCTAssertEqual(processInfo1.processName,
  130. processInfo2.processName, "String")
  131. let version1 = processInfo1.operatingSystemVersion
  132. let version2: NSOperatingSystemVersion? = processInfo2.operatingSystemVersion
  133. XCTAssertEqual(version1.majorVersion,
  134. version2?.majorVersion, "Struct")
  135. XCTAssertEqual(processInfo1.isOperatingSystemAtLeast(version1),
  136. processInfo2.isOperatingSystemAtLeastVersion(version2), "Bool")
  137. let formatter1 = ObjC.NSDateFormatter()
  138. XCTAssertTrue(type(of: formatter1) == Dynamic.self, "Type should be Dynamic")
  139. let formatter2: NSObject? = ObjC.NSDateFormatter()
  140. XCTAssertEqual(formatter2?.className, "NSDateFormatter", "Value isn't unwrapped")
  141. let formatter3 = { () -> NSObject? in
  142. ObjC.NSDateFormatter()
  143. }()
  144. XCTAssertEqual(formatter3?.className, "NSDateFormatter", "Value isn't unwrapped")
  145. formatter1.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
  146. let date = ObjC.NSDate(timeIntervalSince1970: 1_600_000_000)
  147. let string: String? = formatter1.stringFromDate(date)
  148. let newDate: Date? = formatter1.dateFromString(string)
  149. XCTAssertEqual(date.asInferred(),
  150. newDate, "Value isn't unwrapped")
  151. XCTAssertEqual(date.asObject,
  152. formatter1.dateFromString(formatter1.stringFromDate(date)), "Value isn't unwrapped")
  153. }
  154. func testEdgeCases() {
  155. let error = ObjC.NSDateFormatter().invalidMethod()
  156. XCTAssertTrue(error.asObject is Error, "Calling non existing method should return error")
  157. XCTAssertTrue(error.isError, "isError should return true for errors")
  158. let errorChained = error.thisMethodCallHasNoEffect(123).randomProperty
  159. XCTAssertTrue(errorChained === error, "Calling methods and properties form error should return the same object")
  160. let null = ObjC.nil
  161. XCTAssertNil(null.asObject, "Wrapped nil should return nil")
  162. let nullChained = null.thisMethodCallHasNoEffect(123).randomProperty
  163. XCTAssertTrue(nullChained === null, "Calling methods and properties form <nil> should return the same object")
  164. let formatter = ObjC.NSDateFormatter()
  165. XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
  166. formatter.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
  167. XCTAssertNotEqual(formatter.stringFromDate(Date()), "", "Should NOT return an empty string")
  168. formatter.dateFormat = .nil
  169. XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
  170. formatter.dateFormat = ObjC("yyyy-MM-dd HH:mm:ss")
  171. XCTAssertNotEqual(formatter.stringFromDate(Date()), "", "Should NOT return an empty string")
  172. formatter.dateFormat = nil as String? // or String?.none
  173. XCTAssertEqual(formatter.stringFromDate(Date()), "", "Should return an empty string")
  174. }
  175. func testAlternativeMethodNames() {
  176. let formatter = ObjC.NSDateFormatter()
  177. formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
  178. XCTAssertEqual(formatter.stringFromDate(Date()).asString,
  179. formatter.stringFrom(date: Date()), "Alternative methods should work as the original")
  180. XCTAssertEqual(formatter.stringFromDate(Date()).asString,
  181. formatter.string(fromDate: Date()), "Alternative methods should work as the original")
  182. let progress1 = ObjC.NSProgress.progressWithTotalUnitCount(99)
  183. let progress2 = ObjC.NSProgress.progress(withTotalUnitCount: 99)
  184. XCTAssertEqual(progress1.totalUnitCount.asInt,
  185. progress2.totalUnitCount.asInt, "Alternative methods should work as the original")
  186. }
  187. func testHiddenAPI() {
  188. do {
  189. let selector = NSSelectorFromString("lowercaseString")
  190. let target = NSString("ABC")
  191. let methodSignature: NSObject? = ObjC(target).methodSignatureForSelector(selector)
  192. let invocation = ObjC.NSInvocation.invocationWithMethodSignature(methodSignature)
  193. invocation.selector = selector
  194. invocation.invokeWithTarget(target)
  195. var result: NSString?
  196. _ = withUnsafeMutablePointer(to: &result) { pointer in
  197. invocation.getReturnValue(pointer)
  198. }
  199. XCTAssertEqual(result, "abc", "Can't use hidden API")
  200. if let string = result {
  201. _ = Unmanaged.passRetained(string).takeUnretainedValue()
  202. }
  203. }
  204. do {
  205. let selector = NSSelectorFromString("stringByPaddingToLength:withString:startingAtIndex:")
  206. let target = NSString("ABC")
  207. let methodSignature: NSObject? = ObjC(target).methodSignatureForSelector(selector)
  208. let invocation = ObjC.NSInvocation.invocationWithMethodSignature(methodSignature)
  209. invocation.selector = selector
  210. let length: Int = 6
  211. let padString = "0123" as NSString
  212. let index: Int = 1
  213. _ = withUnsafePointer(to: length) { pointer in
  214. invocation.setArgument(pointer, atIndex: 2)
  215. }
  216. _ = withUnsafePointer(to: padString) { pointer in
  217. invocation.setArgument(pointer, atIndex: 3)
  218. }
  219. _ = withUnsafePointer(to: index) { pointer in
  220. invocation.setArgument(pointer, atIndex: 4)
  221. }
  222. invocation.invokeWithTarget(target)
  223. var result: NSString?
  224. _ = withUnsafeMutablePointer(to: &result) { pointer in
  225. invocation.getReturnValue(pointer)
  226. }
  227. XCTAssertEqual(result, "ABC123", "Can't use hidden API")
  228. if let string = result {
  229. _ = Unmanaged.passRetained(string).takeUnretainedValue()
  230. }
  231. }
  232. }
  233. }
  234. extension NSObject {
  235. var className: String { String(describing: type(of: self)) }
  236. }