From a6ea9382512816b0f07869fe7ba4c6cb15cc3b06 Mon Sep 17 00:00:00 2001 From: Robin Macharg Date: Fri, 14 Feb 2020 08:41:39 +0000 Subject: [PATCH] feat: Add metadata accessors to BugsnagEvent Also added a BaseUnitTest to collect common setup patterns. --- CHANGELOG.md | 3 + OSX/Bugsnag.xcodeproj/project.pbxproj | 12 ++ Source/BugsnagEvent.h | 79 ++++++-- Source/BugsnagEvent.m | 108 +++++++---- Tests/BugsnagBaseUnitTest.h | 19 ++ Tests/BugsnagBaseUnitTest.m | 42 +++++ Tests/BugsnagEventTests.m | 172 +++++++++++++++++- Tests/BugsnagTestsDummyClass.h | 17 ++ Tests/BugsnagTestsDummyClass.m | 13 ++ iOS/Bugsnag.xcodeproj/project.pbxproj | 13 ++ .../BugsnagEventFromKSCrashReportTest.m | 30 +-- tvOS/Bugsnag.xcodeproj/project.pbxproj | 12 ++ 12 files changed, 454 insertions(+), 66 deletions(-) create mode 100644 Tests/BugsnagBaseUnitTest.h create mode 100644 Tests/BugsnagBaseUnitTest.m create mode 100644 Tests/BugsnagTestsDummyClass.h create mode 100644 Tests/BugsnagTestsDummyClass.m diff --git a/CHANGELOG.md b/CHANGELOG.md index 877350047..a582861ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,9 @@ Bugsnag Notifiers on other platforms. had usage aligned with this change. [#459](https://github.com/bugsnag/bugsnag-cocoa/pull/459) +* Add metadata accessor methods to `BugsnagEvent` + [#465](https://github.com/bugsnag/bugsnag-cocoa/pull/465) + ## Bug fixes * Fix possible report corruption when using `notify()` from multiple threads diff --git a/OSX/Bugsnag.xcodeproj/project.pbxproj b/OSX/Bugsnag.xcodeproj/project.pbxproj index 36ac32670..7ebc49377 100644 --- a/OSX/Bugsnag.xcodeproj/project.pbxproj +++ b/OSX/Bugsnag.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 00D7AC9C23E97F8100FBE4A7 /* BugsnagEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7AC9A23E97F8100FBE4A7 /* BugsnagEvent.m */; }; 00D7AC9D23E97F8100FBE4A7 /* BugsnagEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 00D7AC9B23E97F8100FBE4A7 /* BugsnagEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; 00D7ACA423E9854C00FBE4A7 /* BugsnagEventTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACA223E984B300FBE4A7 /* BugsnagEventTests.m */; }; + 00F9393323FC168F008C7073 /* BugsnagBaseUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F9393223FC168F008C7073 /* BugsnagBaseUnitTest.m */; }; + 00F9393C23FD2D9B008C7073 /* BugsnagTestsDummyClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F9393A23FD2D9B008C7073 /* BugsnagTestsDummyClass.m */; }; 4B406C1822CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1622CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m */; }; 4B406C1922CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B406C1722CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */; }; 4B775FD422CBE02F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; @@ -188,6 +190,10 @@ 00D7AC9A23E97F8100FBE4A7 /* BugsnagEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagEvent.m; path = ../Source/BugsnagEvent.m; sourceTree = ""; }; 00D7AC9B23E97F8100FBE4A7 /* BugsnagEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagEvent.h; path = ../Source/BugsnagEvent.h; sourceTree = ""; }; 00D7ACA223E984B300FBE4A7 /* BugsnagEventTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagEventTests.m; sourceTree = ""; }; + 00F9393123FC168F008C7073 /* BugsnagBaseUnitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagBaseUnitTest.h; sourceTree = ""; }; + 00F9393223FC168F008C7073 /* BugsnagBaseUnitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagBaseUnitTest.m; sourceTree = ""; }; + 00F9393A23FD2D9B008C7073 /* BugsnagTestsDummyClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagTestsDummyClass.m; sourceTree = ""; }; + 00F9393B23FD2D9B008C7073 /* BugsnagTestsDummyClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugsnagTestsDummyClass.h; sourceTree = ""; }; 4B406C1622CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictMergeTest.m; sourceTree = ""; }; 4B406C1722CAD96400464D1D /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B775FD222CBE02A004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; @@ -465,6 +471,10 @@ 8A2C8FAF1C6BC1F700846019 /* Tests */ = { isa = PBXGroup; children = ( + 00F9393B23FD2D9B008C7073 /* BugsnagTestsDummyClass.h */, + 00F9393A23FD2D9B008C7073 /* BugsnagTestsDummyClass.m */, + 00F9393123FC168F008C7073 /* BugsnagBaseUnitTest.h */, + 00F9393223FC168F008C7073 /* BugsnagBaseUnitTest.m */, 00D7ACA223E984B300FBE4A7 /* BugsnagEventTests.m */, 8AD1E3F823EDDD3F0044F919 /* BSGConnectivityTest.m */, 4B406C1622CAD96400464D1D /* BugsnagCollectionsBSGDictMergeTest.m */, @@ -922,6 +932,7 @@ 4B775FD422CBE02F004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */, E7CE78C91FD94E77001D07E0 /* KSSignalInfo_Tests.m in Sources */, E7CE78C61FD94E77001D07E0 /* KSMach_Tests.m in Sources */, + 00F9393C23FD2D9B008C7073 /* BugsnagTestsDummyClass.m in Sources */, E7CE78D61FD94E9E001D07E0 /* FileBasedTestCase.m in Sources */, E7CE78D51FD94E93001D07E0 /* XCTestCase+KSCrash.m in Sources */, E7CE78CF1FD94E77001D07E0 /* NSError+SimpleConstructor_Tests.m in Sources */, @@ -941,6 +952,7 @@ 8AD1E3FA23EDDD4F0044F919 /* BSGConnectivityTest.m in Sources */, E7CE78C41FD94E77001D07E0 /* KSJSONCodec_Tests.m in Sources */, E7CE78C01FD94E77001D07E0 /* KSCrashSentry_Tests.m in Sources */, + 00F9393323FC168F008C7073 /* BugsnagBaseUnitTest.m in Sources */, E7CE78BE1FD94E77001D07E0 /* KSCrashSentry_NSException_Tests.m in Sources */, E7CE78C21FD94E77001D07E0 /* KSDynamicLinker_Tests.m in Sources */, E7CE78CA1FD94E77001D07E0 /* KSString_Tests.m in Sources */, diff --git a/Source/BugsnagEvent.h b/Source/BugsnagEvent.h index 0e5cdd879..e5eacede2 100644 --- a/Source/BugsnagEvent.h +++ b/Source/BugsnagEvent.h @@ -39,6 +39,10 @@ NSString *_Nonnull BSGFormatSeverity(BSGSeverity severity); @interface BugsnagEvent : NSObject +// ----------------------------------------------------------------------------- +// MARK: - Initialisation +// ----------------------------------------------------------------------------- + /** * Create a new crash report from a JSON crash report generated by * BugsnagCrashSentry @@ -83,6 +87,10 @@ initWithErrorName:(NSString *_Nonnull)name handledState:(BugsnagHandledState *_Nonnull)handledState session:(BugsnagSession *_Nullable)session; +// ----------------------------------------------------------------------------- +// MARK: - Misc. Methods +// ----------------------------------------------------------------------------- + /** * Serialize a crash report as a JSON payload * @@ -111,28 +119,74 @@ __deprecated_msg("Use toJson: instead."); - (void)attachCustomStacktrace:(NSArray *_Nonnull)frames withType:(NSString *_Nonnull)type; +/** + * Returns the enhanced error message for the thread, or nil if none exists. + */ +- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread __deprecated; + +// ----------------------------------------------------------------------------- +// MARK: - Metadata +// ----------------------------------------------------------------------------- + /** * Add metadata to a report to a tab. If the tab does not exist, it will be * added. * * @param metadata The key/value pairs to add - * @param tabName The name of the report section + * @param sectionName The name of the report section */ - (void)addMetadata:(NSDictionary *_Nonnull)metadata - toTabWithName:(NSString *_Nonnull)tabName; + toSectionNamed:(NSString *_Nonnull)sectionName; /** - * Add or remove a value from report metadata. If value is nil, the existing - value + * Add or remove a value from report metadata. If value is nil, the existing value * will be removed. + * + * @param key The key name + * @param value The value to set + * @param sectionName The name of the metadata section + */ +- (void)addMetadataToSectionNamed:(NSString *_Nonnull)sectionName + key:(NSString *_Nonnull)key + value:(id _Nullable)value; + +/** + * Return a piece of metadata in a named section if it exists, or nil. + * + * @param sectionName The name of the metadata section + * @param key The key + * @returns An arbitrary object if it exists for the key or nil. + */ +- (id _Nullable)getMetadataInSection:(NSString *_Nonnull)sectionName + withKey:(NSString *_Nullable)key; + +/** + * Return a named metadata section if it exists, or nil. + * + * @param sectionName The name of the metadata section + * @returns A dictionary of metadata if the section exists, or nil. + */ +- (NSDictionary *_Nullable)getMetadataInSection:(NSString *_Nonnull)sectionName; - @param attributeName The key name - @param value The value to set - @param tabName The name of the report section +/** + * Remove a named metadata section, if it exists. + * + * @param sectionName The name of the section to remove */ -- (void)addAttribute:(NSString *_Nonnull)attributeName - withValue:(id _Nullable)value - toTabWithName:(NSString *_Nonnull)tabName; +- (void)clearMetadataSection:(NSString *_Nonnull)sectionName; + +/** + * Remove a named metadata value if the key exists in the named section. + * + * @param sectionName The name of the section to remove + * @param key The key to remove + */ +- (void)clearMetadataInSection:(NSString *_Nonnull)sectionName + withKey:(NSString *_Nonnull)key; + +// ----------------------------------------------------------------------------- +// MARK: - Properties +// ----------------------------------------------------------------------------- /** * The release stages used to notify at the time this report is captured @@ -219,9 +273,4 @@ __deprecated_msg("Use toJson: instead."); */ @property (readonly, nonatomic, getter=isIncomplete) BOOL incomplete; -/** - * Returns the enhanced error message for the thread, or nil if none exists. - */ -- (NSString *_Nullable)enhancedErrorMessageForThread:(NSDictionary *_Nullable)thread __deprecated; - @end diff --git a/Source/BugsnagEvent.m b/Source/BugsnagEvent.m index 3fd6868e0..c85af4963 100644 --- a/Source/BugsnagEvent.m +++ b/Source/BugsnagEvent.m @@ -354,6 +354,8 @@ - (instancetype)initWithKSReport:(NSDictionary *)report { return self; } +// MARK: - Metadata + @synthesize metadata = _metadata; - (NSDictionary *)metadata { @@ -368,20 +370,82 @@ - (void)setMetadata:(NSDictionary *)metadata { } } -- (void)addMetadata:(NSDictionary *_Nonnull)tabData - toTabWithName:(NSString *_Nonnull)tabName { - NSDictionary *cleanedData = BSGSanitizeDict(tabData); - if ([cleanedData count] == 0) { - bsg_log_err(@"Failed to add metadata: Values not convertible to JSON"); - return; +- (void)addMetadata:(NSDictionary *_Nonnull)metadata + toSectionNamed:(NSString *_Nonnull)sectionName +{ + @synchronized (self) { + NSDictionary *cleanedData = BSGSanitizeDict(metadata); + if ([cleanedData count] == 0) { + bsg_log_err(@"Failed to add metadata: Values not convertible to JSON"); + return; + } + NSMutableDictionary *allMetadata = [self.metadata mutableCopy]; + NSMutableDictionary *allTabData = + allMetadata[sectionName] ?: [NSMutableDictionary new]; + allMetadata[sectionName] = [cleanedData BSG_mergedInto:allTabData]; + self.metadata = allMetadata; + } +} + +- (void)addMetadataToSectionNamed:(NSString *_Nonnull)sectionName + key:(NSString *_Nonnull)key + value:(id _Nullable)value +{ + @synchronized (self) { + NSMutableDictionary *allMetadata = [self.metadata mutableCopy]; + NSMutableDictionary *allTabData = + [allMetadata[sectionName] mutableCopy] ?: [NSMutableDictionary new]; + if (value) { + id cleanedValue = BSGSanitizeObject(value); + if (!cleanedValue) { + bsg_log_err(@"Failed to add metadata: Value of type %@ is not " + @"convertible to JSON", + [value class]); + return; + } + allTabData[key] = cleanedValue; + } else { + [allTabData removeObjectForKey:key]; + } + allMetadata[sectionName] = allTabData; + self.metadata = allMetadata; + } +} + +- (id _Nullable)getMetadataInSection:(NSString *_Nonnull)sectionName + withKey:(NSString *_Nullable)key +{ + @synchronized (self) { + return [[[self metadata] objectForKey:sectionName] objectForKey:key]; } - NSMutableDictionary *allMetadata = [self.metadata mutableCopy]; - NSMutableDictionary *allTabData = - allMetadata[tabName] ?: [NSMutableDictionary new]; - allMetadata[tabName] = [cleanedData BSG_mergedInto:allTabData]; - self.metadata = allMetadata; } +- (NSDictionary *_Nullable)getMetadataInSection:(NSString *_Nonnull)sectionName +{ + @synchronized (self) { + return [[self metadata] objectForKey:sectionName]; + } +} + +- (void)clearMetadataSection:(NSString *_Nonnull)sectionName +{ + @synchronized (self) { + NSMutableDictionary *copy = [[self metadata] mutableCopy]; + [copy removeObjectForKey:sectionName]; + _metadata = copy; + } +} + +- (void)clearMetadataInSection:(NSString *_Nonnull)sectionName + withKey:(NSString *_Nonnull)key +{ + @synchronized (self) { + [[[self metadata] objectForKey:sectionName] removeObjectForKey:key]; + } +} + +// MARK: - apiKey + @synthesize apiKey = _apiKey; - (NSString *)apiKey { @@ -404,28 +468,6 @@ - (void)setApiKey:(NSString *)apiKey { } } -- (void)addAttribute:(NSString *)attributeName - withValue:(id)value - toTabWithName:(NSString *)tabName { - NSMutableDictionary *allMetadata = [self.metadata mutableCopy]; - NSMutableDictionary *allTabData = - [allMetadata[tabName] mutableCopy] ?: [NSMutableDictionary new]; - if (value) { - id cleanedValue = BSGSanitizeObject(value); - if (!cleanedValue) { - bsg_log_err(@"Failed to add metadata: Value of type %@ is not " - @"convertible to JSON", - [value class]); - return; - } - allTabData[attributeName] = cleanedValue; - } else { - [allTabData removeObjectForKey:attributeName]; - } - allMetadata[tabName] = allTabData; - self.metadata = allMetadata; -} - - (BOOL)shouldBeSent { return [self.notifyReleaseStages containsObject:self.releaseStage] || (self.notifyReleaseStages.count == 0 && diff --git a/Tests/BugsnagBaseUnitTest.h b/Tests/BugsnagBaseUnitTest.h new file mode 100644 index 000000000..82b373e7d --- /dev/null +++ b/Tests/BugsnagBaseUnitTest.h @@ -0,0 +1,19 @@ +// +// BugsnagBaseUnitTest.h +// Bugsnag +// +// Created by Robin Macharg on 13/02/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// +// A Unit Test base class that provides useful utility methods. + +#ifndef BugsnagBaseUnitTest_h +#define BugsnagBaseUnitTest_h + +@interface BugsnagBaseUnitTest : XCTestCase + +-(void)setUpBugsnagWillCallNotify:(bool)willNotify; + +@end + +#endif /* BugsnagBaseUnitTest_h */ diff --git a/Tests/BugsnagBaseUnitTest.m b/Tests/BugsnagBaseUnitTest.m new file mode 100644 index 000000000..9382e8559 --- /dev/null +++ b/Tests/BugsnagBaseUnitTest.m @@ -0,0 +1,42 @@ +// +// BugsnagBaseUnitTest.m +// Tests +// +// Created by Robin Macharg on 13/02/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import "Bugsnag.h" +#import "BugsnagConfiguration.h" +#import "BugsnagTestConstants.h" +#import + +@interface BugsnagBaseUnitTest : XCTestCase + +@end + +@implementation BugsnagBaseUnitTest + +/** + * A boilerplate helper method to setup Bugsnag + * If [Bugsnag notify] is to be called during unit testing it should either: + * + * - discard events before sending or + * - send to an arbitrary non-functional endpoint. + * + * We take the former approach. + */ +-(void)setUpBugsnagWillCallNotify:(bool)willNotify { + NSError *error; + BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1 error:&error]; + if (willNotify) { + [configuration addBeforeSendBlock:^bool(NSDictionary * _Nonnull rawEventData, + BugsnagEvent * _Nonnull reports) + { + return false; + }]; + } + [Bugsnag startBugsnagWithConfiguration:configuration]; +} + +@end diff --git a/Tests/BugsnagEventTests.m b/Tests/BugsnagEventTests.m index a1df383a3..774875feb 100644 --- a/Tests/BugsnagEventTests.m +++ b/Tests/BugsnagEventTests.m @@ -13,9 +13,11 @@ #import "Bugsnag.h" #import "BugsnagHandledState.h" #import "BugsnagSession.h" +#import "BugsnagBaseUnitTest.h" #import "BugsnagTestConstants.h" +#import "BugsnagTestsDummyClass.h" -@interface BugsnagEventTests : XCTestCase +@interface BugsnagEventTests : BugsnagBaseUnitTest @end @implementation BugsnagEventTests @@ -442,12 +444,12 @@ - (void)testAppVersionOverride { - (void)testReportAddAttr { BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; - [event addAttribute:@"foo" withValue:@"bar" toTabWithName:@"user"]; + [event addMetadataToSectionNamed:@"user" key:@"foo" value:@"bar"]; } - (void)testReportAddMetadata { BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{@"user.metaData": @{@"user": @{@"id": @"user id"}}}]; - [event addMetadata:@{@"foo": @"bar"} toTabWithName:@"user"]; + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"user"]; } @@ -456,9 +458,8 @@ - (void)testReportAddMetadata { * per-event changes to apiKey. */ - (void)testApiKey { - NSError *error; - BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithApiKey:DUMMY_APIKEY_32CHAR_1 error:&error]; - [Bugsnag startBugsnagWithConfiguration:config]; + + [self setUpBugsnagWillCallNotify:false]; NSException *ex = [[NSException alloc] initWithName:@"myName" reason:@"myReason1" userInfo:nil]; @@ -504,4 +505,163 @@ - (void)testApiKey { }]; } +// MARK: - Metadata interface + +- (void)testAddMetadataSectionKeyValue { + + [self setUpBugsnagWillCallNotify:true]; + + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ + @"user.metaData": @{ + @"user": @{@"id": @"user id"} + }}]; + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section"]; + + // Known + XCTAssertEqual([event getMetadataInSection:@"section" withKey:@"foo"], @"bar"); + XCTAssertNotNil([event getMetadataInSection:@"section"]); + XCTAssertEqual([[event getMetadataInSection:@"section"] count], 1); + [event addMetadata:@{@"baz": @"bam"} toSectionNamed:@"section"]; + XCTAssertEqual([[event getMetadataInSection:@"section"] count], 2); + XCTAssertEqual([event getMetadataInSection:@"section" withKey:@"baz"], @"bam"); + // check type + NSDictionary *v = [event getMetadataInSection:@"section"]; + XCTAssertTrue([((NSString *)[v valueForKey:@"foo"]) isEqualToString:@"bar"]); + + // Unknown + XCTAssertNil([event getMetadataInSection:@"section" withKey:@"bob"]); + XCTAssertNil([event getMetadataInSection:@"anotherSection" withKey:@"baz"]); + XCTAssertNil([event getMetadataInSection:@"dummySection"]); +} + +/** + * Invalid data should not be set. Manually check for coverage of logging code. + */ +- (void)testInvalidSectionData { + [self setUpBugsnagWillCallNotify:true]; + + NSException *ex = [[NSException alloc] initWithName:@"myName" reason:@"myReason1" userInfo:nil]; + + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + NSDictionary *invalidDict = @{}; + NSDictionary *validDict = @{@"myKey" : @"myValue"}; + [event addMetadata:invalidDict toSectionNamed:@"mySection"]; + XCTAssertEqual([[event metadata] count], 0); + [event addMetadata:validDict toSectionNamed:@"mySection"]; + XCTAssertEqual([[event metadata] count], 1); + }]; +} + +- (void)testInvalidKeyValueData { + [self setUpBugsnagWillCallNotify:true]; + + NSException *ex = [[NSException alloc] initWithName:@"myName" reason:@"myReason1" userInfo:nil]; + + [Bugsnag notify:ex block:^(BugsnagEvent * _Nonnull event) { + [event addMetadataToSectionNamed:@"mySection" key:@"myKey" value:[NSNull null]]; + + // Invalid value still causes section to be created + XCTAssertEqual([[event metadata] count], 1); + XCTAssertNil([[event metadata] objectForKey:@"myKey"]); + + [event addMetadataToSectionNamed:@"mySection" key:@"myKey" value:@"aValue"]; + XCTAssertEqual([[event metadata] count], 1); + XCTAssertNotNil([[[event metadata] objectForKey:@"mySection"] objectForKey:@"myKey"]); + + BugsnagTestsDummyClass *dummy = [BugsnagTestsDummyClass new]; + [event addMetadataToSectionNamed:@"mySection" key:@"myNewKey" value:dummy]; + XCTAssertEqual([[event metadata] count], 1); + XCTAssertNil([[[event metadata] objectForKey:@"mySection"] objectForKey:@"myNewKey"]); + + [event addMetadataToSectionNamed:@"mySection" key:@"myNewKey" value:@"realValue"]; + XCTAssertEqual([[event metadata] count], 1); + XCTAssertNotNil([[[event metadata] objectForKey:@"mySection"] objectForKey:@"myNewKey"]); + }]; +} + +- (void)testClearMetadataSection { + // Setup + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ + @"user.metaData": @{ + @"user": @{@"id": @"user id"} + }}]; + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"baz": @"bill"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"alice": @"bob"} toSectionNamed:@"section2"]; + XCTAssertEqual([[event metadata] count], 3); + + // Known + [event clearMetadataSection:@"section1"]; + XCTAssertEqual([[event metadata] count], 2); + + // Unknown + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"baz": @"bill"} toSectionNamed:@"section1"]; + [event clearMetadataSection:@"section3"]; + XCTAssertEqual([[event metadata] count], 3); + + // Empty + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"baz": @"bill"} toSectionNamed:@"section1"]; + [event clearMetadataSection:@"section1"]; + [event clearMetadataSection:@"section2"]; + [event clearMetadataSection:@"section3"]; + XCTAssertEqual([[event metadata] count], 1); + + [event clearMetadataSection:@"user"]; + XCTAssertEqual([[event metadata] count], 0); + + [event clearMetadataSection:@"section1"]; + [event clearMetadataSection:@"section2"]; + [event clearMetadataSection:@"section3"]; + [event clearMetadataSection:@"user"]; + XCTAssertEqual([[event metadata] count], 0); +} + +- (void)testClearMetadataSectionWithKey { + // Setup + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ + @"user.metaData": @{ + @"user": @{@"id": @"user id"} + }}]; + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"baz": @"bill"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"alice": @"bob"} toSectionNamed:@"section2"]; + XCTAssertEqual([[event metadata] count], 3); + + // Remove a key + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 2); + [event clearMetadataInSection:@"section1" withKey:@"foo"]; + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 1); + + // Remove all keys, check section exists + [event clearMetadataInSection:@"section1" withKey:@"baz"]; + XCTAssertNotNil([[event metadata] objectForKey:@"section1"]); + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 0); +} + +- (void)testClearMetadataSectionWithKeyNonExistentKeys { + // Setup + BugsnagEvent *event = [[BugsnagEvent alloc] initWithKSReport:@{ + @"user.metaData": @{ + @"user": @{@"id": @"user id"} + }}]; + [event addMetadata:@{@"foo": @"bar"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"baz": @"bill"} toSectionNamed:@"section1"]; + [event addMetadata:@{@"alice": @"bob"} toSectionNamed:@"section2"]; + XCTAssertEqual([[event metadata] count], 3); + + // Nonexistent key + [event clearMetadataInSection:@"section1" withKey:@"flump"]; + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 2); + [event clearMetadataInSection:@"section1" withKey:@"foo"]; + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 1); + XCTAssertEqual([[event metadata] count], 3); + + // Nonexistent section + [event clearMetadataInSection:@"section52" withKey:@"baz"]; + XCTAssertEqual([[event metadata] count], 3); + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section1"]) count], 1); + XCTAssertEqual([((NSDictionary *)[[event metadata] objectForKey:@"section2"]) count], 1); +} @end diff --git a/Tests/BugsnagTestsDummyClass.h b/Tests/BugsnagTestsDummyClass.h new file mode 100644 index 000000000..c6a5d735b --- /dev/null +++ b/Tests/BugsnagTestsDummyClass.h @@ -0,0 +1,17 @@ +// +// BugsnagTestsDummyClass.h +// Tests +// +// Created by Robin Macharg on 18/02/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BugsnagTestsDummyClass : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/BugsnagTestsDummyClass.m b/Tests/BugsnagTestsDummyClass.m new file mode 100644 index 000000000..fceaa3bc0 --- /dev/null +++ b/Tests/BugsnagTestsDummyClass.m @@ -0,0 +1,13 @@ +// +// BugsnagTestsDummyClass.m +// Tests +// +// Created by Robin Macharg on 18/02/2020. +// Copyright © 2020 Bugsnag. All rights reserved. +// + +#import "BugsnagTestsDummyClass.h" + +@implementation BugsnagTestsDummyClass + +@end diff --git a/iOS/Bugsnag.xcodeproj/project.pbxproj b/iOS/Bugsnag.xcodeproj/project.pbxproj index 15b15532b..8d0d9dea7 100644 --- a/iOS/Bugsnag.xcodeproj/project.pbxproj +++ b/iOS/Bugsnag.xcodeproj/project.pbxproj @@ -18,8 +18,10 @@ 009131BA23F44619000810D9 /* BugsnagMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 009131B923F44619000810D9 /* BugsnagMetadataTests.m */; }; 004753D423F1A4E2009884EB /* BugsnagMetadataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 004753D323F1A4E2009884EB /* BugsnagMetadataTests.swift */; }; 009131BA23F44619000810D9 /* BugsnagMetadataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 009131B923F44619000810D9 /* BugsnagMetadataTests.m */; }; + 009131BE23F5884E000810D9 /* BugsnagBaseUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */; }; 00D7ACAD23E9C63000FBE4A7 /* BugsnagTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */; }; 00D7ACAF23EABBC800FBE4A7 /* BugsnagSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACAE23EABBC800FBE4A7 /* BugsnagSwiftTests.swift */; }; + 00F9393923FC4F64008C7073 /* BugsnagTestsDummyClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F9393823FC4F64008C7073 /* BugsnagTestsDummyClass.m */; }; 4B47970A22A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B47970922A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m */; }; 4B775FCF22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */; }; 4BE6C42622CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE6C42522CAD61A0056305D /* BugsnagCollectionsBSGDictMergeTest.m */; }; @@ -425,8 +427,12 @@ 009131B923F44619000810D9 /* BugsnagMetadataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagMetadataTests.m; path = ../../Tests/BugsnagMetadataTests.m; sourceTree = ""; }; 004753D323F1A4E2009884EB /* BugsnagMetadataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugsnagMetadataTests.swift; sourceTree = ""; }; 009131B923F44619000810D9 /* BugsnagMetadataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagMetadataTests.m; path = ../../Tests/BugsnagMetadataTests.m; sourceTree = ""; }; + 009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagBaseUnitTest.m; path = ../../Tests/BugsnagBaseUnitTest.m; sourceTree = ""; }; + 009131BF23F58930000810D9 /* BugsnagBaseUnitTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BugsnagBaseUnitTest.h; path = ../../Tests/BugsnagBaseUnitTest.h; sourceTree = ""; }; 00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagTests.m; path = ../../Tests/BugsnagTests.m; sourceTree = ""; }; 00D7ACAE23EABBC800FBE4A7 /* BugsnagSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugsnagSwiftTests.swift; sourceTree = ""; }; + 00F9393723FC4F63008C7073 /* BugsnagTestsDummyClass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BugsnagTestsDummyClass.h; path = ../../Tests/BugsnagTestsDummyClass.h; sourceTree = ""; }; + 00F9393823FC4F64008C7073 /* BugsnagTestsDummyClass.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagTestsDummyClass.m; path = ../../Tests/BugsnagTestsDummyClass.m; sourceTree = ""; }; 4B3B193422CA7B0900475354 /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictSetSafeObjectTest.m; path = ../../Tests/BugsnagCollectionsBSGDictSetSafeObjectTest.m; sourceTree = ""; }; 4B47970922A9AE1F00FF9C2E /* BugsnagEventFromKSCrashReportTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagEventFromKSCrashReportTest.m; sourceTree = ""; }; 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BugsnagCollectionsBSGDictInsertIfNotNilTest.m; path = ../../Tests/BugsnagCollectionsBSGDictInsertIfNotNilTest.m; sourceTree = ""; }; @@ -748,6 +754,9 @@ 8A2C8F261C6BBD2300846019 /* Tests */ = { isa = PBXGroup; children = ( + 009131BD23F5884E000810D9 /* BugsnagBaseUnitTest.m */, + 009131BF23F58930000810D9 /* BugsnagBaseUnitTest.h */, + 00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */, 8A2C8F8B1C6BBFDD00846019 /* BugsnagBreadcrumbsTest.m */, 4B3B193422CA7B0900475354 /* BugsnagCollectionsBSGDictSetSafeObjectTest.m */, 4B775FCE22CBDEB4004839C5 /* BugsnagCollectionsBSGDictInsertIfNotNilTest.m */, @@ -789,6 +798,8 @@ 000DF29323DB4B4900A883CE /* TestConstants.m */, 00D7ACAC23E9C63000FBE4A7 /* BugsnagTests.m */, 009131B923F44619000810D9 /* BugsnagMetadataTests.m */, + 00F9393723FC4F63008C7073 /* BugsnagTestsDummyClass.h */, + 00F9393823FC4F64008C7073 /* BugsnagTestsDummyClass.m */, ); name = Tests; path = BugsnagTests; @@ -1283,6 +1294,7 @@ 8A2C8F901C6BBFDD00846019 /* BugsnagEventTests.m in Sources */, 009131BA23F44619000810D9 /* BugsnagMetadataTests.m in Sources */, E77316E31F73E89E00A14F06 /* BugsnagHandledStateTest.m in Sources */, + 009131BE23F5884E000810D9 /* BugsnagBaseUnitTest.m in Sources */, E70EE0961FD7071F00FA745C /* FileBasedTestCase.m in Sources */, E7B3291A1FD707EC0098FC47 /* KSCrashReportConverter_Tests.m in Sources */, E733A76F1FD709B7003EAA29 /* KSCrashReportStore_Tests.m in Sources */, @@ -1299,6 +1311,7 @@ E78C1EF11FCC2F1700B976D3 /* BugsnagSessionTrackerTest.m in Sources */, F4295995C3259BF7D9730BC4 /* BugsnagKSCrashSysInfoParserTest.m in Sources */, E70E52152216E41C00A590AB /* BugsnagSessionTrackerStopTest.m in Sources */, + 00F9393923FC4F64008C7073 /* BugsnagTestsDummyClass.m in Sources */, F4295F017754324FD52CCE46 /* RegisterErrorDataTest.m in Sources */, 00D7ACAF23EABBC800FBE4A7 /* BugsnagSwiftTests.swift in Sources */, F42952D83435C02F8D891C40 /* BugsnagThreadTest.m in Sources */, diff --git a/iOS/BugsnagTests/BugsnagEventFromKSCrashReportTest.m b/iOS/BugsnagTests/BugsnagEventFromKSCrashReportTest.m index 333100b91..2d174137e 100644 --- a/iOS/BugsnagTests/BugsnagEventFromKSCrashReportTest.m +++ b/iOS/BugsnagTests/BugsnagEventFromKSCrashReportTest.m @@ -53,7 +53,7 @@ - (void)testReadNotifyReleaseStagesSends { - (void)testAddMetadataAddsNewTab { NSDictionary *metadata = @{@"color" : @"blue", @"beverage" : @"tea"}; - [self.report addMetadata:metadata toTabWithName:@"user prefs"]; + [self.report addMetadata:metadata toSectionNamed:@"user prefs"]; NSDictionary *prefs = self.report.metadata[@"user prefs"]; XCTAssertEqual(@"blue", prefs[@"color"]); XCTAssertEqual(@"tea", prefs[@"beverage"]); @@ -62,9 +62,9 @@ - (void)testAddMetadataAddsNewTab { - (void)testAddMetadataMergesExistingTab { NSDictionary *oldMetadata = @{@"color" : @"red", @"food" : @"carrots"}; - [self.report addMetadata:oldMetadata toTabWithName:@"user prefs"]; + [self.report addMetadata:oldMetadata toSectionNamed:@"user prefs"]; NSDictionary *metadata = @{@"color" : @"blue", @"beverage" : @"tea"}; - [self.report addMetadata:metadata toTabWithName:@"user prefs"]; + [self.report addMetadata:metadata toSectionNamed:@"user prefs"]; NSDictionary *prefs = self.report.metadata[@"user prefs"]; XCTAssertEqual(@"blue", prefs[@"color"]); XCTAssertEqual(@"tea", prefs[@"beverage"]); @@ -73,25 +73,31 @@ - (void)testAddMetadataMergesExistingTab { } - (void)testAddAttributeAddsNewTab { - [self.report addAttribute:@"color" - withValue:@"blue" - toTabWithName:@"prefs"]; + [self.report addMetadataToSectionNamed:@"prefs" + key:@"color" + value:@"blue"]; NSDictionary *prefs = self.report.metadata[@"prefs"]; XCTAssertEqual(@"blue", prefs[@"color"]); } - (void)testAddAttributeOverridesExistingValue { - [self.report addAttribute:@"color" withValue:@"red" toTabWithName:@"prefs"]; - [self.report addAttribute:@"color" - withValue:@"blue" - toTabWithName:@"prefs"]; + [self.report addMetadataToSectionNamed:@"prefs" + key:@"color" + value:@"red"]; + [self.report addMetadataToSectionNamed:@"prefs" + key:@"color" + value:@"blue"]; NSDictionary *prefs = self.report.metadata[@"prefs"]; XCTAssertEqual(@"blue", prefs[@"color"]); } - (void)testAddAttributeRemovesValue { - [self.report addAttribute:@"color" withValue:@"red" toTabWithName:@"prefs"]; - [self.report addAttribute:@"color" withValue:nil toTabWithName:@"prefs"]; + [self.report addMetadataToSectionNamed:@"prefs" + key:@"color" + value:@"red"]; + [self.report addMetadataToSectionNamed:@"prefs" + key:@"color" + value:nil]; NSDictionary *prefs = self.report.metadata[@"prefs"]; XCTAssertNil(prefs[@"color"]); } diff --git a/tvOS/Bugsnag.xcodeproj/project.pbxproj b/tvOS/Bugsnag.xcodeproj/project.pbxproj index 535ca775e..5eade889e 100644 --- a/tvOS/Bugsnag.xcodeproj/project.pbxproj +++ b/tvOS/Bugsnag.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 00D7ACA723E98A5D00FBE4A7 /* BugsnagEventTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACA623E98A5D00FBE4A7 /* BugsnagEventTests.m */; }; 00D7ACAA23E98A9A00FBE4A7 /* TestConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACA823E98A9A00FBE4A7 /* TestConstants.m */; }; 00D7ACAB23E98A9A00FBE4A7 /* BugsnagEventFromKSCrashReportTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00D7ACA923E98A9A00FBE4A7 /* BugsnagEventFromKSCrashReportTest.m */; }; + 00F9393623FC16DA008C7073 /* BugsnagBaseUnitTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F9393523FC16DA008C7073 /* BugsnagBaseUnitTest.m */; }; + 00F9393F23FD2DDB008C7073 /* BugsnagTestsDummyClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 00F9393E23FD2DDB008C7073 /* BugsnagTestsDummyClass.m */; }; 4B3CD2B622C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B522C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m */; }; 4B3CD2BB22C5676800DBFF33 /* BSGOutOfMemoryWatchdogTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B722C5676700DBFF33 /* BSGOutOfMemoryWatchdogTests.m */; }; 4B3CD2BD22C5676800DBFF33 /* BugsnagThreadTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B3CD2B922C5676700DBFF33 /* BugsnagThreadTest.m */; }; @@ -195,6 +197,10 @@ 00D7ACA623E98A5D00FBE4A7 /* BugsnagEventTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagEventTests.m; path = ../../Tests/BugsnagEventTests.m; sourceTree = ""; }; 00D7ACA823E98A9A00FBE4A7 /* TestConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestConstants.m; path = ../../iOS/BugsnagTests/TestConstants.m; sourceTree = ""; }; 00D7ACA923E98A9A00FBE4A7 /* BugsnagEventFromKSCrashReportTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagEventFromKSCrashReportTest.m; path = ../../iOS/BugsnagTests/BugsnagEventFromKSCrashReportTest.m; sourceTree = ""; }; + 00F9393423FC16DA008C7073 /* BugsnagBaseUnitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagBaseUnitTest.h; path = ../../Tests/BugsnagBaseUnitTest.h; sourceTree = ""; }; + 00F9393523FC16DA008C7073 /* BugsnagBaseUnitTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagBaseUnitTest.m; path = ../../Tests/BugsnagBaseUnitTest.m; sourceTree = ""; }; + 00F9393D23FD2DDB008C7073 /* BugsnagTestsDummyClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BugsnagTestsDummyClass.h; path = ../../Tests/BugsnagTestsDummyClass.h; sourceTree = ""; }; + 00F9393E23FD2DDB008C7073 /* BugsnagTestsDummyClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagTestsDummyClass.m; path = ../../Tests/BugsnagTestsDummyClass.m; sourceTree = ""; }; 4B3CD2B522C5625800DBFF33 /* BugsnagKSCrashSysInfoParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagKSCrashSysInfoParserTest.m; path = ../../iOS/BugsnagTests/BugsnagKSCrashSysInfoParserTest.m; sourceTree = ""; }; 4B3CD2B722C5676700DBFF33 /* BSGOutOfMemoryWatchdogTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BSGOutOfMemoryWatchdogTests.m; path = ../../iOS/BugsnagTests/BSGOutOfMemoryWatchdogTests.m; sourceTree = ""; }; 4B3CD2B922C5676700DBFF33 /* BugsnagThreadTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BugsnagThreadTest.m; path = ../../iOS/BugsnagTests/BugsnagThreadTest.m; sourceTree = ""; }; @@ -472,6 +478,10 @@ 8AB151131D41356800C9B218 /* Tests */ = { isa = PBXGroup; children = ( + 00F9393D23FD2DDB008C7073 /* BugsnagTestsDummyClass.h */, + 00F9393E23FD2DDB008C7073 /* BugsnagTestsDummyClass.m */, + 00F9393423FC16DA008C7073 /* BugsnagBaseUnitTest.h */, + 00F9393523FC16DA008C7073 /* BugsnagBaseUnitTest.m */, 00D7ACA923E98A9A00FBE4A7 /* BugsnagEventFromKSCrashReportTest.m */, 00D7ACA823E98A9A00FBE4A7 /* TestConstants.m */, 00D7ACA623E98A5D00FBE4A7 /* BugsnagEventTests.m */, @@ -961,6 +971,7 @@ E7CE79091FD94F1B001D07E0 /* KSCrashState_Tests.m in Sources */, E7CE78FF1FD94F1B001D07E0 /* KSCrashReportStore_Tests.m in Sources */, E7CE78FD1FD94F1B001D07E0 /* KSString_Tests.m in Sources */, + 00F9393623FC16DA008C7073 /* BugsnagBaseUnitTest.m in Sources */, E762E9F01F73F6CF00E82B43 /* BugsnagHandledStateTest.m in Sources */, E791488F1FD82E77003EFEBF /* BugsnagSessionTrackingPayloadTest.m in Sources */, 8AD9FA8D1E0863A1002859A7 /* BugsnagConfigurationTests.m in Sources */, @@ -971,6 +982,7 @@ 00D7ACAA23E98A9A00FBE4A7 /* TestConstants.m in Sources */, 4B3CD2BB22C5676800DBFF33 /* BSGOutOfMemoryWatchdogTests.m in Sources */, E7CE78F41FD94F1B001D07E0 /* KSFileUtils_Tests.m in Sources */, + 00F9393F23FD2DDB008C7073 /* BugsnagTestsDummyClass.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };