Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #64 from cppforlife/master

Exception symbolication
  • Loading branch information...
commit e15b039c09b0ba56b1e2971a040371ab3a1e17c3 2 parents 2b010e7 + 39862bb
@cppforlife cppforlife authored
View
8 Cedar.xcodeproj/project.pbxproj
@@ -71,6 +71,8 @@
96A07F0B13F276B10021974D /* FocusedSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F0A13F276B10021974D /* FocusedSpec.m */; };
96A07F0F13F27F2F0021974D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F0E13F27F2F0021974D /* main.m */; };
96A07F1113F283E40021974D /* FocusedSpec2.m in Sources */ = {isa = PBXBuildFile; fileRef = 96A07F1013F283E40021974D /* FocusedSpec2.m */; };
+ 96A805E416D9B29C005F87FA /* CDRSpecFailureSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */; };
+ 96A805E516D9B29C005F87FA /* CDRSpecFailureSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96EA1CB9142C6560001A78E0 /* CDRSpecFailureSpec.mm */; };
96B5918F1630F5840068EA5E /* ObjCHeadersSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96B5918E1630F5840068EA5E /* ObjCHeadersSpec.mm */; };
96B591911630F5B10068EA5E /* ObjCHeadersSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 96B5918E1630F5840068EA5E /* ObjCHeadersSpec.mm */; };
96B5F9F9144A81A7000A6A5D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96B5F9F8144A81A7000A6A5D /* UIKit.framework */; };
@@ -1533,6 +1535,7 @@
96158AA3144A9210005895CE /* DummyModel.m in Sources */,
96E807BD1491C6D200388D9D /* OCUnitAppLogicTestsWithSenTestingKit.m in Sources */,
968F9581161AC50800A78D36 /* CDRSymbolicatorSpec.mm in Sources */,
+ 96A805E516D9B29C005F87FA /* CDRSpecFailureSpec.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1563,6 +1566,7 @@
96D34485144A845600352C4A /* OCUnitApplicationTests.mm in Sources */,
96E807BB1491BC7500388D9D /* OCUnitApplicationTestsWithSenTestingKit.m in Sources */,
968F9582161AC58200A78D36 /* CDRSymbolicatorSpec.mm in Sources */,
+ 96A805E416D9B29C005F87FA /* CDRSpecFailureSpec.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1838,6 +1842,7 @@
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "OCUnitAppLogicTests/OCUnitAppLogicTests-Prefix.pch";
INFOPLIST_FILE = "OCUnitAppLogicTests/OCUnitAppLogicTests-Info.plist";
@@ -1994,6 +1999,7 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(DEVELOPER_LIBRARY_DIR)/Frameworks",
);
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "OCUnitAppTests/OCUnitAppTests-Prefix.pch";
INFOPLIST_FILE = "OCUnitAppTests/OCUnitAppTests-Info.plist";
@@ -2162,6 +2168,7 @@
"\"$(SRCROOT)\"",
);
GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "";
INSTALL_PATH = /usr/local/bin;
@@ -2268,6 +2275,7 @@
"$(inherited)",
"\"$(SRCROOT)\"",
);
+ GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/UIKit.framework/Headers/UIKit.h";
HEADER_SEARCH_PATHS = "";
View
6 README.markdown
@@ -421,6 +421,12 @@ you can take advantage of the ability to specify multiple reporters like so:
By default, the XML file will be written to `build/TEST-Cedar.xml` but this
path can be overridden with the `CEDAR_JUNIT_XML_FILE` env variable.
+### Exception Symbolication
+
+By default Cedar does not symbolicate exceptions that caused test failures,
+since symbolicating many exceptions can become a lengthy operation;
+however, this feature can be turned on with `CEDAR_SYMBOLICATE_EXCEPTIONS`.
+
## Code Snippets
View
29 Source/CDRDefaultReporter.m
@@ -1,6 +1,7 @@
#import "CDRDefaultReporter.h"
#import "CDRExample.h"
#import "CDRExampleGroup.h"
+#import "CDRSymbolicator.h"
#import "SpecHelper.h"
#import "CDRSlowTestStatistics.h"
@@ -92,7 +93,8 @@ - (NSString *)failureToken {
}
- (NSString *)failureMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"FAILURE %@\n%@\n",[example fullText], example.failure];
+ return [NSString stringWithFormat:@"FAILURE %@\n%@\n",
+ example.fullText, example.failure];
}
- (NSString *)errorToken {
@@ -100,10 +102,33 @@ - (NSString *)errorToken {
}
- (NSString *)errorMessageForExample:(CDRExample *)example {
- return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n", [example fullText], example.failure];
+ return [NSString stringWithFormat:@"EXCEPTION %@\n%@\n%@",
+ example.fullText, example.failure,
+ [self callStackSymbolsForFailure:example.failure]];
+}
+
+- (NSString *)callStackSymbolsForFailure:(CDRSpecFailure *)failure {
+ // Currently to symbolicate an exception
+ // we shell out to atos; thus this opt-in setting.
+ if (!getenv("CEDAR_SYMBOLICATE_EXCEPTIONS")) return nil;
+
+ NSError *error = nil;
+ NSString *callStackSymbols =
+ [failure callStackSymbolicatedSymbols:&error];
+
+ if (error.domain == kCDRSymbolicatorErrorDomain) {
+ if (error.code == kCDRSymbolicatorErrorNotSuccessful) {
+ NSString *details = [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey];
+ printf("Exception symbolication was not successful.\n"
+ "To turn it off remove CEDAR_SYMBOLICATE_EXCEPTIONS.\n"
+ "Details:\n%s\n", details.UTF8String);
+ }
+ }
+ return callStackSymbols;
}
#pragma mark Private interface
+
- (void)printMessages:(NSArray *)messages {
printf("\n");
View
16 Source/CDRSpec.m
@@ -130,7 +130,21 @@ - (void)markAsFocusedClosestToLineNumber:(NSUInteger)lineNumber {
// wrong lines of code if there are errors present in the code
// - also __LINE__ is unrolled from the outermost block
// which causes incorrect values
- [self.symbolicator symbolicateAddresses:addresses];
+ NSError *error = nil;
+ [self.symbolicator symbolicateAddresses:addresses error:&error];
+
+ if (error.domain == kCDRSymbolicatorErrorDomain) {
+ if (error.code == kCDRSymbolicatorErrorNotAvailable) {
+ printf("Spec location symbolication is not available.\n");
+ } else if (error.code == kCDRSymbolicatorErrorNotSuccessful) {
+ NSString *details = [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey];
+ printf("Spec location symbolication was not successful.\n"
+ "Details:\n%s\n", details.UTF8String);
+ } else {
+ printf("Spec location symbolication failed.\n");
+ }
+ return;
+ }
int bestAddressIndex = [children indexOfObject:self.rootGroup];
View
51 Source/CDRSpecFailure.m
@@ -1,4 +1,5 @@
#import "CDRSpecFailure.h"
+#import "CDRSymbolicator.h"
#import <regex.h>
@interface CDRSpecFailure ()
@@ -9,7 +10,10 @@ + (BOOL)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNum
@implementation CDRSpecFailure
-@synthesize fileName = fileName_, lineNumber = lineNumber_;
+@synthesize
+ fileName = fileName_,
+ lineNumber = lineNumber_,
+ callStackReturnAddresses = callStackReturnAddresses_;
+ (id)specFailureWithReason:(NSString *)reason {
return [[[self alloc] initWithReason:reason] autorelease];
@@ -41,15 +45,22 @@ - (id)initWithRaisedObject:(NSObject *)object {
NSString *reason = nil;
[[self class] extractReason:&reason fileName:&fileName lineNumber:&lineNumber fromObject:object];
- if ((self = [super initWithName:@"Spec Failure" reason:[reason retain] userInfo:nil])) {
+ if ((self = [super initWithName:@"Spec Failure" reason:reason userInfo:nil])) {
fileName_ = [fileName retain];
lineNumber_ = lineNumber;
+
+ // NSException subclasses tend to have
+ // -callStackReturnAddresses and -callStackSymbols.
+ if ([object respondsToSelector:@selector(callStackReturnAddresses)]) {
+ callStackReturnAddresses_ = [[(id)object callStackReturnAddresses] retain];
+ }
}
return self;
}
- (void)dealloc {
[fileName_ release];
+ [callStackReturnAddresses_ release];
[super dealloc];
}
@@ -60,7 +71,41 @@ - (NSString *)description {
return self.reason;
}
-#pragma mark Private Interface
+- (NSString *)callStackSymbolicatedSymbols:(NSError **)error {
+ if (!self.callStackReturnAddresses) return nil;
+
+ CDRSymbolicator *symbolicator =
+ [[[CDRSymbolicator alloc] init] autorelease];
+
+ NSError *symbolicationError = nil;
+ [symbolicator symbolicateAddresses:self.callStackReturnAddresses error:&symbolicationError];
+ if (symbolicationError) {
+ *error = symbolicationError;
+ return nil;
+ }
+
+ NSMutableString *result =
+ [NSMutableString stringWithString:@"Call stack:\n"];
+ BOOL previousAddressWasUnknown = NO;
+
+ for (int i=0; i < self.callStackReturnAddresses.count; i++) {
+ NSUInteger address = [[self.callStackReturnAddresses objectAtIndex:i] unsignedIntegerValue];
+ NSString *fileName = [symbolicator fileNameForStackAddress:address];
+ unsigned long lineNumber = [symbolicator lineNumberForStackAddress:address];
+
+ if (fileName) {
+ previousAddressWasUnknown = NO;
+ [result appendFormat:@" *%@:%lu\n", fileName, lineNumber];
+ } else if (!previousAddressWasUnknown) {
+ previousAddressWasUnknown = YES;
+ [result appendString:@" ...\n"];
+ }
+ }
+ return result;
+}
+
+#pragma mark - Private Interface
+
+ (void)extractReason:(NSString **)reason fileName:(NSString **)fileName lineNumber:(int *)lineNumber fromObject:(NSObject *)object {
if ([object isKindOfClass:[NSException class]]) {
NSDictionary *userInfo = [(NSException *)object userInfo];
View
79 Source/CDRSymbolicator.m
@@ -4,6 +4,9 @@
#import <libunwind.h>
#import <regex.h>
+const NSString *kCDRSymbolicatorErrorDomain = @"kCDRSymbolicatorErrorDomain";
+const NSString *kCDRSymbolicatorErrorMessageKey = @"kCDRSymbolicatorErrorMessage";
+
NSUInteger CDRCallerStackAddress() {
#if __arm__ // libunwind functions are not available
return 0;
@@ -62,24 +65,65 @@ - (NSUInteger)lineNumberForStackAddress:(NSUInteger)address {
return (index == NSNotFound) ? 0 : [[self.lineNumbers objectAtIndex:index] unsignedIntegerValue];
}
-- (void)symbolicateAddresses:(NSArray *)addresses {
+- (void)symbolicateAddresses:(NSArray *)addresses error:(NSError **)error {
+#if __arm__
+ *error = self.buildNotAvailableError;
+ return;
+#else
NSArray *validAddresses = [self.class validAddresses:addresses];
+ if (validAddresses.count == 0) {
+ *error = self.buildNoAddressesError;
+ return;
+ }
CDRAtosTask *atosTask = [CDRAtosTask taskForCurrentTestExecutable];
atosTask.addresses = validAddresses;
[atosTask launch];
+ BOOL atLeastOneSuccessfulSymbolication = NO;
+
for (int i=0; i<validAddresses.count; i++) {
NSString *fileName = nil;
NSNumber *lineNumber = [NSNumber numberWithInt:0];
[atosTask valuesOnLineNumber:i fileName:&fileName lineNumber:&lineNumber];
if (fileName) {
+ atLeastOneSuccessfulSymbolication = YES;
[self.addresses addObject:[validAddresses objectAtIndex:i]];
[self.fileNames addObject:fileName];
[self.lineNumbers addObject:lineNumber];
}
}
+
+ if (!atLeastOneSuccessfulSymbolication) {
+ *error = self.buildNotSuccessfulError;
+ return;
+ }
+#endif
+}
+
+- (NSError *)buildNoAddressesError {
+ NSString *message = @"Must provide at least one address.\n";
+ return [self buildErrorWithCode:kCDRSymbolicatorErrorNoAddresses message:message];
+}
+
+- (NSError *)buildNotAvailableError {
+ NSString *message = @"atos is not available to symbolicate.\n";
+ return [self buildErrorWithCode:kCDRSymbolicatorErrorNotAvailable message:message];
+}
+
+- (NSError *)buildNotSuccessfulError {
+ NSString *message =
+ @"atos was not able to symbolicate.\n"
+ "Try setting compiler Optimization Level to None (-O0).\n";
+ return [self buildErrorWithCode:kCDRSymbolicatorErrorNotSuccessful message:message];
+}
+
+- (NSError *)buildErrorWithCode:(NSUInteger)code message:(NSString *)message {
+ NSDictionary *details =
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ message, kCDRSymbolicatorErrorMessageKey, nil];
+ return [NSError errorWithDomain:(id)kCDRSymbolicatorErrorDomain code:code userInfo:details];
}
+ (NSArray *)validAddresses:(NSArray *)addresses {
@@ -103,6 +147,7 @@ - (void)setArguments:(NSArray *)arguments;
- (void)setEnvironment:(NSDictionary *)dict;
- (void)setStandardOutput:(NSPipe *)pipe;
+- (void)setStandardError:(NSPipe *)pipe;
- (void)launch;
- (void)waitUntilExit;
@end
@@ -132,6 +177,15 @@ - (void)dealloc {
}
- (void)launch {
+ // atos must have at least one address to symbolicate
+ // because it will otherwise wait for stdin.
+ if (self.addresses.count == 0) {
+ [[NSException
+ exceptionWithName:NSInvalidArgumentException
+ reason:@"Must provide at least one address"
+ userInfo:nil] raise];
+ }
+
NSMutableArray *arguments = [NSMutableArray arrayWithObjects:@"-o", self.executablePath, nil];
// Position-independent executables addresses need to be adjusted hence the slide argument
@@ -176,9 +230,6 @@ - (void)valuesOnLineNumber:(NSUInteger)line fileName:(NSString **)fileName lineN
encoding:NSUTF8StringEncoding] autorelease];
*lineNumber = [NSNumber numberWithInteger:lineNumberStr.integerValue];
- } else {
- printf("atos was not able to symbolicate '%s'.\n", buf);
- printf("Try setting compiler Optimization Level to None (-O0).\n");
}
free(matches);
}
@@ -189,12 +240,24 @@ + (NSString *)shellOutWithCommand:(NSString *)command arguments:(NSArray *)argum
[task setLaunchPath:command];
[task setArguments:arguments];
- NSPipe *pipe = [NSPipe pipe];
- [task setStandardOutput:pipe];
- [task launch];
+ NSPipe *standardOutput = [NSPipe pipe];
+ if (standardOutput) {
+ [task setStandardOutput:standardOutput];
+ [task setStandardError:standardOutput];
+ } else return nil;
+
+ @try {
+ [task launch];
+ } @catch (NSException *exception) {
+ // e.g. NSInvalidArgumentException reason: 'launch path is invalid'
+ if (exception.name == NSInvalidArgumentException) {
+ return nil;
+ } else @throw;
+ }
+
+ NSData *data = [[standardOutput fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
- NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
[task release];
View
4 Source/Headers/CDRSpecFailure.h
@@ -3,10 +3,12 @@
@interface CDRSpecFailure : NSException {
NSString *fileName_;
int lineNumber_;
+ NSArray *callStackReturnAddresses_;
}
@property (nonatomic, retain, readonly) NSString *fileName;
@property (nonatomic, assign, readonly) int lineNumber;
+@property (nonatomic, retain, readonly) NSArray *callStackReturnAddresses;
+ (id)specFailureWithReason:(NSString *)reason;
+ (id)specFailureWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
@@ -16,4 +18,6 @@
- (id)initWithReason:(NSString *)reason fileName:(NSString *)fileName lineNumber:(int)lineNumber;
- (id)initWithRaisedObject:(NSObject *)object;
+- (NSString *)callStackSymbolicatedSymbols:(NSError **)error;
+
@end
View
13 Source/Headers/CDRSymbolicator.h
@@ -2,10 +2,17 @@
NSUInteger CDRCallerStackAddress();
-@interface CDRSymbolicator : NSObject
-- (id)init;
-- (void)symbolicateAddresses:(NSArray *)addresses;
+extern NSString *kCDRSymbolicatorErrorDomain;
+extern NSString *kCDRSymbolicatorErrorMessageKey;
+
+typedef enum {
+ kCDRSymbolicatorErrorNotAvailable = 100,
+ kCDRSymbolicatorErrorNotSuccessful,
+ kCDRSymbolicatorErrorNoAddresses,
+} kCDRSymbolicatorError;
+@interface CDRSymbolicator : NSObject
+- (void)symbolicateAddresses:(NSArray *)addresses error:(NSError **)error;
- (NSString *)fileNameForStackAddress:(NSUInteger)address;
- (NSUInteger)lineNumberForStackAddress:(NSUInteger)address;
@end
View
103 Spec/CDRSpecFailureSpec.mm
@@ -8,8 +8,11 @@
#endif
#import "CDRSpecFailure.h"
+#import "CDRSymbolicator.h"
using namespace Cedar::Matchers;
+using namespace Cedar::Doubles;
+
static NSDictionary *reasonsToFileNames;
SPEC_BEGIN(CDRSpecFailureSpec)
@@ -145,6 +148,106 @@
});
});
});
+
+ describe(@"-callStackSymbolicatedSymbols", ^{
+ __block NSString *symbols;
+ __block NSError *error;
+ __block id raisedObject;
+
+ subjectAction(^{
+ CDRSpecFailure *failure =
+ [CDRSpecFailure specFailureWithRaisedObject:raisedObject];
+ error = nil;
+ symbols = [failure callStackSymbolicatedSymbols:&error];
+ });
+
+ context(@"when raised object provides call stack return addresses", ^{
+ void (^objectRaiser)(void) = ^{ [[NSException exceptionWithName:@"name" reason:@"reason" userInfo:nil] raise]; };
+
+ beforeEach(^{
+ // Raise and then catch actual exception
+ // to populate its callStackReturnAddresses.
+ @try {
+ objectRaiser();
+ } @catch (NSException *e) {
+ raisedObject = e;
+ }
+ });
+
+#if __arm__
+ context(@"when symbolication is not available (devices)", ^{
+ it(@"returns nil", ^{
+ symbols should be_nil;
+ });
+
+ it(@"sets not available error", ^{
+ error.domain should equal(kCDRSymbolicatorErrorDomain);
+ error.code should equal(kCDRSymbolicatorErrorNotAvailable);
+ [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey] \
+ should be_instance_of([NSString class]).or_any_subclass();
+ });
+ });
+#else
+ context(@"when symbolication is available (mac, simulator)", ^{
+ context(@"when symbolication is successful", ^{
+ it(@"returns string with symbolicated call stack "
+ "showing originating error location closest to the top", ^{
+ symbols should contain(
+ @" *CDRSpecFailureSpec.mm:165\n"
+ " *CDRSpecFailureSpec.mm:171\n"
+ );
+ });
+
+ it(@"indicates unsymbolicated portions of call stack with '...'", ^{
+ symbols should contain(
+ @"Call stack:\n"
+ " ...\n" // <-+ objc_exception_throw,
+ " *CDRSpecFailureSpec.mm" // | [NSException raise:...], etc.
+ );
+ });
+
+ it(@"includes asterisk before every path "
+ "because paths are not absolute and "
+ "Xcode plugins need to be able to recognize them", ^{
+ symbols should contain(@"*CDRSpecFailureSpec.mm");
+ });
+
+ it(@"does not set error", ^{
+ error should be_nil;
+ });
+ });
+
+ context(@"when symbolication is not successful", ^{
+ beforeEach(^{
+ NSNumber *badAddress = [NSNumber numberWithUnsignedInteger:123];
+ NSArray *addresses = [NSArray arrayWithObject:badAddress];
+ spy_on(raisedObject);
+ raisedObject stub_method("callStackReturnAddresses").and_return(addresses);
+ });
+
+ it(@"returns nil", ^{
+ symbols should be_nil;
+ });
+
+ it(@"sets not successful error", ^{
+ error.domain should equal(kCDRSymbolicatorErrorDomain);
+ error.code should equal(kCDRSymbolicatorErrorNotSuccessful);
+ [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey] \
+ should be_instance_of([NSString class]).or_any_subclass();
+ });
+ });
+ });
+#endif
+ });
+
+ context(@"when raised object does not provide call stack return addresses", ^{
+ beforeEach(^{ raisedObject = @"failure"; });
+
+ it(@"returns nil", ^{
+ symbols should be_nil;
+ });
+ });
+ });
});
SPEC_END
View
2  Spec/CDRSpecSpec.mm
@@ -22,7 +22,7 @@
spec = [[[CDRSpec alloc] init] autorelease];
spy_on(spec.symbolicator);
- spec.symbolicator stub_method("symbolicateAddresses:");
+ spec.symbolicator stub_method("symbolicateAddresses:error:");
spec.symbolicator stub_method("lineNumberForStackAddress:").and_do(^(NSInvocation *i){
NSUInteger lineNumber;
[i getArgument:&lineNumber atIndex:2];
View
144 Spec/CDRSymbolicatorSpec.mm
@@ -5,62 +5,126 @@
using namespace Cedar::Matchers;
-#if !__arm__
SPEC_BEGIN(CDRSymbolicatorSpec)
describe(@"CDRSymbolicator", ^{
- __block CDRExample *example;
- __block CDRExampleGroup *group;
+ __block CDRSymbolicator *symbolicator;
- void (^verifyFileNameAndLineNumber)(CDRExampleBase *, NSString *, int) =
- ^(CDRExampleBase *b, NSString *fileName, int lineNumber) {
- NSNumber *address = [NSNumber numberWithUnsignedInteger:b.stackAddress];
- NSArray *addresses = [NSArray arrayWithObject:address];
-
- CDRSymbolicator *symbolicator = [[CDRSymbolicator alloc] init];
- [symbolicator symbolicateAddresses:addresses];
+ beforeEach(^{
+ symbolicator = [[[CDRSymbolicator alloc] init] autorelease];
+ });
- [[symbolicator fileNameForStackAddress:b.stackAddress] hasSuffix:fileName] should be_truthy;
- [symbolicator lineNumberForStackAddress:b.stackAddress] should equal(lineNumber);
- [symbolicator release];
- };
+#if __arm__
+ context(@"when symbolication is not available (devices)", ^{
+ __block NSArray *addresses;
+ __block NSError *error;
- it(@"identifies file name and line number of an it", ^{
- example = it(@"it", ^{});
- verifyFileNameAndLineNumber(example, @"CDRSymbolicatorSpec.mm", __LINE__-1);
- });
+ beforeEach(^{
+ NSNumber *address = [NSNumber numberWithUnsignedInteger:123];
+ addresses = [NSArray arrayWithObject:address];
+ });
- it(@"identifies file name line number of a describe", ^{
- group = describe(@"describe", ^{});
- verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-1);
- });
+ subjectAction(^{
+ error = nil;
+ [symbolicator symbolicateAddresses:addresses error:&error];
+ });
- it(@"identifies file name line number of a context", ^{
- group = context(@"context", ^{});
- verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-1);
- });
+ it(@"does not return filename or line number", ^{
+ [symbolicator fileNameForStackAddress:0] should be_nil;
+ [symbolicator lineNumberForStackAddress:0] should equal(0);
+ });
- it(@"identifies file name line number of a nested it", ^{
- describe(@"describe", ^{
- example = it(@"it", ^{});
+ it(@"sets not available error", ^{
+ error.domain should equal(kCDRSymbolicatorErrorDomain);
+ error.code should equal(kCDRSymbolicatorErrorNotAvailable);
+ [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey] \
+ should be_instance_of([NSString class]).or_any_subclass();
});
- verifyFileNameAndLineNumber(example, @"CDRSymbolicatorSpec.mm", __LINE__-2);
});
+#else
+ context(@"when symbolication is available (osx, simulator)", ^{
+ context(@"when symbolication is successful", ^{
+ __block CDRExample *example;
+ __block CDRExampleGroup *group;
+
+ void (^verifyFileNameAndLineNumber)(CDRExampleBase *, NSString *, int) =
+ ^(CDRExampleBase *b, NSString *fileName, int lineNumber) {
+ NSNumber *address = [NSNumber numberWithUnsignedInteger:b.stackAddress];
+ NSArray *addresses = [NSArray arrayWithObject:address];
+
+ NSError *error = nil;
+ [symbolicator symbolicateAddresses:addresses error:&error];
+ error should be_nil;
+
+ [[symbolicator fileNameForStackAddress:b.stackAddress] hasSuffix:fileName] should be_truthy;
+ [symbolicator lineNumberForStackAddress:b.stackAddress] should equal(lineNumber);
+ };
+
+ it(@"identifies file name and line number of an it", ^{
+ example = it(@"it", ^{});
+ verifyFileNameAndLineNumber(example, @"CDRSymbolicatorSpec.mm", __LINE__-1);
+ });
- it(@"identifies file name line number of a nested describe", ^{
- describe(@"describe", ^{
- group = describe(@"describe", ^{});
+ it(@"identifies file name line number of a describe", ^{
+ group = describe(@"describe", ^{});
+ verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-1);
+ });
+
+ it(@"identifies file name line number of a context", ^{
+ group = context(@"context", ^{});
+ verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-1);
+ });
+
+ it(@"identifies file name line number of a nested it", ^{
+ describe(@"describe", ^{
+ example = it(@"it", ^{});
+ });
+ verifyFileNameAndLineNumber(example, @"CDRSymbolicatorSpec.mm", __LINE__-2);
+ });
+
+ it(@"identifies file name line number of a nested describe", ^{
+ describe(@"describe", ^{
+ group = describe(@"describe", ^{});
+ });
+ verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-2);
+ });
+
+ it(@"identifies file name line number of a nested context", ^{
+ describe(@"describe", ^{
+ group = context(@"context", ^{});
+ });
+ verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-2);
+ });
});
- verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-2);
- });
- it(@"identifies file name line number of a nested context", ^{
- describe(@"describe", ^{
- group = context(@"context", ^{});
+ context(@"when symbolication is not successful", ^{
+ __block NSArray *addresses;
+ __block NSError *error;
+
+ beforeEach(^{
+ NSNumber *badAddress = [NSNumber numberWithUnsignedInteger:123];
+ addresses = [NSArray arrayWithObject:badAddress];
+ });
+
+ subjectAction(^{
+ error = nil;
+ [symbolicator symbolicateAddresses:addresses error:&error];
+ });
+
+ it(@"does not return filename or line number", ^{
+ [symbolicator fileNameForStackAddress:0] should be_nil;
+ [symbolicator lineNumberForStackAddress:0] should equal(0);
+ });
+
+ it(@"sets not successful error", ^{
+ error.domain should equal(kCDRSymbolicatorErrorDomain);
+ error.code should equal(kCDRSymbolicatorErrorNotSuccessful);
+ [error.userInfo objectForKey:kCDRSymbolicatorErrorMessageKey] \
+ should be_instance_of([NSString class]).or_any_subclass();
+ });
});
- verifyFileNameAndLineNumber(group, @"CDRSymbolicatorSpec.mm", __LINE__-2);
});
+#endif
});
SPEC_END
-#endif
View
13 Spec/Support/FibonacciCalculator.m
@@ -3,18 +3,13 @@
@implementation FibonacciCalculator
- (int)computeFibonnaciNumberVeryVerySlowly:(int)n {
- if (n == 0) {
- return 0;
- }
- if (n == 1) {
- return 1;
- }
- return [self computeFibonnaciNumberVeryVerySlowly:n - 1] + [self computeFibonnaciNumberVeryVerySlowly:n - 2];
+ if (n == 0) return 0;
+ if (n == 1) return 1;
+ return [self computeFibonnaciNumberVeryVerySlowly:n - 1] +
+ [self computeFibonnaciNumberVeryVerySlowly:n - 2];
}
- (int)computeFibonnaciNumberVeryVeryQuickly:(int)n {
return (pow((1 + sqrt(5)) / 2.0, n) - pow((1 - sqrt(5)) / 2.0, n) ) / sqrt(5);
}
-
-
@end
Please sign in to comment.
Something went wrong with that request. Please try again.