diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index bb4769b0f..8afe0fcae 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.1.1", + "version": "6.1.2", "summary": "The Bugsnag crash reporting framework for Apple platforms.", "homepage": "https://bugsnag.com", "license": "MIT", @@ -9,7 +9,7 @@ }, "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.1.1" + "tag": "v6.1.2" }, "frameworks": [ "Foundation", diff --git a/Bugsnag.xcodeproj/project.pbxproj b/Bugsnag.xcodeproj/project.pbxproj index f02585c89..1716ee096 100644 --- a/Bugsnag.xcodeproj/project.pbxproj +++ b/Bugsnag.xcodeproj/project.pbxproj @@ -1480,7 +1480,6 @@ 008969112486DAD000DC48C2 /* BSG_KSMachApple.h */, 008969122486DAD000DC48C2 /* BSG_KSString.h */, 008969132486DAD000DC48C2 /* BSG_KSMach.c */, - 008969142486DAD000DC48C2 /* BSG_RFC3339DateTool.m */, 008969152486DAD000DC48C2 /* BSG_KSLogger.h */, 008969162486DAD000DC48C2 /* BSG_KSBacktrace_Private.h */, 008969172486DAD000DC48C2 /* BSG_KSMach_Arm64.c */, @@ -1498,7 +1497,6 @@ 008969242486DAD000DC48C2 /* BSG_KSString.c */, 008969252486DAD000DC48C2 /* BSG_KSObjC.c */, 008969262486DAD000DC48C2 /* BSG_KSLogger.m */, - 008969272486DAD000DC48C2 /* BSG_RFC3339DateTool.h */, 008969282486DAD000DC48C2 /* BSG_KSMach.h */, ); path = Tools; @@ -1685,6 +1683,8 @@ 00AD1CF524869EE500A27979 /* Helpers */ = { isa = PBXGroup; children = ( + 008969272486DAD000DC48C2 /* BSG_RFC3339DateTool.h */, + 008969142486DAD000DC48C2 /* BSG_RFC3339DateTool.m */, 008968112486DA5600DC48C2 /* BSGSerialization.h */, 008968162486DA5600DC48C2 /* BSGSerialization.m */, 008968102486DA5600DC48C2 /* BugsnagCollections.h */, diff --git a/Bugsnag/BSGOutOfMemoryWatchdog.m b/Bugsnag/BSGOutOfMemoryWatchdog.m index fce581182..76bea98e7 100644 --- a/Bugsnag/BSGOutOfMemoryWatchdog.m +++ b/Bugsnag/BSGOutOfMemoryWatchdog.m @@ -16,6 +16,7 @@ #import "BugsnagSessionTracker.h" #import "Private.h" #import "BugsnagErrorTypes.h" +#import "BSG_RFC3339DateTool.h" @interface BSGOutOfMemoryWatchdog () @property(nonatomic, getter=isWatching) BOOL watching; @@ -26,10 +27,6 @@ @interface BSGOutOfMemoryWatchdog () @property(nonatomic) NSString *codeBundleId; @end -@interface Bugsnag () -+ (NSDateFormatter *_Nonnull)payloadDateFormatter; -@end - @implementation BSGOutOfMemoryWatchdog - (instancetype)init { @@ -152,7 +149,7 @@ - (void)handleTransitionToBackground:(NSNotification *)note { } - (void)handleLowMemoryChange:(NSNotification *)note { - self.cachedFileInfo[@"device"][@"lowMemory"] = [[Bugsnag payloadDateFormatter] + self.cachedFileInfo[@"device"][@"lowMemory"] = [BSG_RFC3339DateTool stringFromDate:[NSDate date]]; [self writeSentinelFile]; } diff --git a/Bugsnag/Bugsnag.m b/Bugsnag/Bugsnag.m index 879de84ba..995afbfc4 100644 --- a/Bugsnag/Bugsnag.m +++ b/Bugsnag/Bugsnag.m @@ -210,17 +210,6 @@ + (BOOL)resumeSession { } } -+ (NSDateFormatter *)payloadDateFormatter { - static NSDateFormatter *formatter; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - formatter = [NSDateFormatter new]; - formatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"; - formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; - }); - return formatter; -} - + (void)addRuntimeVersionInfo:(NSString *)info withKey:(NSString *)key { if ([self bugsnagStarted]) { diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 3c31ed6b7..60e426085 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -353,10 +353,6 @@ @interface BugsnagUser () - (instancetype)initWithDictionary:(NSDictionary *)dict; @end -@interface Bugsnag () -+ (NSDateFormatter *_Nonnull)payloadDateFormatter; -@end - @interface BugsnagBreadcrumbs () @property(nonatomic, readwrite, strong) NSMutableArray *breadcrumbs; @end @@ -1177,7 +1173,7 @@ - (void)orientationChanged:(NSNotification *)notification { } - (void)lowMemoryWarning:(NSNotification *)notif { - [self.state addMetadata:[[Bugsnag payloadDateFormatter] stringFromDate:[NSDate date]] + [self.state addMetadata:[BSG_RFC3339DateTool stringFromDate:[NSDate date]] withKey:BSEventLowMemoryWarning toSection:BSGKeyDeviceState]; diff --git a/Bugsnag/Delivery/BugsnagSessionTrackingApiClient.m b/Bugsnag/Delivery/BugsnagSessionTrackingApiClient.m index f709f9aa2..b087b6c46 100644 --- a/Bugsnag/Delivery/BugsnagSessionTrackingApiClient.m +++ b/Bugsnag/Delivery/BugsnagSessionTrackingApiClient.m @@ -50,12 +50,15 @@ - (void)deliverSessionsInStore:(BugsnagSessionFileStore *)store { NSDictionary *filesWithIds = [store allFilesByName]; for (NSString *fileId in [filesWithIds allKeys]) { - // skip dupe requests - if ([self isActiveRequest:fileId]) { - continue; + + // De-duplicate files as deletion of the file is asynchronous and so multiple calls + // to this method will result in multiple send requests + @synchronized (self.activeIds) { + if ([self.activeIds containsObject:fileId]) { + continue; + } + [self.activeIds addObject:fileId]; } - // add request - [self.activeIds addObject:fileId]; BugsnagSession *session = [[BugsnagSession alloc] initWithDictionary:filesWithIds[fileId]]; @@ -64,7 +67,6 @@ - (void)deliverSessionsInStore:(BugsnagSessionFileStore *)store { initWithSessions:@[session] config:[Bugsnag configuration] codeBundleId:self.codeBundleId]; - NSUInteger sessionCount = payload.sessions.count; NSMutableDictionary *data = [payload toJson]; NSDictionary *HTTPHeaders = @{ @"Bugsnag-Payload-Version": @"1.0", @@ -77,26 +79,19 @@ - (void)deliverSessionsInStore:(BugsnagSessionFileStore *)store { headers:HTTPHeaders onCompletion:^(NSUInteger sentCount, BOOL success, NSError *error) { if (success && error == nil) { - bsg_log_info(@"Sent %lu sessions to Bugsnag", (unsigned long) sessionCount); + bsg_log_info(@"Sent session %@ to Bugsnag", session.id); [store deleteFileWithId:fileId]; } else { bsg_log_warn(@"Failed to send sessions to Bugsnag: %@", error); } // remove request - [self.activeIds removeObject:fileId]; + @synchronized (self.activeIds) { + [self.activeIds removeObject:fileId]; + } }]; }]; } } -- (BOOL)isActiveRequest:(NSString *)fileId { - for (NSString *val in self.activeIds) { - if ([val isEqualToString:fileId]) { - return true; - } - } - return false; -} - @end diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.h b/Bugsnag/Helpers/BSG_RFC3339DateTool.h similarity index 100% rename from Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.h rename to Bugsnag/Helpers/BSG_RFC3339DateTool.h diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.m b/Bugsnag/Helpers/BSG_RFC3339DateTool.m similarity index 61% rename from Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.m rename to Bugsnag/Helpers/BSG_RFC3339DateTool.m index 26f5e44bf..99b5a0f1a 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_RFC3339DateTool.m +++ b/Bugsnag/Helpers/BSG_RFC3339DateTool.m @@ -24,14 +24,18 @@ #import "BSG_RFC3339DateTool.h" +@interface BSG_RFC3339DateTool () ++ (NSDateFormatter *)iosFormatterInstance; +@end + @implementation BSG_RFC3339DateTool -static NSString *const kDateFormatterKey = @"RfcDateFormatter"; +static NSString *const kRfcDateFormatterKey = @"RfcDateFormatter"; +static NSString *const kIsoDateFormatterKey = @"IsoDateFormatter"; + (NSDateFormatter *)sharedInstance { NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; - NSDateFormatter *formatter = threadDict[kDateFormatterKey]; - + NSDateFormatter *formatter = threadDict[kRfcDateFormatterKey]; if (formatter == nil) { formatter = [NSDateFormatter new]; NSLocale *locale = @@ -39,8 +43,30 @@ + (NSDateFormatter *)sharedInstance { [formatter setLocale:locale]; [formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; - threadDict[kDateFormatterKey] = formatter; + threadDict[kRfcDateFormatterKey] = formatter; } + + formatter = threadDict[kRfcDateFormatterKey]; + return formatter; +} + +/** +Used internally to convert any dates with timezones (from older notifier versions) to Zulu time + */ ++ (NSDateFormatter *)iosFormatterInstance { + NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; + NSDateFormatter *formatter = threadDict[kIsoDateFormatterKey]; + if (formatter == nil) { + formatter = [NSDateFormatter new]; + NSLocale *locale = + [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + [formatter setLocale:locale]; + [formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"]; + [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + threadDict[kIsoDateFormatterKey] = formatter; + } + + formatter = threadDict[kIsoDateFormatterKey]; return formatter; } @@ -55,7 +81,11 @@ + (NSDate *)dateFromString:(NSString *)string { if (![string isKindOfClass:[NSString class]]) { return nil; } - return [[self sharedInstance] dateFromString:string]; + NSDate *date = [[self sharedInstance] dateFromString:string]; + if (!date) { + date = [[self iosFormatterInstance] dateFromString:string]; + } + return date; } + (NSString *)stringFromUNIXTimestamp:(unsigned long long)timestamp { diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h index b9a31ac82..b442c1382 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.h @@ -119,6 +119,11 @@ */ - (NSArray *)captureThreads:(NSException *)exc depth:(int)depth; +/** + * Collects information about the application's foreground state (duration in foreground/background) + */ +- (NSDictionary *)captureAppStats; + /** If YES, reports will be sent even if a debugger is attached * * Default: NO diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m index 77f8400ff..55390ccad 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrash.m @@ -39,6 +39,8 @@ #import "BugsnagThread.h" #import "BSGSerialization.h" #import "BugsnagErrorReportSink.h" +#import "BugsnagCollections.h" +#import "BSG_KSCrashReportFields.h" #if BSG_HAS_UIKIT #import @@ -320,6 +322,16 @@ - (void)sendAllReports { return @[]; } +- (NSDictionary *)captureAppStats { + BSG_KSCrash_State state = crashContext()->state; + bsg_kscrashstate_updateDurationStats(&state); + NSMutableDictionary *dict = [NSMutableDictionary new]; + BSGDictSetSafeObject(dict, @(state.activeDurationSinceLaunch), @BSG_KSCrashField_ActiveTimeSinceLaunch); + BSGDictSetSafeObject(dict, @(state.backgroundDurationSinceLaunch), @BSG_KSCrashField_BGTimeSinceLaunch); + BSGDictSetSafeObject(dict, @(state.applicationIsInForeground), @BSG_KSCrashField_AppInFG); + return dict; +} + - (void)reportUserException:(NSString *)name reason:(NSString *)reason handledState:(NSDictionary *)handledState diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c index 61ec57190..f076b79a7 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c @@ -243,5 +243,4 @@ char *bsg_kscrash_captureThreadTrace(int discardDepth, int frameCount, uintptr_t bsg_kscrashsentry_resume_threads_user(false); } return trace; - } diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.h index f5368e058..192f85eb6 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.h @@ -121,6 +121,14 @@ void bsg_kscrashstate_notifyAppCrash(BSG_KSCrashType type); */ const BSG_KSCrash_State *bsg_kscrashstate_currentState(void); +/** + * Updates the stats for duration in foreground/background. This needs to + * be updated whenever an error report is captured. + * + * @param state the kscrash state + */ +void bsg_kscrashstate_updateDurationStats(BSG_KSCrash_State *const state); + #ifdef __cplusplus } #endif diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m index 9ed3e31fb..57e6ba340 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m @@ -356,7 +356,15 @@ void bsg_kscrashstate_notifyAppTerminate(void) { void bsg_kscrashstate_notifyAppCrash(BSG_KSCrashType type) { BSG_KSCrash_State *const state = bsg_g_state; const char *const stateFilePath = bsg_g_stateFilePath; + bsg_kscrashstate_updateDurationStats(state); + BOOL didCrash = type != BSG_KSCrashTypeUserReported; + state->crashedThisLaunch |= didCrash; + if (didCrash) { + bsg_kscrashstate_i_saveState(state, stateFilePath); + } +} +void bsg_kscrashstate_updateDurationStats(BSG_KSCrash_State *const state) { const double duration = bsg_ksmachtimeDifferenceInSeconds( mach_absolute_time(), state->appStateTransitionTime); if (state->applicationIsActive) { @@ -366,11 +374,6 @@ void bsg_kscrashstate_notifyAppCrash(BSG_KSCrashType type) { state->backgroundDurationSinceLaunch += duration; state->backgroundDurationSinceLastCrash += duration; } - BOOL didCrash = type != BSG_KSCrashTypeUserReported; - state->crashedThisLaunch |= didCrash; - if (didCrash) { - bsg_kscrashstate_i_saveState(state, stateFilePath); - } } const BSG_KSCrash_State *bsg_kscrashstate_currentState(void) { diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index 109ecac92..af11e3e0c 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -38,6 +38,7 @@ #import "BSG_KSLogger.h" #import "BSG_KSCrashReportFields.h" #import "BSG_KSMach.h" +#import "BSG_KSCrash.h" #import #if BSG_PLATFORM_IOS || BSG_PLATFORM_TVOS @@ -416,6 +417,8 @@ + (NSDictionary *)systemInfo { }; BSGDictSetSafeObject(sysInfo, memory, @BSG_KSSystemField_Memory); + NSDictionary *statsInfo = [[BSG_KSCrash sharedInstance] captureAppStats]; + BSGDictSetSafeObject(sysInfo, statsInfo, @BSG_KSCrashField_AppStats); return sysInfo; } diff --git a/Bugsnag/Payload/BugsnagBreadcrumb.m b/Bugsnag/Payload/BugsnagBreadcrumb.m index 8c2ff6c91..655774c28 100644 --- a/Bugsnag/Payload/BugsnagBreadcrumb.m +++ b/Bugsnag/Payload/BugsnagBreadcrumb.m @@ -23,6 +23,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // +#import "BSG_RFC3339DateTool.h" + #import "BugsnagBreadcrumb.h" #import "BugsnagBreadcrumbs.h" #import "Bugsnag.h" @@ -72,10 +74,6 @@ BSGBreadcrumbType BSGBreadcrumbTypeFromString(NSString *value) { } } -@interface Bugsnag () -+ (NSDateFormatter *_Nonnull)payloadDateFormatter; -@end - @implementation BugsnagBreadcrumb - (instancetype)init { @@ -93,8 +91,7 @@ - (BOOL)isValid { - (NSDictionary *)objectValue { @synchronized (self) { - NSString *timestamp = - [[Bugsnag payloadDateFormatter] stringFromDate:_timestamp]; + NSString *timestamp = [BSG_RFC3339DateTool stringFromDate:_timestamp]; if (timestamp && _message.length > 0) { NSMutableDictionary *metadata = [NSMutableDictionary new]; for (NSString *key in _metadata) { @@ -212,7 +209,7 @@ + (instancetype)breadcrumbFromDict:(NSDictionary *)dict { return [self breadcrumbWithBlock:^(BugsnagBreadcrumb *crumb) { crumb.message = dict[BSGKeyMessage] ?: dict[BSGKeyName]; crumb.metadata = dict[BSGKeyMetadata] ?: dict[@"metadata"]; - crumb.timestamp = [[Bugsnag payloadDateFormatter] dateFromString:dict[BSGKeyTimestamp]]; + crumb.timestamp = [BSG_RFC3339DateTool dateFromString:dict[BSGKeyTimestamp]]; crumb.type = BSGBreadcrumbTypeFromString(dict[BSGKeyType]); }]; } diff --git a/Bugsnag/Payload/BugsnagDeviceWithState.m b/Bugsnag/Payload/BugsnagDeviceWithState.m index 2ed38dc3f..5c939a913 100644 --- a/Bugsnag/Payload/BugsnagDeviceWithState.m +++ b/Bugsnag/Payload/BugsnagDeviceWithState.m @@ -7,6 +7,7 @@ // #import "BugsnagPlatformConditional.h" +#import "BSG_RFC3339DateTool.h" #import "BugsnagDeviceWithState.h" #import "BugsnagCollections.h" @@ -53,10 +54,6 @@ return freeBytes; } -@interface Bugsnag () -+ (NSDateFormatter *)payloadDateFormatter; -@end - @interface BugsnagDevice () + (void)populateFields:(BugsnagDevice *)device dictionary:(NSDictionary *)event; @@ -64,19 +61,8 @@ + (void)populateFields:(BugsnagDevice *)device - (NSDictionary *)toDictionary; @end -@interface BugsnagDeviceWithState () -@property (nonatomic, readonly) NSDateFormatter *formatter; -@end - @implementation BugsnagDeviceWithState -- (instancetype)init { - if (self = [super init]) { - _formatter = [Bugsnag payloadDateFormatter]; - } - return self; -} - + (BugsnagDeviceWithState *) deviceFromJson:(NSDictionary *)json { BugsnagDeviceWithState *device = [BugsnagDeviceWithState new]; device.id = json[@"id"]; @@ -99,7 +85,7 @@ + (BugsnagDeviceWithState *) deviceFromJson:(NSDictionary *)json { id time = json[@"time"]; if (time && [time isKindOfClass:[NSString class]]) { - device.time = [device.formatter dateFromString:time]; + device.time = [BSG_RFC3339DateTool dateFromString:time]; } return device; } @@ -125,7 +111,7 @@ + (BugsnagDeviceWithState *)deviceWithDictionary:(NSDictionary *)event { NSString *val = [event valueForKeyPath:@"report.timestamp"]; if (val != nil) { - device.time = [device.formatter dateFromString:val]; + device.time = [BSG_RFC3339DateTool dateFromString:val]; } return device; } @@ -138,7 +124,7 @@ - (NSDictionary *)toDictionary { BSGDictInsertIfNotNil(dict, self.orientation, @"orientation"); if (self.time != nil) { - BSGDictInsertIfNotNil(dict, [self.formatter stringFromDate:self.time], @"time"); + BSGDictInsertIfNotNil(dict, [BSG_RFC3339DateTool stringFromDate:self.time], @"time"); } return dict; } diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index c028b45d4..d07cbe9ac 100644 --- a/Bugsnag/Payload/BugsnagNotifier.m +++ b/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else self.name = @"Bugsnag Objective-C"; #endif - self.version = @"6.1.1"; + self.version = @"6.1.2"; self.url = @"https://github.com/bugsnag/bugsnag-cocoa"; self.dependencies = [NSMutableArray new]; } diff --git a/CHANGELOG.md b/CHANGELOG.md index fc52938e4..55b2ebd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ Changelog ========= +## 6.1.2 (2020-07-21) + +### Bug fixes + +* Ensure foreground stats are recorded for handled errors + [#755](https://github.com/bugsnag/bugsnag-cocoa/pull/755) + +* Synchronize access to sessions being delivered in de-duplication code + [#756](https://github.com/bugsnag/bugsnag-cocoa/pull/756) + +* Removed non-thread safe date formatter + [#758](https://github.com/bugsnag/bugsnag-cocoa/pull/758) + ## 6.1.1 (2020-07-16) ### Bug fixes diff --git a/Tests/BugsnagClientMirrorTest.m b/Tests/BugsnagClientMirrorTest.m index 8876734cc..c6d8039d5 100644 --- a/Tests/BugsnagClientMirrorTest.m +++ b/Tests/BugsnagClientMirrorTest.m @@ -124,7 +124,6 @@ - (void)setUp { self.clientWhitelist = [NSSet setWithArray:@[ @"startWithApiKey: @24@0:8@16", @"startWithConfiguration: @24@0:8@16", - @"payloadDateFormatter @16@0:8", @"updateCodeBundleId: v24@0:8@16", @"instance @16@0:8", @"client @16@0:8", diff --git a/Tests/BugsnagErrorReportSinkTests.m b/Tests/BugsnagErrorReportSinkTests.m index 96ceaf328..582751d0f 100644 --- a/Tests/BugsnagErrorReportSinkTests.m +++ b/Tests/BugsnagErrorReportSinkTests.m @@ -309,7 +309,7 @@ - (void)testEventDevice { XCTAssertEqualObjects(device[@"jailbroken"], @YES); XCTAssertEqualObjects(device[@"freeMemory"], @742920192); XCTAssertEqualObjects(device[@"orientation"], @"unknown"); - XCTAssertEqualObjects(device[@"time"], @"2014-12-02T01:56:13+0000"); + XCTAssertEqualObjects(device[@"time"], @"2014-12-02T01:56:13Z"); } - (void)testEventApp { diff --git a/Tests/BugsnagEventPersistLoadTest.m b/Tests/BugsnagEventPersistLoadTest.m index c4cd122a1..18edd0182 100644 --- a/Tests/BugsnagEventPersistLoadTest.m +++ b/Tests/BugsnagEventPersistLoadTest.m @@ -17,9 +17,6 @@ #import "BugsnagHandledState.h" #import "Bugsnag.h" -@interface Bugsnag () -+ (NSDateFormatter *)payloadDateFormatter; -@end #import "BugsnagError.h" #import "BugsnagStackframe.h" #import "BugsnagThread.h" @@ -170,6 +167,10 @@ - (void)testDeviceFieldsOverride { } } }]; + + NSDateFormatter *formatter = [NSDateFormatter new]; + formatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"; + formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; BugsnagDeviceWithState *device = event.device; XCTAssertEqualObjects(@920234094, device.freeDisk); @@ -177,7 +178,7 @@ - (void)testDeviceFieldsOverride { XCTAssertEqualObjects(@92092340923, device.totalMemory); XCTAssertEqualObjects(@"landscape", device.orientation); XCTAssertFalse(device.jailbroken); - NSString *date = [device.formatter stringFromDate:device.time]; + NSString *date = [formatter stringFromDate:device.time]; XCTAssertEqualObjects(@"2020-05-11T15:36:09+0000", date); XCTAssertEqualObjects(@"f0a9b99", device.id); @@ -264,7 +265,11 @@ - (void)testBreadcrumbsOverride { XCTAssertEqual(1, [event.breadcrumbs count]); XCTAssertEqual(BSGBreadcrumbTypeManual, breadcrumb.type); XCTAssertEqualObjects(@"installed NDK", breadcrumb.message); - NSDate *date = [[Bugsnag payloadDateFormatter] dateFromString:@"2020-02-14T16:12:23+001"]; + + NSDateFormatter *formatter = [NSDateFormatter new]; + formatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"; + formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + NSDate *date = [formatter dateFromString:@"2020-02-14T16:12:23+001"]; XCTAssertEqualObjects(date, breadcrumb.timestamp); XCTAssertEqualObjects(@{ @"custom": @{ @@ -348,8 +353,11 @@ - (void)testSessionOverride { } } }]; - - NSDate *date = [[Bugsnag payloadDateFormatter] dateFromString:@"2020-05-18T13:13:24Z"]; + + NSDateFormatter *formatter = [NSDateFormatter new]; + formatter.dateFormat = @"yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZ"; + formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; + NSDate *date = [formatter dateFromString:@"2020-05-18T13:13:24Z"]; XCTAssertEqualObjects(date, event.session.startedAt); XCTAssertEqualObjects(@"123", event.session.id); XCTAssertEqual(1, event.session.unhandledCount); diff --git a/Tests/KSCrash/RFC3339DateTool_Tests.m b/Tests/KSCrash/RFC3339DateTool_Tests.m index bd2b9791a..523283d8a 100755 --- a/Tests/KSCrash/RFC3339DateTool_Tests.m +++ b/Tests/KSCrash/RFC3339DateTool_Tests.m @@ -68,6 +68,25 @@ - (void) testDateFromString XCTAssertEqualObjects(actual, expected, @""); } +- (void) testDateFromStringWithTimezone +{ + NSDate* expected = [self gmtDateWithYear:2000 month:1 day:2 hour:3 minute:4 second:5]; + NSDate* actual = [BSG_RFC3339DateTool dateFromString:@"2000-01-02T03:04:05+0000"]; + + XCTAssertEqualObjects(actual, expected, @""); +} + +- (void) testDateFromStringWithTimezonePlus2 +{ + NSDate* expected = [self gmtDateWithYear:2000 month:1 day:2 hour:1 minute:4 second:5]; + NSDate* actual = [BSG_RFC3339DateTool dateFromString:@"2000-01-02T03:04:05+0200"]; + + XCTAssertEqualObjects(actual, expected, @""); + + // Convert back again to verify overall effect + XCTAssertEqualObjects([BSG_RFC3339DateTool stringFromDate:actual], @"2000-01-02T01:04:05Z"); +} + - (void) testStringFromUnixTimestamp { NSDate* date = [self gmtDateWithYear:2000 month:1 day:2 hour:3 minute:4 second:5]; diff --git a/Tests/report.json b/Tests/report.json index 8df5f3cfc..d5e5bcfc6 100644 --- a/Tests/report.json +++ b/Tests/report.json @@ -2103,8 +2103,8 @@ "depth": 4, "severity": "warning", "breadcrumbs": [ - {"message": "App launched", "timestamp": "2020-02-14T16:12:22+0000", "type": "manual", "metaData":{}}, - {"message": "Tapped button", "timestamp": "2020-02-14T16:12:24+0000", "type": "manual", "metaData":{}} + {"message": "App launched", "timestamp": "2020-02-14T16:12:22Z", "type": "manual", "metaData":{}}, + {"message": "Tapped button", "timestamp": "2020-02-14T16:12:24Z", "type": "manual", "metaData":{}} ] } } diff --git a/VERSION b/VERSION index 132c6def5..5e3254243 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.1 \ No newline at end of file +6.1.2