RACMulticastConnectionSpec.m 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. //
  2. // RACMulticastConnectionSpec.m
  3. // ReactiveCocoa
  4. //
  5. // Created by Josh Abernathy on 10/8/12.
  6. // Copyright (c) 2012 GitHub, Inc. All rights reserved.
  7. //
  8. #import <Quick/Quick.h>
  9. #import <Nimble/Nimble.h>
  10. #import "RACMulticastConnection.h"
  11. #import "RACDisposable.h"
  12. #import "RACSignal+Operations.h"
  13. #import "RACSubscriber.h"
  14. #import "RACReplaySubject.h"
  15. #import "RACScheduler.h"
  16. #import <libkern/OSAtomic.h>
  17. QuickSpecBegin(RACMulticastConnectionSpec)
  18. __block NSUInteger subscriptionCount = 0;
  19. __block RACMulticastConnection *connection;
  20. qck_beforeEach(^{
  21. subscriptionCount = 0;
  22. connection = [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  23. subscriptionCount++;
  24. return (RACDisposable *)nil;
  25. }] publish];
  26. expect(@(subscriptionCount)).to(equal(@0));
  27. });
  28. qck_describe(@"-connect", ^{
  29. qck_it(@"should subscribe to the underlying signal", ^{
  30. [connection connect];
  31. expect(@(subscriptionCount)).to(equal(@1));
  32. });
  33. qck_it(@"should return the same disposable for each invocation", ^{
  34. RACDisposable *d1 = [connection connect];
  35. RACDisposable *d2 = [connection connect];
  36. expect(d1).to(equal(d2));
  37. expect(@(subscriptionCount)).to(equal(@1));
  38. });
  39. qck_it(@"shouldn't reconnect after disposal", ^{
  40. RACDisposable *disposable1 = [connection connect];
  41. expect(@(subscriptionCount)).to(equal(@1));
  42. [disposable1 dispose];
  43. RACDisposable *disposable2 = [connection connect];
  44. expect(@(subscriptionCount)).to(equal(@1));
  45. expect(disposable1).to(equal(disposable2));
  46. });
  47. qck_it(@"shouldn't race when connecting", ^{
  48. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  49. RACMulticastConnection *connection = [[RACSignal
  50. defer:^ id {
  51. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  52. return nil;
  53. }]
  54. publish];
  55. __block RACDisposable *disposable;
  56. [RACScheduler.scheduler schedule:^{
  57. disposable = [connection connect];
  58. dispatch_semaphore_signal(semaphore);
  59. }];
  60. expect([connection connect]).notTo(beNil());
  61. dispatch_semaphore_signal(semaphore);
  62. expect(disposable).toEventuallyNot(beNil());
  63. });
  64. });
  65. qck_describe(@"-autoconnect", ^{
  66. __block RACSignal *autoconnectedSignal;
  67. qck_beforeEach(^{
  68. autoconnectedSignal = [connection autoconnect];
  69. });
  70. qck_it(@"should subscribe to the multicasted signal on the first subscription", ^{
  71. expect(@(subscriptionCount)).to(equal(@0));
  72. [autoconnectedSignal subscribeNext:^(id x) {}];
  73. expect(@(subscriptionCount)).to(equal(@1));
  74. [autoconnectedSignal subscribeNext:^(id x) {}];
  75. expect(@(subscriptionCount)).to(equal(@1));
  76. });
  77. qck_it(@"should dispose of the multicasted subscription when the signal has no subscribers", ^{
  78. __block BOOL disposed = NO;
  79. __block id<RACSubscriber> connectionSubscriber = nil;
  80. RACSignal *signal = [[[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
  81. // Keep the subscriber alive so it doesn't trigger disposal on dealloc
  82. connectionSubscriber = subscriber;
  83. subscriptionCount++;
  84. return [RACDisposable disposableWithBlock:^{
  85. disposed = YES;
  86. }];
  87. }] publish] autoconnect];
  88. RACDisposable *disposable = [signal subscribeNext:^(id x) {}];
  89. expect(@(disposed)).to(beFalsy());
  90. [disposable dispose];
  91. expect(@(disposed)).to(beTruthy());
  92. });
  93. qck_it(@"shouldn't reconnect after disposal", ^{
  94. RACDisposable *disposable = [autoconnectedSignal subscribeNext:^(id x) {}];
  95. expect(@(subscriptionCount)).to(equal(@1));
  96. [disposable dispose];
  97. disposable = [autoconnectedSignal subscribeNext:^(id x) {}];
  98. expect(@(subscriptionCount)).to(equal(@1));
  99. [disposable dispose];
  100. });
  101. qck_it(@"should replay values after disposal when multicasted to a replay subject", ^{
  102. RACSubject *subject = [RACSubject subject];
  103. RACSignal *signal = [[subject multicast:[RACReplaySubject subject]] autoconnect];
  104. NSMutableArray *results1 = [NSMutableArray array];
  105. RACDisposable *disposable = [signal subscribeNext:^(id x) {
  106. [results1 addObject:x];
  107. }];
  108. [subject sendNext:@1];
  109. [subject sendNext:@2];
  110. expect(results1).to(equal((@[ @1, @2 ])));
  111. [disposable dispose];
  112. NSMutableArray *results2 = [NSMutableArray array];
  113. [signal subscribeNext:^(id x) {
  114. [results2 addObject:x];
  115. }];
  116. expect(results2).toEventually(equal((@[ @1, @2 ])));
  117. });
  118. });
  119. QuickSpecEnd