//
//  RACSignalSpec.m
//  ReactiveCocoa
//
//  Created by Josh Abernathy on 3/2/12.
//  Copyright (c) 2012 GitHub, Inc. All rights reserved.
//

#import "RACPropertySignalExamples.h"
#import "RACSequenceExamples.h"
#import "RACStreamExamples.h"
#import "RACTestObject.h"

#import "EXTKeyPathCoding.h"
#import "NSObject+RACDeallocating.h"
#import "NSObject+RACPropertySubscribing.h"
#import "RACBehaviorSubject.h"
#import "RACCommand.h"
#import "RACCompoundDisposable.h"
#import "RACDisposable.h"
#import "RACEvent.h"
#import "RACGroupedSignal.h"
#import "RACMulticastConnection.h"
#import "RACReplaySubject.h"
#import "RACScheduler.h"
#import "RACSignal+Operations.h"
#import "RACSignalStartExamples.h"
#import "RACSubject.h"
#import "RACSubscriber+Private.h"
#import "RACSubscriber.h"
#import "RACTestScheduler.h"
#import "RACTuple.h"
#import "RACUnit.h"
#import <libkern/OSAtomic.h>

// Set in a beforeAll below.
static NSError *RACSignalTestError;

static NSString * const RACSignalMergeConcurrentCompletionExampleGroup = @"RACSignalMergeConcurrentCompletionExampleGroup";
static NSString * const RACSignalMaxConcurrent = @"RACSignalMaxConcurrent";
SharedExampleGroupsBegin(mergeConcurrentCompletionName);

sharedExamplesFor(RACSignalMergeConcurrentCompletionExampleGroup, ^(NSDictionary *data) {
	it(@"should complete only after the source and all its signals have completed", ^{
		RACSubject *subject1 = [RACSubject subject];
		RACSubject *subject2 = [RACSubject subject];
		RACSubject *subject3 = [RACSubject subject];

		RACSubject *signalsSubject = [RACSubject subject];
		__block BOOL completed = NO;
		[[signalsSubject flatten:[data[RACSignalMaxConcurrent] unsignedIntegerValue]] subscribeCompleted:^{
			completed = YES;
		}];

		[signalsSubject sendNext:subject1];
		[subject1 sendCompleted];

		expect(completed).to.beFalsy();

		[signalsSubject sendNext:subject2];
		[signalsSubject sendNext:subject3];

		[signalsSubject sendCompleted];

		expect(completed).to.beFalsy();

		[subject2 sendCompleted];

		expect(completed).to.beFalsy();

		[subject3 sendCompleted];

		expect(completed).to.beTruthy();
	});
});

SharedExampleGroupsEnd

SpecBegin(RACSignal)

beforeAll(^{
	// We do this instead of a macro to ensure that to.equal() will work
	// correctly (by matching identity), even if -[NSError isEqual:] is broken.
	RACSignalTestError = [NSError errorWithDomain:@"foo" code:100 userInfo:nil];
});

describe(@"RACStream", ^{
	id verifyValues = ^(RACSignal *signal, NSArray *expectedValues) {
		expect(signal).notTo.beNil();

		NSMutableArray *collectedValues = [NSMutableArray array];

		__block BOOL success = NO;
		__block NSError *error = nil;
		[signal subscribeNext:^(id value) {
			[collectedValues addObject:value];
		} error:^(NSError *receivedError) {
			error = receivedError;
		} completed:^{
			success = YES;
		}];

		expect(success).will.beTruthy();
		expect(error).to.beNil();
		expect(collectedValues).to.equal(expectedValues);
	};

	RACSignal *infiniteSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
		__block volatile int32_t done = 0;

		[RACScheduler.mainThreadScheduler schedule:^{
			while (!done) {
				[subscriber sendNext:RACUnit.defaultUnit];
			}
		}];

		return [RACDisposable disposableWithBlock:^{
			OSAtomicIncrement32Barrier(&done);
		}];
	}];

	itShouldBehaveLike(RACStreamExamples, ^{
		return @{
			RACStreamExamplesClass: RACSignal.class,
			RACStreamExamplesVerifyValuesBlock: verifyValues,
			RACStreamExamplesInfiniteStream: infiniteSignal
		};
	});
});

describe(@"-bind:", ^{
	__block RACSubject *signals;
	__block BOOL disposed;
	__block id lastValue;
	__block RACSubject *values;

	beforeEach(^{
		// Tests send a (RACSignal, BOOL) pair that are used below in -bind:.
		signals = [RACSubject subject];

		disposed = NO;
		RACSignal *source = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			[signals subscribe:subscriber];

			return [RACDisposable disposableWithBlock:^{
				disposed = YES;
			}];
		}];

		RACSignal *bind = [source bind:^{
			return ^(RACTuple *x, BOOL *stop) {
				RACTupleUnpack(RACSignal *signal, NSNumber *stopValue) = x;
				*stop = stopValue.boolValue;
				return signal;
			};
		}];

		lastValue = nil;
		[bind subscribeNext:^(id x) {
			lastValue = x;
		}];

		// Send `bind` an open ended subject to subscribe to. These tests make
		// use of this in two ways:
		//   1. Used to test a regression bug where -bind: would not actually
		//      stop when instructed to. This bug manifested itself only when
		//      there were subscriptions that lived on past the point at which
		//      -bind: was stopped. This subject represents such a subscription.
		//   2. Test that values sent by this subject are received by `bind`'s
		//      subscriber, even *after* -bind: has been instructed to stop.
		values = [RACSubject subject];
		[signals sendNext:RACTuplePack(values, @NO)];
		expect(disposed).to.beFalsy();
	});

	it(@"should dispose source signal when stopped with nil signal", ^{
		// Tell -bind: to stop by sending it a `nil` signal.
		[signals sendNext:RACTuplePack(nil, @NO)];
		expect(disposed).to.beTruthy();

		// Should still receive values sent after stopping.
		expect(lastValue).to.beNil();
		[values sendNext:RACUnit.defaultUnit];
		expect(lastValue).to.equal(RACUnit.defaultUnit);
	});

	it(@"should dispose source signal when stop flag set to YES", ^{
		// Tell -bind: to stop by setting the stop flag to YES.
		[signals sendNext:RACTuplePack([RACSignal return:@1], @YES)];
		expect(disposed).to.beTruthy();

		// Should still recieve last signal sent at the time of setting stop to YES.
		expect(lastValue).to.equal(@1);

		// Should still receive values sent after stopping.
		[values sendNext:@2];
		expect(lastValue).to.equal(@2);
	});
});

describe(@"subscribing", ^{
	__block RACSignal *signal = nil;
	id nextValueSent = @"1";
	
	beforeEach(^{
		signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:nextValueSent];
			[subscriber sendCompleted];
			return nil;
		}];
	});
	
	it(@"should get next values", ^{
		__block id nextValueReceived = nil;
		[signal subscribeNext:^(id x) {
			nextValueReceived = x;
		} error:^(NSError *error) {
			
		} completed:^{
			
		}];
		
		expect(nextValueReceived).to.equal(nextValueSent);
	});
	
	it(@"should get completed", ^{
		__block BOOL didGetCompleted = NO;
		[signal subscribeNext:^(id x) {
			
		} error:^(NSError *error) {
			
		} completed:^{
			didGetCompleted = YES;
		}];
		
		expect(didGetCompleted).to.beTruthy();
	});
	
	it(@"should not get an error", ^{
		__block BOOL didGetError = NO;
		[signal subscribeNext:^(id x) {
			
		} error:^(NSError *error) {
			didGetError = YES;
		} completed:^{
			
		}];
		
		expect(didGetError).to.beFalsy();
	});
	
	it(@"shouldn't get anything after dispose", ^{
		RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];
		NSMutableArray *receivedValues = [NSMutableArray array];

		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@0];

			[scheduler afterDelay:0 schedule:^{
				[subscriber sendNext:@1];
			}];

			return nil;
		}];

		RACDisposable *disposable = [signal subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];

		NSArray *expectedValues = @[ @0 ];
		expect(receivedValues).to.equal(expectedValues);
		
		[disposable dispose];
		[scheduler stepAll];
		
		expect(receivedValues).to.equal(expectedValues);
	});

	it(@"should have a current scheduler in didSubscribe block", ^{
		__block RACScheduler *currentScheduler;
		RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			currentScheduler = RACScheduler.currentScheduler;
			[subscriber sendCompleted];
			return nil;
		}];

		[signal subscribeNext:^(id x) {}];
		expect(currentScheduler).notTo.beNil();

		currentScheduler = nil;
		dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
			[signal subscribeNext:^(id x) {}];
		});
		expect(currentScheduler).willNot.beNil();
	});
	
	it(@"should automatically dispose of other subscriptions from +createSignal:", ^{
		__block BOOL innerDisposed = NO;

		RACSignal *innerSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				innerDisposed = YES;
			}];
		}];

		RACSignal *outerSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[innerSignal subscribe:subscriber];
			return nil;
		}];

		RACDisposable *disposable = [outerSignal subscribeCompleted:^{}];
		expect(disposable).notTo.beNil();
		expect(innerDisposed).to.beFalsy();

		[disposable dispose];
		expect(innerDisposed).to.beTruthy();
	});
});

describe(@"-takeUntil:", ^{
	it(@"should support value as trigger", ^{
		__block BOOL shouldBeGettingItems = YES;
		RACSubject *subject = [RACSubject subject];
		RACSubject *cutOffSubject = [RACSubject subject];
		[[subject takeUntil:cutOffSubject] subscribeNext:^(id x) {
			expect(shouldBeGettingItems).to.beTruthy();
		}];

		shouldBeGettingItems = YES;
		[subject sendNext:@"test 1"];
		[subject sendNext:@"test 2"];

		[cutOffSubject sendNext:[RACUnit defaultUnit]];

		shouldBeGettingItems = NO;
		[subject sendNext:@"test 3"];
	});
    
	it(@"should support completion as trigger", ^{
		__block BOOL shouldBeGettingItems = YES;
		RACSubject *subject = [RACSubject subject];
		RACSubject *cutOffSubject = [RACSubject subject];
		[[subject takeUntil:cutOffSubject] subscribeNext:^(id x) {
			expect(shouldBeGettingItems).to.beTruthy();
		}];
        
		[cutOffSubject sendCompleted];
        
		shouldBeGettingItems = NO;
		[subject sendNext:@"should not go through"];
	});

	it(@"should squelch any values sent immediately upon subscription", ^{
		RACSignal *valueSignal = [RACSignal return:RACUnit.defaultUnit];
		RACSignal *cutOffSignal = [RACSignal empty];

		__block BOOL gotNext = NO;
		__block BOOL completed = NO;

		[[valueSignal takeUntil:cutOffSignal] subscribeNext:^(id _) {
			gotNext = YES;
		} completed:^{
			completed = YES;
		}];

		expect(gotNext).to.beFalsy();
		expect(completed).to.beTruthy();
	});
});

describe(@"-takeUntilReplacement:", ^{
	it(@"should forward values from the receiver until it's replaced", ^{
		RACSubject *receiver = [RACSubject subject];
		RACSubject *replacement = [RACSubject subject];

		NSMutableArray *receivedValues = [NSMutableArray array];

		[[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];

		expect(receivedValues).to.equal(@[]);

		[receiver sendNext:@1];
		expect(receivedValues).to.equal(@[ @1 ]);

		[receiver sendNext:@2];
		expect(receivedValues).to.equal((@[ @1, @2 ]));

		[replacement sendNext:@3];
		expect(receivedValues).to.equal((@[ @1, @2, @3 ]));

		[receiver sendNext:@4];
		expect(receivedValues).to.equal((@[ @1, @2, @3 ]));

		[replacement sendNext:@5];
		expect(receivedValues).to.equal((@[ @1, @2, @3, @5 ]));
	});

	it(@"should forward error from the receiver", ^{
		RACSubject *receiver = [RACSubject subject];
		__block BOOL receivedError = NO;

		[[receiver takeUntilReplacement:RACSignal.never] subscribeError:^(NSError *error) {
			receivedError = YES;
		}];

		[receiver sendError:nil];
		expect(receivedError).to.beTruthy();
	});

	it(@"should not forward completed from the receiver", ^{
		RACSubject *receiver = [RACSubject subject];
		__block BOOL receivedCompleted = NO;

		[[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted: ^{
			receivedCompleted = YES;
		}];

		[receiver sendCompleted];
		expect(receivedCompleted).to.beFalsy();
	});

	it(@"should forward error from the replacement signal", ^{
		RACSubject *replacement = [RACSubject subject];
		__block BOOL receivedError = NO;

		[[RACSignal.never takeUntilReplacement:replacement] subscribeError:^(NSError *error) {
			receivedError = YES;
		}];

		[replacement sendError:nil];
		expect(receivedError).to.beTruthy();
	});

	it(@"should forward completed from the replacement signal", ^{
		RACSubject *replacement = [RACSubject subject];
		__block BOOL receivedCompleted = NO;

		[[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted: ^{
			receivedCompleted = YES;
		}];

		[replacement sendCompleted];
		expect(receivedCompleted).to.beTruthy();
	});
	
	it(@"should not forward values from the receiver if both send synchronously", ^{
		RACSignal *receiver = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:@2];
			[subscriber sendNext:@3];
			return nil;
		}];
		RACSignal *replacement = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@4];
			[subscriber sendNext:@5];
			[subscriber sendNext:@6];
			return nil;
		}];

		NSMutableArray *receivedValues = [NSMutableArray array];

		[[receiver takeUntilReplacement:replacement] subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];

		expect(receivedValues).to.equal((@[ @4, @5, @6 ]));
	});

	it(@"should dispose of the receiver when it's disposed of", ^{
		__block BOOL receiverDisposed = NO;
		RACSignal *receiver = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				receiverDisposed = YES;
			}];
		}];

		[[[receiver takeUntilReplacement:RACSignal.never] subscribeCompleted:^{}] dispose];

		expect(receiverDisposed).to.beTruthy();
	});

	it(@"should dispose of the replacement signal when it's disposed of", ^{
		__block BOOL replacementDisposed = NO;
		RACSignal *replacement = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				replacementDisposed = YES;
			}];
		}];

		[[[RACSignal.never takeUntilReplacement:replacement] subscribeCompleted:^{}] dispose];

		expect(replacementDisposed).to.beTruthy();
	});

	it(@"should dispose of the receiver when the replacement signal sends an event", ^{
		__block BOOL receiverDisposed = NO;
		RACSignal *receiver = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				receiverDisposed = YES;
			}];
		}];
		RACSubject *replacement = [RACSubject subject];

		[[receiver takeUntilReplacement:replacement] subscribeCompleted:^{}];

		expect(receiverDisposed).to.beFalsy();

		[replacement sendNext:nil];
		
		expect(receiverDisposed).to.beTruthy();
	});
});

