diff --git a/Bugsnag/Payload/BugsnagEvent.m b/Bugsnag/Payload/BugsnagEvent.m index f23c0a848..5a66d9964 100644 --- a/Bugsnag/Payload/BugsnagEvent.m +++ b/Bugsnag/Payload/BugsnagEvent.m @@ -339,6 +339,30 @@ - (instancetype)initWithKSCrashData:(NSDictionary *)event { NSDictionary *error = [event valueForKeyPath:@"crash.error"]; NSString *errorType = error[BSGKeyType]; + // Data coming from KSCrash will have either user.handledCount = 0 and user.unhandledCount > 0, or the opposite + BOOL wasUnhandled = [event valueForKeyPath:@"user.unhandledCount"] == nil || + [[event valueForKeyPath:@"user.unhandledCount"] intValue] > 0; + BOOL wasUnhandledChanged = [event valueForKeyPath:@"user.unhandled"] != nil && + [[event valueForKeyPath:@"user.unhandled"] boolValue] != wasUnhandled; + BOOL isUnhandled = wasUnhandled; + + if (wasUnhandledChanged) { + isUnhandled = !wasUnhandled; + NSMutableDictionary *user = [event[BSGKeyUser] mutableCopy]; + user[@"unhandled"] = @(isUnhandled); + user[@"unhandledOverridden"] = @YES; + if (wasUnhandled) { + user[@"unhandledCount"] = @0; + user[@"handledCount"] = @1; + } else { + user[@"unhandledCount"] = @1; + user[@"handledCount"] = @0; + } + NSMutableDictionary *eventCopy = [event mutableCopy]; + eventCopy[BSGKeyUser] = user; + event = eventCopy; + } + id userMetadata = [event valueForKeyPath:@"user.metaData"]; BugsnagMetadata *metadata; @@ -386,13 +410,15 @@ - (instancetype)initWithKSCrashData:(NSDictionary *)event { BugsnagHandledState *handledState; if (recordedState) { handledState = [[BugsnagHandledState alloc] initWithDictionary:recordedState]; - } else { // the event was unhandled. + } else { // the event was (probably) unhandled. BOOL isSignal = [BSGKeySignal isEqualToString:errorType]; SeverityReasonType severityReason = isSignal ? Signal : UnhandledException; handledState = [BugsnagHandledState handledStateWithSeverityReason:severityReason severity:BSGSeverityError attrValue:errors[0].errorClass]; + handledState.unhandled = isUnhandled; + handledState.unhandledOverridden = wasUnhandledChanged; } NSMutableDictionary *userAtCrash = [self parseOnCrashData:event]; @@ -695,7 +721,9 @@ - (NSDictionary *)toJson { BSGDictSetSafeObject(event, @(self.handledState.unhandled), BSGKeyUnhandled); - BSGDictSetSafeObject(event, @(self.handledState.unhandledOverridden), BSGKeyUnhandledOverridden); + if (self.handledState.unhandledOverridden) { + BSGDictSetSafeObject(event, @(self.handledState.unhandledOverridden), BSGKeyUnhandledOverridden); + } // serialize handled/unhandled into payload NSMutableDictionary *severityReason = [NSMutableDictionary new]; diff --git a/Tests/BugsnagErrorReportSinkTests.m b/Tests/BugsnagErrorReportSinkTests.m index 2bb5d5f06..55a9e37e5 100644 --- a/Tests/BugsnagErrorReportSinkTests.m +++ b/Tests/BugsnagErrorReportSinkTests.m @@ -121,7 +121,6 @@ - (void)testCorrectEventKeys { @"severityReason", @"threads", @"unhandled", - @"unhandledOverridden", @"user", ]; XCTAssertEqualObjects(actualKeys, eventKeys); diff --git a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj index 751a21675..c74ec9cc4 100644 --- a/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios-swift-cocoapods/iOSTestApp.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ A1117E592535B29800014FDA /* OOMEnabledErrorTypesScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */; }; A1117E5B2536036400014FDA /* OOMSessionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E5A2536036400014FDA /* OOMSessionScenario.swift */; }; C4D0B5FF8E60C0835B86DFE9 /* Pods_iOSTestApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4994F05E0421A0B037DD2CC5 /* Pods_iOSTestApp.framework */; }; + CBB787912578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB787902578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m */; }; CBE1C9242574F532004B8B5B /* OnErrorOverwriteUnhandledFalseScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE1C9222574F532004B8B5B /* OnErrorOverwriteUnhandledFalseScenario.swift */; }; CBE1C9252574F532004B8B5B /* OnErrorOverwriteUnhandledTrueScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE1C9232574F532004B8B5B /* OnErrorOverwriteUnhandledTrueScenario.swift */; }; E700EE48247D1158008CFFB6 /* UserEventOverrideScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = E700EE47247D1158008CFFB6 /* UserEventOverrideScenario.swift */; }; @@ -205,6 +206,8 @@ A1117E562535B22300014FDA /* OOMAutoDetectErrorsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMAutoDetectErrorsScenario.swift; sourceTree = ""; }; A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMEnabledErrorTypesScenario.swift; sourceTree = ""; }; A1117E5A2536036400014FDA /* OOMSessionScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMSessionScenario.swift; sourceTree = ""; }; + CBB7878F2578FC0C0071BDE4 /* MarkUnhandledHandledScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkUnhandledHandledScenario.h; sourceTree = ""; }; + CBB787902578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MarkUnhandledHandledScenario.m; sourceTree = ""; }; CBE1C9222574F532004B8B5B /* OnErrorOverwriteUnhandledFalseScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledFalseScenario.swift; sourceTree = ""; }; CBE1C9232574F532004B8B5B /* OnErrorOverwriteUnhandledTrueScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnErrorOverwriteUnhandledTrueScenario.swift; sourceTree = ""; }; E700EE47247D1158008CFFB6 /* UserEventOverrideScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEventOverrideScenario.swift; sourceTree = ""; }; @@ -549,6 +552,8 @@ 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */, F4295ABA693D273D52AA9F6B /* Scenario.h */, F42954E8B66F3FB7F5333CF7 /* Scenario.m */, + CBB7878F2578FC0C0071BDE4 /* MarkUnhandledHandledScenario.h */, + CBB787902578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m */, E7A324D4247E707D008B0052 /* Session Callbacks */, F49695AC2445471400105DA9 /* Session stopping */, F49695AB244546CA00105DA9 /* Session tracking */, @@ -910,6 +915,7 @@ E7A324EA247E9DA5008B0052 /* BreadcrumbCallbackOverrideScenario.swift in Sources */, F42955DB6D08642528917FAB /* CxxExceptionScenario.mm in Sources */, 8A3B5F2B240807EE00CE4A3A /* ModifyBreadcrumbInNotify.swift in Sources */, + CBB787912578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m in Sources */, 8A32DB8222424E3000EDD92F /* NSExceptionShiftScenario.m in Sources */, F42954B7318A02824C65C514 /* ObjCMsgSendScenario.m in Sources */, E700EE5D247D322D008CFFB6 /* OnSendCallbackOrderScenario.swift in Sources */, diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index 27cb19756..d01511523 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -126,6 +126,7 @@ 01F47D30254B1B3100B184AD /* AutoContextNSExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F47CBF254B1B3000B184AD /* AutoContextNSExceptionScenario.swift */; }; 01F47D31254B1B3100B184AD /* OverwriteLinkRegisterScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F47CC0254B1B3000B184AD /* OverwriteLinkRegisterScenario.m */; }; 01F47D32254B1B3100B184AD /* ResumeSessionOOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01F47CC1254B1B3000B184AD /* ResumeSessionOOMScenario.m */; }; + CBB7878E2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB7878C2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -318,6 +319,8 @@ 01F47CC1254B1B3000B184AD /* ResumeSessionOOMScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResumeSessionOOMScenario.m; sourceTree = ""; }; 01F47CC2254B1B3000B184AD /* SIGBUSScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SIGBUSScenario.h; sourceTree = ""; }; 01F47CC3254B1B3100B184AD /* SIGFPEScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SIGFPEScenario.h; sourceTree = ""; }; + CBB7878C2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MarkUnhandledHandledScenario.m; sourceTree = ""; }; + CBB7878D2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkUnhandledHandledScenario.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -486,6 +489,8 @@ 01F47C90254B1B2F00B184AD /* UnhandledInternalNotifyScenario.swift */, 01F47C38254B1B2D00B184AD /* UnhandledMachExceptionScenario.h */, 01F47C4C254B1B2D00B184AD /* UnhandledMachExceptionScenario.m */, + CBB7878D2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.h */, + CBB7878C2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.m */, 01F47CB7254B1B3000B184AD /* UserDefaultInfoScenario.swift */, 01F47CB3254B1B3000B184AD /* UserDisabledScenario.swift */, 01F47C75254B1B2E00B184AD /* UserEmailScenario.swift */, @@ -656,6 +661,7 @@ 01F47CDC254B1B3100B184AD /* HandledErrorScenario.swift in Sources */, 01F47CFD254B1B3100B184AD /* OnCrashHandlerScenario.m in Sources */, 01F47D29254B1B3100B184AD /* DiscardSessionScenario.swift in Sources */, + CBB7878E2578FB3F0071BDE4 /* MarkUnhandledHandledScenario.m in Sources */, 01F47CCC254B1B3100B184AD /* OnSendCallbackOrderScenario.swift in Sources */, 01F47D04254B1B3100B184AD /* ModifyBreadcrumbScenario.swift in Sources */, 01F47CD3254B1B3100B184AD /* EnabledBreadcrumbTypesIsNilScenario.swift in Sources */, diff --git a/features/fixtures/shared/scenarios/AbortScenario.h b/features/fixtures/shared/scenarios/AbortScenario.h index 912e86602..4f7419347 100644 --- a/features/fixtures/shared/scenarios/AbortScenario.h +++ b/features/fixtures/shared/scenarios/AbortScenario.h @@ -25,7 +25,7 @@ */ #import -#import "Scenario.h" +#import "MarkUnhandledHandledScenario.h" /** * Call abort() to terminate the program. @@ -33,3 +33,7 @@ @interface AbortScenario: Scenario @end + +@interface AbortOverrideScenario: MarkUnhandledHandledScenario + +@end diff --git a/features/fixtures/shared/scenarios/AbortScenario.m b/features/fixtures/shared/scenarios/AbortScenario.m index 046501d36..c5e57873f 100644 --- a/features/fixtures/shared/scenarios/AbortScenario.m +++ b/features/fixtures/shared/scenarios/AbortScenario.m @@ -38,3 +38,16 @@ - (void)run { } @end + +@implementation AbortOverrideScenario + +- (void)startBugsnag { + self.config.autoTrackSessions = NO; + [super startBugsnag]; +} + +- (void)run { + abort(); +} + +@end diff --git a/features/fixtures/shared/scenarios/CxxExceptionScenario.h b/features/fixtures/shared/scenarios/CxxExceptionScenario.h index 6304b2a9f..b07e2ff89 100644 --- a/features/fixtures/shared/scenarios/CxxExceptionScenario.h +++ b/features/fixtures/shared/scenarios/CxxExceptionScenario.h @@ -25,8 +25,11 @@ */ #import -#import "Scenario.h" +#import "MarkUnhandledHandledScenario.h" @interface CxxExceptionScenario : Scenario @end + +@interface CxxExceptionOverrideScenario : MarkUnhandledHandledScenario +@end diff --git a/features/fixtures/shared/scenarios/CxxExceptionScenario.mm b/features/fixtures/shared/scenarios/CxxExceptionScenario.mm index bb4cfcea0..0708a63de 100644 --- a/features/fixtures/shared/scenarios/CxxExceptionScenario.mm +++ b/features/fixtures/shared/scenarios/CxxExceptionScenario.mm @@ -55,3 +55,20 @@ - (void)crash __attribute__((noreturn)) { } @end + +@implementation CxxExceptionOverrideScenario + +- (void)startBugsnag { + self.config.autoTrackSessions = NO; + [super startBugsnag]; +} + +- (void)run { + [self crash]; +} + +- (void)crash __attribute__((noreturn)) { + throw new kaboom_exception; +} + +@end diff --git a/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.h b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.h new file mode 100644 index 000000000..3ee83a33e --- /dev/null +++ b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.h @@ -0,0 +1,17 @@ +// +// MarkUnhandledHandledScenario.h +// iOSTestApp +// +// Created by Karl Stenerud on 03.12.20. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import "Scenario.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MarkUnhandledHandledScenario : Scenario + +@end + +NS_ASSUME_NONNULL_END diff --git a/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m new file mode 100644 index 000000000..d394089ab --- /dev/null +++ b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m @@ -0,0 +1,18 @@ +// +// MarkUnhandledHandledScenario.m +// iOSTestApp +// +// Created by Karl Stenerud on 03.12.20. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import "MarkUnhandledHandledScenario.h" + +@implementation MarkUnhandledHandledScenario + +- (void)startBugsnag { + self.config.onCrashHandler = markErrorHandledCallback; + [super startBugsnag]; +} + +@end diff --git a/features/fixtures/shared/scenarios/ObjCExceptionScenario.h b/features/fixtures/shared/scenarios/ObjCExceptionScenario.h index 05b434266..4e551ce82 100644 --- a/features/fixtures/shared/scenarios/ObjCExceptionScenario.h +++ b/features/fixtures/shared/scenarios/ObjCExceptionScenario.h @@ -25,8 +25,10 @@ */ #import -#import "Scenario.h" - +#import "MarkUnhandledHandledScenario.h" @interface ObjCExceptionScenario : Scenario @end + +@interface ObjCExceptionOverrideScenario : MarkUnhandledHandledScenario +@end diff --git a/features/fixtures/shared/scenarios/ObjCExceptionScenario.m b/features/fixtures/shared/scenarios/ObjCExceptionScenario.m index 7bfdc9cb1..8ec9b6f9e 100644 --- a/features/fixtures/shared/scenarios/ObjCExceptionScenario.m +++ b/features/fixtures/shared/scenarios/ObjCExceptionScenario.m @@ -44,3 +44,17 @@ - (void)run __attribute__((noreturn)) { } @end + +@implementation ObjCExceptionOverrideScenario + +- (void)startBugsnag { + self.config.autoTrackSessions = NO; + [super startBugsnag]; +} + +- (void)run __attribute__((noreturn)) { + @throw [NSException exceptionWithName:NSGenericException reason:@"An uncaught exception! SCREAM." + userInfo:@{NSLocalizedDescriptionKey: @"I'm in your program, catching your exceptions!"}]; +} + +@end diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 3ff51647b..2e1c392f2 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -6,6 +6,8 @@ #import #import +void markErrorHandledCallback(const BSG_KSCrashReportWriter * _Nonnull writer); + @interface Scenario : NSObject @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index af35f8c08..f03c2d052 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -6,6 +6,10 @@ #import "Scenario.h" +void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { + writer->addBooleanElement(writer, "unhandled", false); +} + @implementation Scenario + (Scenario *)createScenarioNamed:(NSString *)className diff --git a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.h b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.h index 0bc8ecef5..14deb79fe 100644 --- a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.h +++ b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.h @@ -7,7 +7,7 @@ // #import -#import "Scenario.h" +#import "MarkUnhandledHandledScenario.h" NS_ASSUME_NONNULL_BEGIN @@ -15,4 +15,8 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface UnhandledMachExceptionOverrideScenario : MarkUnhandledHandledScenario + +@end + NS_ASSUME_NONNULL_END diff --git a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m index b806b04fe..c48a35e8c 100644 --- a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m +++ b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m @@ -21,3 +21,17 @@ - (void)run { } @end + +@implementation UnhandledMachExceptionOverrideScenario + +- (void)startBugsnag { + self.config.autoTrackSessions = NO; + [super startBugsnag]; +} + +- (void)run { + void (*ptr)(void) = (void *)0xDEADBEEF; + ptr(); +} + +@end diff --git a/features/steps/ios_steps.rb b/features/steps/ios_steps.rb index 6270ea3cc..9902b2cd8 100644 --- a/features/steps/ios_steps.rb +++ b/features/steps/ios_steps.rb @@ -252,7 +252,6 @@ def request_fields_are_equal(key, index_a, index_b) And the event "severity" equals "error" And the event "severityReason.type" equals "outOfMemory" And the event "unhandled" is true - And the event "unhandledOverridden" is false } end diff --git a/features/unhandled_cpp_exception.feature b/features/unhandled_cpp_exception.feature index 758a62a34..fd9adef4c 100644 --- a/features/unhandled_cpp_exception.feature +++ b/features/unhandled_cpp_exception.feature @@ -14,3 +14,16 @@ Feature: Thrown C++ exceptions are captured by Bugsnag And the event "severity" equals "error" And the event "unhandled" is true And the event "severityReason.type" equals "unhandledException" + + Scenario: Throwing a C++ exception with unhandled override + When I run "CxxExceptionOverrideScenario" and relaunch the app + And I configure Bugsnag for "CxxExceptionOverrideScenario" + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "iOS Bugsnag Notifier" notifier + And the exception "errorClass" equals "P16kaboom_exception" + And the exception "type" equals "cocoa" + And the payload field "events.0.exceptions.0.stacktrace" is an array with 0 elements + And the event "severity" equals "error" + And the event "unhandled" is false + And the event "unhandledOverridden" is true + And the event "severityReason.type" equals "unhandledException" diff --git a/features/unhandled_mach_exception.feature b/features/unhandled_mach_exception.feature index 8492f7874..38c591277 100644 --- a/features/unhandled_mach_exception.feature +++ b/features/unhandled_mach_exception.feature @@ -20,3 +20,22 @@ Feature: Bugsnag captures an unhandled mach exception And the event "severity" equals "error" And the event "unhandled" is true And the event "severityReason.type" equals "unhandledException" + + Scenario: Trigger a mach exception with unhandled override + When I run "UnhandledMachExceptionOverrideScenario" and relaunch the app + And I configure Bugsnag for "UnhandledMachExceptionOverrideScenario" + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "iOS Bugsnag Notifier" notifier + And the event "exceptions.0.errorClass" equals "EXC_BAD_ACCESS" + And the event "exceptions.0.message" equals "Attempted to dereference garbage pointer 0xdeadbeef." + And the event "metaData.error.address" equals 3735928559 + And the event "metaData.error.type" equals "mach" + And the event "metaData.error.mach.code" equals "0x101" + And the event "metaData.error.mach.code_name" equals "EXC_ARM_DA_ALIGN" + And the event "metaData.error.mach.exception" equals 1 + And the event "metaData.error.mach.exception_name" equals "EXC_BAD_ACCESS" + And the event "metaData.error.mach.subcode" equals "0xdeadbeef" + And the event "severity" equals "error" + And the event "unhandled" is false + And the event "unhandledOverridden" is true + And the event "severityReason.type" equals "unhandledException" diff --git a/features/unhandled_nsexception.feature b/features/unhandled_nsexception.feature index 069d4bf9e..ecf3aaf34 100644 --- a/features/unhandled_nsexception.feature +++ b/features/unhandled_nsexception.feature @@ -17,3 +17,19 @@ Feature: Uncaught NSExceptions are captured by Bugsnag And the event "severity" equals "error" And the event "unhandled" is true And the event "severityReason.type" equals "unhandledException" + + Scenario: Throw a NSException with unhandled override + When I run "ObjCExceptionOverrideScenario" and relaunch the app + And I configure Bugsnag for "ObjCExceptionOverrideScenario" + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "iOS Bugsnag Notifier" notifier + And the exception "message" equals "An uncaught exception! SCREAM." + And the exception "errorClass" equals "NSGenericException" + And the "method" of stack frame 0 equals "" + And the "method" of stack frame 1 equals "objc_exception_throw" + And the "method" of stack frame 2 equals "-[ObjCExceptionOverrideScenario run]" + And the payload field "events.0.device.time" is a date + And the event "severity" equals "error" + And the event "unhandled" is false + And the event "unhandledOverridden" is true + And the event "severityReason.type" equals "unhandledException" diff --git a/features/unhandled_signal.feature b/features/unhandled_signal.feature index eb3517a83..31047a497 100644 --- a/features/unhandled_signal.feature +++ b/features/unhandled_signal.feature @@ -19,6 +19,23 @@ Feature: Signals are captured as error reports in Bugsnag And the event "severityReason.type" equals "signal" And the event "severityReason.attributes.signalType" equals "SIGABRT" + Scenario: Triggering SIGABRT with unhandled override + When I run "AbortScenarioOverride" and relaunch the app + And I configure Bugsnag for "AbortScenarioOverride" + And I wait to receive a request + Then the request is valid for the error reporting API version "4.0" for the "iOS Bugsnag Notifier" notifier + And the payload field "events" is an array with 1 elements + And the exception "errorClass" equals "SIGABRT" + And the "method" of stack frame 0 equals "__pthread_kill" + And the "method" of stack frame 1 matches "^(| ?pthread_kill)$" + And the "method" of stack frame 2 equals "abort" + And the "method" of stack frame 3 equals "-[AbortScenarioOverride run]" + And the event "severity" equals "error" + And the event "unhandled" is false + And the event "unhandledOverridden" is true + And the event "severityReason.type" equals "signal" + And the event "severityReason.attributes.signalType" equals "SIGABRT" + Scenario: Triggering SIGPIPE When I run "SIGPIPEScenario" and relaunch the app And I configure Bugsnag for "SIGPIPEScenario"