RACSubjectSpec.m 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. //
  2. // RACSubjectSpec.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Josh Abernathy on 6/24/12.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. @import Quick;
  9. @import Nimble;
  10. #import "RACSubscriberExamples.h"
  11. #import <libkern/OSAtomic.h>
  12. #import <ReactiveCocoa/EXTScope.h>
  13. #import "RACBehaviorSubject.h"
  14. #import "RACCompoundDisposable.h"
  15. #import "RACDisposable.h"
  16. #import "RACReplaySubject.h"
  17. #import "RACScheduler.h"
  18. #import "RACSignal+Operations.h"
  19. #import "RACSubject.h"
  20. #import "RACUnit.h"
  21. @interface RACTestSubscriber : NSObject <RACSubscriber>
  22. @property (nonatomic, strong, readonly) RACDisposable *disposable;
  23. @end
  24. @implementation RACTestSubscriber
  25. - (instancetype)init {
  26. self = [super init];
  27. _disposable = [RACDisposable new];
  28. return self;
  29. }
  30. - (void)sendNext:(id)value {}
  31. - (void)sendError:(NSError *)error {}
  32. - (void)sendCompleted {}
  33. - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable {
  34. [disposable addDisposable:self.disposable];
  35. }
  36. @end
  37. QuickSpecBegin(RACSubjectSpec)
  38. qck_describe(@"RACSubject", ^{
  39. __block RACSubject *subject;
  40. __block NSMutableArray *values;
  41. __block BOOL success;
  42. __block NSError *error;
  43. qck_beforeEach(^{
  44. values = [NSMutableArray array];
  45. subject = [RACSubject subject];
  46. success = YES;
  47. error = nil;
  48. [subject subscribeNext:^(id value) {
  49. [values addObject:value];
  50. } error:^(NSError *e) {
  51. error = e;
  52. success = NO;
  53. } completed:^{
  54. success = YES;
  55. }];
  56. });
  57. qck_it(@"should dispose the paired disposable when a subscription terminates", ^{
  58. RACSubject* subject = [RACSubject new];
  59. RACTestSubscriber* subscriber = [RACTestSubscriber new];
  60. [[subject subscribe:subscriber] dispose];
  61. expect(@(subscriber.disposable.disposed)).to(beTruthy());
  62. });
  63. qck_itBehavesLike(RACSubscriberExamples, ^{
  64. return @{
  65. RACSubscriberExampleSubscriber: subject,
  66. RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy],
  67. RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy],
  68. RACSubscriberExampleSuccessBlock: [^{ return success; } copy]
  69. };
  70. });
  71. });
  72. qck_describe(@"RACReplaySubject", ^{
  73. __block RACReplaySubject *subject = nil;
  74. qck_describe(@"with a capacity of 1", ^{
  75. qck_beforeEach(^{
  76. subject = [RACReplaySubject replaySubjectWithCapacity:1];
  77. });
  78. qck_it(@"should send the last value", ^{
  79. id firstValue = @"blah";
  80. id secondValue = @"more blah";
  81. [subject sendNext:firstValue];
  82. [subject sendNext:secondValue];
  83. __block id valueReceived = nil;
  84. [subject subscribeNext:^(id x) {
  85. valueReceived = x;
  86. }];
  87. expect(valueReceived).to(equal(secondValue));
  88. });
  89. qck_it(@"should send the last value to new subscribers after completion", ^{
  90. id firstValue = @"blah";
  91. id secondValue = @"more blah";
  92. __block id valueReceived = nil;
  93. __block NSUInteger nextsReceived = 0;
  94. [subject sendNext:firstValue];
  95. [subject sendNext:secondValue];
  96. expect(@(nextsReceived)).to(equal(@0));
  97. expect(valueReceived).to(beNil());
  98. [subject sendCompleted];
  99. [subject subscribeNext:^(id x) {
  100. valueReceived = x;
  101. nextsReceived++;
  102. }];
  103. expect(@(nextsReceived)).to(equal(@1));
  104. expect(valueReceived).to(equal(secondValue));
  105. });
  106. qck_it(@"should not send any values to new subscribers if none were sent originally", ^{
  107. [subject sendCompleted];
  108. __block BOOL nextInvoked = NO;
  109. [subject subscribeNext:^(id x) {
  110. nextInvoked = YES;
  111. }];
  112. expect(@(nextInvoked)).to(beFalsy());
  113. });
  114. qck_it(@"should resend errors", ^{
  115. NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain code:0 userInfo:nil];
  116. [subject sendError:error];
  117. __block BOOL errorSent = NO;
  118. [subject subscribeError:^(NSError *sentError) {
  119. expect(sentError).to(equal(error));
  120. errorSent = YES;
  121. }];
  122. expect(@(errorSent)).to(beTruthy());
  123. });
  124. qck_it(@"should resend nil errors", ^{
  125. [subject sendError:nil];
  126. __block BOOL errorSent = NO;
  127. [subject subscribeError:^(NSError *sentError) {
  128. expect(sentError).to(beNil());
  129. errorSent = YES;
  130. }];
  131. expect(@(errorSent)).to(beTruthy());
  132. });
  133. });
  134. qck_describe(@"with an unlimited capacity", ^{
  135. qck_beforeEach(^{
  136. subject = [RACReplaySubject subject];
  137. });
  138. qck_itBehavesLike(RACSubscriberExamples, ^{
  139. return @{
  140. RACSubscriberExampleSubscriber: subject,
  141. RACSubscriberExampleValuesReceivedBlock: [^{
  142. NSMutableArray *values = [NSMutableArray array];
  143. // This subscription should synchronously dump all values already
  144. // received into 'values'.
  145. [subject subscribeNext:^(id value) {
  146. [values addObject:value];
  147. }];
  148. return values;
  149. } copy],
  150. RACSubscriberExampleErrorReceivedBlock: [^{
  151. __block NSError *error = nil;
  152. [subject subscribeError:^(NSError *x) {
  153. error = x;
  154. }];
  155. return error;
  156. } copy],
  157. RACSubscriberExampleSuccessBlock: [^{
  158. __block BOOL success = YES;
  159. [subject subscribeError:^(NSError *x) {
  160. success = NO;
  161. }];
  162. return success;
  163. } copy]
  164. };
  165. });
  166. qck_it(@"should send both values to new subscribers after completion", ^{
  167. id firstValue = @"blah";
  168. id secondValue = @"more blah";
  169. [subject sendNext:firstValue];
  170. [subject sendNext:secondValue];
  171. [subject sendCompleted];
  172. __block BOOL completed = NO;
  173. NSMutableArray *valuesReceived = [NSMutableArray array];
  174. [subject subscribeNext:^(id x) {
  175. [valuesReceived addObject:x];
  176. } completed:^{
  177. completed = YES;
  178. }];
  179. expect(valuesReceived).to(haveCount(@2));
  180. NSArray *expected = [NSArray arrayWithObjects:firstValue, secondValue, nil];
  181. expect(valuesReceived).to(equal(expected));
  182. expect(@(completed)).to(beTruthy());
  183. });
  184. qck_it(@"should send values in the same order live as when replaying", ^{
  185. NSUInteger count = 49317;
  186. // Just leak it, ain't no thang.
  187. __unsafe_unretained volatile id *values = (__unsafe_unretained id *)calloc(count, sizeof(*values));
  188. __block volatile int32_t nextIndex = 0;
  189. [subject subscribeNext:^(NSNumber *value) {
  190. int32_t indexPlusOne = OSAtomicIncrement32(&nextIndex);
  191. values[indexPlusOne - 1] = value;
  192. }];
  193. dispatch_queue_t queue = dispatch_queue_create("org.reactivecocoa.ReactiveCocoa.RACSubjectSpec", DISPATCH_QUEUE_CONCURRENT);
  194. dispatch_suspend(queue);
  195. for (NSUInteger i = 0; i < count; i++) {
  196. dispatch_async(queue, ^{
  197. [subject sendNext:@(i)];
  198. });
  199. }
  200. dispatch_resume(queue);
  201. dispatch_barrier_sync(queue, ^{
  202. [subject sendCompleted];
  203. });
  204. OSMemoryBarrier();
  205. NSArray *liveValues = [NSArray arrayWithObjects:(id *)values count:(NSUInteger)nextIndex];
  206. expect(liveValues).to(haveCount(@(count)));
  207. NSArray *replayedValues = subject.toArray;
  208. expect(replayedValues).to(haveCount(@(count)));
  209. // It should return the same ordering for multiple invocations too.
  210. expect(replayedValues).to(equal(subject.toArray));
  211. [replayedValues enumerateObjectsUsingBlock:^(id value, NSUInteger index, BOOL *stop) {
  212. expect(liveValues[index]).to(equal(value));
  213. }];
  214. });
  215. qck_it(@"should have a current scheduler when replaying", ^{
  216. [subject sendNext:RACUnit.defaultUnit];
  217. __block RACScheduler *currentScheduler;
  218. [subject subscribeNext:^(id x) {
  219. currentScheduler = RACScheduler.currentScheduler;
  220. }];
  221. expect(currentScheduler).notTo(beNil());
  222. currentScheduler = nil;
  223. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  224. [subject subscribeNext:^(id x) {
  225. currentScheduler = RACScheduler.currentScheduler;
  226. }];
  227. });
  228. expect(currentScheduler).toEventuallyNot(beNil());
  229. });
  230. qck_it(@"should stop replaying when the subscription is disposed", ^{
  231. NSMutableArray *values = [NSMutableArray array];
  232. [subject sendNext:@0];
  233. [subject sendNext:@1];
  234. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  235. __block RACDisposable *disposable = [subject subscribeNext:^(id x) {
  236. expect(disposable).notTo(beNil());
  237. [values addObject:x];
  238. [disposable dispose];
  239. }];
  240. });
  241. expect(values).toEventually(equal(@[ @0 ]));
  242. });
  243. qck_it(@"should finish replaying before completing", ^{
  244. [subject sendNext:@1];
  245. __block id received;
  246. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  247. [subject subscribeNext:^(id x) {
  248. received = x;
  249. }];
  250. [subject sendCompleted];
  251. });
  252. expect(received).toEventually(equal(@1));
  253. });
  254. qck_it(@"should finish replaying before erroring", ^{
  255. [subject sendNext:@1];
  256. __block id received;
  257. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  258. [subject subscribeNext:^(id x) {
  259. received = x;
  260. }];
  261. [subject sendError:[NSError errorWithDomain:@"blah" code:-99 userInfo:nil]];
  262. });
  263. expect(received).toEventually(equal(@1));
  264. });
  265. qck_it(@"should finish replaying before sending new values", ^{
  266. [subject sendNext:@1];
  267. NSMutableArray *received = [NSMutableArray array];
  268. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  269. [subject subscribeNext:^(id x) {
  270. [received addObject:x];
  271. }];
  272. [subject sendNext:@2];
  273. });
  274. NSArray *expected = @[ @1, @2 ];
  275. expect(received).toEventually(equal(expected));
  276. });
  277. });
  278. });
  279. QuickSpecEnd