describe(@"disposal", ^{
	it(@"should dispose of the didSubscribe disposable", ^{
		__block BOOL innerDisposed = NO;
		RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				innerDisposed = YES;
			}];
		}];

		expect(innerDisposed).to.beFalsy();

		RACDisposable *disposable = [signal subscribeNext:^(id x) {}];
		expect(disposable).notTo.beNil();

		[disposable dispose];
		expect(innerDisposed).to.beTruthy();
	});

	it(@"should dispose of the didSubscribe disposable asynchronously", ^{
		__block BOOL innerDisposed = NO;
		RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				innerDisposed = YES;
			}];
		}];

		[[RACScheduler scheduler] schedule:^{
			RACDisposable *disposable = [signal subscribeNext:^(id x) {}];
			[disposable dispose];
		}];

		expect(innerDisposed).will.beTruthy();
	});
});

describe(@"querying", ^{
	__block RACSignal *signal = nil;
	id nextValueSent = @"1";
	
	beforeEach(^{
		signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:nextValueSent];
			[subscriber sendNext:@"other value"];
			[subscriber sendCompleted];
			return nil;
		}];
	});
	
	it(@"should return first 'next' value with -firstOrDefault:success:error:", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:@2];
			[subscriber sendNext:@3];
			[subscriber sendCompleted];
			return nil;
		}];

		expect(signal).notTo.beNil();

		__block BOOL success = NO;
		__block NSError *error = nil;
		expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@1);
		expect(success).to.beTruthy();
		expect(error).to.beNil();
	});
	
	it(@"should return first default value with -firstOrDefault:success:error:", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendCompleted];
			return nil;
		}];

		expect(signal).notTo.beNil();

		__block BOOL success = NO;
		__block NSError *error = nil;
		expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
		expect(success).to.beTruthy();
		expect(error).to.beNil();
	});
	
	it(@"should return error with -firstOrDefault:success:error:", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendError:RACSignalTestError];
			return nil;
		}];

		expect(signal).notTo.beNil();

		__block BOOL success = NO;
		__block NSError *error = nil;
		expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
		expect(success).to.beFalsy();
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"shouldn't crash when returning an error from a background scheduler", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[[RACScheduler scheduler] schedule:^{
				[subscriber sendError:RACSignalTestError];
			}];

			return nil;
		}];

		expect(signal).notTo.beNil();

		__block BOOL success = NO;
		__block NSError *error = nil;
		expect([signal firstOrDefault:@5 success:&success error:&error]).to.equal(@5);
		expect(success).to.beFalsy();
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should terminate the subscription after returning from -firstOrDefault:success:error:", ^{
		__block BOOL disposed = NO;
		RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			[subscriber sendNext:RACUnit.defaultUnit];

			return [RACDisposable disposableWithBlock:^{
				disposed = YES;
			}];
		}];

		expect(signal).notTo.beNil();
		expect(disposed).to.beFalsy();

		expect([signal firstOrDefault:nil success:NULL error:NULL]).to.equal(RACUnit.defaultUnit);
		expect(disposed).to.beTruthy();
	});

	it(@"should return YES from -waitUntilCompleted: when successful", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:RACUnit.defaultUnit];
			[subscriber sendCompleted];
			return nil;
		}];

		__block NSError *error = nil;
		expect([signal waitUntilCompleted:&error]).to.beTruthy();
		expect(error).to.beNil();
	});

	it(@"should return NO from -waitUntilCompleted: upon error", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:RACUnit.defaultUnit];
			[subscriber sendError:RACSignalTestError];
			return nil;
		}];

		__block NSError *error = nil;
		expect([signal waitUntilCompleted:&error]).to.beFalsy();
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should return a delayed value from -asynchronousFirstOrDefault:success:error:", ^{
		RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0];

		__block BOOL scheduledBlockRan = NO;
		[RACScheduler.mainThreadScheduler schedule:^{
			scheduledBlockRan = YES;
		}];

		expect(scheduledBlockRan).to.beFalsy();

		BOOL success = NO;
		NSError *error = nil;
		id value = [signal asynchronousFirstOrDefault:nil success:&success error:&error];

		expect(scheduledBlockRan).to.beTruthy();

		expect(value).to.equal(RACUnit.defaultUnit);
		expect(success).to.beTruthy();
		expect(error).to.beNil();
	});

	it(@"should return a default value from -asynchronousFirstOrDefault:success:error:", ^{
		RACSignal *signal = [[RACSignal error:RACSignalTestError] delay:0];

		__block BOOL scheduledBlockRan = NO;
		[RACScheduler.mainThreadScheduler schedule:^{
			scheduledBlockRan = YES;
		}];

		expect(scheduledBlockRan).to.beFalsy();

		BOOL success = NO;
		NSError *error = nil;
		id value = [signal asynchronousFirstOrDefault:RACUnit.defaultUnit success:&success error:&error];

		expect(scheduledBlockRan).to.beTruthy();

		expect(value).to.equal(RACUnit.defaultUnit);
		expect(success).to.beFalsy();
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should return a delayed error from -asynchronousFirstOrDefault:success:error:", ^{
		RACSignal *signal = [[RACSignal
			createSignal:^(id<RACSubscriber> subscriber) {
				return [[RACScheduler scheduler] schedule:^{
					[subscriber sendError:RACSignalTestError];
				}];
			}]
			deliverOn:RACScheduler.mainThreadScheduler];

		__block NSError *error = nil;
		__block BOOL success = NO;
		expect([signal asynchronousFirstOrDefault:nil success:&success error:&error]).to.beNil();

		expect(success).to.beFalsy();
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should terminate the subscription after returning from -asynchronousFirstOrDefault:success:error:", ^{
		__block BOOL disposed = NO;
		RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			[[RACScheduler scheduler] schedule:^{
				[subscriber sendNext:RACUnit.defaultUnit];
			}];

			return [RACDisposable disposableWithBlock:^{
				disposed = YES;
			}];
		}];

		expect(signal).notTo.beNil();
		expect(disposed).to.beFalsy();

		expect([signal asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(RACUnit.defaultUnit);
		expect(disposed).will.beTruthy();
	});

	it(@"should return a delayed success from -asynchronouslyWaitUntilCompleted:", ^{
		RACSignal *signal = [[RACSignal return:RACUnit.defaultUnit] delay:0];

		__block BOOL scheduledBlockRan = NO;
		[RACScheduler.mainThreadScheduler schedule:^{
			scheduledBlockRan = YES;
		}];

		expect(scheduledBlockRan).to.beFalsy();

		NSError *error = nil;
		BOOL success = [signal asynchronouslyWaitUntilCompleted:&error];

		expect(scheduledBlockRan).to.beTruthy();

		expect(success).to.beTruthy();
		expect(error).to.beNil();
	});
});

describe(@"continuation", ^{
	it(@"should repeat after completion", ^{
		__block NSUInteger numberOfSubscriptions = 0;
		RACScheduler *scheduler = [RACScheduler scheduler];

		RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			return [scheduler schedule:^{
				if (numberOfSubscriptions == 3) {
					[subscriber sendError:RACSignalTestError];
					return;
				}
				
				numberOfSubscriptions++;
				
				[subscriber sendNext:@"1"];
				[subscriber sendCompleted];
				[subscriber sendError:RACSignalTestError];
			}];
		}];
		
		__block NSUInteger nextCount = 0;
		__block BOOL gotCompleted = NO;
		[[signal repeat] subscribeNext:^(id x) {
			nextCount++;
		} error:^(NSError *error) {
			
		} completed:^{
			gotCompleted = YES;
		}];
		
		expect(nextCount).will.equal(3);
		expect(gotCompleted).to.beFalsy();
	});

	it(@"should stop repeating when disposed", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}];

		NSMutableArray *values = [NSMutableArray array];

		__block BOOL completed = NO;
		__block RACDisposable *disposable = [[signal repeat] subscribeNext:^(id x) {
			[values addObject:x];
			[disposable dispose];
		} completed:^{
			completed = YES;
		}];

		expect(values).will.equal(@[ @1 ]);
		expect(completed).to.beFalsy();
	});

	it(@"should stop repeating when disposed by -take:", ^{
		RACSignal *signal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}];

		NSMutableArray *values = [NSMutableArray array];

		__block BOOL completed = NO;
		[[[signal repeat] take:1] subscribeNext:^(id x) {
			[values addObject:x];
		} completed:^{
			completed = YES;
		}];

		expect(values).will.equal(@[ @1 ]);
		expect(completed).to.beTruthy();
	});
});

describe(@"+combineLatestWith:", ^{
	__block RACSubject *subject1 = nil;
	__block RACSubject *subject2 = nil;
	__block RACSignal *combined = nil;
	
	beforeEach(^{
		subject1 = [RACSubject subject];
		subject2 = [RACSubject subject];
		combined = [RACSignal combineLatest:@[ subject1, subject2 ]];
	});
	
	it(@"should send next only once both signals send next", ^{
		__block RACTuple *tuple;
		
		[combined subscribeNext:^(id x) {
			tuple = x;
		}];
		
		expect(tuple).to.beNil();

		[subject1 sendNext:@"1"];
		expect(tuple).to.beNil();

		[subject2 sendNext:@"2"];
		expect(tuple).to.equal(RACTuplePack(@"1", @"2"));
	});
	
	it(@"should send nexts when either signal sends multiple times", ^{
		NSMutableArray *results = [NSMutableArray array];
		[combined subscribeNext:^(id x) {
			[results addObject:x];
		}];
		
		[subject1 sendNext:@"1"];
		[subject2 sendNext:@"2"];
		
		[subject1 sendNext:@"3"];
		[subject2 sendNext:@"4"];
		
		expect(results[0]).to.equal(RACTuplePack(@"1", @"2"));
		expect(results[1]).to.equal(RACTuplePack(@"3", @"2"));
		expect(results[2]).to.equal(RACTuplePack(@"3", @"4"));
	});
	
	it(@"should complete when only both signals complete", ^{
		__block BOOL completed = NO;
		
		[combined subscribeCompleted:^{
			completed = YES;
		}];

		expect(completed).to.beFalsy();
		
		[subject1 sendCompleted];
		expect(completed).to.beFalsy();

		[subject2 sendCompleted];
		expect(completed).to.beTruthy();
	});
	
	it(@"should error when either signal errors", ^{
		__block NSError *receivedError = nil;
		[combined subscribeError:^(NSError *error) {
			receivedError = error;
		}];
		
		[subject1 sendError:RACSignalTestError];
		expect(receivedError).to.equal(RACSignalTestError);
	});

	it(@"shouldn't create a retain cycle", ^{
		__block BOOL subjectDeallocd = NO;
		__block BOOL signalDeallocd = NO;

		@autoreleasepool {
			RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject];
			[subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				subjectDeallocd = YES;
			}]];
			
			RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal combineLatest:@[ subject ]];
			[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				signalDeallocd = YES;
			}]];

			[signal subscribeCompleted:^{}];
			[subject sendCompleted];
		}

		expect(subjectDeallocd).will.beTruthy();
		expect(signalDeallocd).will.beTruthy();
	});

	it(@"should combine the same signal", ^{
		RACSignal *combined = [subject1 combineLatestWith:subject1];

		__block RACTuple *tuple;
		[combined subscribeNext:^(id x) {
			tuple = x;
		}];
		
		[subject1 sendNext:@"foo"];
		expect(tuple).to.equal(RACTuplePack(@"foo", @"foo"));
		
		[subject1 sendNext:@"bar"];
		expect(tuple).to.equal(RACTuplePack(@"bar", @"bar"));
	});
    
	it(@"should combine the same side-effecting signal", ^{
		__block NSUInteger counter = 0;
		RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@(++counter)];
			[subscriber sendCompleted];
			return nil;
		}];

		RACSignal *combined = [sideEffectingSignal combineLatestWith:sideEffectingSignal];
		expect(counter).to.equal(0);

		NSMutableArray *receivedValues = [NSMutableArray array];
		[combined subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];
		
		expect(counter).to.equal(2);

		NSArray *expected = @[ RACTuplePack(@1, @2) ];
		expect(receivedValues).to.equal(expected);
	});
});

