blob: 9ad4c3b2b73ca12f4454f14411158e279e0e8bcf [file] [log] [blame]
//
// RACBacktraceSpec.m
// ReactiveCocoa
//
// Created by Justin Spahr-Summers on 2012-12-24.
// Copyright (c) 2012 GitHub, Inc. All rights reserved.
//
#import "RACBacktrace.h"
#import "NSArray+RACSequenceAdditions.h"
#import "RACReplaySubject.h"
#import "RACScheduler.h"
#import "RACSequence.h"
#import "RACSignal+Operations.h"
#ifdef DEBUG
static RACBacktrace *previousBacktrace;
static void capturePreviousBacktrace(void *context) {
previousBacktrace = [RACBacktrace backtrace].previousThreadBacktrace;
}
typedef struct {
dispatch_queue_t queue;
NSUInteger i;
__unsafe_unretained RACSubject *doneSubject;
} RACDeepRecursionContext;
static void recurseDeeply(void *ptr) {
RACDeepRecursionContext *context = ptr;
if (context->i++ < 10000) {
rac_dispatch_async_f(context->queue, context, recurseDeeply);
} else {
[context->doneSubject sendCompleted];
}
}
SpecBegin(RACBacktrace)
__block dispatch_block_t block;
beforeEach(^{
expect([RACBacktrace backtrace].previousThreadBacktrace).to.beNil();
previousBacktrace = nil;
block = ^{
capturePreviousBacktrace(NULL);
};
});
it(@"should capture the current backtrace", ^{
RACBacktrace *backtrace = [RACBacktrace backtrace];
expect(backtrace).notTo.beNil();
});
describe(@"with a GCD queue", ^{
__block dispatch_queue_t queue;
beforeEach(^{
queue = dispatch_queue_create("com.github.ReactiveCocoa.RACBacktraceSpec", DISPATCH_QUEUE_SERIAL);
});
afterEach(^{
dispatch_barrier_sync(queue, ^{});
dispatch_release(queue);
});
it(@"should trace across dispatch_async", ^{
rac_dispatch_async(queue, block);
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_async to the main thread", ^{
rac_dispatch_async(queue, ^{
rac_dispatch_async(dispatch_get_main_queue(), block);
});
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_async_f", ^{
rac_dispatch_async_f(queue, NULL, &capturePreviousBacktrace);
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_barrier_async", ^{
rac_dispatch_barrier_async(queue, block);
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_barrier_async_f", ^{
rac_dispatch_barrier_async_f(queue, NULL, &capturePreviousBacktrace);
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_after", ^{
rac_dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1), queue, block);
expect(previousBacktrace).willNot.beNil();
});
it(@"should trace across dispatch_after_f", ^{
rac_dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, 1), queue, NULL, &capturePreviousBacktrace);
expect(previousBacktrace).willNot.beNil();
});
it(@"shouldn't overflow the stack when deallocating a huge backtrace list", ^{
RACSubject *doneSubject = [RACReplaySubject subject];
RACDeepRecursionContext context = {
.queue = queue,
.i = 0,
.doneSubject = doneSubject
};
rac_dispatch_async_f(queue, &context, &recurseDeeply);
[doneSubject waitUntilCompleted:NULL];
});
});
it(@"should trace across a RACScheduler", ^{
[[RACScheduler scheduler] schedule:block];
expect(previousBacktrace).willNot.beNil();
});
it(@"shouldn't go bonkers with RACScheduler", ^{
NSMutableArray *a = [NSMutableArray array];
for (NSUInteger i = 0; i < 5000; i++) {
[a addObject:@(i)];
}
[[a.rac_sequence signalWithScheduler:[RACScheduler scheduler]] subscribeCompleted:^{}];
});
// Tracing across NSOperationQueue only works on OS X because it depends on
// interposing through dynamic linking
#ifndef __IPHONE_OS_VERSION_MIN_REQUIRED
it(@"should trace across an NSOperationQueue", ^{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:block];
expect(previousBacktrace).willNot.beNil();
});
#endif
SpecEnd
#endif