| // | |
| // RACSubscriberSpec.m | |
| // ReactiveCocoa | |
| // | |
| // Created by Justin Spahr-Summers on 2012-11-27. | |
| // Copyright (c) 2012 GitHub, Inc. All rights reserved. | |
| // | |
| #import "RACSubscriberExamples.h" | |
| #import "RACSubscriber.h" | |
| #import "RACSubscriber+Private.h" | |
| #import <libkern/OSAtomic.h> | |
| SpecBegin(RACSubscriber) | |
| __block RACSubscriber *subscriber; | |
| __block NSMutableArray *values; | |
| __block volatile BOOL finished; | |
| __block volatile int32_t nextsAfterFinished; | |
| __block BOOL success; | |
| __block NSError *error; | |
| beforeEach(^{ | |
| values = [NSMutableArray array]; | |
| finished = NO; | |
| nextsAfterFinished = 0; | |
| success = YES; | |
| error = nil; | |
| subscriber = [RACSubscriber subscriberWithNext:^(id value) { | |
| if (finished) OSAtomicIncrement32Barrier(&nextsAfterFinished); | |
| [values addObject:value]; | |
| } error:^(NSError *e) { | |
| error = e; | |
| success = NO; | |
| } completed:^{ | |
| success = YES; | |
| }]; | |
| }); | |
| itShouldBehaveLike(RACSubscriberExamples, ^{ | |
| return @{ | |
| RACSubscriberExampleSubscriber: subscriber, | |
| RACSubscriberExampleValuesReceivedBlock: [^{ return [values copy]; } copy], | |
| RACSubscriberExampleErrorReceivedBlock: [^{ return error; } copy], | |
| RACSubscriberExampleSuccessBlock: [^{ return success; } copy] | |
| }; | |
| }); | |
| describe(@"finishing", ^{ | |
| __block void (^sendValues)(void); | |
| __block BOOL expectedSuccess; | |
| __block dispatch_group_t dispatchGroup; | |
| __block dispatch_queue_t concurrentQueue; | |
| beforeEach(^{ | |
| dispatchGroup = dispatch_group_create(); | |
| expect(dispatchGroup).notTo.beNil(); | |
| concurrentQueue = dispatch_queue_create("com.github.ReactiveCocoa.RACSubscriberSpec", DISPATCH_QUEUE_CONCURRENT); | |
| expect(concurrentQueue).notTo.beNil(); | |
| dispatch_suspend(concurrentQueue); | |
| sendValues = [^{ | |
| for (NSUInteger i = 0; i < 15; i++) { | |
| dispatch_group_async(dispatchGroup, concurrentQueue, ^{ | |
| [subscriber sendNext:@(i)]; | |
| }); | |
| } | |
| } copy]; | |
| sendValues(); | |
| }); | |
| afterEach(^{ | |
| sendValues(); | |
| dispatch_resume(concurrentQueue); | |
| // Time out after one second. | |
| dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); | |
| expect(dispatch_group_wait(dispatchGroup, time)).to.equal(0); | |
| dispatch_release(dispatchGroup); | |
| dispatchGroup = NULL; | |
| dispatch_release(concurrentQueue); | |
| concurrentQueue = NULL; | |
| expect(nextsAfterFinished).to.equal(0); | |
| if (expectedSuccess) { | |
| expect(success).to.beTruthy(); | |
| expect(error).to.beNil(); | |
| } else { | |
| expect(success).to.beFalsy(); | |
| } | |
| }); | |
| it(@"should never invoke next after sending completed", ^{ | |
| expectedSuccess = YES; | |
| dispatch_group_async(dispatchGroup, concurrentQueue, ^{ | |
| [subscriber sendCompleted]; | |
| finished = YES; | |
| OSMemoryBarrier(); | |
| }); | |
| }); | |
| it(@"should never invoke next after sending error", ^{ | |
| expectedSuccess = NO; | |
| dispatch_group_async(dispatchGroup, concurrentQueue, ^{ | |
| [subscriber sendError:nil]; | |
| finished = YES; | |
| OSMemoryBarrier(); | |
| }); | |
| }); | |
| }); | |
| SpecEnd |