describe(@"+combineLatest:", ^{
	it(@"should return tuples even when only combining one signal", ^{
		RACSubject *subject = [RACSubject subject];

		__block RACTuple *tuple;
		[[RACSignal combineLatest:@[ subject ]] subscribeNext:^(id x) {
			tuple = x;
		}];

		[subject sendNext:@"foo"];
		expect(tuple).to.equal(RACTuplePack(@"foo"));
	});

	it(@"should complete immediately when not given any signals", ^{
		RACSignal *signal = [RACSignal combineLatest:@[]];

		__block BOOL completed = NO;
		[signal subscribeCompleted:^{
			completed = YES;
		}];

		expect(completed).to.beTruthy();
	});

	it(@"should only complete after all its signals complete", ^{
		RACSubject *subject1 = [RACSubject subject];
		RACSubject *subject2 = [RACSubject subject];
		RACSubject *subject3 = [RACSubject subject];
		RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ]];

		__block BOOL completed = NO;
		[combined subscribeCompleted:^{
			completed = YES;
		}];

		expect(completed).to.beFalsy();

		[subject1 sendCompleted];
		expect(completed).to.beFalsy();

		[subject2 sendCompleted];
		expect(completed).to.beFalsy();

		[subject3 sendCompleted];
		expect(completed).to.beTruthy();
	});
});

describe(@"+combineLatest:reduce:", ^{
	__block RACSubject *subject1;
	__block RACSubject *subject2;
	__block RACSubject *subject3;

	beforeEach(^{
		subject1 = [RACSubject subject];
		subject2 = [RACSubject subject];
		subject3 = [RACSubject subject];
	});

	it(@"should send nils for nil values", ^{
		__block id receivedVal1;
		__block id receivedVal2;
		__block id receivedVal3;

		RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^ id (id val1, id val2, id val3) {
			receivedVal1 = val1;
			receivedVal2 = val2;
			receivedVal3 = val3;
			return nil;
		}];

		__block BOOL gotValue = NO;
		[combined subscribeNext:^(id x) {
			gotValue = YES;
		}];

		[subject1 sendNext:nil];
		[subject2 sendNext:nil];
		[subject3 sendNext:nil];

		expect(gotValue).to.beTruthy();
		expect(receivedVal1).to.beNil();
		expect(receivedVal2).to.beNil();
		expect(receivedVal3).to.beNil();
	});

	it(@"should send the return result of the reduce block", ^{
		RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3) {
			return [NSString stringWithFormat:@"%@: %@%@", string1, string2, string3];
		}];

		__block id received;
		[combined subscribeNext:^(id x) {
			received = x;
		}];

		[subject1 sendNext:@"hello"];
		[subject2 sendNext:@"world"];
		[subject3 sendNext:@"!!1"];

		expect(received).to.equal(@"hello: world!!1");
	});
	
	it(@"should handle multiples of the same signals", ^{
		RACSignal *combined = [RACSignal combineLatest:@[ subject1, subject2, subject1, subject3 ] reduce:^(NSString *string1, NSString *string2, NSString *string3, NSString *string4) {
			return [NSString stringWithFormat:@"%@ : %@ = %@ : %@", string1, string2, string3, string4];
		}];
		
		NSMutableArray *receivedValues = NSMutableArray.array;
		
		[combined subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];
		
		[subject1 sendNext:@"apples"];
		expect(receivedValues.lastObject).to.beNil();
		
		[subject2 sendNext:@"oranges"];
		expect(receivedValues.lastObject).to.beNil();

		[subject3 sendNext:@"cattle"];
		expect(receivedValues.lastObject).to.equal(@"apples : oranges = apples : cattle");
		
		[subject1 sendNext:@"horses"];
		expect(receivedValues.lastObject).to.equal(@"horses : oranges = horses : cattle");
	});
    
	it(@"should handle multiples of the same side-effecting signal", ^{
		__block NSUInteger counter = 0;
		RACSignal *sideEffectingSignal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@(++counter)];
			[subscriber sendCompleted];
			return nil;
		}];

		RACSignal *combined = [RACSignal combineLatest:@[ sideEffectingSignal, sideEffectingSignal, sideEffectingSignal ] reduce:^(id x, id y, id z) {
			return [NSString stringWithFormat:@"%@%@%@", x, y, z];
		}];

		NSMutableArray *receivedValues = [NSMutableArray array];
		expect(counter).to.equal(0);
		
		[combined subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];
		
		expect(counter).to.equal(3);
		expect(receivedValues).to.equal(@[ @"123" ]);
	});
});

describe(@"distinctUntilChanged", ^{
	it(@"should only send values that are distinct from the previous value", ^{
		RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:@2];
			[subscriber sendNext:@2];
			[subscriber sendNext:@1];
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}] distinctUntilChanged];
		
		NSArray *values = sub.toArray;
		NSArray *expected = @[ @1, @2, @1 ];
		expect(values).to.equal(expected);
	});

	it(@"shouldn't consider nils to always be distinct", ^{
		RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:nil];
			[subscriber sendNext:nil];
			[subscriber sendNext:nil];
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}] distinctUntilChanged];
		
		NSArray *values = sub.toArray;
		NSArray *expected = @[ @1, [NSNull null], @1 ];
		expect(values).to.equal(expected);
	});

	it(@"should consider initial nil to be distinct", ^{
		RACSignal *sub = [[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:nil];
			[subscriber sendNext:nil];
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}] distinctUntilChanged];
		
		NSArray *values = sub.toArray;
		NSArray *expected = @[ [NSNull null], @1 ];
		expect(values).to.equal(expected);
	});
});

describe(@"RACObserve", ^{
	__block RACTestObject *testObject;

	beforeEach(^{
		testObject = [[RACTestObject alloc] init];
	});

	it(@"should work with object properties", ^{
		NSArray *expected = @[ @"hello", @"world" ];
		testObject.objectValue = expected[0];

		NSMutableArray *valuesReceived = [NSMutableArray array];
		[RACObserve(testObject, objectValue) subscribeNext:^(id x) {
			[valuesReceived addObject:x];
		}];

		testObject.objectValue = expected[1];

		expect(valuesReceived).to.equal(expected);
	});

	it(@"should work with non-object properties", ^{
		NSArray *expected = @[ @42, @43 ];
		testObject.integerValue = [expected[0] integerValue];

		NSMutableArray *valuesReceived = [NSMutableArray array];
		[RACObserve(testObject, integerValue) subscribeNext:^(id x) {
			[valuesReceived addObject:x];
		}];

		testObject.integerValue = [expected[1] integerValue];

		expect(valuesReceived).to.equal(expected);
	});

	it(@"should read the initial value upon subscription", ^{
		testObject.objectValue = @"foo";

		RACSignal *signal = RACObserve(testObject, objectValue);
		testObject.objectValue = @"bar";

		expect([signal first]).to.equal(@"bar");
	});
});

describe(@"-setKeyPath:onObject:", ^{
	id setupBlock = ^(RACTestObject *testObject, NSString *keyPath, id nilValue, RACSignal *signal) {
		[signal setKeyPath:keyPath onObject:testObject nilValue:nilValue];
	};

	itShouldBehaveLike(RACPropertySignalExamples, ^{
		return @{ RACPropertySignalExamplesSetupBlock: setupBlock };
	});

	it(@"shouldn't send values to dealloc'd objects", ^{
		RACSubject *subject = [RACSubject subject];
		@autoreleasepool {
			RACTestObject *testObject __attribute__((objc_precise_lifetime)) = [[RACTestObject alloc] init];
			[subject setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
			expect(testObject.objectValue).to.beNil();

			[subject sendNext:@1];
			expect(testObject.objectValue).to.equal(@1);

			[subject sendNext:@2];
			expect(testObject.objectValue).to.equal(@2);
		}

		// This shouldn't do anything.
		[subject sendNext:@3];
	});

	it(@"should allow a new derivation after the signal's completed", ^{
		RACSubject *subject1 = [RACSubject subject];
		RACTestObject *testObject = [[RACTestObject alloc] init];
		[subject1 setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
		[subject1 sendCompleted];

		RACSubject *subject2 = [RACSubject subject];
		// This will assert if the previous completion didn't dispose of the
		// subscription.
		[subject2 setKeyPath:@keypath(testObject.objectValue) onObject:testObject];
	});

	it(@"should set the given value when nil is received", ^{
		RACSubject *subject = [RACSubject subject];
		RACTestObject *testObject = [[RACTestObject alloc] init];
		[subject setKeyPath:@keypath(testObject.integerValue) onObject:testObject nilValue:@5];

		[subject sendNext:@1];
		expect(testObject.integerValue).to.equal(1);

		[subject sendNext:nil];
		expect(testObject.integerValue).to.equal(5);

		[subject sendCompleted];
		expect(testObject.integerValue).to.equal(5);
	});
});

describe(@"memory management", ^{
	it(@"should dealloc signals if the signal does nothing", ^{
		__block BOOL deallocd = NO;
		@autoreleasepool {
			RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
				return nil;
			}];

			[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				deallocd = YES;
			}]];
		}

		expect(deallocd).will.beTruthy();
	});

	it(@"should retain signals for a single run loop iteration", ^{
		__block BOOL deallocd = NO;

		@autoreleasepool {
			RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
				return nil;
			}];

			[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				deallocd = YES;
			}]];
		}

		expect(deallocd).to.beFalsy();
		expect(deallocd).will.beTruthy();
	});

	it(@"should dealloc signals if the signal immediately completes", ^{
		__block BOOL deallocd = NO;
		@autoreleasepool {
			__block BOOL done = NO;

			RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
				[subscriber sendCompleted];
				return nil;
			}];

			[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				deallocd = YES;
			}]];

			[signal subscribeCompleted:^{
				done = YES;
			}];

			expect(done).will.beTruthy();
		}
		
		expect(deallocd).will.beTruthy();
	});

	it(@"should dealloc a replay subject if it completes immediately", ^{
		__block BOOL completed = NO;
		__block BOOL deallocd = NO;
		@autoreleasepool {
			RACReplaySubject *subject __attribute__((objc_precise_lifetime)) = [RACReplaySubject subject];
			[subject sendCompleted];

			[subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				deallocd = YES;
			}]];

			[subject subscribeCompleted:^{
				completed = YES;
			}];
		}

		expect(completed).will.beTruthy();

		expect(deallocd).will.beTruthy();
	});

	it(@"should dealloc if the signal was created on a background queue", ^{
		__block BOOL completed = NO;
		__block BOOL deallocd = NO;
		@autoreleasepool {
			[[RACScheduler scheduler] schedule:^{
				RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
					[subscriber sendCompleted];
					return nil;
				}];

				[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
					deallocd = YES;
				}]];

				[signal subscribeCompleted:^{
					completed = YES;
				}];
			}];
		}

		expect(completed).will.beTruthy();

		expect(deallocd).will.beTruthy();
	});

	it(@"should dealloc if the signal was created on a background queue, never gets any subscribers, and the background queue gets delayed", ^{
		__block BOOL deallocd = NO;
		@autoreleasepool {
			[[RACScheduler scheduler] schedule:^{
				RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
					return nil;
				}];

				[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
					deallocd = YES;
				}]];

				[NSThread sleepForTimeInterval:1];

				expect(deallocd).to.beFalsy();
			}];
		}

		// The default test timeout is 1s so we'd race to see if the queue delay
		// or default timeout happens first. To avoid that, just bump the
		// timeout slightly for this test.
		NSTimeInterval originalTestTimeout = Expecta.asynchronousTestTimeout;
		Expecta.asynchronousTestTimeout = 1.1f;
		expect(deallocd).will.beTruthy();
		Expecta.asynchronousTestTimeout = originalTestTimeout;
	});

	it(@"should retain signals when subscribing", ^{
		__block BOOL deallocd = NO;

		RACDisposable *disposable;
		@autoreleasepool {
			@autoreleasepool {
				@autoreleasepool {
					RACSignal *signal __attribute__((objc_precise_lifetime)) = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
						return nil;
					}];
					
					[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
						deallocd = YES;
					}]];
					
					disposable = [signal subscribeCompleted:^{}];
				}
				
				// Spin the run loop to account for RAC magic that retains the
				// signal for a single iteration.
				[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
			}
			
			expect(deallocd).to.beFalsy();
			
			[disposable dispose];
		}
		expect(deallocd).will.beTruthy();
	});

	it(@"should retain intermediate signals when subscribing", ^{
		RACSubject *subject = [RACSubject subject];
		expect(subject).notTo.beNil();

		__block BOOL gotNext = NO;
		__block BOOL completed = NO;

		RACDisposable *disposable;

		@autoreleasepool {
			@autoreleasepool {
				RACSignal *intermediateSignal = [subject doNext:^(id _) {
					gotNext = YES;
				}];

				expect(intermediateSignal).notTo.beNil();

				disposable = [intermediateSignal subscribeCompleted:^{
					completed = YES;
				}];
			}

			// Spin the run loop to account for RAC magic that retains the
			// signal for a single iteration.
			[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
		}

		[subject sendNext:@5];
		expect(gotNext).to.beTruthy();

		[subject sendCompleted];
		expect(completed).to.beTruthy();
		
		[disposable dispose];
	});
});

