/Tests/Unit/ObjectSubclassingControllerTests.m

https://gitlab.com/iranjith4/Parse-SDK-iOS-OSX · Objective C · 422 lines · 311 code · 95 blank · 16 comment · 2 complexity · bfc1af69771e65818952b6fe1f986459 MD5 · raw file

  1. /**
  2. * Copyright (c) 2015-present, Parse, LLC.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. #import "PFObject+Subclass.h"
  10. #import "PFObjectPrivate.h"
  11. #import "PFObjectSubclassingController.h"
  12. #import "PFRelation.h"
  13. #import "PFSubclassing.h"
  14. #import "PFUnitTestCase.h"
  15. #import "ParseUnitTests-Swift.h"
  16. @interface TestSubclass : PFObject<PFSubclassing>
  17. @end
  18. @interface NotSubclass : PFObject<PFSubclassing>
  19. @end
  20. @interface PropertySubclass : PFObject<PFSubclassing> {
  21. @public
  22. id _ivarProperty;
  23. }
  24. @property (nonatomic, assign) int primitiveProperty;
  25. @property (nonatomic, strong) id objectProperty;
  26. @property (nonatomic, strong, readonly) PFRelation *relationProperty;
  27. @property (nonatomic, strong) PFRelation *badRelation;
  28. @property (nonatomic, strong) id ivarProperty;
  29. @property (nonatomic, copy) id aCopyProperty;
  30. @property (nonatomic, assign) CGPoint badProperty;
  31. @end
  32. @interface BadSubclass : TestSubclass
  33. @end
  34. @interface GoodSubclass : TestSubclass
  35. @end
  36. @implementation TestSubclass
  37. + (NSString *)parseClassName {
  38. return @"TestSubclass";
  39. }
  40. @end
  41. @implementation NotSubclass
  42. + (NSString *)parseClassName {
  43. return @"TestSubclass";
  44. }
  45. @end
  46. @implementation PropertySubclass
  47. @dynamic primitiveProperty, objectProperty, relationProperty, ivarProperty, aCopyProperty, badProperty, badRelation;
  48. + (NSString *)parseClassName {
  49. return @"PropertySubclass";
  50. }
  51. - (void)badSelector {
  52. }
  53. @end
  54. @implementation BadSubclass
  55. + (NSString *)parseClassName {
  56. return @"Bad";
  57. }
  58. @end
  59. @implementation GoodSubclass
  60. @end
  61. @interface ObjectSubclassingControllerTests : PFUnitTestCase
  62. @end
  63. @implementation ObjectSubclassingControllerTests
  64. ///--------------------------------------
  65. #pragma mark - Helpers
  66. ///--------------------------------------
  67. - (void)badSelector {
  68. // To shut the compiler up
  69. }
  70. - (NSInvocation *)_forwardingInvocationForTarget:(PFObject *)target
  71. selector:(SEL)aSelector
  72. controller:(PFObjectSubclassingController *)controller {
  73. NSMethodSignature *methodSignature = [controller forwardingMethodSignatureForSelector:aSelector
  74. ofClass:[target class]];
  75. if (methodSignature == nil) {
  76. methodSignature = [target methodSignatureForSelector:aSelector];
  77. }
  78. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
  79. [invocation setTarget:target];
  80. [invocation setSelector:aSelector];
  81. return invocation;
  82. }
  83. ///--------------------------------------
  84. #pragma mark - Tests
  85. ///--------------------------------------
  86. - (void)testConstructor {
  87. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  88. XCTAssertNotNil(subclassingController);
  89. }
  90. - (void)testRegister {
  91. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  92. [subclassingController registerSubclass:[TestSubclass class]];
  93. XCTAssertEqual([TestSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  94. }
  95. - (void)testRegistrationAfterMethodResolution {
  96. PropertySubclass *subclass = [[PropertySubclass alloc] initWithClassName:@"Yolo"];
  97. XCTAssertNoThrow(subclass.primitiveProperty = 1);
  98. XCTAssertEqual(subclass.primitiveProperty, 1);
  99. [PropertySubclass registerSubclass];
  100. XCTAssertEqual(subclass.primitiveProperty, 1);
  101. XCTAssertNoThrow(subclass.primitiveProperty = 2);
  102. XCTAssertEqual(subclass.primitiveProperty, 2);
  103. }
  104. - (void)testUnregister {
  105. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  106. [subclassingController registerSubclass:[TestSubclass class]];
  107. XCTAssertEqual([TestSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  108. [subclassingController unregisterSubclass:[TestSubclass class]];
  109. XCTAssertNil([subclassingController subclassForParseClassName:@"TestSubclass"]);
  110. }
  111. - (void)testSubclassingEdgeCases {
  112. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  113. [subclassingController registerSubclass:[TestSubclass class]];
  114. XCTAssertEqual([TestSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  115. XCTAssertThrows([subclassingController registerSubclass:[BadSubclass class]]);
  116. XCTAssertEqual([TestSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  117. XCTAssertNoThrow([subclassingController registerSubclass:[GoodSubclass class]]);
  118. XCTAssertEqual([GoodSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  119. XCTAssertNoThrow([subclassingController registerSubclass:[TestSubclass class]]);
  120. XCTAssertEqual([GoodSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  121. XCTAssertThrows([subclassingController registerSubclass:[NotSubclass class]]);
  122. XCTAssertEqual([GoodSubclass class], [subclassingController subclassForParseClassName:@"TestSubclass"]);
  123. }
  124. - (void)testForwardingMethodSignature {
  125. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  126. [subclassingController registerSubclass:[PropertySubclass class]];
  127. XCTAssertEqualObjects([subclassingController forwardingMethodSignatureForSelector:@selector(primitiveProperty)
  128. ofClass:[PropertySubclass class]],
  129. [NSMethodSignature signatureWithObjCTypes:"i@:"]);
  130. XCTAssertEqualObjects([subclassingController forwardingMethodSignatureForSelector:@selector(setPrimitiveProperty:)
  131. ofClass:[PropertySubclass class]],
  132. [NSMethodSignature signatureWithObjCTypes:"v@:i"]);
  133. XCTAssertNil([subclassingController forwardingMethodSignatureForSelector:@selector(badSelector)
  134. ofClass:[PropertySubclass class]]);
  135. }
  136. - (void)testBadForwarding {
  137. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  138. // Don't register subclass with controller.
  139. [PropertySubclass registerSubclass];
  140. PropertySubclass *object = [[PropertySubclass alloc] init];
  141. [subclassingController registerSubclass:[PropertySubclass class]];
  142. NSInvocation *invocation = [self _forwardingInvocationForTarget:object
  143. selector:@selector(badSelector)
  144. controller:subclassingController];
  145. XCTAssertFalse([subclassingController forwardObjectInvocation:invocation withObject:object]);
  146. // This will print the warning message to the console, which gives us 100% test coverage!
  147. invocation = [self _forwardingInvocationForTarget:object
  148. selector:@selector(badRelation)
  149. controller:subclassingController];
  150. }
  151. - (void)testForwardingGetter {
  152. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  153. [PropertySubclass registerSubclass];
  154. [subclassingController registerSubclass:[PropertySubclass class]];
  155. #define AssertInvocationAssertValueEquals(invocation, type, value) ({ \
  156. type _expected = (value); \
  157. type _actual; [invocation getReturnValue:&_actual]; \
  158. XCTAssertEqual(_expected, _actual); \
  159. })
  160. PropertySubclass *target = [[PropertySubclass alloc] init];
  161. target[@"primitiveProperty"] = @1337;
  162. target[@"objectProperty"] = @"Hello, World!";
  163. target[@"aCopyProperty"] = [[NSMutableString alloc] initWithString:@"Hello, World!"];
  164. target[@"badProperty"] = @"Some Value";
  165. target->_ivarProperty = @8675309;
  166. NSInvocation *invocation = [self _forwardingInvocationForTarget:target
  167. selector:@selector(primitiveProperty)
  168. controller:subclassingController];
  169. [subclassingController forwardObjectInvocation:invocation withObject:target];
  170. AssertInvocationAssertValueEquals(invocation, int, 1337);
  171. invocation = [self _forwardingInvocationForTarget:target
  172. selector:@selector(objectProperty)
  173. controller:subclassingController];
  174. [subclassingController forwardObjectInvocation:invocation withObject:target];
  175. AssertInvocationAssertValueEquals(invocation, __unsafe_unretained id, @"Hello, World!");
  176. invocation = [self _forwardingInvocationForTarget:target
  177. selector:@selector(ivarProperty)
  178. controller:subclassingController];
  179. [subclassingController forwardObjectInvocation:invocation withObject:target];
  180. AssertInvocationAssertValueEquals(invocation, __unsafe_unretained id, target->_ivarProperty);
  181. target[@"objectProperty"] = [NSNull null];
  182. invocation = [self _forwardingInvocationForTarget:target
  183. selector:@selector(objectProperty)
  184. controller:subclassingController];
  185. [subclassingController forwardObjectInvocation:invocation withObject:target];
  186. AssertInvocationAssertValueEquals(invocation, __unsafe_unretained id, nil);
  187. invocation = [self _forwardingInvocationForTarget:target
  188. selector:@selector(relationProperty)
  189. controller:subclassingController];
  190. [subclassingController forwardObjectInvocation:invocation withObject:target];
  191. __unsafe_unretained PFRelation *returnValue = nil;
  192. [invocation getReturnValue:&returnValue];
  193. XCTAssertTrue([returnValue isKindOfClass:[PFRelation class]]);
  194. invocation = [self _forwardingInvocationForTarget:target
  195. selector:@selector(aCopyProperty)
  196. controller:subclassingController];
  197. [subclassingController forwardObjectInvocation:invocation withObject:target];
  198. __unsafe_unretained NSString *copyPropertyValue = nil;
  199. [invocation getReturnValue:&copyPropertyValue];
  200. // Ensure our mutable string is now immutable.
  201. XCTAssertThrows([(NSMutableString *)copyPropertyValue appendString:@"foo"]);
  202. invocation = [self _forwardingInvocationForTarget:target
  203. selector:@selector(badProperty)
  204. controller:subclassingController];
  205. XCTAssertThrows([subclassingController forwardObjectInvocation:invocation withObject:target]);
  206. }
  207. - (void)testForwardingSetter {
  208. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  209. [PropertySubclass registerSubclass];
  210. [subclassingController registerSubclass:[PropertySubclass class]];
  211. PropertySubclass *target = [[PropertySubclass alloc] init];
  212. id objectAgument = nil;
  213. NSInvocation *invocation = [self _forwardingInvocationForTarget:target
  214. selector:@selector(setObjectProperty:)
  215. controller:subclassingController];
  216. objectAgument = @"Hello, World!";
  217. [invocation setArgument:&objectAgument atIndex:2];
  218. [subclassingController forwardObjectInvocation:invocation withObject:target];
  219. XCTAssertEqualObjects(target[@"objectProperty"], @"Hello, World!");
  220. invocation = [self _forwardingInvocationForTarget:target
  221. selector:@selector(setPrimitiveProperty:)
  222. controller:subclassingController];
  223. [invocation setArgument:&(int) { 1337 } atIndex:2];
  224. [subclassingController forwardObjectInvocation:invocation withObject:target];
  225. XCTAssertEqualObjects(target[@"primitiveProperty"], @1337);
  226. invocation = [self _forwardingInvocationForTarget:target
  227. selector:@selector(setIvarProperty:)
  228. controller:subclassingController];
  229. objectAgument = @8675309;
  230. [invocation setArgument:&objectAgument atIndex:2];
  231. [subclassingController forwardObjectInvocation:invocation withObject:target];
  232. XCTAssertEqualObjects(target->_ivarProperty, @8675309);
  233. invocation = [self _forwardingInvocationForTarget:target
  234. selector:@selector(setObjectProperty:)
  235. controller:subclassingController];
  236. objectAgument = nil;
  237. [invocation setArgument:&objectAgument atIndex:2];
  238. [subclassingController forwardObjectInvocation:invocation withObject:target];
  239. XCTAssertEqualObjects(target[@"objectProperty"], nil);
  240. invocation = [self _forwardingInvocationForTarget:target
  241. selector:@selector(setACopyProperty:)
  242. controller:subclassingController];
  243. objectAgument = [[NSMutableString alloc] initWithString:@"Hello, World!"];
  244. [invocation setArgument:&objectAgument atIndex:2];
  245. [subclassingController forwardObjectInvocation:invocation withObject:target];
  246. XCTAssertThrows([target[@"aCopyProperty"] appendString:@"foo"]);
  247. invocation = [self _forwardingInvocationForTarget:target
  248. selector:@selector(setBadProperty:)
  249. controller:subclassingController];
  250. [invocation setArgument:&(CGPoint) { 1, 1 } atIndex:2];
  251. XCTAssertThrows([subclassingController forwardObjectInvocation:invocation withObject:target]);
  252. }
  253. - (void)testSwiftGetters {
  254. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  255. [SwiftSubclass registerSubclass];
  256. [subclassingController registerSubclass:[SwiftSubclass class]];
  257. #define AssertInvocationAssertValueEquals(invocation, type, value) ({ \
  258. type _expected = (value); \
  259. type _actual; [invocation getReturnValue:&_actual]; \
  260. XCTAssertEqual(_expected, _actual); \
  261. })
  262. SwiftSubclass *target = [[SwiftSubclass alloc] init];
  263. target[@"primitiveProperty"] = @1337;
  264. target[@"objectProperty"] = @"Hello, World!";
  265. target[@"aCopyProperty"] = [[NSMutableString alloc] initWithString:@"Hello, World!"];
  266. target[@"badProperty"] = @"Some Value";
  267. NSInvocation *invocation = [self _forwardingInvocationForTarget:target
  268. selector:@selector(primitiveProperty)
  269. controller:subclassingController];
  270. [subclassingController forwardObjectInvocation:invocation withObject:target];
  271. AssertInvocationAssertValueEquals(invocation, NSInteger, 1337);
  272. invocation = [self _forwardingInvocationForTarget:target
  273. selector:@selector(objectProperty)
  274. controller:subclassingController];
  275. [subclassingController forwardObjectInvocation:invocation withObject:target];
  276. AssertInvocationAssertValueEquals(invocation, __unsafe_unretained id, @"Hello, World!");
  277. target[@"objectProperty"] = [NSNull null];
  278. invocation = [self _forwardingInvocationForTarget:target
  279. selector:@selector(objectProperty)
  280. controller:subclassingController];
  281. [subclassingController forwardObjectInvocation:invocation withObject:target];
  282. AssertInvocationAssertValueEquals(invocation, __unsafe_unretained id, nil);
  283. invocation = [self _forwardingInvocationForTarget:target
  284. selector:@selector(relationProperty)
  285. controller:subclassingController];
  286. [subclassingController forwardObjectInvocation:invocation withObject:target];
  287. __unsafe_unretained PFRelation *returnValue = nil;
  288. [invocation getReturnValue:&returnValue];
  289. XCTAssertTrue([returnValue isKindOfClass:[PFRelation class]]);
  290. invocation = [self _forwardingInvocationForTarget:target
  291. selector:@selector(badProperty)
  292. controller:subclassingController];
  293. XCTAssertThrows([subclassingController forwardObjectInvocation:invocation withObject:target]);
  294. }
  295. - (void)testSwiftSetters {
  296. PFObjectSubclassingController *subclassingController = [[PFObjectSubclassingController alloc] init];
  297. [SwiftSubclass registerSubclass];
  298. [subclassingController registerSubclass:[SwiftSubclass class]];
  299. SwiftSubclass *target = [[SwiftSubclass alloc] init];
  300. id objectAgument = nil;
  301. NSInvocation *invocation = [self _forwardingInvocationForTarget:target
  302. selector:@selector(setObjectProperty:)
  303. controller:subclassingController];
  304. objectAgument = @"Hello, World!";
  305. [invocation setArgument:&objectAgument atIndex:2];
  306. [subclassingController forwardObjectInvocation:invocation withObject:target];
  307. XCTAssertEqualObjects(target[@"objectProperty"], @"Hello, World!");
  308. invocation = [self _forwardingInvocationForTarget:target
  309. selector:@selector(setPrimitiveProperty:)
  310. controller:subclassingController];
  311. [invocation setArgument:&(NSInteger) { 1337 } atIndex:2];
  312. [subclassingController forwardObjectInvocation:invocation withObject:target];
  313. XCTAssertEqualObjects(target[@"primitiveProperty"], @1337);
  314. invocation = [self _forwardingInvocationForTarget:target
  315. selector:@selector(setObjectProperty:)
  316. controller:subclassingController];
  317. objectAgument = nil;
  318. [invocation setArgument:&objectAgument atIndex:2];
  319. [subclassingController forwardObjectInvocation:invocation withObject:target];
  320. XCTAssertEqualObjects(target[@"objectProperty"], nil);
  321. invocation = [self _forwardingInvocationForTarget:target
  322. selector:@selector(setBadProperty:)
  323. controller:subclassingController];
  324. [invocation setArgument:&(CGPoint) { 1, 1 } atIndex:2];
  325. XCTAssertThrows([subclassingController forwardObjectInvocation:invocation withObject:target]);
  326. }
  327. @end