diff --git a/CHANGELOG.md b/CHANGELOG.md index 51d38ade7..5e91bfab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Enhancements + +* Capture basic report diagnostics in the file path in case of crash report + content corruption + ## 5.17.3 (2018-12-19) ### Bug Fixes diff --git a/OSX/Bugsnag.xcodeproj/project.pbxproj b/OSX/Bugsnag.xcodeproj/project.pbxproj index eb63a4c0f..875a33720 100644 --- a/OSX/Bugsnag.xcodeproj/project.pbxproj +++ b/OSX/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8A12006C221C50F40008C9C3 /* BSGFilepathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A12006B221C50F40008C9C3 /* BSGFilepathTests.m */; }; 8A2C8FAC1C6BC1F700846019 /* Bugsnag.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8FA11C6BC1F700846019 /* Bugsnag.framework */; }; 8A2C8FCD1C6BC2C800846019 /* Bugsnag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A2C8FBB1C6BC2C800846019 /* Bugsnag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8A2C8FCE1C6BC2C800846019 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8FBC1C6BC2C800846019 /* Bugsnag.m */; }; @@ -188,6 +189,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 8A12006B221C50F40008C9C3 /* BSGFilepathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSGFilepathTests.m; sourceTree = ""; }; 8A2C8FA11C6BC1F700846019 /* Bugsnag.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Bugsnag.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8A2C8FA61C6BC1F700846019 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8A2C8FAB1C6BC1F700846019 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -469,6 +471,7 @@ 8A2C8FAF1C6BC1F700846019 /* Tests */ = { isa = PBXGroup; children = ( + 8A12006B221C50F40008C9C3 /* BSGFilepathTests.m */, E7CE78861FD94E40001D07E0 /* KSCrash */, E791482D1FD82B0C003EFEBF /* BugsnagSessionTest.m */, E791482B1FD82B0C003EFEBF /* BugsnagSessionTrackerTest.m */, @@ -933,6 +936,7 @@ 8ACF0F752018136200173809 /* BugsnagCrashReportTests.m in Sources */, E7CE78CC1FD94E77001D07E0 /* KSSystemInfo_Tests.m in Sources */, E7CE78C91FD94E77001D07E0 /* KSSignalInfo_Tests.m in Sources */, + 8A12006C221C50F40008C9C3 /* BSGFilepathTests.m in Sources */, E7CE78C61FD94E77001D07E0 /* KSMach_Tests.m in Sources */, E7CE78D61FD94E9E001D07E0 /* FileBasedTestCase.m in Sources */, E7CE78D51FD94E93001D07E0 /* XCTestCase+KSCrash.m in Sources */, diff --git a/Source/BugsnagCrashReport.h b/Source/BugsnagCrashReport.h index 6d82b7a55..b389ba7b9 100644 --- a/Source/BugsnagCrashReport.h +++ b/Source/BugsnagCrashReport.h @@ -39,6 +39,18 @@ NSString *_Nonnull BSGFormatSeverity(BSGSeverity severity); @interface BugsnagCrashReport : NSObject +/** + * Create a new crash report from a JSON crash report generated by + * BugsnagCrashSentry + * + * @param report a BugsnagCrashSentry JSON report + * @param metadata additional report info encoded as a string + * + * @return a Bugsnag crash report + */ +- (instancetype _Nonnull)initWithKSReport:(NSDictionary *_Nonnull)report + fileMetadata:(NSString *)metadata; + /** * Create a new crash report from a JSON crash report generated by * BugsnagCrashSentry @@ -193,6 +205,10 @@ __deprecated_msg("Use toJson: instead."); */ @property(readwrite, copy, nullable) NSDictionary *appState; +/** + * If YES, a complete report was not able to be obtained at generation time + */ +@property (readonly, nonatomic, getter=isIncomplete) BOOL incomplete; /** * Returns the enhanced error message for the thread, or nil if none exists. diff --git a/Source/BugsnagCrashReport.m b/Source/BugsnagCrashReport.m index 00eb36769..87c9aa5d7 100644 --- a/Source/BugsnagCrashReport.m +++ b/Source/BugsnagCrashReport.m @@ -70,7 +70,8 @@ } NSString *_Nonnull BSGParseErrorClass(NSDictionary *error, - NSString *errorType) { + NSString *errorType, + NSString *fallbackValue) { NSString *errorClass; if ([errorType isEqualToString:BSGKeyCppException]) { @@ -86,7 +87,7 @@ } if (!errorClass) { // use a default value - errorClass = @"Exception"; + errorClass = fallbackValue.length > 0 ? fallbackValue : @"Exception"; } return errorClass; } @@ -184,6 +185,13 @@ + (instancetype)errorDataFromThreads:(NSArray *)threads; - (instancetype)initWithClass:(NSString *_Nonnull)errorClass message:(NSString *_Nonnull)errorMessage NS_DESIGNATED_INITIALIZER; @end +@interface FallbackReportData : NSObject +@property (nonatomic, strong) NSString *errorClass; +@property (nonatomic, getter=isUnhandled) BOOL unhandled; +@property (nonatomic) BSGSeverity severity; +- (instancetype)initWithMetadata:(NSString *)metadata; +@end + @interface BugsnagCrashReport () /** @@ -212,25 +220,33 @@ @interface BugsnagCrashReport () @property(nonatomic, readwrite, copy, nullable) NSDictionary *customException; @property(nonatomic) BugsnagSession *session; +@property (nonatomic, readwrite, getter=isIncomplete) BOOL incomplete; @end @implementation BugsnagCrashReport - (instancetype)initWithKSReport:(NSDictionary *)report { + return [self initWithKSReport:report fileMetadata:@""]; +} + +- (instancetype)initWithKSReport:(NSDictionary *)report + fileMetadata:(NSString *)metadata { if (self = [super init]) { + FallbackReportData *fallback = [[FallbackReportData alloc] initWithMetadata:metadata]; _notifyReleaseStages = [report valueForKeyPath:@"user.config.notifyReleaseStages"]; _releaseStage = BSGParseReleaseStage(report); _error = [report valueForKeyPath:@"crash.error"]; + _incomplete = report.count == 0; _errorType = _error[BSGKeyType]; _threads = [report valueForKeyPath:@"crash.threads"]; RegisterErrorData *data = [RegisterErrorData errorDataFromThreads:_threads]; if (data) { - _errorClass = data.errorClass; + _errorClass = data.errorClass ?: fallback.errorClass; _errorMessage = data.errorMessage; } else { - _errorClass = BSGParseErrorClass(_error, _errorType); + _errorClass = BSGParseErrorClass(_error, _errorType, fallback.errorClass); _errorMessage = BSGParseErrorMessage(report, _error, _errorType); } _binaryImages = report[@"binary_images"]; @@ -259,7 +275,7 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { // only makes sense to use serialised value for handled exceptions _depth = [[report valueForKeyPath:@"user.state.crash.depth"] unsignedIntegerValue]; - } else { // the event was unhandled. + } else if (_errorType != nil) { // the event was unhandled. BOOL isSignal = [BSGKeySignal isEqualToString:_errorType]; SeverityReasonType severityReason = isSignal ? Signal : UnhandledException; @@ -268,6 +284,11 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { severity:BSGSeverityError attrValue:_errorClass]; _depth = 0; + } else { // Incomplete report + SeverityReasonType severityReason = [fallback isUnhandled] ? UnhandledException : HandledError; + _handledState = [BugsnagHandledState handledStateWithSeverityReason:severityReason + severity:fallback.severity + attrValue:nil]; } _severity = _handledState.currentSeverity; @@ -478,7 +499,11 @@ - (NSDictionary *)toJson { BSGDictSetSafeObject(event, BSGFormatSeverity(self.severity), BSGKeySeverity); BSGDictSetSafeObject(event, [self breadcrumbs], BSGKeyBreadcrumbs); BSGDictSetSafeObject(event, metaData, BSGKeyMetaData); - + + if ([self isIncomplete]) { + BSGDictSetSafeObject(event, @YES, BSGKeyIncomplete); + } + NSDictionary *device = [self.device bsg_mergedInto:self.deviceState]; BSGDictSetSafeObject(event, device, BSGKeyDevice); @@ -620,6 +645,42 @@ - (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)th @end +@implementation FallbackReportData + +- (instancetype)initWithMetadata:(NSString *)metadata { + if (self = [super init]) { + NSString *separator = @"-"; + NSString *location = metadata; + NSRange range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + _errorClass = [location substringFromIndex:range.location + 1]; + location = [location substringToIndex:range.location]; + } + range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + NSString *value = [location substringFromIndex:range.location + 1]; + _unhandled = ![value isEqualToString:@"h"]; + location = [location substringToIndex:range.location + 1]; + } else { + _unhandled = YES; + } + range = [location rangeOfString:separator options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + NSString *value = [location substringFromIndex:range.location]; + if ([value isEqualToString:@"w"]) { + _severity = BSGSeverityWarning; + } else if ([value isEqualToString:@"i"]) { + _severity = BSGSeverityInfo; + } else { + _severity = BSGSeverityError; + } + } + } + return self; +} + +@end + @implementation RegisterErrorData + (instancetype)errorDataFromThreads:(NSArray *)threads { for (NSDictionary *thread in threads) { diff --git a/Source/BugsnagKeys.h b/Source/BugsnagKeys.h index 54a89693f..aabd15608 100644 --- a/Source/BugsnagKeys.h +++ b/Source/BugsnagKeys.h @@ -19,6 +19,7 @@ static NSString *const BSGKeyName = @"name"; static NSString *const BSGKeyTimestamp = @"timestamp"; static NSString *const BSGKeyType = @"type"; static NSString *const BSGKeyMetaData = @"metaData"; +static NSString *const BSGKeyIncomplete = @"incomplete"; static NSString *const BSGKeyId = @"id"; static NSString *const BSGKeyUser = @"user"; static NSString *const BSGKeyEmail = @"email"; diff --git a/Source/BugsnagSink.m b/Source/BugsnagSink.m index d93755fea..f81920713 100644 --- a/Source/BugsnagSink.m +++ b/Source/BugsnagSink.m @@ -61,10 +61,13 @@ - (void)filterReports:(NSDictionary *)reports NSMutableArray *bugsnagReports = [NSMutableArray new]; BugsnagConfiguration *configuration = [Bugsnag configuration]; - for (NSDictionary *report in reports) { - BugsnagCrashReport *bugsnagReport = [[BugsnagCrashReport alloc] initWithKSReport:report]; - BOOL incompleteReport = (![@"standard" isEqualToString:[report valueForKeyPath:@"report.type"]] || - [[report objectForKey:@"incomplete"] boolValue]); + for (NSString *fileKey in reports) { + NSDictionary *report = reports[fileKey]; + BugsnagCrashReport *bugsnagReport = [[BugsnagCrashReport alloc] initWithKSReport:report + fileMetadata:fileKey]; + BOOL incompleteReport = ([bugsnagReport isIncomplete] + || ![@"standard" isEqualToString:[report valueForKeyPath:@"report.type"]] + || [[report objectForKey:@"incomplete"] boolValue]); if (incompleteReport) { // append app/device data as this is unlikely to change between sessions NSDictionary *sysInfo = [BSG_KSSystemInfo systemInfo]; @@ -120,7 +123,7 @@ - (void)filterReports:(NSDictionary *)reports #pragma clang diagnostic ignored "-Wdeprecated-declarations" for (BugsnagBeforeNotifyHook hook in configuration.beforeNotifyHooks) { if (reportData) { - reportData = hook(reports, reportData); + reportData = hook(bugsnagReports, reportData); } else { break; } diff --git a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c index 6f380470a..cf77c3866 100644 --- a/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c +++ b/Source/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c @@ -64,12 +64,13 @@ static char *bsg_g_stateFilePath; #pragma mark - Utility - // ============================================================================ static const int bsg_filepath_len = 512; +static const int bsg_error_class_filepath_len = 21; static inline BSG_KSCrash_Context *crashContext(void) { return &bsg_g_crashReportContext; } -int bsg_create_filepath(char *base, char filepath[bsg_filepath_len], char severity, char error_class[21]) { +int bsg_create_filepath(char *base, char filepath[bsg_filepath_len], char severity, char error_class[bsg_error_class_filepath_len]) { int length; for (length = 0; length < bsg_filepath_len; length++) { if (base[length] == '\0') { @@ -87,10 +88,13 @@ int bsg_create_filepath(char *base, char filepath[bsg_filepath_len], char severi filepath[length++] = '-'; filepath[length++] = context->crash.crashType == BSG_KSCrashTypeUserReported ? 'h' : 'u'; filepath[length++] = '-'; - for (int i = 0; i < 21; i++) { + for (int i = 0; i < bsg_error_class_filepath_len; i++) { char c = error_class[i]; if (c == '\0') break; + else if (c == 47 || c > 126 || c <= 0) + // disallow '/' and characters outside of the ascii range + continue; filepath[length++] = c; } // add suffix @@ -99,6 +103,7 @@ int bsg_create_filepath(char *base, char filepath[bsg_filepath_len], char severi filepath[length++] = 's'; filepath[length++] = 'o'; filepath[length++] = 'n'; + filepath[length++] = '\0'; return length; } diff --git a/Tests/BSGFilepathTests.m b/Tests/BSGFilepathTests.m new file mode 100644 index 000000000..7c65756d3 --- /dev/null +++ b/Tests/BSGFilepathTests.m @@ -0,0 +1,50 @@ +// +// BSGFilepathTests.m +// Tests +// +// Created by Delisa on 2/19/19. +// Copyright © 2019 Bugsnag. All rights reserved. +// + +#import +#include + + +int bsg_create_filepath(char *base, char filepath[512], char severity, char error_class[21]); + +@interface BSGFilepathTests : XCTestCase + +@end + +@implementation BSGFilepathTests + +- (void)testEncodeCharacters { + char *base = "/path/to/it/imagine this is a UUID.json"; + char filepath[512]; + bsg_create_filepath(base, filepath, 'w', "😃 HappyError"); + XCTAssertEqual(0, strcmp(filepath, "/path/to/it/imagine this is a UUID-w-u- HappyError.json")); +} + +- (void)testTruncateUnicodeCharacters { + char *base = "/path/to/it/imagine this is a UUID.json"; + char filepath[512]; + // The char limit is not on a character boundary + bsg_create_filepath(base, filepath, 'e', "AnExtremelyLongLong😃"); + XCTAssertEqual(0, strcmp(filepath, "/path/to/it/imagine this is a UUID-e-u-AnExtremelyLongLong.json")); +} + +- (void)testEmptyErrorClassFromUnicode { + char *base = "/path/to/it/imagine this is a UUID.json"; + char filepath[512]; + bsg_create_filepath(base, filepath, 'e', "🀦🀨🁺😃"); + XCTAssertEqual(0, strcmp(filepath, "/path/to/it/imagine this is a UUID-e-u-.json")); +} + +- (void)testErrorClassLength { + char *base = "imagine this is a UUID.json"; + char filepath[512]; + bsg_create_filepath(base, filepath, 'i', "AnExtremelyLongLongErrorNameOmg"); + XCTAssertEqual(0, strcmp(filepath, "imagine this is a UUID-i-u-AnExtremelyLongLongEr.json")); +} + +@end diff --git a/Tests/BugsnagCrashReportTests.m b/Tests/BugsnagCrashReportTests.m index 54cfebbbd..b69fb9bd4 100644 --- a/Tests/BugsnagCrashReportTests.m +++ b/Tests/BugsnagCrashReportTests.m @@ -14,6 +14,7 @@ #import "BugsnagHandledState.h" #import "BugsnagSession.h" + @interface BugsnagCrashReportTests : XCTestCase @property BugsnagCrashReport *report; @end @@ -178,6 +179,45 @@ - (void)testDefaultErrorMessageNil { payload[@"exceptions"][0][@"message"]); } +- (void)testIncomplete { + XCTAssertTrue([[[BugsnagCrashReport alloc] initWithKSReport:@{}] isIncomplete]); + XCTAssertFalse([[[BugsnagCrashReport alloc] initWithKSReport:@{@"foo": @"bar"}] isIncomplete]); +} + +- (void)testFallbackValues { + BugsnagCrashReport *report = + [[BugsnagCrashReport alloc] initWithKSReport:@{} fileMetadata:@"w-h-SomeErr thing"]; + XCTAssertTrue([report isIncomplete]); + NSDictionary *payload = [report toJson]; + XCTAssertEqualObjects(@"SomeErr thing", payload[@"exceptions"][0][@"errorClass"]); + XCTAssertEqualObjects(@"warning", payload[@"severity"]); + XCTAssertEqualObjects(@NO, payload[@"unhandled"]); +} + +- (void)testUnneededFallbackValues { + BugsnagHandledState *state = [BugsnagHandledState handledStateWithSeverityReason:UserCallbackSetSeverity + severity:BSGSeverityInfo + attrValue:nil]; + NSDictionary *dict = @{@"user.handledState": [state toJson]}; + BugsnagCrashReport *report = + [[BugsnagCrashReport alloc] initWithKSReport:dict fileMetadata:@"w-h-SomeErr thing"]; + XCTAssertFalse([report isIncomplete]); + NSDictionary *payload = [report toJson]; + XCTAssertEqualObjects(@"SomeErr thing", payload[@"exceptions"][0][@"errorClass"]); + XCTAssertEqualObjects(@"info", payload[@"severity"]); + XCTAssertEqualObjects(@NO, payload[@"unhandled"]); +} + +- (void)testUnhandledFallbackValues { + BugsnagCrashReport *report = + [[BugsnagCrashReport alloc] initWithKSReport:@{} fileMetadata:@"foofoo-e-u-SomeErr thing"]; + XCTAssertTrue([report isIncomplete]); + NSDictionary *payload = [report toJson]; + XCTAssertEqualObjects(@"SomeErr thing", payload[@"exceptions"][0][@"errorClass"]); + XCTAssertEqualObjects(@"error", payload[@"severity"]); + XCTAssertEqualObjects(@YES, payload[@"unhandled"]); +} + - (void)testDefaultErrorMessageNilForEmptyThreads { BugsnagCrashReport *report = [[BugsnagCrashReport alloc] initWithKSReport:@{ @"threads" : @[] @@ -533,5 +573,4 @@ - (void)testAppVersionOverride { XCTAssertEqualObjects(@"1.2.3", dictionary[@"app"][@"version"]); } - @end diff --git a/features/crashprobe.feature b/features/crashprobe.feature index d119c9612..989abd03e 100644 --- a/features/crashprobe.feature +++ b/features/crashprobe.feature @@ -218,3 +218,14 @@ Scenario: Access a non-object as an object And the exception "message" equals "Attempted to dereference garbage pointer 0x10." And the exception "errorClass" equals "EXC_BAD_ACCESS" And the "method" of stack frame 0 equals "objc_msgSend" + +Scenario: Crash report file corruption + When I set environment variable "BUGSNAG_API_KEY" to "a35a2a72bd230ac0aa0f52715bbdc6aa" + And I crash the app using "AccessNonObjectScenario" + And I corrupt all reports on disk + And I relaunch the app + Then I should receive a request + And the request is a valid for the error reporting API + And the exception "errorClass" equals "EXC_BAD_ACCESS" + And the event "unhandled" is true + And the event "severity" equals "error" diff --git a/features/steps/ios_steps.rb b/features/steps/ios_steps.rb index 9766630f3..e71eda56f 100644 --- a/features/steps/ios_steps.rb +++ b/features/steps/ios_steps.rb @@ -43,6 +43,15 @@ assert_not_equal(value1, value2) end +When("I corrupt all reports on disk") do + app_path = `xcrun simctl get_app_container booted com.bugsnag.iOSTestApp`.chomp + app_path.gsub!(/(.*Containers).*/, '\1') + files = Dir.glob("#{app_path}/**/KSCrashReports/iOSTestApp/*.json") + files.each do |path| + File.open(path, 'w') {|file| file.truncate(0) } + end +end + Then("each event in the payload for request {int} matches one of:") do |request_index, table| events = read_key_path(find_request(request_index)[:body], "events") table.hashes.each do |values| diff --git a/iOS/Bugsnag.xcodeproj/project.pbxproj b/iOS/Bugsnag.xcodeproj/project.pbxproj index d8cc3ea44..e9d60fcb2 100644 --- a/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8A12006A221C36420008C9C3 /* BSGFilepathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A120069221C36420008C9C3 /* BSGFilepathTests.m */; }; 8A2C8F231C6BBD2300846019 /* Bugsnag.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2C8F181C6BBD2300846019 /* Bugsnag.framework */; }; 8A2C8F4F1C6BBE3C00846019 /* Bugsnag.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A2C8F3D1C6BBE3C00846019 /* Bugsnag.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8A2C8F501C6BBE3C00846019 /* Bugsnag.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A2C8F3E1C6BBE3C00846019 /* Bugsnag.m */; }; @@ -410,6 +411,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8A120069221C36420008C9C3 /* BSGFilepathTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BSGFilepathTests.m; path = ../../Tests/BSGFilepathTests.m; sourceTree = ""; }; 8A2C8F181C6BBD2300846019 /* Bugsnag.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Bugsnag.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8A2C8F1D1C6BBD2300846019 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 8A2C8F221C6BBD2300846019 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -732,6 +734,7 @@ F429554A50F3ABE60537F70E /* BugsnagKSCrashSysInfoParserTest.m */, F42954B7D892334E7551F0F3 /* RegisterErrorDataTest.m */, F429551527EAE3AFE1F605FE /* BugsnagThreadTest.m */, + 8A120069221C36420008C9C3 /* BSGFilepathTests.m */, ); name = Tests; path = BugsnagTests; @@ -1244,6 +1247,7 @@ E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, F4295995C3259BF7D9730BC4 /* BugsnagKSCrashSysInfoParserTest.m in Sources */, F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */, + 8A12006A221C36420008C9C3 /* BSGFilepathTests.m in Sources */, F42952D83435C02F8D891C40 /* BugsnagThreadTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/tvOS/Bugsnag.xcodeproj/project.pbxproj b/tvOS/Bugsnag.xcodeproj/project.pbxproj index 5f1f9aeb8..e1d3c867b 100644 --- a/tvOS/Bugsnag.xcodeproj/project.pbxproj +++ b/tvOS/Bugsnag.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8A12006E221C51550008C9C3 /* BSGFilepathTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A12006D221C51550008C9C3 /* BSGFilepathTests.m */; }; 8A48EF291EAA824100B70024 /* BugsnagLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A48EF281EAA824100B70024 /* BugsnagLogger.h */; }; 8A627CD51EC3B69300F7C04E /* BSGSerialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A627CD31EC3B69300F7C04E /* BSGSerialization.h */; }; 8A627CD61EC3B69300F7C04E /* BSGSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 8A627CD41EC3B69300F7C04E /* BSGSerialization.m */; }; @@ -187,6 +188,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 8A12006D221C51550008C9C3 /* BSGFilepathTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGFilepathTests.m; path = ../../Tests/BSGFilepathTests.m; sourceTree = ""; }; 8A48EF281EAA824100B70024 /* BugsnagLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagLogger.h; path = ../Source/BugsnagLogger.h; sourceTree = ""; }; 8A627CD31EC3B69300F7C04E /* BSGSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BSGSerialization.h; path = ../Source/BSGSerialization.h; sourceTree = ""; }; 8A627CD41EC3B69300F7C04E /* BSGSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGSerialization.m; path = ../Source/BSGSerialization.m; sourceTree = ""; }; @@ -465,6 +467,7 @@ isa = PBXGroup; children = ( E7CE78D71FD94EF1001D07E0 /* KSCrash */, + 8A12006D221C51550008C9C3 /* BSGFilepathTests.m */, E791488C1FD82E77003EFEBF /* BugsnagSessionTest.m */, E791488A1FD82E77003EFEBF /* BugsnagSessionTrackerTest.m */, E791488B1FD82E77003EFEBF /* BugsnagSessionTrackingPayloadTest.m */, @@ -941,6 +944,7 @@ E7CE79051FD94F1B001D07E0 /* KSDynamicLinker_Tests.m in Sources */, E7CE78F61FD94F1B001D07E0 /* FileBasedTestCase.m in Sources */, E7CE79031FD94F1B001D07E0 /* KSCrashReportConverter_Tests.m in Sources */, + 8A12006E221C51550008C9C3 /* BSGFilepathTests.m in Sources */, E79148911FD82E77003EFEBF /* BugsnagUserTest.m in Sources */, E791488E1FD82E77003EFEBF /* BugsnagSessionTrackerTest.m in Sources */, E79148901FD82E77003EFEBF /* BugsnagSessionTest.m in Sources */,