describe(@"+merge:", ^{
	__block RACSubject *sub1;
	__block RACSubject *sub2;
	__block RACSignal *merged;
	beforeEach(^{
		sub1 = [RACSubject subject];
		sub2 = [RACSubject subject];
		merged = [RACSignal merge:@[ sub1, sub2 ].objectEnumerator];
	});

	it(@"should send all values from both signals", ^{
		NSMutableArray *values = [NSMutableArray array];
		[merged subscribeNext:^(id x) {
			[values addObject:x];
		}];

		[sub1 sendNext:@1];
		[sub2 sendNext:@2];
		[sub2 sendNext:@3];
		[sub1 sendNext:@4];

		NSArray *expected = @[ @1, @2, @3, @4 ];
		expect(values).to.equal(expected);
	});

	it(@"should send an error if one occurs", ^{
		__block NSError *errorReceived;
		[merged subscribeError:^(NSError *error) {
			errorReceived = error;
		}];

		[sub1 sendError:RACSignalTestError];
		expect(errorReceived).to.equal(RACSignalTestError);
	});

	it(@"should complete only after both signals complete", ^{
		NSMutableArray *values = [NSMutableArray array];
		__block BOOL completed = NO;
		[merged subscribeNext:^(id x) {
			[values addObject:x];
		} completed:^{
			completed = YES;
		}];

		[sub1 sendNext:@1];
		[sub2 sendNext:@2];
		[sub2 sendNext:@3];
		[sub2 sendCompleted];
		expect(completed).to.beFalsy();

		[sub1 sendNext:@4];
		[sub1 sendCompleted];
		expect(completed).to.beTruthy();

		NSArray *expected = @[ @1, @2, @3, @4 ];
		expect(values).to.equal(expected);
	});

	it(@"should complete immediately when not given any signals", ^{
		RACSignal *signal = [RACSignal merge:@[].objectEnumerator];

		__block BOOL completed = NO;
		[signal subscribeCompleted:^{
			completed = YES;
		}];

		expect(completed).to.beTruthy();
	});

	it(@"should complete only after both signals complete for any number of subscribers", ^{
		__block BOOL completed1 = NO;
		__block BOOL completed2 = NO;
		[merged subscribeCompleted:^{
			completed1 = YES;
		}];

		[merged subscribeCompleted:^{
			completed2 = YES;
		}];

		expect(completed1).to.beFalsy();
		expect(completed2).to.beFalsy();

		[sub1 sendCompleted];
		[sub2 sendCompleted];
		expect(completed1).to.beTruthy();
		expect(completed2).to.beTruthy();
	});
});

describe(@"-flatten:", ^{
	__block BOOL subscribedTo1 = NO;
	__block BOOL subscribedTo2 = NO;
	__block BOOL subscribedTo3 = NO;
	__block RACSignal *sub1;
	__block RACSignal *sub2;
	__block RACSignal *sub3;
	__block RACSubject *subject1;
	__block RACSubject *subject2;
	__block RACSubject *subject3;
	__block RACSubject *signalsSubject;
	__block NSMutableArray *values;
	
	beforeEach(^{
		subscribedTo1 = NO;
		subject1 = [RACSubject subject];
		sub1 = [RACSignal defer:^{
			subscribedTo1 = YES;
			return subject1;
		}];

		subscribedTo2 = NO;
		subject2 = [RACSubject subject];
		sub2 = [RACSignal defer:^{
			subscribedTo2 = YES;
			return subject2;
		}];

		subscribedTo3 = NO;
		subject3 = [RACSubject subject];
		sub3 = [RACSignal defer:^{
			subscribedTo3 = YES;
			return subject3;
		}];

		signalsSubject = [RACSubject subject];

		values = [NSMutableArray array];
	});

	describe(@"when its max is 0", ^{
		it(@"should merge all the signals concurrently", ^{
			[[signalsSubject flatten:0] subscribeNext:^(id x) {
				[values addObject:x];
			}];

			expect(subscribedTo1).to.beFalsy();
			expect(subscribedTo2).to.beFalsy();
			expect(subscribedTo3).to.beFalsy();

			[signalsSubject sendNext:sub1];
			[signalsSubject sendNext:sub2];

			expect(subscribedTo1).to.beTruthy();
			expect(subscribedTo2).to.beTruthy();
			expect(subscribedTo3).to.beFalsy();

			[subject1 sendNext:@1];

			[signalsSubject sendNext:sub3];
			
			expect(subscribedTo1).to.beTruthy();
			expect(subscribedTo2).to.beTruthy();
			expect(subscribedTo3).to.beTruthy();

			[subject1 sendCompleted];

			[subject2 sendNext:@2];
			[subject2 sendCompleted];

			[subject3 sendNext:@3];
			[subject3 sendCompleted];

			NSArray *expected = @[ @1, @2, @3 ];
			expect(values).to.equal(expected);
		});

		itShouldBehaveLike(RACSignalMergeConcurrentCompletionExampleGroup, @{ RACSignalMaxConcurrent: @0 });
	});

	describe(@"when its max is > 0", ^{
		it(@"should merge only the given number at a time", ^{
			[[signalsSubject flatten:1] subscribeNext:^(id x) {
				[values addObject:x];
			}];

			expect(subscribedTo1).to.beFalsy();
			expect(subscribedTo2).to.beFalsy();
			expect(subscribedTo3).to.beFalsy();

			[signalsSubject sendNext:sub1];
			[signalsSubject sendNext:sub2];

			expect(subscribedTo1).to.beTruthy();
			expect(subscribedTo2).to.beFalsy();
			expect(subscribedTo3).to.beFalsy();

			[subject1 sendNext:@1];

			[signalsSubject sendNext:sub3];

			expect(subscribedTo1).to.beTruthy();
			expect(subscribedTo2).to.beFalsy();
			expect(subscribedTo3).to.beFalsy();

			[signalsSubject sendCompleted];

			expect(subscribedTo1).to.beTruthy();
			expect(subscribedTo2).to.beFalsy();
			expect(subscribedTo3).to.beFalsy();

			[subject1 sendCompleted];

			expect(subscribedTo2).to.beTruthy();
			expect(subscribedTo3).to.beFalsy();

			[subject2 sendNext:@2];
			[subject2 sendCompleted];

			expect(subscribedTo3).to.beTruthy();

			[subject3 sendNext:@3];
			[subject3 sendCompleted];

			NSArray *expected = @[ @1, @2, @3 ];
			expect(values).to.equal(expected);
		});

		itShouldBehaveLike(RACSignalMergeConcurrentCompletionExampleGroup, @{ RACSignalMaxConcurrent: @1 });
	});

	it(@"shouldn't create a retain cycle", ^{
		__block BOOL subjectDeallocd = NO;
		__block BOOL signalDeallocd = NO;
		@autoreleasepool {
			RACSubject *subject __attribute__((objc_precise_lifetime)) = [RACSubject subject];
			[subject.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				subjectDeallocd = YES;
			}]];

			RACSignal *signal __attribute__((objc_precise_lifetime)) = [subject flatten];
			[signal.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
				signalDeallocd = YES;
			}]];

			[signal subscribeCompleted:^{}];

			[subject sendCompleted];
		}

		expect(subjectDeallocd).will.beTruthy();
		expect(signalDeallocd).will.beTruthy();
	});

	it(@"should not crash when disposing while subscribing", ^{
		RACDisposable *disposable = [[signalsSubject flatten:0] subscribeCompleted:^{
		}];

		[signalsSubject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[disposable dispose];
			[subscriber sendCompleted];
			return nil;
		}]];

		[signalsSubject sendCompleted];
	});

	it(@"should dispose after last synchronous signal subscription and should not crash", ^{

		RACSignal *flattened = [signalsSubject flatten:1];
		RACDisposable *flattenDisposable = [flattened subscribeCompleted:^{}];

		RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) {
			expect(flattenDisposable.disposed).to.beFalsy();
			[subscriber sendCompleted];
			expect(flattenDisposable.disposed).to.beTruthy();
			return nil;
		}];

		RACSignal *asyncSignal = [sub1 delay:0];

		[signalsSubject sendNext:asyncSignal];
		[signalsSubject sendNext:syncSignal];

		[signalsSubject sendCompleted];

		[subject1 sendCompleted];

		expect(flattenDisposable.disposed).will.beTruthy();
	});

	it(@"should not crash when disposed because of takeUntil:", ^{
		for (int i = 0; i < 100; i++) {
			RACSubject *flattenedReceiver = [RACSubject subject];
			RACSignal *done = [flattenedReceiver map:^(NSNumber *n) {
				return @(n.integerValue == 1);
			}];

			RACSignal *flattened = [signalsSubject flatten:1];

			RACDisposable *flattenDisposable = [[flattened takeUntil:[done ignore:@NO]] subscribe:flattenedReceiver];

			RACSignal *syncSignal = [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) {
				expect(flattenDisposable.disposed).to.beFalsy();
				[subscriber sendNext:@1];
				expect(flattenDisposable.disposed).to.beTruthy();
				[subscriber sendCompleted];
				return nil;
			}];

			RACSignal *asyncSignal = [sub1 delay:0];
			[subject1 sendNext:@0];

			[signalsSubject sendNext:asyncSignal];
			[signalsSubject sendNext:syncSignal];
			[signalsSubject sendCompleted];

			[subject1 sendCompleted];

			expect(flattenDisposable.disposed).will.beTruthy();
		}
	});
});

describe(@"-switchToLatest", ^{
	__block RACSubject *subject;

	__block NSMutableArray *values;
	__block NSError *lastError = nil;
	__block BOOL completed = NO;

	beforeEach(^{
		subject = [RACSubject subject];

		values = [NSMutableArray array];
		lastError = nil;
		completed = NO;

		[[subject switchToLatest] subscribeNext:^(id x) {
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			[values addObject:x];
		} error:^(NSError *error) {
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			lastError = error;
		} completed:^{
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			completed = YES;
		}];
	});

	it(@"should send values from the most recent signal", ^{
		[subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:@2];
			return nil;
		}]];

		[subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@3];
			[subscriber sendNext:@4];
			return nil;
		}]];

		NSArray *expected = @[ @1, @2, @3, @4 ];
		expect(values).to.equal(expected);
	});

	it(@"should send errors from the most recent signal", ^{
		[subject sendNext:[RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
			return nil;
		}]];

		expect(lastError).notTo.beNil();
	});

	it(@"should not send completed if only the switching signal completes", ^{
		[subject sendNext:RACSignal.never];

		expect(completed).to.beFalsy();

		[subject sendCompleted];
		expect(completed).to.beFalsy();
	});
	
	it(@"should send completed when the switching signal completes and the last sent signal does", ^{
		[subject sendNext:RACSignal.empty];
		
		expect(completed).to.beFalsy();
		
		[subject sendCompleted];
		expect(completed).to.beTruthy();
	});

	it(@"should accept nil signals", ^{
		[subject sendNext:nil];
		[subject sendNext:[RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			[subscriber sendNext:@1];
			[subscriber sendNext:@2];
			return nil;
		}]];

		NSArray *expected = @[ @1, @2 ];
		expect(values).to.equal(expected);
	});

	it(@"should return a cold signal", ^{
		__block NSUInteger subscriptions = 0;
		RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			subscriptions++;
			[subscriber sendNext:[RACSignal empty]];
			return nil;
		}];

		RACSignal *switched = [signalOfSignals switchToLatest];

		[[switched publish] connect];
		expect(subscriptions).to.equal(1);

		[[switched publish] connect];
		expect(subscriptions).to.equal(2);
	});
});

