| // | |
| // RACChannelExamples.m | |
| // ReactiveCocoa | |
| // | |
| // Created by Uri Baghin on 30/12/2012. | |
| // Copyright (c) 2012 GitHub, Inc. All rights reserved. | |
| // | |
| #import "RACChannelExamples.h" | |
| #import "NSObject+RACDeallocating.h" | |
| #import "NSObject+RACPropertySubscribing.h" | |
| #import "RACChannel.h" | |
| #import "RACCompoundDisposable.h" | |
| #import "RACDisposable.h" | |
| #import "RACSignal+Operations.h" | |
| NSString * const RACChannelExamples = @"RACChannelExamples"; | |
| NSString * const RACChannelExampleCreateBlock = @"RACChannelExampleCreateBlock"; | |
| NSString * const RACViewChannelExamples = @"RACViewChannelExamples"; | |
| NSString * const RACViewChannelExampleCreateViewBlock = @"RACViewChannelExampleCreateViewBlock"; | |
| NSString * const RACViewChannelExampleCreateTerminalBlock = @"RACViewChannelExampleCreateTerminalBlock"; | |
| NSString * const RACViewChannelExampleKeyPath = @"RACViewChannelExampleKeyPath"; | |
| NSString * const RACViewChannelExampleSetViewValueBlock = @"RACViewChannelExampleSetViewValueBlock"; | |
| SharedExampleGroupsBegin(RACChannelExamples) | |
| sharedExamplesFor(RACChannelExamples, ^(NSDictionary *data) { | |
| __block RACChannel * (^getChannel)(void); | |
| __block RACChannel *channel; | |
| id value1 = @"test value 1"; | |
| id value2 = @"test value 2"; | |
| id value3 = @"test value 3"; | |
| NSArray *values = @[ value1, value2, value3 ]; | |
| before(^{ | |
| getChannel = data[RACChannelExampleCreateBlock]; | |
| channel = getChannel(); | |
| }); | |
| it(@"should not send any leadingTerminal value on subscription", ^{ | |
| __block id receivedValue = nil; | |
| [channel.followingTerminal sendNext:value1]; | |
| [channel.leadingTerminal subscribeNext:^(id x) { | |
| receivedValue = x; | |
| }]; | |
| expect(receivedValue).to.beNil(); | |
| [channel.followingTerminal sendNext:value2]; | |
| expect(receivedValue).to.equal(value2); | |
| }); | |
| it(@"should send the latest followingTerminal value on subscription", ^{ | |
| __block id receivedValue = nil; | |
| [channel.leadingTerminal sendNext:value1]; | |
| [[channel.followingTerminal take:1] subscribeNext:^(id x) { | |
| receivedValue = x; | |
| }]; | |
| expect(receivedValue).to.equal(value1); | |
| [channel.leadingTerminal sendNext:value2]; | |
| [[channel.followingTerminal take:1] subscribeNext:^(id x) { | |
| receivedValue = x; | |
| }]; | |
| expect(receivedValue).to.equal(value2); | |
| }); | |
| it(@"should send leadingTerminal values as they change", ^{ | |
| NSMutableArray *receivedValues = [NSMutableArray array]; | |
| [channel.leadingTerminal subscribeNext:^(id x) { | |
| [receivedValues addObject:x]; | |
| }]; | |
| [channel.followingTerminal sendNext:value1]; | |
| [channel.followingTerminal sendNext:value2]; | |
| [channel.followingTerminal sendNext:value3]; | |
| expect(receivedValues).to.equal(values); | |
| }); | |
| it(@"should send followingTerminal values as they change", ^{ | |
| [channel.leadingTerminal sendNext:value1]; | |
| NSMutableArray *receivedValues = [NSMutableArray array]; | |
| [channel.followingTerminal subscribeNext:^(id x) { | |
| [receivedValues addObject:x]; | |
| }]; | |
| [channel.leadingTerminal sendNext:value2]; | |
| [channel.leadingTerminal sendNext:value3]; | |
| expect(receivedValues).to.equal(values); | |
| }); | |
| it(@"should complete both signals when the leadingTerminal is completed", ^{ | |
| __block BOOL completedLeft = NO; | |
| [channel.leadingTerminal subscribeCompleted:^{ | |
| completedLeft = YES; | |
| }]; | |
| __block BOOL completedRight = NO; | |
| [channel.followingTerminal subscribeCompleted:^{ | |
| completedRight = YES; | |
| }]; | |
| [channel.leadingTerminal sendCompleted]; | |
| expect(completedLeft).to.beTruthy(); | |
| expect(completedRight).to.beTruthy(); | |
| }); | |
| it(@"should complete both signals when the followingTerminal is completed", ^{ | |
| __block BOOL completedLeft = NO; | |
| [channel.leadingTerminal subscribeCompleted:^{ | |
| completedLeft = YES; | |
| }]; | |
| __block BOOL completedRight = NO; | |
| [channel.followingTerminal subscribeCompleted:^{ | |
| completedRight = YES; | |
| }]; | |
| [channel.followingTerminal sendCompleted]; | |
| expect(completedLeft).to.beTruthy(); | |
| expect(completedRight).to.beTruthy(); | |
| }); | |
| it(@"should replay completion to new subscribers", ^{ | |
| [channel.leadingTerminal sendCompleted]; | |
| __block BOOL completedLeft = NO; | |
| [channel.leadingTerminal subscribeCompleted:^{ | |
| completedLeft = YES; | |
| }]; | |
| __block BOOL completedRight = NO; | |
| [channel.followingTerminal subscribeCompleted:^{ | |
| completedRight = YES; | |
| }]; | |
| expect(completedLeft).to.beTruthy(); | |
| expect(completedRight).to.beTruthy(); | |
| }); | |
| }); | |
| SharedExampleGroupsEnd | |
| SharedExampleGroupsBegin(RACViewChannelExamples) | |
| sharedExamplesFor(RACViewChannelExamples, ^(NSDictionary *data) { | |
| __block NSString *keyPath; | |
| __block NSObject * (^getView)(void); | |
| __block RACChannelTerminal * (^getTerminal)(NSObject *); | |
| __block void (^setViewValue)(NSObject *view, NSNumber *value); | |
| __block NSObject *testView; | |
| __block RACChannelTerminal *endpoint; | |
| beforeEach(^{ | |
| keyPath = data[RACViewChannelExampleKeyPath]; | |
| getTerminal = data[RACViewChannelExampleCreateTerminalBlock]; | |
| getView = data[RACViewChannelExampleCreateViewBlock]; | |
| setViewValue = data[RACViewChannelExampleSetViewValueBlock]; | |
| testView = getView(); | |
| endpoint = getTerminal(testView); | |
| }); | |
| it(@"should not send changes made by the channel itself", ^{ | |
| __block BOOL receivedNext = NO; | |
| [endpoint subscribeNext:^(id x) { | |
| receivedNext = YES; | |
| }]; | |
| expect(receivedNext).to.beFalsy(); | |
| [endpoint sendNext:@0.1]; | |
| expect(receivedNext).to.beFalsy(); | |
| [endpoint sendNext:@0.2]; | |
| expect(receivedNext).to.beFalsy(); | |
| [endpoint sendCompleted]; | |
| expect(receivedNext).to.beFalsy(); | |
| }); | |
| it(@"should not send progammatic changes made to the view", ^{ | |
| __block BOOL receivedNext = NO; | |
| [endpoint subscribeNext:^(id x) { | |
| receivedNext = YES; | |
| }]; | |
| expect(receivedNext).to.beFalsy(); | |
| [testView setValue:@0.1 forKeyPath:keyPath]; | |
| expect(receivedNext).to.beFalsy(); | |
| [testView setValue:@0.2 forKeyPath:keyPath]; | |
| expect(receivedNext).to.beFalsy(); | |
| }); | |
| it(@"should not have a starting value", ^{ | |
| __block BOOL receivedNext = NO; | |
| [endpoint subscribeNext:^(id x) { | |
| receivedNext = YES; | |
| }]; | |
| expect(receivedNext).to.beFalsy(); | |
| }); | |
| it(@"should send view changes", ^{ | |
| __block NSString *received; | |
| [endpoint subscribeNext:^(id x) { | |
| received = x; | |
| }]; | |
| setViewValue(testView, @0.1); | |
| expect(received).to.equal(@0.1); | |
| setViewValue(testView, @0.2); | |
| expect(received).to.equal(@0.2); | |
| }); | |
| it(@"should set values on the view", ^{ | |
| [endpoint sendNext:@0.1]; | |
| expect([testView valueForKeyPath:keyPath]).to.equal(@0.1); | |
| [endpoint sendNext:@0.2]; | |
| expect([testView valueForKeyPath:keyPath]).to.equal(@0.2); | |
| }); | |
| it(@"should not echo changes back to the channel", ^{ | |
| __block NSUInteger receivedCount = 0; | |
| [endpoint subscribeNext:^(id _) { | |
| receivedCount++; | |
| }]; | |
| expect(receivedCount).to.equal(0); | |
| [endpoint sendNext:@0.1]; | |
| expect(receivedCount).to.equal(0); | |
| setViewValue(testView, @0.2); | |
| expect(receivedCount).to.equal(1); | |
| }); | |
| it(@"should complete when the view deallocates", ^{ | |
| __block BOOL deallocated = NO; | |
| __block BOOL completed = NO; | |
| @autoreleasepool { | |
| NSObject *view __attribute__((objc_precise_lifetime)) = getView(); | |
| [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ | |
| deallocated = YES; | |
| }]]; | |
| RACChannelTerminal *terminal = getTerminal(view); | |
| [terminal subscribeCompleted:^{ | |
| completed = YES; | |
| }]; | |
| expect(deallocated).to.beFalsy(); | |
| expect(completed).to.beFalsy(); | |
| } | |
| expect(deallocated).to.beTruthy(); | |
| expect(completed).to.beTruthy(); | |
| }); | |
| it(@"should deallocate after the view deallocates", ^{ | |
| __block BOOL viewDeallocated = NO; | |
| __block BOOL terminalDeallocated = NO; | |
| @autoreleasepool { | |
| NSObject *view __attribute__((objc_precise_lifetime)) = getView(); | |
| [view.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ | |
| viewDeallocated = YES; | |
| }]]; | |
| RACChannelTerminal *terminal = getTerminal(view); | |
| [terminal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{ | |
| terminalDeallocated = YES; | |
| }]]; | |
| expect(viewDeallocated).to.beFalsy(); | |
| expect(terminalDeallocated).to.beFalsy(); | |
| } | |
| expect(viewDeallocated).to.beTruthy(); | |
| expect(terminalDeallocated).will.beTruthy(); | |
| }); | |
| }); | |
| SharedExampleGroupsEnd |