NSObjectRACLiftingSpec.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. //
  2. // NSObjectRACLifting.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Josh Abernathy on 10/2/12.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Quick/Quick.h>
  9. #import <Nimble/Nimble.h>
  10. #import "RACTestObject.h"
  11. #import "NSObject+RACLifting.h"
  12. #import "NSObject+RACDeallocating.h"
  13. #import "NSObject+RACPropertySubscribing.h"
  14. #import "RACCompoundDisposable.h"
  15. #import "RACDisposable.h"
  16. #import "RACSubject.h"
  17. #import "RACTuple.h"
  18. #import "RACUnit.h"
  19. QuickSpecBegin(NSObjectRACLiftingSpec)
  20. qck_describe(@"-rac_liftSelector:withSignals:", ^{
  21. __block RACTestObject *object;
  22. qck_beforeEach(^{
  23. object = [[RACTestObject alloc] init];
  24. });
  25. qck_it(@"should call the selector with the value of the signal", ^{
  26. RACSubject *subject = [RACSubject subject];
  27. [object rac_liftSelector:@selector(setObjectValue:) withSignals:subject, nil];
  28. expect(object.objectValue).to(beNil());
  29. [subject sendNext:@1];
  30. expect(object.objectValue).to(equal(@1));
  31. [subject sendNext:@42];
  32. expect(object.objectValue).to(equal(@42));
  33. });
  34. });
  35. qck_describe(@"-rac_liftSelector:withSignalsFromArray:", ^{
  36. __block RACTestObject *object;
  37. qck_beforeEach(^{
  38. object = [[RACTestObject alloc] init];
  39. });
  40. qck_it(@"should call the selector with the value of the signal", ^{
  41. RACSubject *subject = [RACSubject subject];
  42. [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
  43. expect(object.objectValue).to(beNil());
  44. [subject sendNext:@1];
  45. expect(object.objectValue).to(equal(@1));
  46. [subject sendNext:@42];
  47. expect(object.objectValue).to(equal(@42));
  48. });
  49. qck_it(@"should call the selector with the value of the signal unboxed", ^{
  50. RACSubject *subject = [RACSubject subject];
  51. [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
  52. expect(@(object.integerValue)).to(equal(@0));
  53. [subject sendNext:@1];
  54. expect(@(object.integerValue)).to(equal(@1));
  55. [subject sendNext:@42];
  56. expect(@(object.integerValue)).to(equal(@42));
  57. });
  58. qck_it(@"should work with multiple arguments", ^{
  59. RACSubject *objectValueSubject = [RACSubject subject];
  60. RACSubject *integerValueSubject = [RACSubject subject];
  61. [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectValueSubject, integerValueSubject ]];
  62. expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beFalsy());
  63. expect(object.objectValue).to(beNil());
  64. expect(@(object.integerValue)).to(equal(@0));
  65. [objectValueSubject sendNext:@1];
  66. expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beFalsy());
  67. expect(object.objectValue).to(beNil());
  68. expect(@(object.integerValue)).to(equal(@0));
  69. [integerValueSubject sendNext:@42];
  70. expect(@(object.hasInvokedSetObjectValueAndIntegerValue)).to(beTruthy());
  71. expect(object.objectValue).to(equal(@1));
  72. expect(@(object.integerValue)).to(equal(@42));
  73. });
  74. qck_it(@"should work with signals that immediately start with a value", ^{
  75. RACSubject *subject = [RACSubject subject];
  76. [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ [subject startWith:@42] ]];
  77. expect(object.objectValue).to(equal(@42));
  78. [subject sendNext:@1];
  79. expect(object.objectValue).to(equal(@1));
  80. });
  81. qck_it(@"should work with signals that send nil", ^{
  82. RACSubject *subject = [RACSubject subject];
  83. [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
  84. [subject sendNext:nil];
  85. expect(object.objectValue).to(beNil());
  86. [subject sendNext:RACTupleNil.tupleNil];
  87. expect(object.objectValue).to(beNil());
  88. });
  89. qck_it(@"should work with integers", ^{
  90. RACSubject *subject = [RACSubject subject];
  91. [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
  92. expect(@(object.integerValue)).to(equal(@0));
  93. [subject sendNext:@1];
  94. expect(@(object.integerValue)).to(equal(@1));
  95. });
  96. qck_it(@"should convert between numeric types", ^{
  97. RACSubject *subject = [RACSubject subject];
  98. [object rac_liftSelector:@selector(setIntegerValue:) withSignalsFromArray:@[ subject ]];
  99. expect(@(object.integerValue)).to(equal(@0));
  100. [subject sendNext:@1.0];
  101. expect(@(object.integerValue)).to(equal(@1));
  102. });
  103. qck_it(@"should work with class objects", ^{
  104. RACSubject *subject = [RACSubject subject];
  105. [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
  106. expect(object.objectValue).to(beNil());
  107. [subject sendNext:self.class];
  108. expect(object.objectValue).to(equal(self.class));
  109. });
  110. qck_it(@"should work for char pointer", ^{
  111. RACSubject *subject = [RACSubject subject];
  112. [object rac_liftSelector:@selector(setCharPointerValue:) withSignalsFromArray:@[ subject ]];
  113. expect(@((size_t)object.charPointerValue)).to(equal(@0));
  114. NSString *string = @"blah blah blah";
  115. [subject sendNext:string];
  116. expect(@(object.charPointerValue)).to(equal(string));
  117. });
  118. qck_it(@"should work for const char pointer", ^{
  119. RACSubject *subject = [RACSubject subject];
  120. [object rac_liftSelector:@selector(setConstCharPointerValue:) withSignalsFromArray:@[ subject ]];
  121. expect(@((size_t)object.constCharPointerValue)).to(equal(@0));
  122. NSString *string = @"blah blah blah";
  123. [subject sendNext:string];
  124. expect(@(object.constCharPointerValue)).to(equal(string));
  125. });
  126. qck_it(@"should work for CGRect", ^{
  127. RACSubject *subject = [RACSubject subject];
  128. [object rac_liftSelector:@selector(setRectValue:) withSignalsFromArray:@[ subject ]];
  129. expect(@(CGRectEqualToRect(object.rectValue, CGRectZero))).to(beTruthy());
  130. CGRect value = CGRectMake(10, 20, 30, 40);
  131. [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGRect)]];
  132. expect(@(CGRectEqualToRect(object.rectValue, value))).to(beTruthy());
  133. });
  134. qck_it(@"should work for CGSize", ^{
  135. RACSubject *subject = [RACSubject subject];
  136. [object rac_liftSelector:@selector(setSizeValue:) withSignalsFromArray:@[ subject ]];
  137. expect(@(CGSizeEqualToSize(object.sizeValue, CGSizeZero))).to(beTruthy());
  138. CGSize value = CGSizeMake(10, 20);
  139. [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGSize)]];
  140. expect(@(CGSizeEqualToSize(object.sizeValue, value))).to(beTruthy());
  141. });
  142. qck_it(@"should work for CGPoint", ^{
  143. RACSubject *subject = [RACSubject subject];
  144. [object rac_liftSelector:@selector(setPointValue:) withSignalsFromArray:@[ subject ]];
  145. expect(@(CGPointEqualToPoint(object.pointValue, CGPointZero))).to(beTruthy());
  146. CGPoint value = CGPointMake(10, 20);
  147. [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(CGPoint)]];
  148. expect(@(CGPointEqualToPoint(object.pointValue, value))).to(beTruthy());
  149. });
  150. qck_it(@"should work for NSRange", ^{
  151. RACSubject *subject = [RACSubject subject];
  152. [object rac_liftSelector:@selector(setRangeValue:) withSignalsFromArray:@[ subject ]];
  153. expect(@(NSEqualRanges(object.rangeValue, NSMakeRange(0, 0)))).to(beTruthy());
  154. NSRange value = NSMakeRange(10, 20);
  155. [subject sendNext:[NSValue valueWithRange:value]];
  156. expect(@(NSEqualRanges(object.rangeValue, value))).to(beTruthy());
  157. });
  158. qck_it(@"should work for _Bool", ^{
  159. RACSubject *subject = [RACSubject subject];
  160. [object rac_liftSelector:@selector(setC99BoolValue:) withSignalsFromArray:@[ subject ]];
  161. expect(@(object.c99BoolValue)).to(beFalsy());
  162. _Bool value = true;
  163. [subject sendNext:@(value)];
  164. expect(@(object.c99BoolValue)).to(beTruthy());
  165. });
  166. qck_it(@"should work for primitive pointers", ^{
  167. RACSubject *subject = [RACSubject subject];
  168. [object rac_liftSelector:@selector(write5ToIntPointer:) withSignalsFromArray:@[ subject ]];
  169. int value = 0;
  170. int *valuePointer = &value;
  171. expect(@(value)).to(equal(@0));
  172. [subject sendNext:[NSValue valueWithPointer:valuePointer]];
  173. expect(@(value)).to(equal(@5));
  174. });
  175. qck_it(@"should work for custom structs", ^{
  176. RACSubject *subject = [RACSubject subject];
  177. [object rac_liftSelector:@selector(setStructValue:) withSignalsFromArray:@[ subject ]];
  178. expect(@(object.structValue.integerField)).to(equal(@0));
  179. expect(@(object.structValue.doubleField)).to(equal(@0.0));
  180. RACTestStruct value = (RACTestStruct){7, 1.23};
  181. [subject sendNext:[NSValue valueWithBytes:&value objCType:@encode(typeof(value))]];
  182. expect(@(object.structValue.integerField)).to(equal(@(value.integerField)));
  183. expect(@(object.structValue.doubleField)).to(equal(@(value.doubleField)));
  184. });
  185. qck_it(@"should send the latest value of the signal as the right argument", ^{
  186. RACSubject *subject = [RACSubject subject];
  187. [object rac_liftSelector:@selector(setObjectValue:andIntegerValue:) withSignalsFromArray:@[ [RACSignal return:@"object"], subject ]];
  188. [subject sendNext:@1];
  189. expect(object.objectValue).to(equal(@"object"));
  190. expect(@(object.integerValue)).to(equal(@1));
  191. });
  192. qck_describe(@"the returned signal", ^{
  193. qck_it(@"should send the return value of the method invocation", ^{
  194. RACSubject *objectSubject = [RACSubject subject];
  195. RACSubject *integerSubject = [RACSubject subject];
  196. RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]];
  197. __block NSString *result;
  198. [signal subscribeNext:^(id x) {
  199. result = x;
  200. }];
  201. [objectSubject sendNext:@"Magic number"];
  202. expect(result).to(beNil());
  203. [integerSubject sendNext:@42];
  204. expect(result).to(equal(@"Magic number: 42"));
  205. });
  206. qck_it(@"should send RACUnit.defaultUnit for void-returning methods", ^{
  207. RACSubject *subject = [RACSubject subject];
  208. RACSignal *signal = [object rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
  209. __block id result;
  210. [signal subscribeNext:^(id x) {
  211. result = x;
  212. }];
  213. [subject sendNext:@1];
  214. expect(result).to(equal(RACUnit.defaultUnit));
  215. });
  216. qck_it(@"should support integer returning methods", ^{
  217. RACSubject *subject = [RACSubject subject];
  218. RACSignal *signal = [object rac_liftSelector:@selector(doubleInteger:) withSignalsFromArray:@[ subject ]];
  219. __block id result;
  220. [signal subscribeNext:^(id x) {
  221. result = x;
  222. }];
  223. [subject sendNext:@1];
  224. expect(result).to(equal(@2));
  225. });
  226. qck_it(@"should support char * returning methods", ^{
  227. RACSubject *subject = [RACSubject subject];
  228. RACSignal *signal = [object rac_liftSelector:@selector(doubleString:) withSignalsFromArray:@[ subject ]];
  229. __block id result;
  230. [signal subscribeNext:^(id x) {
  231. result = x;
  232. }];
  233. [subject sendNext:@"test"];
  234. expect(result).to(equal(@"testtest"));
  235. });
  236. qck_it(@"should support const char * returning methods", ^{
  237. RACSubject *subject = [RACSubject subject];
  238. RACSignal *signal = [object rac_liftSelector:@selector(doubleConstString:) withSignalsFromArray:@[ subject ]];
  239. __block id result;
  240. [signal subscribeNext:^(id x) {
  241. result = x;
  242. }];
  243. [subject sendNext:@"test"];
  244. expect(result).to(equal(@"testtest"));
  245. });
  246. qck_it(@"should support struct returning methods", ^{
  247. RACSubject *subject = [RACSubject subject];
  248. RACSignal *signal = [object rac_liftSelector:@selector(doubleStruct:) withSignalsFromArray:@[ subject ]];
  249. __block NSValue *boxedResult;
  250. [signal subscribeNext:^(id x) {
  251. boxedResult = x;
  252. }];
  253. RACTestStruct value = {4, 12.3};
  254. NSValue *boxedValue = [NSValue valueWithBytes:&value objCType:@encode(typeof(value))];
  255. [subject sendNext:boxedValue];
  256. RACTestStruct result = {0, 0.0};
  257. [boxedResult getValue:&result];
  258. expect(@(result.integerField)).to(equal(@8));
  259. expect(@(result.doubleField)).to(equal(@24.6));
  260. });
  261. qck_it(@"should support block arguments and returns", ^{
  262. RACSubject *subject = [RACSubject subject];
  263. RACSignal *signal = [object rac_liftSelector:@selector(wrapBlock:) withSignalsFromArray:@[ subject ]];
  264. __block BOOL blockInvoked = NO;
  265. dispatch_block_t testBlock = ^{
  266. blockInvoked = YES;
  267. };
  268. __block dispatch_block_t result;
  269. [signal subscribeNext:^(id x) {
  270. result = x;
  271. }];
  272. [subject sendNext:testBlock];
  273. expect(result).notTo(beNil());
  274. result();
  275. expect(@(blockInvoked)).to(beTruthy());
  276. });
  277. qck_it(@"should replay the last value", ^{
  278. RACSubject *objectSubject = [RACSubject subject];
  279. RACSubject *integerSubject = [RACSubject subject];
  280. RACSignal *signal = [object rac_liftSelector:@selector(combineObjectValue:andIntegerValue:) withSignalsFromArray:@[ objectSubject, integerSubject ]];
  281. [objectSubject sendNext:@"Magic number"];
  282. [integerSubject sendNext:@42];
  283. [integerSubject sendNext:@43];
  284. __block NSString *result;
  285. [signal subscribeNext:^(id x) {
  286. result = x;
  287. }];
  288. expect(result).to(equal(@"Magic number: 43"));
  289. });
  290. });
  291. qck_it(@"shouldn't strongly capture the receiver", ^{
  292. __block BOOL dealloced = NO;
  293. @autoreleasepool {
  294. RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
  295. [testObject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  296. dealloced = YES;
  297. }]];
  298. RACSubject *subject = [RACSubject subject];
  299. [testObject rac_liftSelector:@selector(setObjectValue:) withSignalsFromArray:@[ subject ]];
  300. [subject sendNext:@1];
  301. }
  302. expect(@(dealloced)).to(beTruthy());
  303. });
  304. });
  305. QuickSpecEnd