describe(@"+switch:cases:default:", ^{
	__block RACSubject *keySubject;

	__block RACSubject *subjectZero;
	__block RACSubject *subjectOne;
	__block RACSubject *subjectTwo;

	__block RACSubject *defaultSubject;

	__block NSMutableArray *values;
	__block NSError *lastError = nil;
	__block BOOL completed = NO;

	beforeEach(^{
		keySubject = [RACSubject subject];

		subjectZero = [RACSubject subject];
		subjectOne = [RACSubject subject];
		subjectTwo = [RACSubject subject];

		defaultSubject = [RACSubject subject];

		values = [NSMutableArray array];
		lastError = nil;
		completed = NO;
	});

	describe(@"switching between values with a default", ^{
		__block RACSignal *switchSignal;

		beforeEach(^{
			switchSignal = [RACSignal switch:keySubject cases:@{
				@0: subjectZero,
				@1: subjectOne,
				@2: subjectTwo,
			} default:[RACSignal never]];

			[switchSignal subscribeNext:^(id x) {
				expect(lastError).to.beNil();
				expect(completed).to.beFalsy();

				[values addObject:x];
			} error:^(NSError *error) {
				expect(lastError).to.beNil();
				expect(completed).to.beFalsy();

				lastError = error;
			} completed:^{
				expect(lastError).to.beNil();
				expect(completed).to.beFalsy();

				completed = YES;
			}];
		});

		it(@"should not send any values before a key is sent", ^{
			[subjectZero sendNext:RACUnit.defaultUnit];
			[subjectOne sendNext:RACUnit.defaultUnit];
			[subjectTwo sendNext:RACUnit.defaultUnit];

			expect(values).to.equal(@[]);
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();
		});

		it(@"should send events based on the latest key", ^{
			[keySubject sendNext:@0];

			[subjectZero sendNext:@"zero"];
			[subjectZero sendNext:@"zero"];
			[subjectOne sendNext:@"one"];
			[subjectTwo sendNext:@"two"];

			NSArray *expected = @[ @"zero", @"zero" ];
			expect(values).to.equal(expected);

			[keySubject sendNext:@1];

			[subjectZero sendNext:@"zero"];
			[subjectOne sendNext:@"one"];
			[subjectTwo sendNext:@"two"];

			expected = @[ @"zero", @"zero", @"one" ];
			expect(values).to.equal(expected);

			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			[keySubject sendNext:@2];

			[subjectZero sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
			[subjectOne sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
			expect(lastError).to.beNil();

			[subjectTwo sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
			expect(lastError).notTo.beNil();
		});

		it(@"should not send completed when only the key signal completes", ^{
			[keySubject sendNext:@0];
			[subjectZero sendNext:@"zero"];
			[keySubject sendCompleted];

			expect(values).to.equal(@[ @"zero" ]);
			expect(completed).to.beFalsy();
		});

		it(@"should send completed when the key signal and the latest sent signal complete", ^{
			[keySubject sendNext:@0];
			[subjectZero sendNext:@"zero"];
			[keySubject sendCompleted];
			[subjectZero sendCompleted];

			expect(values).to.equal(@[ @"zero" ]);
			expect(completed).to.beTruthy();
		});
	});

	it(@"should use the default signal if key that was sent does not have an associated signal", ^{
		[[RACSignal
			switch:keySubject
			cases:@{
				@0: subjectZero,
				@1: subjectOne,
			}
			default:defaultSubject]
			subscribeNext:^(id x) {
				[values addObject:x];
			}];

		[keySubject sendNext:@"not a valid key"];
		[defaultSubject sendNext:@"default"];

		expect(values).to.equal(@[ @"default" ]);

		[keySubject sendNext:nil];
		[defaultSubject sendNext:@"default"];

		expect(values).to.equal((@[ @"default", @"default" ]));
	});

	it(@"should send an error if key that was sent does not have an associated signal and there's no default", ^{
		[[RACSignal
			switch:keySubject
			cases:@{
				@0: subjectZero,
				@1: subjectOne,
			}
			default:nil]
			subscribeNext:^(id x) {
				[values addObject:x];
			} error:^(NSError *error) {
				lastError = error;
			}];

		[keySubject sendNext:@0];
		[subjectZero sendNext:@"zero"];

		expect(values).to.equal(@[ @"zero" ]);
		expect(lastError).to.beNil();

		[keySubject sendNext:nil];

		expect(values).to.equal(@[ @"zero" ]);
		expect(lastError).notTo.beNil();
		expect(lastError.domain).to.equal(RACSignalErrorDomain);
		expect(lastError.code).to.equal(RACSignalErrorNoMatchingCase);
	});

	it(@"should match RACTupleNil case when a nil value is sent", ^{
		[[RACSignal
			switch:keySubject
			cases:@{
				RACTupleNil.tupleNil: subjectZero,
			}
			default:defaultSubject]
			subscribeNext:^(id x) {
				[values addObject:x];
			}];

		[keySubject sendNext:nil];
		[subjectZero sendNext:@"zero"];
		expect(values).to.equal(@[ @"zero" ]);
	});
});

describe(@"+if:then:else", ^{
	__block RACSubject *boolSubject;
	__block RACSubject *trueSubject;
	__block RACSubject *falseSubject;

	__block NSMutableArray *values;
	__block NSError *lastError = nil;
	__block BOOL completed = NO;

	beforeEach(^{
		boolSubject = [RACSubject subject];
		trueSubject = [RACSubject subject];
		falseSubject = [RACSubject subject];

		values = [NSMutableArray array];
		lastError = nil;
		completed = NO;

		[[RACSignal if:boolSubject then:trueSubject else:falseSubject] subscribeNext:^(id x) {
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			[values addObject:x];
		} error:^(NSError *error) {
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			lastError = error;
		} completed:^{
			expect(lastError).to.beNil();
			expect(completed).to.beFalsy();

			completed = YES;
		}];
	});

	it(@"should not send any values before a boolean is sent", ^{
		[trueSubject sendNext:RACUnit.defaultUnit];
		[falseSubject sendNext:RACUnit.defaultUnit];

		expect(values).to.equal(@[]);
		expect(lastError).to.beNil();
		expect(completed).to.beFalsy();
	});

	it(@"should send events based on the latest boolean", ^{
		[boolSubject sendNext:@YES];

		[trueSubject sendNext:@"foo"];
		[falseSubject sendNext:@"buzz"];
		[trueSubject sendNext:@"bar"];

		NSArray *expected = @[ @"foo", @"bar" ];
		expect(values).to.equal(expected);
		expect(lastError).to.beNil();
		expect(completed).to.beFalsy();

		[boolSubject sendNext:@NO];

		[trueSubject sendNext:@"baz"];
		[falseSubject sendNext:@"buzz"];
		[trueSubject sendNext:@"barfoo"];

		expected = @[ @"foo", @"bar", @"buzz" ];
		expect(values).to.equal(expected);
		expect(lastError).to.beNil();
		expect(completed).to.beFalsy();

		[trueSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
		expect(lastError).to.beNil();

		[falseSubject sendError:[NSError errorWithDomain:@"" code:-1 userInfo:nil]];
		expect(lastError).notTo.beNil();
	});

	it(@"should not send completed when only the BOOL signal completes", ^{
		[boolSubject sendNext:@YES];
		[trueSubject sendNext:@"foo"];
		[boolSubject sendCompleted];
		
		expect(values).to.equal(@[ @"foo" ]);
		expect(completed).to.beFalsy();
	});

	it(@"should send completed when the BOOL signal and the latest sent signal complete", ^{
		[boolSubject sendNext:@YES];
		[trueSubject sendNext:@"foo"];
		[trueSubject sendCompleted];
		[boolSubject sendCompleted];

		expect(values).to.equal(@[ @"foo" ]);
		expect(completed).to.beTruthy();
	});
});

describe(@"+interval:onScheduler: and +interval:onScheduler:withLeeway:", ^{
	static const NSTimeInterval interval = 0.1;
	static const NSTimeInterval leeway = 0.2;
	
	__block void (^testTimer)(RACSignal *, NSNumber *, NSNumber *) = nil;
	
	before(^{
		testTimer = [^(RACSignal *timer, NSNumber *minInterval, NSNumber *leeway) {
			__block NSUInteger nextsReceived = 0;

			NSTimeInterval startTime = NSDate.timeIntervalSinceReferenceDate;
			[[timer take:3] subscribeNext:^(NSDate *date) {
				++nextsReceived;

				NSTimeInterval currentTime = date.timeIntervalSinceReferenceDate;

				// Uniformly distribute the expected interval for all
				// received values. We do this instead of saving a timestamp
				// because a delayed interval may cause the _next_ value to
				// send sooner than the interval.
				NSTimeInterval expectedMinInterval = minInterval.doubleValue * nextsReceived;
				NSTimeInterval expectedMaxInterval = expectedMinInterval + leeway.doubleValue * 3 + 0.05;

				expect(currentTime - startTime).beGreaterThanOrEqualTo(expectedMinInterval);
				expect(currentTime - startTime).beLessThanOrEqualTo(expectedMaxInterval);
			}];
			
			expect(nextsReceived).will.equal(3);
		} copy];
	});
	
	describe(@"+interval:onScheduler:", ^{
		it(@"should work on the main thread scheduler", ^{
			testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler], @(interval), @0);
		});
		
		it(@"should work on a background scheduler", ^{
			testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler]], @(interval), @0);
		});
	});
	
	describe(@"+interval:onScheduler:withLeeway:", ^{
		it(@"should work on the main thread scheduler", ^{
			testTimer([RACSignal interval:interval onScheduler:RACScheduler.mainThreadScheduler withLeeway:leeway], @(interval), @(leeway));
		});
		
		it(@"should work on a background scheduler", ^{
			testTimer([RACSignal interval:interval onScheduler:[RACScheduler scheduler] withLeeway:leeway], @(interval), @(leeway));
		});
	});
});

describe(@"-timeout:onScheduler:", ^{
	__block RACSubject *subject;

	beforeEach(^{
		subject = [RACSubject subject];
	});

	it(@"should time out", ^{
		RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];

		__block NSError *receivedError = nil;
		[[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) {
			receivedError = e;
		}];

		expect(receivedError).to.beNil();

		[scheduler stepAll];
		expect(receivedError).willNot.beNil();
		expect(receivedError.domain).to.equal(RACSignalErrorDomain);
		expect(receivedError.code).to.equal(RACSignalErrorTimedOut);
	});

	it(@"should pass through events while not timed out", ^{
		__block id next = nil;
		__block BOOL completed = NO;
		[[subject timeout:1 onScheduler:RACScheduler.mainThreadScheduler] subscribeNext:^(id x) {
			next = x;
		} completed:^{
			completed = YES;
		}];

		[subject sendNext:RACUnit.defaultUnit];
		expect(next).to.equal(RACUnit.defaultUnit);

		[subject sendCompleted];
		expect(completed).to.beTruthy();
	});

	it(@"should not time out after disposal", ^{
		RACTestScheduler *scheduler = [[RACTestScheduler alloc] init];

		__block NSError *receivedError = nil;
		RACDisposable *disposable = [[subject timeout:1 onScheduler:scheduler] subscribeError:^(NSError *e) {
			receivedError = e;
		}];

		[disposable dispose];
		[scheduler stepAll];
		expect(receivedError).to.beNil();
	});
});

describe(@"-delay:", ^{
	__block RACSubject *subject;
	__block RACSignal *delayedSignal;

	beforeEach(^{
		subject = [RACSubject subject];
		delayedSignal = [subject delay:0];
	});

	it(@"should delay nexts", ^{
		__block id next = nil;
		[delayedSignal subscribeNext:^(id x) {
			next = x;
		}];

		[subject sendNext:@"foo"];
		expect(next).to.beNil();
		expect(next).will.equal(@"foo");
	});

	it(@"should delay completed", ^{
		__block BOOL completed = NO;
		[delayedSignal subscribeCompleted:^{
			completed = YES;
		}];

		[subject sendCompleted];
		expect(completed).to.beFalsy();
		expect(completed).will.beTruthy();
	});

	it(@"should not delay errors", ^{
		__block NSError *error = nil;
		[delayedSignal subscribeError:^(NSError *e) {
			error = e;
		}];

		[subject sendError:RACSignalTestError];
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should cancel delayed events when disposed", ^{
		__block id next = nil;
		RACDisposable *disposable = [delayedSignal subscribeNext:^(id x) {
			next = x;
		}];

		[subject sendNext:@"foo"];

		__block BOOL done = NO;
		[RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
			done = YES;
		}];

		[disposable dispose];

		expect(done).will.beTruthy();
		expect(next).to.beNil();
	});
});

describe(@"-catch:", ^{
	it(@"should subscribe to ensuing signal on error", ^{
		RACSubject *subject = [RACSubject subject];

		RACSignal *signal = [subject catch:^(NSError *error) {
			return [RACSignal return:@41];
		}];

		__block id value = nil;
		[signal subscribeNext:^(id x) {
			value = x;
		}];

		[subject sendError:RACSignalTestError];
		expect(value).to.equal(@41);
	});

	it(@"should prevent source error from propagating", ^{
		RACSubject *subject = [RACSubject subject];

		RACSignal *signal = [subject catch:^(NSError *error) {
			return [RACSignal empty];
		}];

		__block BOOL errorReceived = NO;
		[signal subscribeError:^(NSError *error) {
			errorReceived = YES;
		}];

		[subject sendError:RACSignalTestError];
		expect(errorReceived).to.beFalsy();
	});

	it(@"should propagate error from ensuing signal", ^{
		RACSubject *subject = [RACSubject subject];

		NSError *secondaryError = [NSError errorWithDomain:@"bubs" code:41 userInfo:nil];
		RACSignal *signal = [subject catch:^(NSError *error) {
			return [RACSignal error:secondaryError];
		}];

		__block NSError *errorReceived = nil;
		[signal subscribeError:^(NSError *error) {
			errorReceived = error;
		}];

		[subject sendError:RACSignalTestError];
		expect(errorReceived).to.equal(secondaryError);
	});

	it(@"should dispose ensuing signal", ^{
		RACSubject *subject = [RACSubject subject];

		__block BOOL disposed = NO;
		RACSignal *signal = [subject catch:^(NSError *error) {
			return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
				return [RACDisposable disposableWithBlock:^{
					disposed = YES;
				}];
			}];
		}];

		RACDisposable *disposable = [signal subscribeCompleted:^{}];
		[subject sendError:RACSignalTestError];
		[disposable dispose];

		expect(disposed).will.beTruthy();
	});
});

