blob: 946a3070f82e4e6a195bf2feb4c18e79a87bcd40 [file] [log] [blame] [edit]
//
// RACSubscriberExamples.m
// ReactiveCocoa
//
// Created by Justin Spahr-Summers on 2012-11-27.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "RACSubscriberExamples.h"
#import "NSObject+RACDeallocating.h"
#import "RACCompoundDisposable.h"
#import "RACDisposable.h"
#import "RACSubject.h"
#import "RACSubscriber.h"
NSString * const RACSubscriberExamples = @"RACSubscriberExamples";
NSString * const RACSubscriberExampleSubscriber = @"RACSubscriberExampleSubscriber";
NSString * const RACSubscriberExampleValuesReceivedBlock = @"RACSubscriberExampleValuesReceivedBlock";
NSString * const RACSubscriberExampleErrorReceivedBlock = @"RACSubscriberExampleErrorReceivedBlock";
NSString * const RACSubscriberExampleSuccessBlock = @"RACSubscriberExampleSuccessBlock";
SharedExampleGroupsBegin(RACSubscriberExamples)
sharedExamplesFor(RACSubscriberExamples, ^(NSDictionary *data) {
__block NSArray * (^valuesReceived)(void);
__block NSError * (^errorReceived)(void);
__block BOOL (^success)(void);
__block id<RACSubscriber> subscriber;
beforeEach(^{
valuesReceived = data[RACSubscriberExampleValuesReceivedBlock];
errorReceived = data[RACSubscriberExampleErrorReceivedBlock];
success = data[RACSubscriberExampleSuccessBlock];
subscriber = data[RACSubscriberExampleSubscriber];
expect(subscriber).notTo.beNil();
});
it(@"should accept a nil error", ^{
[subscriber sendError:nil];
expect(success()).to.beFalsy();
expect(errorReceived()).to.beNil();
expect(valuesReceived()).to.equal(@[]);
});
describe(@"with values", ^{
__block NSSet *values;
beforeEach(^{
NSMutableSet *mutableValues = [NSMutableSet set];
for (NSUInteger i = 0; i < 20; i++) {
[mutableValues addObject:@(i)];
}
values = [mutableValues copy];
});
it(@"should send nexts serially, even when delivered from multiple threads", ^{
NSArray *allValues = values.allObjects;
dispatch_apply(allValues.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [^(size_t index) {
[subscriber sendNext:allValues[index]];
} copy]);
expect(success()).to.beTruthy();
expect(errorReceived()).to.beNil();
NSSet *valuesReceivedSet = [NSSet setWithArray:valuesReceived()];
expect(valuesReceivedSet).to.equal(values);
});
});
describe(@"multiple subscriptions", ^{
__block RACSubject *first;
__block RACSubject *second;
beforeEach(^{
first = [RACSubject subject];
[first subscribe:subscriber];
second = [RACSubject subject];
[second subscribe:subscriber];
});
it(@"should send values from all subscriptions", ^{
[first sendNext:@"foo"];
[second sendNext:@"bar"];
[first sendNext:@"buzz"];
[second sendNext:@"baz"];
expect(success()).to.beTruthy();
expect(errorReceived()).to.beNil();
NSArray *expected = @[ @"foo", @"bar", @"buzz", @"baz" ];
expect(valuesReceived()).to.equal(expected);
});
it(@"should terminate after the first error from any subscription", ^{
NSError *error = [NSError errorWithDomain:@"" code:-1 userInfo:nil];
[first sendNext:@"foo"];
[second sendError:error];
[first sendNext:@"buzz"];
expect(success()).to.beFalsy();
expect(errorReceived()).to.equal(error);
NSArray *expected = @[ @"foo" ];
expect(valuesReceived()).to.equal(expected);
});
it(@"should terminate after the first completed from any subscription", ^{
[first sendNext:@"foo"];
[second sendNext:@"bar"];
[first sendCompleted];
[second sendNext:@"baz"];
expect(success()).to.beTruthy();
expect(errorReceived()).to.beNil();
NSArray *expected = @[ @"foo", @"bar" ];
expect(valuesReceived()).to.equal(expected);
});
it(@"should dispose of all current subscriptions upon termination", ^{
__block BOOL firstDisposed = NO;
RACSignal *firstDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [RACDisposable disposableWithBlock:^{
firstDisposed = YES;
}];
}];
__block BOOL secondDisposed = NO;
RACSignal *secondDisposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [RACDisposable disposableWithBlock:^{
secondDisposed = YES;
}];
}];
[firstDisposableSignal subscribe:subscriber];
[secondDisposableSignal subscribe:subscriber];
expect(firstDisposed).to.beFalsy();
expect(secondDisposed).to.beFalsy();
[first sendCompleted];
expect(firstDisposed).to.beTruthy();
expect(secondDisposed).to.beTruthy();
});
it(@"should dispose of future subscriptions upon termination", ^{
__block BOOL disposed = NO;
RACSignal *disposableSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [RACDisposable disposableWithBlock:^{
disposed = YES;
}];
}];
[first sendCompleted];
expect(disposed).to.beFalsy();
[disposableSignal subscribe:subscriber];
expect(disposed).to.beTruthy();
});
});
describe(@"memory management", ^{
it(@"should not retain disposed disposables", ^{
__block BOOL disposableDeallocd = NO;
@autoreleasepool {
RACCompoundDisposable *disposable __attribute__((objc_precise_lifetime)) = [RACCompoundDisposable disposableWithBlock:^{}];
[disposable.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
disposableDeallocd = YES;
}]];
[subscriber didSubscribeWithDisposable:disposable];
[disposable dispose];
}
expect(disposableDeallocd).to.beTruthy();
});
});
});
SharedExampleGroupsEnd