SwiftObjectInterfaceTests.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. import XCTest
  19. import Realm
  20. import Foundation
  21. class OuterClass {
  22. class InnerClass {
  23. }
  24. }
  25. class SwiftStringObjectSubclass : SwiftStringObject {
  26. @objc dynamic var stringCol2 = ""
  27. }
  28. class SwiftSelfRefrencingSubclass: SwiftStringObject {
  29. @objc dynamic var objects = RLMArray<SwiftSelfRefrencingSubclass>(objectClassName: SwiftSelfRefrencingSubclass.className())
  30. }
  31. class SwiftDefaultObject: RLMObject {
  32. @objc dynamic var intCol = 1
  33. @objc dynamic var boolCol = true
  34. override class func defaultPropertyValues() -> [AnyHashable : Any]? {
  35. return ["intCol": 2]
  36. }
  37. }
  38. class SwiftOptionalNumberObject: RLMObject {
  39. @objc dynamic var intCol: NSNumber? = 1
  40. @objc dynamic var floatCol: NSNumber? = 2.2 as Float as NSNumber
  41. @objc dynamic var doubleCol: NSNumber? = 3.3
  42. @objc dynamic var boolCol: NSNumber? = true
  43. }
  44. class SwiftObjectInterfaceTests: RLMTestCase {
  45. // Swift models
  46. func testSwiftObject() {
  47. let realm = realmWithTestPath()
  48. realm.beginWriteTransaction()
  49. let obj = SwiftObject()
  50. realm.add(obj)
  51. obj.boolCol = true
  52. obj.intCol = 1234
  53. obj.floatCol = 1.1
  54. obj.doubleCol = 2.2
  55. obj.stringCol = "abcd"
  56. obj.binaryCol = "abcd".data(using: String.Encoding.utf8)
  57. obj.dateCol = Date(timeIntervalSince1970: 123)
  58. obj.objectCol = SwiftBoolObject()
  59. obj.objectCol.boolCol = true
  60. obj.arrayCol.add(obj.objectCol)
  61. try! realm.commitWriteTransaction()
  62. let data = "abcd".data(using: String.Encoding.utf8)
  63. let firstObj = SwiftObject.allObjects(in: realm).firstObject() as! SwiftObject
  64. XCTAssertEqual(firstObj.boolCol, true, "should be true")
  65. XCTAssertEqual(firstObj.intCol, 1234, "should be 1234")
  66. XCTAssertEqual(firstObj.floatCol, Float(1.1), "should be 1.1")
  67. XCTAssertEqual(firstObj.doubleCol, 2.2, "should be 2.2")
  68. XCTAssertEqual(firstObj.stringCol, "abcd", "should be abcd")
  69. XCTAssertEqual(firstObj.binaryCol!, data!)
  70. XCTAssertEqual(firstObj.dateCol, Date(timeIntervalSince1970: 123), "should be epoch + 123")
  71. XCTAssertEqual(firstObj.objectCol.boolCol, true, "should be true")
  72. XCTAssertEqual(obj.arrayCol.count, UInt(1), "array count should be 1")
  73. XCTAssertEqual(obj.arrayCol.firstObject()!.boolCol, true, "should be true")
  74. }
  75. func testDefaultValueSwiftObject() {
  76. let realm = realmWithTestPath()
  77. realm.beginWriteTransaction()
  78. realm.add(SwiftObject())
  79. try! realm.commitWriteTransaction()
  80. let data = "a".data(using: String.Encoding.utf8)
  81. let firstObj = SwiftObject.allObjects(in: realm).firstObject() as! SwiftObject
  82. XCTAssertEqual(firstObj.boolCol, false, "should be false")
  83. XCTAssertEqual(firstObj.intCol, 123, "should be 123")
  84. XCTAssertEqual(firstObj.floatCol, Float(1.23), "should be 1.23")
  85. XCTAssertEqual(firstObj.doubleCol, 12.3, "should be 12.3")
  86. XCTAssertEqual(firstObj.stringCol, "a", "should be a")
  87. XCTAssertEqual(firstObj.binaryCol!, data!)
  88. XCTAssertEqual(firstObj.dateCol, Date(timeIntervalSince1970: 1), "should be epoch + 1")
  89. XCTAssertEqual(firstObj.objectCol.boolCol, false, "should be false")
  90. XCTAssertEqual(firstObj.arrayCol.count, UInt(0), "array count should be zero")
  91. }
  92. func testMergedDefaultValuesSwiftObject() {
  93. let realm = self.realmWithTestPath()
  94. realm.beginWriteTransaction()
  95. _ = SwiftDefaultObject.create(in: realm, withValue: NSDictionary())
  96. try! realm.commitWriteTransaction()
  97. let object = SwiftDefaultObject.allObjects(in: realm).firstObject() as! SwiftDefaultObject
  98. XCTAssertEqual(object.intCol, 2, "defaultPropertyValues should override native property default value")
  99. XCTAssertEqual(object.boolCol, true, "native property default value should be used if defaultPropertyValues doesn't contain that key")
  100. }
  101. func testSubclass() {
  102. // test className methods
  103. XCTAssertEqual("SwiftStringObject", SwiftStringObject.className())
  104. XCTAssertEqual("SwiftStringObjectSubclass", SwiftStringObjectSubclass.className())
  105. let realm = RLMRealm.default()
  106. realm.beginWriteTransaction()
  107. _ = SwiftStringObject.createInDefaultRealm(withValue: ["string"])
  108. _ = SwiftStringObjectSubclass.createInDefaultRealm(withValue: ["string", "string2"])
  109. try! realm.commitWriteTransaction()
  110. // ensure creation in proper table
  111. XCTAssertEqual(UInt(1), SwiftStringObjectSubclass.allObjects().count)
  112. XCTAssertEqual(UInt(1), SwiftStringObject.allObjects().count)
  113. try! realm.transaction {
  114. // create self referencing subclass
  115. let sub = SwiftSelfRefrencingSubclass.createInDefaultRealm(withValue: ["string", []])
  116. let sub2 = SwiftSelfRefrencingSubclass()
  117. sub.objects.add(sub2)
  118. }
  119. }
  120. func testOptionalNSNumberProperties() {
  121. let realm = realmWithTestPath()
  122. let no = SwiftOptionalNumberObject()
  123. XCTAssertEqual([.int, .float, .double, .bool], no.objectSchema.properties.map { $0.type })
  124. XCTAssertEqual(1, no.intCol!)
  125. XCTAssertEqual(2.2 as Float as NSNumber, no.floatCol!)
  126. XCTAssertEqual(3.3, no.doubleCol!)
  127. XCTAssertEqual(true, no.boolCol!)
  128. try! realm.transaction {
  129. realm.add(no)
  130. no.intCol = nil
  131. no.floatCol = nil
  132. no.doubleCol = nil
  133. no.boolCol = nil
  134. }
  135. XCTAssertNil(no.intCol)
  136. XCTAssertNil(no.floatCol)
  137. XCTAssertNil(no.doubleCol)
  138. XCTAssertNil(no.boolCol)
  139. try! realm.transaction {
  140. no.intCol = 1.1
  141. no.floatCol = 2.2 as Float as NSNumber
  142. no.doubleCol = 3.3
  143. no.boolCol = false
  144. }
  145. XCTAssertEqual(1, no.intCol!)
  146. XCTAssertEqual(2.2 as Float as NSNumber, no.floatCol!)
  147. XCTAssertEqual(3.3, no.doubleCol!)
  148. XCTAssertEqual(false, no.boolCol!)
  149. }
  150. func testOptionalSwiftProperties() {
  151. let realm = realmWithTestPath()
  152. try! realm.transaction { realm.add(SwiftOptionalObject()) }
  153. let firstObj = SwiftOptionalObject.allObjects(in: realm).firstObject() as! SwiftOptionalObject
  154. XCTAssertNil(firstObj.optObjectCol)
  155. XCTAssertNil(firstObj.optStringCol)
  156. XCTAssertNil(firstObj.optNSStringCol)
  157. XCTAssertNil(firstObj.optBinaryCol)
  158. XCTAssertNil(firstObj.optDateCol)
  159. try! realm.transaction {
  160. firstObj.optObjectCol = SwiftBoolObject()
  161. firstObj.optObjectCol!.boolCol = true
  162. firstObj.optStringCol = "Hi!"
  163. firstObj.optNSStringCol = "Hi!"
  164. firstObj.optBinaryCol = Data(bytes: "hi", count: 2)
  165. firstObj.optDateCol = Date(timeIntervalSinceReferenceDate: 10)
  166. }
  167. XCTAssertTrue(firstObj.optObjectCol!.boolCol)
  168. XCTAssertEqual(firstObj.optStringCol!, "Hi!")
  169. XCTAssertEqual(firstObj.optNSStringCol!, "Hi!")
  170. XCTAssertEqual(firstObj.optBinaryCol!, Data(bytes: "hi", count: 2))
  171. XCTAssertEqual(firstObj.optDateCol!, Date(timeIntervalSinceReferenceDate: 10))
  172. try! realm.transaction {
  173. firstObj.optObjectCol = nil
  174. firstObj.optStringCol = nil
  175. firstObj.optNSStringCol = nil
  176. firstObj.optBinaryCol = nil
  177. firstObj.optDateCol = nil
  178. }
  179. XCTAssertNil(firstObj.optObjectCol)
  180. XCTAssertNil(firstObj.optStringCol)
  181. XCTAssertNil(firstObj.optNSStringCol)
  182. XCTAssertNil(firstObj.optBinaryCol)
  183. XCTAssertNil(firstObj.optDateCol)
  184. }
  185. func testSwiftClassNameIsDemangled() {
  186. XCTAssertEqual(SwiftObject.className(), "SwiftObject",
  187. "Calling className() on Swift class should return demangled name")
  188. }
  189. func testPrimitiveArray() {
  190. let obj = SwiftPrimitiveArrayObject()
  191. let str = "str" as NSString
  192. let data = "str".data(using: .utf8)! as Data as NSData
  193. let date = NSDate()
  194. let str2 = "str2" as NSString
  195. let data2 = "str2".data(using: .utf8)! as Data as NSData
  196. let date2 = NSDate(timeIntervalSince1970: 0)
  197. obj.stringCol.add(str)
  198. XCTAssertEqual(obj.stringCol[0], str)
  199. XCTAssertEqual(obj.stringCol.index(of: str), 0)
  200. XCTAssertEqual(obj.stringCol.index(of: str2), UInt(NSNotFound))
  201. obj.dataCol.add(data)
  202. XCTAssertEqual(obj.dataCol[0], data)
  203. XCTAssertEqual(obj.dataCol.index(of: data), 0)
  204. XCTAssertEqual(obj.dataCol.index(of: data2), UInt(NSNotFound))
  205. obj.dateCol.add(date)
  206. XCTAssertEqual(obj.dateCol[0], date)
  207. XCTAssertEqual(obj.dateCol.index(of: date), 0)
  208. XCTAssertEqual(obj.dateCol.index(of: date2), UInt(NSNotFound))
  209. obj.optStringCol.add(str)
  210. XCTAssertEqual(obj.optStringCol[0], str)
  211. obj.optDataCol.add(data)
  212. XCTAssertEqual(obj.optDataCol[0], data)
  213. obj.optDateCol.add(date)
  214. XCTAssertEqual(obj.optDateCol[0], date)
  215. obj.optStringCol.add(NSNull())
  216. XCTAssertEqual(obj.optStringCol[1], NSNull())
  217. obj.optDataCol.add(NSNull())
  218. XCTAssertEqual(obj.optDataCol[1], NSNull())
  219. obj.optDateCol.add(NSNull())
  220. XCTAssertEqual(obj.optDateCol[1], NSNull())
  221. assertThrowsWithReasonMatching(obj.optDataCol.add(str), ".*")
  222. }
  223. // Objective-C models
  224. // Note: Swift doesn't support custom accessor names
  225. // so we test to make sure models with custom accessors can still be accessed
  226. func testCustomAccessors() {
  227. let realm = realmWithTestPath()
  228. realm.beginWriteTransaction()
  229. let ca = CustomAccessorsObject.create(in: realm, withValue: ["name", 2])
  230. XCTAssertEqual(ca.name!, "name", "name property should be name.")
  231. ca.age = 99
  232. XCTAssertEqual(ca.age, Int32(99), "age property should be 99")
  233. try! realm.commitWriteTransaction()
  234. }
  235. func testClassExtension() {
  236. let realm = realmWithTestPath()
  237. realm.beginWriteTransaction()
  238. let bObject = BaseClassStringObject()
  239. bObject.intCol = 1
  240. bObject.stringCol = "stringVal"
  241. realm.add(bObject)
  242. try! realm.commitWriteTransaction()
  243. let objectFromRealm = BaseClassStringObject.allObjects(in: realm)[0] as! BaseClassStringObject
  244. XCTAssertEqual(objectFromRealm.intCol, Int32(1), "Should be 1")
  245. XCTAssertEqual(objectFromRealm.stringCol!, "stringVal", "Should be stringVal")
  246. }
  247. func testCreateOrUpdate() {
  248. let realm = RLMRealm.default()
  249. realm.beginWriteTransaction()
  250. _ = SwiftPrimaryStringObject.createOrUpdateInDefaultRealm(withValue: ["string", 1])
  251. let objects = SwiftPrimaryStringObject.allObjects();
  252. XCTAssertEqual(objects.count, UInt(1), "Should have 1 object");
  253. XCTAssertEqual((objects[0] as! SwiftPrimaryStringObject).intCol, 1, "Value should be 1");
  254. _ = SwiftPrimaryStringObject.createOrUpdateInDefaultRealm(withValue: ["stringCol": "string2", "intCol": 2])
  255. XCTAssertEqual(objects.count, UInt(2), "Should have 2 objects")
  256. _ = SwiftPrimaryStringObject.createOrUpdateInDefaultRealm(withValue: ["string", 3])
  257. XCTAssertEqual(objects.count, UInt(2), "Should have 2 objects")
  258. XCTAssertEqual((objects[0] as! SwiftPrimaryStringObject).intCol, 3, "Value should be 3");
  259. try! realm.commitWriteTransaction()
  260. }
  261. func testObjectForPrimaryKey() {
  262. let realm = RLMRealm.default()
  263. realm.beginWriteTransaction()
  264. _ = SwiftPrimaryStringObject.createOrUpdateInDefaultRealm(withValue: ["string", 1])
  265. let obj = SwiftPrimaryStringObject.object(forPrimaryKey: "string")
  266. XCTAssertNotNil(obj!)
  267. XCTAssertEqual(obj!.intCol, 1)
  268. realm.cancelWriteTransaction()
  269. }
  270. // if this fails (and you haven't changed the test module name), the checks
  271. // for swift class names and the demangling logic need to be updated
  272. func testNSStringFromClassDemangledTopLevelClassNames() {
  273. XCTAssertEqual(NSStringFromClass(OuterClass.self), "Tests.OuterClass")
  274. }
  275. // if this fails (and you haven't changed the test module name), the prefix
  276. // check in RLMSchema initialization needs to be updated
  277. func testNestedClassNameMangling() {
  278. XCTAssertEqual(NSStringFromClass(OuterClass.InnerClass.self), "_TtCC5Tests10OuterClass10InnerClass")
  279. }
  280. }