describe(@"-try:", ^{
	__block RACSubject *subject;
	__block NSError *receivedError;
	__block NSMutableArray *nextValues;
	__block BOOL completed;
	
	beforeEach(^{
		subject = [RACSubject subject];
		nextValues = [NSMutableArray array];
		completed = NO;
		receivedError = nil;
		
		[[subject try:^(NSString *value, NSError **error) {
			if (value != nil) return YES;
			
			if (error != nil) *error = RACSignalTestError;
			
			return NO;
		}] subscribeNext:^(id x) {
			[nextValues addObject:x];
		} error:^(NSError *error) {
			receivedError = error;
		} completed:^{
			completed = YES;
		}];
	});
	
	it(@"should pass values while YES is returned from the tryBlock", ^{
		[subject sendNext:@"foo"];
		[subject sendNext:@"bar"];
		[subject sendNext:@"baz"];
		[subject sendNext:@"buzz"];
		[subject sendCompleted];
		
		NSArray *receivedValues = [nextValues copy];
		NSArray *expectedValues = @[ @"foo", @"bar", @"baz", @"buzz" ];
		
		expect(receivedError).to.beNil();
		expect(receivedValues).to.equal(expectedValues);
		expect(completed).to.beTruthy();
	});
	
	it(@"should pass values until NO is returned from the tryBlock", ^{
		[subject sendNext:@"foo"];
		[subject sendNext:@"bar"];
		[subject sendNext:nil];
		[subject sendNext:@"buzz"];
		[subject sendCompleted];
		
		NSArray *receivedValues = [nextValues copy];
		NSArray *expectedValues = @[ @"foo", @"bar" ];
		
		expect(receivedError).to.equal(RACSignalTestError);
		expect(receivedValues).to.equal(expectedValues);
		expect(completed).to.beFalsy();
	});
});

describe(@"-tryMap:", ^{
	__block RACSubject *subject;
	__block NSError *receivedError;
	__block NSMutableArray *nextValues;
	__block BOOL completed;
	
	beforeEach(^{
		subject = [RACSubject subject];
		nextValues = [NSMutableArray array];
		completed = NO;
		receivedError = nil;
		
		[[subject tryMap:^ id (NSString *value, NSError **error) {
			if (value != nil) return [NSString stringWithFormat:@"%@_a", value];
			
			if (error != nil) *error = RACSignalTestError;

			return nil;
		}] subscribeNext:^(id x) {
			[nextValues addObject:x];
		} error:^(NSError *error) {
			receivedError = error;
		} completed:^{
			completed = YES;
		}];
	});
	
	it(@"should map values with the mapBlock", ^{
		[subject sendNext:@"foo"];
		[subject sendNext:@"bar"];
		[subject sendNext:@"baz"];
		[subject sendNext:@"buzz"];
		[subject sendCompleted];

		NSArray *receivedValues = [nextValues copy];
		NSArray *expectedValues = @[ @"foo_a", @"bar_a", @"baz_a", @"buzz_a" ];
		
		expect(receivedError).to.beNil();
		expect(receivedValues).to.equal(expectedValues);
		expect(completed).to.beTruthy();
	});
	
	it(@"should map values with the mapBlock, until the mapBlock returns nil", ^{
		[subject sendNext:@"foo"];
		[subject sendNext:@"bar"];
		[subject sendNext:nil];
		[subject sendNext:@"buzz"];
		[subject sendCompleted];
		
		NSArray *receivedValues = [nextValues copy];
		NSArray *expectedValues = @[ @"foo_a", @"bar_a" ];
		
		expect(receivedError).to.equal(RACSignalTestError);
		expect(receivedValues).to.equal(expectedValues);
		expect(completed).to.beFalsy();
	});
});

describe(@"throttling", ^{
	__block RACSubject *subject;

	beforeEach(^{
		subject = [RACSubject subject];
	});

	describe(@"-throttle:", ^{
		__block RACSignal *throttledSignal;

		beforeEach(^{
			throttledSignal = [subject throttle:0];
		});

		it(@"should throttle nexts", ^{
			NSMutableArray *valuesReceived = [NSMutableArray array];
			[throttledSignal subscribeNext:^(id x) {
				[valuesReceived addObject:x];
			}];

			[subject sendNext:@"foo"];
			[subject sendNext:@"bar"];
			expect(valuesReceived).to.equal(@[]);

			NSArray *expected = @[ @"bar" ];
			expect(valuesReceived).will.equal(expected);

			[subject sendNext:@"buzz"];
			expect(valuesReceived).to.equal(expected);

			expected = @[ @"bar", @"buzz" ];
			expect(valuesReceived).will.equal(expected);
		});

		it(@"should forward completed immediately", ^{
			__block BOOL completed = NO;
			[throttledSignal subscribeCompleted:^{
				completed = YES;
			}];

			[subject sendCompleted];
			expect(completed).to.beTruthy();
		});

		it(@"should forward errors immediately", ^{
			__block NSError *error = nil;
			[throttledSignal subscribeError:^(NSError *e) {
				error = e;
			}];

			[subject sendError:RACSignalTestError];
			expect(error).to.equal(RACSignalTestError);
		});

		it(@"should cancel future nexts when disposed", ^{
			__block id next = nil;
			RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) {
				next = x;
			}];

			[subject sendNext:@"foo"];

			__block BOOL done = NO;
			[RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
				done = YES;
			}];

			[disposable dispose];

			expect(done).will.beTruthy();
			expect(next).to.beNil();
		});
	});

	describe(@"-throttle:valuesPassingTest:", ^{
		__block RACSignal *throttledSignal;
		__block BOOL shouldThrottle;

		beforeEach(^{
			shouldThrottle = YES;

			__block id value = nil;
			throttledSignal = [[subject
				doNext:^(id x) {
					value = x;
				}]
				throttle:0 valuesPassingTest:^(id x) {
					// Make sure that we're given the latest value.
					expect(x).to.beIdenticalTo(value);

					return shouldThrottle;
				}];

			expect(throttledSignal).notTo.beNil();
		});

		describe(@"nexts", ^{
			__block NSMutableArray *valuesReceived;
			__block NSMutableArray *expected;

			beforeEach(^{
				expected = [[NSMutableArray alloc] init];
				valuesReceived = [[NSMutableArray alloc] init];

				[throttledSignal subscribeNext:^(id x) {
					[valuesReceived addObject:x];
				}];
			});

			it(@"should forward unthrottled values immediately", ^{
				shouldThrottle = NO;
				[subject sendNext:@"foo"];

				[expected addObject:@"foo"];
				expect(valuesReceived).to.equal(expected);
			});

			it(@"should delay throttled values", ^{
				[subject sendNext:@"bar"];
				expect(valuesReceived).to.equal(expected);

				[expected addObject:@"bar"];
				expect(valuesReceived).will.equal(expected);
			});

			it(@"should drop buffered values when a throttled value arrives", ^{
				[subject sendNext:@"foo"];
				[subject sendNext:@"bar"];
				[subject sendNext:@"buzz"];
				expect(valuesReceived).to.equal(expected);

				[expected addObject:@"buzz"];
				expect(valuesReceived).will.equal(expected);
			});

			it(@"should drop buffered values when an immediate value arrives", ^{
				[subject sendNext:@"foo"];
				[subject sendNext:@"bar"];

				shouldThrottle = NO;
				[subject sendNext:@"buzz"];
				[expected addObject:@"buzz"];
				expect(valuesReceived).to.equal(expected);

				// Make sure that nothing weird happens when sending another
				// throttled value.
				shouldThrottle = YES;
				[subject sendNext:@"baz"];
				expect(valuesReceived).to.equal(expected);

				[expected addObject:@"baz"];
				expect(valuesReceived).will.equal(expected);
			});

			it(@"should not be resent upon completion", ^{
				[subject sendNext:@"bar"];
				[expected addObject:@"bar"];
				expect(valuesReceived).will.equal(expected);

				[subject sendCompleted];
				expect(valuesReceived).to.equal(expected);
			});
		});

		it(@"should forward completed immediately", ^{
			__block BOOL completed = NO;
			[throttledSignal subscribeCompleted:^{
				completed = YES;
			}];

			[subject sendCompleted];
			expect(completed).to.beTruthy();
		});

		it(@"should forward errors immediately", ^{
			__block NSError *error = nil;
			[throttledSignal subscribeError:^(NSError *e) {
				error = e;
			}];

			[subject sendError:RACSignalTestError];
			expect(error).to.equal(RACSignalTestError);
		});

		it(@"should cancel future nexts when disposed", ^{
			__block id next = nil;
			RACDisposable *disposable = [throttledSignal subscribeNext:^(id x) {
				next = x;
			}];

			[subject sendNext:@"foo"];

			__block BOOL done = NO;
			[RACScheduler.mainThreadScheduler after:[NSDate date] schedule:^{
				done = YES;
			}];

			[disposable dispose];

			expect(done).will.beTruthy();
			expect(next).to.beNil();
		});
	});
});

describe(@"-then:", ^{
	it(@"should continue onto returned signal", ^{
		RACSubject *subject = [RACSubject subject];

		__block id value = nil;
		[[subject then:^{
			return [RACSignal return:@2];
		}] subscribeNext:^(id x) {
			value = x;
		}];

		[subject sendNext:@1];

		// The value shouldn't change until the first signal completes.
		expect(value).to.beNil();

		[subject sendCompleted];

		expect(value).to.equal(@2);
	});

	it(@"should sequence even if no next value is sent", ^{
		RACSubject *subject = [RACSubject subject];

		__block id value = nil;
		[[subject then:^{
			return [RACSignal return:RACUnit.defaultUnit];
		}] subscribeNext:^(id x) {
			value = x;
		}];

		[subject sendCompleted];

		expect(value).to.equal(RACUnit.defaultUnit);
	});
});

describe(@"-sequence", ^{
	RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
		[subscriber sendNext:@1];
		[subscriber sendNext:@2];
		[subscriber sendNext:@3];
		[subscriber sendNext:@4];
		[subscriber sendCompleted];
		return nil;
	}];

	itShouldBehaveLike(RACSequenceExamples, ^{
		return @{
			RACSequenceExampleSequence: signal.sequence,
			RACSequenceExampleExpectedValues: @[ @1, @2, @3, @4 ]
		};
	});
});

it(@"should complete take: even if the original signal doesn't", ^{
	RACSignal *sendOne = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
		[subscriber sendNext:RACUnit.defaultUnit];
		return nil;
	}];

	__block id value = nil;
	__block BOOL completed = NO;
	[[sendOne take:1] subscribeNext:^(id received) {
		value = received;
	} completed:^{
		completed = YES;
	}];

	expect(value).to.equal(RACUnit.defaultUnit);
	expect(completed).to.beTruthy();
});

