RACSubscriberExamples.m 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //
  2. // RACSubscriberExamples.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Justin Spahr-Summers on 2012-11-27.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Quick/Quick.h>
  9. #import <Nimble/Nimble.h>
  10. #import "RACSubscriberExamples.h"
  11. #import "NSObject+RACDeallocating.h"
  12. #import "RACCompoundDisposable.h"
  13. #import "RACDisposable.h"
  14. #import "RACSubject.h"
  15. #import "RACSubscriber.h"
  16. NSString * const RACSubscriberExamples = @"RACSubscriberExamples";
  17. NSString * const RACSubscriberExampleSubscriber = @"RACSubscriberExampleSubscriber";
  18. NSString * const RACSubscriberExampleValuesReceivedBlock = @"RACSubscriberExampleValuesReceivedBlock";
  19. NSString * const RACSubscriberExampleErrorReceivedBlock = @"RACSubscriberExampleErrorReceivedBlock";
  20. NSString * const RACSubscriberExampleSuccessBlock = @"RACSubscriberExampleSuccessBlock";
  21. QuickConfigurationBegin(RACSubscriberExampleGroups)
  22. + (void)configure:(Configuration *)configuration {
  23. sharedExamples(RACSubscriberExamples, ^(QCKDSLSharedExampleContext exampleContext) {
  24. __block NSArray * (^valuesReceived)(void);
  25. __block NSError * (^errorReceived)(void);
  26. __block BOOL (^success)(void);
  27. __block id<RACSubscriber> subscriber;
  28. qck_beforeEach(^{
  29. valuesReceived = exampleContext()[RACSubscriberExampleValuesReceivedBlock];
  30. errorReceived = exampleContext()[RACSubscriberExampleErrorReceivedBlock];
  31. success = exampleContext()[RACSubscriberExampleSuccessBlock];
  32. subscriber = exampleContext()[RACSubscriberExampleSubscriber];
  33. expect(subscriber).notTo(beNil());
  34. });
  35. qck_it(@"should accept a nil error", ^{
  36. [subscriber sendError:nil];
  37. expect(@(success())).to(beFalsy());
  38. expect(errorReceived()).to(beNil());
  39. expect(valuesReceived()).to(equal(@[]));
  40. });
  41. qck_describe(@"with values", ^{
  42. __block NSSet *values;
  43. qck_beforeEach(^{
  44. NSMutableSet *mutableValues = [NSMutableSet set];
  45. for (NSUInteger i = 0; i < 20; i++) {
  46. [mutableValues addObject:@(i)];
  47. }
  48. values = [mutableValues copy];
  49. });
  50. qck_it(@"should send nexts serially, even when delivered from multiple threads", ^{
  51. NSArray *allValues = values.allObjects;
  52. dispatch_apply(allValues.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [^(size_t index) {
  53. [subscriber sendNext:allValues[index]];
  54. } copy]);
  55. expect(@(success())).to(beTruthy());
  56. expect(errorReceived()).to(beNil());
  57. NSSet *valuesReceivedSet = [NSSet setWithArray:valuesReceived()];
  58. expect(valuesReceivedSet).to(equal(values));
  59. });
  60. });
  61. qck_describe(@"multiple subscriptions", ^{
  62. __block RACSubject *first;
  63. __block RACSubject *second;
  64. qck_beforeEach(^{
  65. first = [RACSubject subject];
  66. [first subscribe:subscriber];
  67. second = [RACSubject subject];
  68. [second subscribe:subscriber];
  69. });
  70. qck_it(@"should send values from all subscriptions", ^{
  71. [first sendNext:@"foo"];
  72. [second sendNext:@"bar"];
  73. [first sendNext:@"buzz"];
  74. [second sendNext:@"baz"];
  75. expect(@(success())).to(beTruthy());
  76. expect(errorReceived()).to(beNil());
  77. NSArray *expected = @[ @"foo", @"bar", @"buzz", @"baz" ];
  78. expect(valuesReceived()).to(equal(expected));
  79. });
  80. qck_it(@"should terminate after the first error from any subscription", ^{
  81. NSError *error = [NSError errorWithDomain:@"" code:-1 userInfo:nil];
  82. [first sendNext:@"foo"];
  83. [second sendError:error];
  84. [first sendNext:@"buzz"];
  85. expect(@(success())).to(beFalsy());
  86. expect(errorReceived()).to(equal(error));
  87. NSArray *expected = @[ @"foo" ];
  88. expect(valuesReceived()).to(equal(expected));
  89. });
  90. qck_it(@"should terminate after the first completed from any subscription", ^{
  91. [first sendNext:@"foo"];
  92. [second sendNext:@"bar"];
  93. [first sendCompleted];
  94. [second sendNext:@"baz"];
  95. expect(@(success())).to(beTruthy());
  96. expect(errorReceived()).to(beNil());
  97. NSArray *expected = @[ @"foo", @"bar" ];
  98. expect(valuesReceived()).to(equal(expected));
  99. });
  100. qck_it(@"should dispose of all current subscriptions upon termination", ^{
  101. __block BOOL firstDisposed = NO;
  102. RACSignal *firstDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  103. return [RACDisposable disposableWithBlock:^{
  104. firstDisposed = YES;
  105. }];
  106. }];
  107. __block BOOL secondDisposed = NO;
  108. RACSignal *secondDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  109. return [RACDisposable disposableWithBlock:^{
  110. secondDisposed = YES;
  111. }];
  112. }];
  113. [firstDisposableSignal subscribe:subscriber];
  114. [secondDisposableSignal subscribe:subscriber];
  115. expect(@(firstDisposed)).to(beFalsy());
  116. expect(@(secondDisposed)).to(beFalsy());
  117. [first sendCompleted];
  118. expect(@(firstDisposed)).to(beTruthy());
  119. expect(@(secondDisposed)).to(beTruthy());
  120. });
  121. qck_it(@"should dispose of future subscriptions upon termination", ^{
  122. __block BOOL disposed = NO;
  123. RACSignal *disposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  124. return [RACDisposable disposableWithBlock:^{
  125. disposed = YES;
  126. }];
  127. }];
  128. [first sendCompleted];
  129. expect(@(disposed)).to(beFalsy());
  130. [disposableSignal subscribe:subscriber];
  131. expect(@(disposed)).to(beTruthy());
  132. });
  133. });
  134. qck_describe(@"memory management", ^{
  135. qck_it(@"should not retain disposed disposables", ^{
  136. __block BOOL disposableDeallocd = NO;
  137. @autoreleasepool {
  138. RACCompoundDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACCompoundDisposable disposableWithBlock:^{}];
  139. [disposable.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
  140. disposableDeallocd = YES;
  141. }]];
  142. [subscriber didSubscribeWithDisposable:disposable];
  143. [disposable dispose];
  144. }
  145. expect(@(disposableDeallocd)).to(beTruthy());
  146. });
  147. });
  148. });
  149. }
  150. QuickConfigurationEnd