describe(@"+zip:", ^{
	__block RACSubject *subject1 = nil;
	__block RACSubject *subject2 = nil;
	__block BOOL hasSentError = NO;
	__block BOOL hasSentCompleted = NO;
	__block RACDisposable *disposable = nil;
	__block void (^send2NextAndErrorTo1)(void) = nil;
	__block void (^send3NextAndErrorTo1)(void) = nil;
	__block void (^send2NextAndCompletedTo2)(void) = nil;
	__block void (^send3NextAndCompletedTo2)(void) = nil;
	
	before(^{
		send2NextAndErrorTo1 = [^{
			[subject1 sendNext:@1];
			[subject1 sendNext:@2];
			[subject1 sendError:RACSignalTestError];
		} copy];
		send3NextAndErrorTo1 = [^{
			[subject1 sendNext:@1];
			[subject1 sendNext:@2];
			[subject1 sendNext:@3];
			[subject1 sendError:RACSignalTestError];
		} copy];
		send2NextAndCompletedTo2 = [^{
			[subject2 sendNext:@1];
			[subject2 sendNext:@2];
			[subject2 sendCompleted];
		} copy];
		send3NextAndCompletedTo2 = [^{
			[subject2 sendNext:@1];
			[subject2 sendNext:@2];
			[subject2 sendNext:@3];
			[subject2 sendCompleted];
		} copy];
		subject1 = [RACSubject subject];
		subject2 = [RACSubject subject];
		hasSentError = NO;
		hasSentCompleted = NO;
		disposable = [[RACSignal zip:@[ subject1, subject2 ]] subscribeError:^(NSError *error) {
			hasSentError = YES;
		} completed:^{
			hasSentCompleted = YES;
		}];
	});
	
	after(^{
		[disposable dispose];
	});
	
	it(@"should complete as soon as no new zipped values are possible", ^{
		[subject1 sendNext:@1];
		[subject2 sendNext:@1];
		expect(hasSentCompleted).to.beFalsy();
		
		[subject1 sendNext:@2];
		[subject1 sendCompleted];
		expect(hasSentCompleted).to.beFalsy();
		
		[subject2 sendNext:@2];
		expect(hasSentCompleted).to.beTruthy();
	});
	
	it(@"outcome should not be dependent on order of signals", ^{
		[subject2 sendCompleted];
		expect(hasSentCompleted).to.beTruthy();
	});
    
	it(@"should forward errors sent earlier than (time-wise) and before (position-wise) a complete", ^{
		send2NextAndErrorTo1();
		send3NextAndCompletedTo2();
		expect(hasSentError).to.beTruthy();
		expect(hasSentCompleted).to.beFalsy();
	});
	
	it(@"should forward errors sent earlier than (time-wise) and after (position-wise) a complete", ^{
		send3NextAndErrorTo1();
		send2NextAndCompletedTo2();
		expect(hasSentError).to.beTruthy();
		expect(hasSentCompleted).to.beFalsy();
	});
	
	it(@"should forward errors sent later than (time-wise) and before (position-wise) a complete", ^{
		send3NextAndCompletedTo2();
		send2NextAndErrorTo1();
		expect(hasSentError).to.beTruthy();
		expect(hasSentCompleted).to.beFalsy();
	});
	
	it(@"should ignore errors sent later than (time-wise) and after (position-wise) a complete", ^{
		send2NextAndCompletedTo2();
		send3NextAndErrorTo1();
		expect(hasSentError).to.beFalsy();
		expect(hasSentCompleted).to.beTruthy();
	});
	
	it(@"should handle signals sending values unevenly", ^{
		__block NSError *receivedError = nil;
		__block BOOL hasCompleted = NO;
		
		RACSubject *a = [RACSubject subject];
		RACSubject *b = [RACSubject subject];
		RACSubject *c = [RACSubject subject];
		
		NSMutableArray *receivedValues = NSMutableArray.array;
		NSArray *expectedValues = nil;
		
		[[RACSignal zip:@[ a, b, c ] reduce:^(NSNumber *a, NSNumber *b, NSNumber *c) {
			return [NSString stringWithFormat:@"%@%@%@", a, b, c];
		}] subscribeNext:^(id x) {
			[receivedValues addObject:x];
		} error:^(NSError *error) {
			receivedError = error;
		} completed:^{
			hasCompleted = YES;
		}];
		
		[a sendNext:@1];
		[a sendNext:@2];
		[a sendNext:@3];
		
		[b sendNext:@1];
		
		[c sendNext:@1];
		[c sendNext:@2];
		
		// a: [===......]
		// b: [=........]
		// c: [==.......]
		
		expectedValues = @[ @"111" ];
		expect(receivedValues).to.equal(expectedValues);
		expect(receivedError).to.beNil();
		expect(hasCompleted).to.beFalsy();
		
		[b sendNext:@2];
		[b sendNext:@3];
		[b sendNext:@4];
		[b sendCompleted];
		
		// a: [===......]
		// b: [====C....]
		// c: [==.......]
		
		expectedValues = @[ @"111", @"222" ];
		expect(receivedValues).to.equal(expectedValues);
		expect(receivedError).to.beNil();
		expect(hasCompleted).to.beFalsy();
		
		[c sendNext:@3];
		[c sendNext:@4];
		[c sendNext:@5];
		[c sendError:RACSignalTestError];
		
		// a: [===......]
		// b: [====C....]
		// c: [=====E...]
		
		expectedValues = @[ @"111", @"222", @"333" ];
		expect(receivedValues).to.equal(expectedValues);
		expect(receivedError).to.equal(RACSignalTestError);
		expect(hasCompleted).to.beFalsy();
		
		[a sendNext:@4];
		[a sendNext:@5];
		[a sendNext:@6];
		[a sendNext:@7];
		
		// a: [=======..]
		// b: [====C....]
		// c: [=====E...]
		
		expectedValues = @[ @"111", @"222", @"333" ];
		expect(receivedValues).to.equal(expectedValues);
		expect(receivedError).to.equal(RACSignalTestError);
		expect(hasCompleted).to.beFalsy();
	});
	
	it(@"should handle multiples of the same side-effecting signal", ^{
		__block NSUInteger counter = 0;
		RACSignal *sideEffectingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			++counter;
			[subscriber sendNext:@1];
			[subscriber sendCompleted];
			return nil;
		}];
		RACSignal *combined = [RACSignal zip:@[ sideEffectingSignal, sideEffectingSignal ] reduce:^ NSString * (id x, id y) {
			return [NSString stringWithFormat:@"%@%@", x, y];
		}];
		NSMutableArray *receivedValues = NSMutableArray.array;
		
		expect(counter).to.equal(0);
		
		[combined subscribeNext:^(id x) {
			[receivedValues addObject:x];
		}];
		
		expect(counter).to.equal(2);
		expect(receivedValues).to.equal(@[ @"11" ]);
	});
});

describe(@"-sample:", ^{
	it(@"should send the latest value when the sampler signal fires", ^{
		RACSubject *subject = [RACSubject subject];
		RACSubject *sampleSubject = [RACSubject subject];
		RACSignal *sampled = [subject sample:sampleSubject];
		NSMutableArray *values = [NSMutableArray array];
		[sampled subscribeNext:^(id x) {
			[values addObject:x];
		}];
		
		[sampleSubject sendNext:RACUnit.defaultUnit];
		expect(values).to.equal(@[]);
		
		[subject sendNext:@1];
		[subject sendNext:@2];
		expect(values).to.equal(@[]);

		[sampleSubject sendNext:RACUnit.defaultUnit];
		NSArray *expected = @[ @2 ];
		expect(values).to.equal(expected);

		[subject sendNext:@3];
		expect(values).to.equal(expected);

		[sampleSubject sendNext:RACUnit.defaultUnit];
		expected = @[ @2, @3 ];
		expect(values).to.equal(expected);

		[sampleSubject sendNext:RACUnit.defaultUnit];
		expected = @[ @2, @3, @3 ];
		expect(values).to.equal(expected);
	});
});

describe(@"-collect", ^{
	__block RACSubject *subject;
	__block RACSignal *collected;

	__block id value;
	__block BOOL hasCompleted;

	beforeEach(^{
		subject = [RACSubject subject];
		collected = [subject collect];
		
		value = nil;
		hasCompleted = NO;
		
		[collected subscribeNext:^(id x) {
			value = x;
		} completed:^{
			hasCompleted = YES;
		}];
	});
	
	it(@"should send a single array when the original signal completes", ^{
		NSArray *expected = @[ @1, @2, @3 ];

		[subject sendNext:@1];
		[subject sendNext:@2];
		[subject sendNext:@3];
		expect(value).to.beNil();

		[subject sendCompleted];
		expect(value).to.equal(expected);
		expect(hasCompleted).to.beTruthy();
	});

	it(@"should add NSNull to an array for nil values", ^{
		NSArray *expected = @[ NSNull.null, @1, NSNull.null ];
		
		[subject sendNext:nil];
		[subject sendNext:@1];
		[subject sendNext:nil];
		expect(value).to.beNil();
		
		[subject sendCompleted];
		expect(value).to.equal(expected);
		expect(hasCompleted).to.beTruthy();
	});
});

describe(@"-bufferWithTime:", ^{
	__block RACTestScheduler *scheduler;

	__block RACSubject *input;
	__block RACSignal *bufferedInput;
	__block RACTuple *latestValue;

	beforeEach(^{
		scheduler = [[RACTestScheduler alloc] init];

		input = [RACSubject subject];
		bufferedInput = [input bufferWithTime:1 onScheduler:scheduler];
		latestValue = nil;

		[bufferedInput subscribeNext:^(RACTuple *x) {
			latestValue = x;
		}];
	});

	it(@"should buffer nexts", ^{
		[input sendNext:@1];
		[input sendNext:@2];

		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@1, @2));
		
		[input sendNext:@3];
		[input sendNext:@4];

		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@3, @4));
	});

	it(@"should not perform buffering until a value is sent", ^{
		[input sendNext:@1];
		[input sendNext:@2];
		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@1, @2));

		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@1, @2));
		
		[input sendNext:@3];
		[input sendNext:@4];
		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@3, @4));
	});

	it(@"should flush any buffered nexts upon completion", ^{
		[input sendNext:@1];
		[input sendCompleted];
		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(@1));
	});

	it(@"should support NSNull values", ^{
		[input sendNext:NSNull.null];
		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(NSNull.null));
	});

	it(@"should buffer nil values", ^{
		[input sendNext:nil];
		[scheduler stepAll];
		expect(latestValue).to.equal(RACTuplePack(nil));
	});
});

describe(@"-concat", ^{
	__block RACSubject *subject;

	__block RACSignal *oneSignal;
	__block RACSignal *twoSignal;
	__block RACSignal *threeSignal;

	__block RACSignal *errorSignal;
	__block RACSignal *completedSignal;

	beforeEach(^{
		subject = [RACReplaySubject subject];

		oneSignal = [RACSignal return:@1];
		twoSignal = [RACSignal return:@2];
		threeSignal = [RACSignal return:@3];

		errorSignal = [RACSignal error:RACSignalTestError];
		completedSignal = RACSignal.empty;
	});

	it(@"should concatenate the values of inner signals", ^{
		[subject sendNext:oneSignal];
		[subject sendNext:twoSignal];
		[subject sendNext:completedSignal];
		[subject sendNext:threeSignal];

		NSMutableArray *values = [NSMutableArray array];
		[[subject concat] subscribeNext:^(id x) {
			[values addObject:x];
		}];

		NSArray *expected = @[ @1, @2, @3 ];
		expect(values).to.equal(expected);
	});

	it(@"should complete only after all signals complete", ^{
		RACReplaySubject *valuesSubject = [RACReplaySubject subject];

		[subject sendNext:valuesSubject];
		[subject sendCompleted];

		[valuesSubject sendNext:@1];
		[valuesSubject sendNext:@2];
		[valuesSubject sendCompleted];

		NSArray *expected = @[ @1, @2 ];
		expect([[subject concat] toArray]).to.equal(expected);
	});

	it(@"should pass through errors", ^{
		[subject sendNext:errorSignal];
		
		NSError *error = nil;
		[[subject concat] firstOrDefault:nil success:NULL error:&error];
		expect(error).to.equal(RACSignalTestError);
	});

	it(@"should concat signals sent later", ^{
		[subject sendNext:oneSignal];

		NSMutableArray *values = [NSMutableArray array];
		[[subject concat] subscribeNext:^(id x) {
			[values addObject:x];
		}];

		NSArray *expected = @[ @1 ];
		expect(values).to.equal(expected);

		[subject sendNext:[twoSignal delay:0]];

		expected = @[ @1, @2 ];
		expect(values).will.equal(expected);

		[subject sendNext:threeSignal];

		expected = @[ @1, @2, @3 ];
		expect(values).to.equal(expected);
	});

	it(@"should dispose the current signal", ^{
		__block BOOL disposed = NO;
		RACSignal *innerSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				disposed = YES;
			}];
		}];

		RACDisposable *concatDisposable = [[subject concat] subscribeCompleted:^{}];
		
		[subject sendNext:innerSignal];
		expect(disposed).notTo.beTruthy();

		[concatDisposable dispose];
		expect(disposed).to.beTruthy();
	});

	it(@"should dispose later signals", ^{
		__block BOOL disposed = NO;
		RACSignal *laterSignal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
			return [RACDisposable disposableWithBlock:^{
				disposed = YES;
			}];
		}];

		RACSubject *firstSignal = [RACSubject subject];
		RACSignal *outerSignal = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:firstSignal];
			[subscriber sendNext:laterSignal];
			return nil;
		}];

		RACDisposable *concatDisposable = [[outerSignal concat] subscribeCompleted:^{}];

		[firstSignal sendCompleted];
		expect(disposed).notTo.beTruthy();

		[concatDisposable dispose];
		expect(disposed).to.beTruthy();
	});
});

describe(@"-initially:", ^{
	__block RACSubject *subject;

	__block NSUInteger initiallyInvokedCount;
	__block RACSignal *signal;

	beforeEach(^{
		subject = [RACSubject subject];

		initiallyInvokedCount = 0;
		signal = [subject initially:^{
			++initiallyInvokedCount;
		}];
	});

	it(@"should not run without a subscription", ^{
		[subject sendCompleted];
		expect(initiallyInvokedCount).to.equal(0);
	});

	it(@"should run on subscription", ^{
		[signal subscribe:[RACSubscriber new]];
		expect(initiallyInvokedCount).to.equal(1);
	});

	it(@"should re-run for each subscription", ^{
		[signal subscribe:[RACSubscriber new]];
		[signal subscribe:[RACSubscriber new]];
		expect(initiallyInvokedCount).to.equal(2);
	});
});

describe(@"-finally:", ^{
	__block RACSubject *subject;

	__block BOOL finallyInvoked;
	__block RACSignal *signal;

	beforeEach(^{
		subject = [RACSubject subject];
		
		finallyInvoked = NO;
		signal = [subject finally:^{
			finallyInvoked = YES;
		}];
	});

	it(@"should not run finally without a subscription", ^{
		[subject sendCompleted];
		expect(finallyInvoked).to.beFalsy();
	});

	describe(@"with a subscription", ^{
		__block RACDisposable *disposable;

		beforeEach(^{
			disposable = [signal subscribeCompleted:^{}];
		});
		
		afterEach(^{
			[disposable dispose];
		});

		it(@"should not run finally upon next", ^{
			[subject sendNext:RACUnit.defaultUnit];
			expect(finallyInvoked).to.beFalsy();
		});

		it(@"should run finally upon completed", ^{
			[subject sendCompleted];
			expect(finallyInvoked).to.beTruthy();
		});

		it(@"should run finally upon error", ^{
			[subject sendError:nil];
			expect(finallyInvoked).to.beTruthy();
		});
	});
});

describe(@"-ignoreValues", ^{
	__block RACSubject *subject;

	__block BOOL gotNext;
	__block BOOL gotCompleted;
	__block NSError *receivedError;

	beforeEach(^{
		subject = [RACSubject subject];

		gotNext = NO;
		gotCompleted = NO;
		receivedError = nil;

		[[subject ignoreValues] subscribeNext:^(id _) {
			gotNext = YES;
		} error:^(NSError *error) {
			receivedError = error;
		} completed:^{
			gotCompleted = YES;
		}];
	});

	it(@"should skip nexts and pass through completed", ^{
		[subject sendNext:RACUnit.defaultUnit];
		[subject sendCompleted];

		expect(gotNext).to.beFalsy();
		expect(gotCompleted).to.beTruthy();
		expect(receivedError).to.beNil();
	});

	it(@"should skip nexts and pass through errors", ^{
		[subject sendNext:RACUnit.defaultUnit];
		[subject sendError:RACSignalTestError];

		expect(gotNext).to.beFalsy();
		expect(gotCompleted).to.beFalsy();
		expect(receivedError).to.equal(RACSignalTestError);
	});
});

describe(@"-materialize", ^{
	it(@"should convert nexts and completed into RACEvents", ^{
		NSArray *events = [[[RACSignal return:RACUnit.defaultUnit] materialize] toArray];
		NSArray *expected = @[
			[RACEvent eventWithValue:RACUnit.defaultUnit],
			RACEvent.completedEvent
		];

		expect(events).to.equal(expected);
	});

	it(@"should convert errors into RACEvents and complete", ^{
		NSArray *events = [[[RACSignal error:RACSignalTestError] materialize] toArray];
		NSArray *expected = @[ [RACEvent eventWithError:RACSignalTestError] ];
		expect(events).to.equal(expected);
	});
});

describe(@"-dematerialize", ^{
	it(@"should convert nexts from RACEvents", ^{
		RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:[RACEvent eventWithValue:@1]];
			[subscriber sendNext:[RACEvent eventWithValue:@2]];
			[subscriber sendCompleted];
			return nil;
		}];

		NSArray *expected = @[ @1, @2 ];
		expect([[events dematerialize] toArray]).to.equal(expected);
	});

	it(@"should convert completed from a RACEvent", ^{
		RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:[RACEvent eventWithValue:@1]];
			[subscriber sendNext:RACEvent.completedEvent];
			[subscriber sendNext:[RACEvent eventWithValue:@2]];
			[subscriber sendCompleted];
			return nil;
		}];

		NSArray *expected = @[ @1 ];
		expect([[events dematerialize] toArray]).to.equal(expected);
	});

	it(@"should convert error from a RACEvent", ^{
		RACSignal *events = [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
			[subscriber sendNext:[RACEvent eventWithError:RACSignalTestError]];
			[subscriber sendNext:[RACEvent eventWithValue:@1]];
			[subscriber sendCompleted];
			return nil;
		}];

		__block NSError *error = nil;
		expect([[events dematerialize] firstOrDefault:nil success:NULL error:&error]).to.beNil();
		expect(error).to.equal(RACSignalTestError);
	});
});

describe(@"-not", ^{
	it(@"should invert every BOOL sent", ^{
		RACSubject *subject = [RACReplaySubject subject];
		[subject sendNext:@NO];
		[subject sendNext:@YES];
		[subject sendCompleted];
		NSArray *results = [[subject not] toArray];
		NSArray *expected = @[ @YES, @NO ];
		expect(results).to.equal(expected);
	});
});

describe(@"-and", ^{
	it(@"should return YES if all YES values are sent", ^{
		RACSubject *subject = [RACReplaySubject subject];
		
		[subject sendNext:RACTuplePack(@YES, @NO, @YES)];
		[subject sendNext:RACTuplePack(@NO, @NO, @NO)];
		[subject sendNext:RACTuplePack(@YES, @YES, @YES)];
		[subject sendCompleted];
		
		NSArray *results = [[subject and] toArray];
		NSArray *expected = @[ @NO, @NO, @YES ];
		
		expect(results).to.equal(expected);
	});
});

describe(@"-or", ^{
	it(@"should return YES for any YES values sent", ^{
		RACSubject *subject = [RACReplaySubject subject];
		
		[subject sendNext:RACTuplePack(@YES, @NO, @YES)];
		[subject sendNext:RACTuplePack(@NO, @NO, @NO)];
		[subject sendCompleted];
		
		NSArray *results = [[subject or] toArray];
		NSArray *expected = @[ @YES, @NO ];
		
		expect(results).to.equal(expected);
	});
});

describe(@"-groupBy:", ^{
	it(@"should send completed to all grouped signals.", ^{
		RACSubject *subject = [RACReplaySubject subject];

		__block NSUInteger groupedSignalCount = 0;
		__block NSUInteger completedGroupedSignalCount = 0;
		[[subject groupBy:^(NSNumber *number) {
			return @(floorf(number.floatValue));
		}] subscribeNext:^(RACGroupedSignal *groupedSignal) {
			++groupedSignalCount;

			[groupedSignal subscribeCompleted:^{
				++completedGroupedSignalCount;
			}];
		}];

		[subject sendNext:@1];
		[subject sendNext:@2];
		[subject sendCompleted];

		expect(completedGroupedSignalCount).to.equal(groupedSignalCount);
	});

	it(@"should send error to all grouped signals.", ^{
		RACSubject *subject = [RACReplaySubject subject];

		__block NSUInteger groupedSignalCount = 0;
		__block NSUInteger erroneousGroupedSignalCount = 0;
		[[subject groupBy:^(NSNumber *number) {
			return @(floorf(number.floatValue));
		}] subscribeNext:^(RACGroupedSignal *groupedSignal) {
			++groupedSignalCount;

			[groupedSignal subscribeError:^(NSError *error) {
				++erroneousGroupedSignalCount;

				expect(error.domain).to.equal(@"TestDomain");
				expect(error.code).to.equal(123);
			}];
		}];

		[subject sendNext:@1];
		[subject sendNext:@2];
		[subject sendError:[NSError errorWithDomain:@"TestDomain" code:123 userInfo:nil]];

		expect(erroneousGroupedSignalCount).to.equal(groupedSignalCount);
	});
});

describe(@"starting signals", ^{
	describe(@"+startLazilyWithScheduler:block:", ^{
		itBehavesLike(RACSignalStartSharedExamplesName, ^{
			NSArray *expectedValues = @[ @42, @43 ];
			RACScheduler *scheduler = [RACScheduler scheduler];
			RACSignal *signal = [RACSignal startLazilyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
				for (id value in expectedValues) {
					[subscriber sendNext:value];
				}
				[subscriber sendCompleted];
			}];
			return @{
				RACSignalStartSignal: signal,
				RACSignalStartExpectedValues: expectedValues,
				RACSignalStartExpectedScheduler: scheduler,
			};
		});

		__block NSUInteger invokedCount = 0;
		__block void (^subscribe)(void);

		beforeEach(^{
			invokedCount = 0;

			RACSignal *signal = [RACSignal startLazilyWithScheduler:RACScheduler.immediateScheduler block:^(id<RACSubscriber> subscriber) {
				invokedCount++;
				[subscriber sendNext:@42];
				[subscriber sendCompleted];
			}];

			subscribe = [^{
				[signal subscribe:[RACSubscriber subscriberWithNext:nil error:nil completed:nil]];
			} copy];
		});

		it(@"should only invoke the block on subscription", ^{
			expect(invokedCount).to.equal(0);
			subscribe();
			expect(invokedCount).to.equal(1);
		});

		it(@"should only invoke the block once", ^{
			expect(invokedCount).to.equal(0);
			subscribe();
			expect(invokedCount).to.equal(1);
			subscribe();
			expect(invokedCount).to.equal(1);
			subscribe();
			expect(invokedCount).to.equal(1);
		});

		it(@"should invoke the block on the given scheduler", ^{
			RACScheduler *scheduler = [RACScheduler scheduler];
			__block RACScheduler *currentScheduler;
			[[[RACSignal
				startLazilyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
					currentScheduler = RACScheduler.currentScheduler;
				}]
				publish]
				connect];

			expect(currentScheduler).will.equal(scheduler);
		});
	});

	describe(@"+startEagerlyWithScheduler:block:", ^{
		itBehavesLike(RACSignalStartSharedExamplesName, ^{
			NSArray *expectedValues = @[ @42, @43 ];
			RACScheduler *scheduler = [RACScheduler scheduler];
			RACSignal *signal = [RACSignal startEagerlyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
				for (id value in expectedValues) {
					[subscriber sendNext:value];
				}
				[subscriber sendCompleted];
			}];
			return @{
				RACSignalStartSignal: signal,
				RACSignalStartExpectedValues: expectedValues,
				RACSignalStartExpectedScheduler: scheduler,
			};
		});

		it(@"should immediately invoke the block", ^{
			__block BOOL blockInvoked = NO;
			[RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber> subscriber) {
				blockInvoked = YES;
			}];

			expect(blockInvoked).will.beTruthy();
		});

		it(@"should only invoke the block once", ^{
			__block NSUInteger invokedCount = 0;
			RACSignal *signal = [RACSignal startEagerlyWithScheduler:RACScheduler.immediateScheduler block:^(id<RACSubscriber> subscriber) {
				invokedCount++;
			}];

			expect(invokedCount).to.equal(1);

			[[signal publish] connect];
			expect(invokedCount).to.equal(1);

			[[signal publish] connect];
			expect(invokedCount).to.equal(1);
		});

		it(@"should invoke the block on the given scheduler", ^{
			RACScheduler *scheduler = [RACScheduler scheduler];
			__block RACScheduler *currentScheduler;
			[RACSignal startEagerlyWithScheduler:scheduler block:^(id<RACSubscriber> subscriber) {
				currentScheduler = RACScheduler.currentScheduler;
			}];

			expect(currentScheduler).will.equal(scheduler);
		});
	});
});

describe(@"-toArray", ^{
	__block RACSubject *subject;
	
	beforeEach(^{
		subject = [RACReplaySubject subject];
	});
	
	it(@"should return an array which contains NSNulls for nil values", ^{
		NSArray *expected = @[ NSNull.null, @1, NSNull.null ];
		
		[subject sendNext:nil];
		[subject sendNext:@1];
		[subject sendNext:nil];
		[subject sendCompleted];
		
		expect([subject toArray]).to.equal(expected);
	});

	it(@"should return nil upon error", ^{
		[subject sendError:nil];
		expect([subject toArray]).to.beNil();
	});

	it(@"should return nil upon error even if some nexts were sent", ^{
		[subject sendNext:@1];
		[subject sendNext:@2];
		[subject sendError:nil];
		
		expect([subject toArray]).to.beNil();
	});
});

describe(@"-ignore:", ^{
	it(@"should ignore nil", ^{
		RACSignal *signal = [[RACSignal
			createSignal:^ id (id<RACSubscriber> subscriber) {
				[subscriber sendNext:@1];
				[subscriber sendNext:nil];
				[subscriber sendNext:@3];
				[subscriber sendNext:@4];
				[subscriber sendNext:nil];
				[subscriber sendCompleted];
				return nil;
			}]
			ignore:nil];
		
		NSArray *expected = @[ @1, @3, @4 ];
		expect([signal toArray]).to.equal(expected);
	});
});

describe(@"-replayLazily", ^{
	__block NSUInteger subscriptionCount;
	__block BOOL disposed;

	__block RACSignal *signal;
	__block RACSubject *disposeSubject;
	__block RACSignal *replayedSignal;

	beforeEach(^{
		subscriptionCount = 0;
		disposed = NO;

		signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
			subscriptionCount++;
			[subscriber sendNext:RACUnit.defaultUnit];

			RACDisposable *schedulingDisposable = [RACScheduler.mainThreadScheduler schedule:^{
				[subscriber sendNext:RACUnit.defaultUnit];
				[subscriber sendCompleted];
			}];

			return [RACDisposable disposableWithBlock:^{
				[schedulingDisposable dispose];
				disposed = YES;
			}];
		}];

		disposeSubject = [RACSubject subject];
		replayedSignal = [[signal takeUntil:disposeSubject] replayLazily];
	});

	it(@"should forward the input signal upon subscription", ^{
		expect(subscriptionCount).to.equal(0);

		expect([replayedSignal asynchronouslyWaitUntilCompleted:NULL]).to.beTruthy();
		expect(subscriptionCount).to.equal(1);
	});

	it(@"should replay the input signal for future subscriptions", ^{
		NSArray *events = [[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL];
		expect(events).notTo.beNil();

		expect([[[replayedSignal materialize] collect] asynchronousFirstOrDefault:nil success:NULL error:NULL]).to.equal(events);
		expect(subscriptionCount).to.equal(1);
	});

	it(@"should replay even after disposal", ^{
		__block NSUInteger valueCount = 0;
		[replayedSignal subscribeNext:^(id x) {
			valueCount++;
		}];

		[disposeSubject sendCompleted];
		expect(valueCount).to.equal(1);
		expect([[replayedSignal toArray] count]).to.equal(valueCount);
	});
});

SpecEnd
