From 924b6abc364183bfbc994e04c1a55324aaa96099 Mon Sep 17 00:00:00 2001 From: Tom Longridge Date: Thu, 26 Jan 2023 12:38:08 +0000 Subject: [PATCH 01/37] build: remove infer due to homebrew deprecation --- .github/workflows/pull_request.yml | 4 +--- .../project.xcworkspace/contents.xcworkspacedata | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 examples/objective-c-ios/objective-c-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1759f3e35..1af84fae0 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -14,11 +14,9 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} - name: Install dependencies - run: brew install infer oclint && gem install xcpretty + run: brew install oclint && gem install xcpretty - name: Build framework run: make compile_commands.json - - name: Infer - run: make infer - name: OCLint run: make oclint diff --git a/examples/objective-c-ios/objective-c-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/objective-c-ios/objective-c-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/examples/objective-c-ios/objective-c-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + From 4c356ab5a9ba5a7146d514901daca147ad37dfb8 Mon Sep 17 00:00:00 2001 From: matth-bugsnag <113024072+matth-bugsnag@users.noreply.github.com> Date: Thu, 19 Jan 2023 11:28:56 +0000 Subject: [PATCH 02/37] onError blocked from running if releaseStage not in enabledReleaseStages fixing build error onError will run when NO enabledReleaseStage set Refactoring releaseStage check Updated changelog --- Bugsnag/Client/BugsnagClient.m | 6 ++++++ CHANGELOG.md | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 9c3297dfc..3efd52a86 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -747,6 +747,12 @@ - (void)notifyErrorOrException:(id)errorOrException block:(BugsnagOnErrorBlock)b - (void)notifyInternal:(BugsnagEvent *_Nonnull)event block:(BugsnagOnErrorBlock)block { + // Checks whether releaseStage is in enabledReleaseStages, blocking onError callback from running if it is not. + if (!self.configuration.shouldSendReports || ![event shouldBeSent]) { + bsg_log_info("Discarding error because releaseStage '%@' not in enabledReleaseStages", self.configuration.releaseStage); + return; + } + NSString *errorClass = event.errors.firstObject.errorClass; if ([self.configuration shouldDiscardErrorClass:errorClass]) { bsg_log_info(@"Discarding event because errorClass \"%@\" matched configuration.discardClasses", errorClass); diff --git a/CHANGELOG.md b/CHANGELOG.md index 481685654..b7c0eada0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ Changelog ========= +## TBD + +## Bug fixes + +* onError blocked from running if releaseStage not in enabledReleaseStages. + [1518](https://github.com/bugsnag/bugsnag-cocoa/pull/1518) + ## 6.25.2 (2023-01-18) From cb914aec57f34b130e4630deedcf6c6b19c620b6 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 20 Jan 2023 22:03:14 +0000 Subject: [PATCH 03/37] Something built --- .../ios/iOSTestApp/ViewController.swift | 15 ++++- .../scenarios/ManyConcurrentNotifyScenario.m | 6 +- .../NetworkBreadcrumbsScenario.swift | 12 +--- features/fixtures/shared/scenarios/Scenario.h | 16 +++-- features/fixtures/shared/scenarios/Scenario.m | 61 +++++++++++++------ 5 files changed, 73 insertions(+), 37 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 694f12841..b654b3379 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -14,6 +14,7 @@ class ViewController: UIViewController { @IBOutlet var scenarioNameField : UITextField! @IBOutlet var scenarioMetaDataField : UITextField! @IBOutlet var apiKeyField: UITextField! + var mazeRunnerAddress: String = ""; override func viewDidLoad() { super.viewDidLoad() @@ -46,6 +47,15 @@ class ViewController: UIViewController { Scenario.clearPersistentData() } + func loadMazeRunnerAddress() -> String { +#if TARGET_OS_IOS + return ""; +#else + return "localhost:9339"; +#endif + + } + internal func prepareScenario() { var config: BugsnagConfiguration? if (apiKeyField.text!.count > 0) { @@ -56,12 +66,13 @@ class ViewController: UIViewController { config = BugsnagConfiguration(apiKeyField.text!) } - Scenario.createScenarioNamed(scenarioNameField.text!, withConfig: config) + + Scenario.createScenarioNamed(scenarioNameField.text!, withConfig: config, withMazeAddress: loadMazeRunnerAddress()) Scenario.current!.eventMode = scenarioMetaDataField.text } @IBAction func executeCommand(_ sender: Any) { - Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in + Scenario.current!.executeMazeRunnerCommand { _, scenarioName, eventMode in self.scenarioNameField.text = scenarioName self.scenarioMetaDataField.text = eventMode } diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index 97330e1e8..03b5adf66 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -12,8 +12,10 @@ @implementation FooError @implementation ManyConcurrentNotifyScenario -- (instancetype)initWithConfig:(BugsnagConfiguration *)config { - if (self = [super initWithConfig:config]) { +- (instancetype)initWithConfig:(BugsnagConfiguration *)config + withMazeAddress:(NSString *)mazeAddress { + if (self = [super initWithConfig:config + withMazeAddress:mazeAddress]) { _queue1 = dispatch_queue_create("Log Queue 1", DISPATCH_QUEUE_CONCURRENT); _queue2 = dispatch_queue_create("Log Queue 2", DISPATCH_QUEUE_CONCURRENT); } diff --git a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift index 101e370e2..668041fef 100644 --- a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift +++ b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift @@ -10,18 +10,12 @@ import Foundation @available(iOS 10.0, macOS 10.12, *) class NetworkBreadcrumbsScenario : Scenario { - - lazy var baseURL: URL = { - var components = URLComponents(string: Scenario.mazeRunnerURL.absoluteString)! - components.port = 9340 // `/reflect` listens on a different port :-(( - return components.url! - }() - + override func startBugsnag() { config.autoTrackSessions = false; config.add(BugsnagNetworkRequestPlugin()) config.addOnBreadcrumb { - ($0.metadata["url"] as? String ?? "").hasPrefix(self.baseURL.absoluteString) + ($0.metadata["url"] as? String ?? "").hasPrefix(self.baseMazeAddress) } super.startBugsnag() @@ -38,7 +32,7 @@ class NetworkBreadcrumbsScenario : Scenario { } func query(string: String) { - let url = URL(string: string, relativeTo: baseURL)! + let url = URL(string: self.baseMazeAddress + string)! let semaphore = DispatchSemaphore(value: 0) let task = URLSession.shared.dataTask(with: url) {(data, response, error) in diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 31e5e02eb..0ddbe5e6d 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -15,15 +15,21 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @interface Scenario : NSObject -@property (class, readonly) NSURL *mazeRunnerURL; +@property (nonatomic, readonly) NSString *baseMazeAddress; -@property (strong, nonatomic, nonnull) BugsnagConfiguration *config; +// TODO Do we still need this? +@property (nonatomic, readonly) NSURL *mazeRunnerURL; -+ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(nullable BugsnagConfiguration *)config; +@property (strong, nonatomic, nonnull) BugsnagConfiguration *config; ++ (Scenario *)createScenarioNamed:(NSString *)className + withConfig:(nullable BugsnagConfiguration *)config + withMazeAddress:(NSString *)mazeAddress; + @property (class, readonly, nullable) Scenario *currentScenario; -- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config; +- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config + withMazeAddress:(NSString *) mazeAddress; /** * Executes the test case @@ -42,7 +48,7 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); + (void)clearPersistentData; -+ (void)executeMazeRunnerCommand:(nullable void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; +- (void)executeMazeRunnerCommand:(nullable void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; @end diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 6db35ec47..c32742e7e 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -7,14 +7,11 @@ #import #if TARGET_OS_IOS -#define MAZE_RUNNER_URL "http://bs-local.com:9339" #define SWIFT_MODULE "iOSTestApp" #elif TARGET_OS_OSX -#define MAZE_RUNNER_URL "http://localhost:9339" #define SWIFT_MODULE "macOSTestApp" #elif TARGET_OS_WATCH #import "watchos_maze_host.h" -#define MAZE_RUNNER_URL "http://" WATCHOS_MAZE_HOST ":9339" #define SWIFT_MODULE "watchOSTestApp_WatchKit_Extension" #else #error Unsupported TARGET_OS @@ -43,6 +40,7 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { @implementation Scenario { dispatch_block_t _onEventDelivery; dispatch_block_t _onSessionDelivery; + NSString *_baseMazeAddress; } + (void)load { @@ -71,11 +69,9 @@ + (void)load { }]; } -+ (NSURL *)mazeRunnerURL { - return [NSURL URLWithString:@MAZE_RUNNER_URL]; -} - -+ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { ++ (Scenario *)createScenarioNamed:(NSString *)className + withConfig:(BugsnagConfiguration *)config + withMazeAddress:(NSString *)mazeAddress { Class class = NSClassFromString(className) ?: NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); @@ -83,21 +79,46 @@ + (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfi [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; } - return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); + return (theScenario = [(Scenario *)[class alloc] initWithConfig:config + withMazeAddress:mazeAddress]); } + (Scenario *)currentScenario { return theScenario; } -- (instancetype)initWithConfig:(BugsnagConfiguration *)config { +- (NSString *)readMazeRunnerAddress { + // TODO Read from disk + return NULL; +} + +- (NSURL *)mazeRunnerURL { + + return NULL; + + // NSData to read from disk + // NSJsonSerialzation +} + +- (NSString *)baseMazeAddress { + + return NULL; + + // NSData + // NSJsonSerialzation +} + + +- (instancetype)initWithConfig:(BugsnagConfiguration *)config + withMazeAddress:(NSString *) mazeAddress { if (self = [super init]) { + _baseMazeAddress = mazeAddress; if (config) { _config = config; } else { _config = [[BugsnagConfiguration alloc] initWithApiKey:@"12312312312312312312312312312312"]; - _config.endpoints.notify = @MAZE_RUNNER_URL "/notify"; - _config.endpoints.sessions = @MAZE_RUNNER_URL "/sessions"; + _config.endpoints.notify = [NSString stringWithFormat:@"%@/notify", _baseMazeAddress]; + _config.endpoints.sessions = [NSString stringWithFormat:@"%@/sessions", _baseMazeAddress]; } #if !TARGET_OS_WATCH _config.enabledErrorTypes.ooms = NO; @@ -215,11 +236,11 @@ + (void)clearPersistentData { #endif } -+ (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { +- (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { NSLog(@"%s", __PRETTY_FUNCTION__); NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@MAZE_RUNNER_URL "/command"]]; + NSString *commandEndpoint = [NSString stringWithFormat:@"%@/command", _baseMazeAddress]; + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:commandEndpoint]]; [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (![response isKindOfClass:[NSHTTPURLResponse class]] || [(NSHTTPURLResponse *)response statusCode] != 200) { NSLog(@"%s request failed with %@", __PRETTY_FUNCTION__, response ?: error); @@ -240,7 +261,7 @@ + (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioN } if ([[command objectForKey:@"reset_data"] isEqual:@YES]) { - [self clearPersistentData]; + [Scenario clearPersistentData]; } if (preHandler) { @@ -259,7 +280,7 @@ + (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioN }] resume]; } -+ (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { +- (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); [self startBugsnagForScenario:scenarioName eventMode:eventMode]; @@ -268,10 +289,12 @@ + (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { [theScenario run]; } -+ (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { +- (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); - theScenario = [Scenario createScenarioNamed:scenarioName withConfig:nil]; + theScenario = [Scenario createScenarioNamed:scenarioName + withConfig:nil + withMazeAddress:_baseMazeAddress]; theScenario.eventMode = eventMode; NSLog(@"Starting scenario \"%@\"", NSStringFromClass([theScenario class])); From 5654163af73529f02ea73add82cec6c187332d2c Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Sat, 21 Jan 2023 09:03:07 +0000 Subject: [PATCH 04/37] Rename --- .../fixtures/ios/iOSTestApp/ViewController.swift | 9 ++++++--- .../shared/scenarios/ManyConcurrentNotifyScenario.m | 4 ++-- features/fixtures/shared/scenarios/Scenario.h | 6 +++--- features/fixtures/shared/scenarios/Scenario.m | 12 ++++++------ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index b654b3379..b9487c7f4 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -49,9 +49,10 @@ class ViewController: UIViewController { func loadMazeRunnerAddress() -> String { #if TARGET_OS_IOS - return ""; + // TODO Load dynamically from file + return "https://bs-local.com:9339"; #else - return "localhost:9339"; + return "http://localhost:9339"; #endif } @@ -67,7 +68,9 @@ class ViewController: UIViewController { } - Scenario.createScenarioNamed(scenarioNameField.text!, withConfig: config, withMazeAddress: loadMazeRunnerAddress()) + Scenario.createScenarioNamed(scenarioNameField.text!, + withConfig: config, + andMazeAddress: loadMazeRunnerAddress()) Scenario.current!.eventMode = scenarioMetaDataField.text } diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index 03b5adf66..6d8cca619 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -13,9 +13,9 @@ @implementation FooError @implementation ManyConcurrentNotifyScenario - (instancetype)initWithConfig:(BugsnagConfiguration *)config - withMazeAddress:(NSString *)mazeAddress { + andMazeAddress:(NSString *)mazeAddress { if (self = [super initWithConfig:config - withMazeAddress:mazeAddress]) { + andMazeAddress:mazeAddress]) { _queue1 = dispatch_queue_create("Log Queue 1", DISPATCH_QUEUE_CONCURRENT); _queue2 = dispatch_queue_create("Log Queue 2", DISPATCH_QUEUE_CONCURRENT); } diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 0ddbe5e6d..fc4e6b0b4 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -23,13 +23,13 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; + (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(nullable BugsnagConfiguration *)config - withMazeAddress:(NSString *)mazeAddress; + withConfig:(nullable BugsnagConfiguration *)config + andMazeAddress:(NSString *)mazeAddress; @property (class, readonly, nullable) Scenario *currentScenario; - (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config - withMazeAddress:(NSString *) mazeAddress; + andMazeAddress:(NSString *) mazeAddress; /** * Executes the test case diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index c32742e7e..a33e1c3e9 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -70,8 +70,8 @@ + (void)load { } + (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(BugsnagConfiguration *)config - withMazeAddress:(NSString *)mazeAddress { + withConfig:(BugsnagConfiguration *)config + andMazeAddress:(NSString *)mazeAddress { Class class = NSClassFromString(className) ?: NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); @@ -80,7 +80,7 @@ + (Scenario *)createScenarioNamed:(NSString *)className } return (theScenario = [(Scenario *)[class alloc] initWithConfig:config - withMazeAddress:mazeAddress]); + andMazeAddress:mazeAddress]); } + (Scenario *)currentScenario { @@ -110,7 +110,7 @@ - (NSString *)baseMazeAddress { - (instancetype)initWithConfig:(BugsnagConfiguration *)config - withMazeAddress:(NSString *) mazeAddress { + andMazeAddress:(NSString *) mazeAddress { if (self = [super init]) { _baseMazeAddress = mazeAddress; if (config) { @@ -293,8 +293,8 @@ - (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)e NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); theScenario = [Scenario createScenarioNamed:scenarioName - withConfig:nil - withMazeAddress:_baseMazeAddress]; + withConfig:nil + andMazeAddress:_baseMazeAddress]; theScenario.eventMode = eventMode; NSLog(@"Starting scenario \"%@\"", NSStringFromClass([theScenario class])); From dea94383e115a2f750e79f0595925653b26c7d0c Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Sat, 21 Jan 2023 09:24:28 +0000 Subject: [PATCH 05/37] Fix macOS build --- .../macos/macOSTestApp/MainWindowController.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 3bc1254b9..2974c8751 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -54,7 +54,9 @@ - (IBAction)runScenario:(id)sender { // Cater for multiple calls to -run if (!Scenario.currentScenario) { - [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; + [Scenario createScenarioNamed:self.scenarioName + withConfig:[self configuration] + andMazeAddress:@"http://localhost:9339"]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -74,7 +76,9 @@ - (IBAction)runScenario:(id)sender { - (IBAction)startBugsnag:(id)sender { BSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; + [Scenario createScenarioNamed:self.scenarioName + withConfig:[self configuration] + andMazeAddress:@"http://localhost:9339"]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -92,7 +96,8 @@ - (IBAction)useDashboardEndpoints:(id)sender { } - (IBAction)executeMazeRunnerCommand:(id)sender { - [Scenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ + + [Scenario.currentScenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ self.scenarioName = scenarioName; self.scenarioMetadata = eventMode; }]; From 2b7cdb68e09aad1634da0fca9f0743b4b85d92d4 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 23 Jan 2023 15:28:00 +0000 Subject: [PATCH 06/37] Scenarios running --- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 4 ++ .../ios/iOSTestApp/ViewController.swift | 34 ++++++++--- .../macos/macOSTestApp/MainWindowController.m | 8 +-- .../scenarios/ManyConcurrentNotifyScenario.m | 3 +- .../NetworkBreadcrumbsScenario.swift | 4 +- features/fixtures/shared/scenarios/Scenario.h | 14 ++--- features/fixtures/shared/scenarios/Scenario.m | 61 ++++++++----------- .../shared/scenarios/ScenarioFactory.swift | 13 ++++ features/support/env.rb | 6 -- 9 files changed, 78 insertions(+), 69 deletions(-) create mode 100644 features/fixtures/shared/scenarios/ScenarioFactory.swift diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index bb7ae576a..85b5df743 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ A1117E5B2536036400014FDA /* OOMSessionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E5A2536036400014FDA /* OOMSessionScenario.swift */; }; AA6ACD1C2773E0B3006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD202773E3F0006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */; }; + AAC12335297ECF650011146B /* ScenarioFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC12334297ECF650011146B /* ScenarioFactory.swift */; }; AAFEF9EA26EB533800980A10 /* NetworkBreadcrumbsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */; }; CB0AE1F1287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0AE1F0287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */; }; CB42FF9026F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB42FF8F26F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a */; }; @@ -316,6 +317,7 @@ A1117E5A2536036400014FDA /* OOMSessionScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMSessionScenario.swift; sourceTree = ""; }; AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; + AAC12334297ECF650011146B /* ScenarioFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScenarioFactory.swift; sourceTree = ""; }; AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBreadcrumbsScenario.swift; sourceTree = ""; }; AAFEFA0226EB6B5A00980A10 /* BugsnagNetworkRequestPlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BugsnagNetworkRequestPlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CB0AE1F0287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnSendErrorCallbackFeatureFlagsScenario.swift; sourceTree = ""; }; @@ -640,6 +642,7 @@ 010BAAF22833CE570003FF36 /* UserPersistencePersistUserClientScenario.m */, 010BAAF82833CE570003FF36 /* UserPersistencePersistUserScenario.m */, E700EE49247D1164008CFFB6 /* UserSessionOverrideScenario.swift */, + AAC12334297ECF650011146B /* ScenarioFactory.swift */, ); name = scenarios; path = ../shared/scenarios; @@ -784,6 +787,7 @@ 8A38C5D124094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift in Sources */, 010BAB292833D08F0003FF36 /* BareboneTestUnhandledErrorScenario.swift in Sources */, 01F6B75F2832757F00B75C5D /* OversizedHandledErrorScenario.swift in Sources */, + AAC12335297ECF650011146B /* ScenarioFactory.swift in Sources */, 010BAB002833CE570003FF36 /* DisableNSExceptionScenario.m in Sources */, 8AEEBBD020FC9E1D00C60763 /* AutoSessionMixedEventsScenario.m in Sources */, 010BAB052833CE570003FF36 /* DisableAllExceptManualExceptionsAndCrashScenario.m in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index b9487c7f4..d563ff406 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -21,22 +21,39 @@ class ViewController: UIViewController { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))) NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) apiKeyField.text = UserDefaults.standard.string(forKey: "apiKey") + + log("SKW 1") + + Scenario.baseMazeAddress = loadMazeRunnerAddress() } @IBAction func runTestScenario() { + + log("SKW 2") + // Cater for multiple calls to run() if Scenario.current == nil { + + log("SKW 3") + prepareScenario() + log("SKW 4") + log("Starting Bugsnag for scenario: \(Scenario.current!)") Scenario.current!.startBugsnag() } + + log("SKW 5") log("Running scenario: \(Scenario.current!)") Scenario.current!.run() } @IBAction func startBugsnag() { + + log("SKW 6") + prepareScenario() log("Starting Bugsnag for scenario: \(Scenario.current!)") @@ -44,20 +61,18 @@ class ViewController: UIViewController { } @IBAction func clearPersistentData(_ sender: Any) { + log("SKW 7") Scenario.clearPersistentData() } func loadMazeRunnerAddress() -> String { -#if TARGET_OS_IOS // TODO Load dynamically from file - return "https://bs-local.com:9339"; -#else - return "http://localhost:9339"; -#endif - + return "http://bs-local.com:9339"; } internal func prepareScenario() { + log("SKW 9") + var config: BugsnagConfiguration? if (apiKeyField.text!.count > 0) { // Manual testing mode - use the real dashboard and the API key provided @@ -69,13 +84,14 @@ class ViewController: UIViewController { Scenario.createScenarioNamed(scenarioNameField.text!, - withConfig: config, - andMazeAddress: loadMazeRunnerAddress()) + withConfig: config) Scenario.current!.eventMode = scenarioMetaDataField.text } @IBAction func executeCommand(_ sender: Any) { - Scenario.current!.executeMazeRunnerCommand { _, scenarioName, eventMode in + log("SKW 10") + + Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in self.scenarioNameField.text = scenarioName self.scenarioMetaDataField.text = eventMode } diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 2974c8751..51c67ae00 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -55,8 +55,7 @@ - (IBAction)runScenario:(id)sender { // Cater for multiple calls to -run if (!Scenario.currentScenario) { [Scenario createScenarioNamed:self.scenarioName - withConfig:[self configuration] - andMazeAddress:@"http://localhost:9339"]; + withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -77,8 +76,7 @@ - (IBAction)startBugsnag:(id)sender { BSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); [Scenario createScenarioNamed:self.scenarioName - withConfig:[self configuration] - andMazeAddress:@"http://localhost:9339"]; + withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -97,7 +95,7 @@ - (IBAction)useDashboardEndpoints:(id)sender { - (IBAction)executeMazeRunnerCommand:(id)sender { - [Scenario.currentScenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ + [Scenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ self.scenarioName = scenarioName; self.scenarioMetadata = eventMode; }]; diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index 6d8cca619..777159f46 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -14,8 +14,7 @@ @implementation ManyConcurrentNotifyScenario - (instancetype)initWithConfig:(BugsnagConfiguration *)config andMazeAddress:(NSString *)mazeAddress { - if (self = [super initWithConfig:config - andMazeAddress:mazeAddress]) { + if (self = [super initWithConfig:config]) { _queue1 = dispatch_queue_create("Log Queue 1", DISPATCH_QUEUE_CONCURRENT); _queue2 = dispatch_queue_create("Log Queue 2", DISPATCH_QUEUE_CONCURRENT); } diff --git a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift index 668041fef..62c4b05a0 100644 --- a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift +++ b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift @@ -15,7 +15,7 @@ class NetworkBreadcrumbsScenario : Scenario { config.autoTrackSessions = false; config.add(BugsnagNetworkRequestPlugin()) config.addOnBreadcrumb { - ($0.metadata["url"] as? String ?? "").hasPrefix(self.baseMazeAddress) + ($0.metadata["url"] as? String ?? "").hasPrefix(Scenario.baseMazeAddress) } super.startBugsnag() @@ -32,7 +32,7 @@ class NetworkBreadcrumbsScenario : Scenario { } func query(string: String) { - let url = URL(string: self.baseMazeAddress + string)! + let url = URL(string: Scenario.baseMazeAddress + string)! let semaphore = DispatchSemaphore(value: 0) let task = URLSession.shared.dataTask(with: url) {(data, response, error) in diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index fc4e6b0b4..387df60ef 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -15,21 +15,15 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @interface Scenario : NSObject -@property (nonatomic, readonly) NSString *baseMazeAddress; - -// TODO Do we still need this? -@property (nonatomic, readonly) NSURL *mazeRunnerURL; - @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; + (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(nullable BugsnagConfiguration *)config - andMazeAddress:(NSString *)mazeAddress; + withConfig:(nullable BugsnagConfiguration *)config; +@property (class, readwrite) NSString *baseMazeAddress; @property (class, readonly, nullable) Scenario *currentScenario; -- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config - andMazeAddress:(NSString *) mazeAddress; +- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config; /** * Executes the test case @@ -48,7 +42,7 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); + (void)clearPersistentData; -- (void)executeMazeRunnerCommand:(nullable void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; ++ (void)executeMazeRunnerCommand:(nullable void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; @end diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index a33e1c3e9..941bc09b8 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -32,6 +32,7 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { // MARK: - static Scenario *theScenario; +static NSString *theBaseMazeAddress; #if !TARGET_OS_WATCH static char ksLogPath[PATH_MAX]; @@ -40,7 +41,6 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { @implementation Scenario { dispatch_block_t _onEventDelivery; dispatch_block_t _onSessionDelivery; - NSString *_baseMazeAddress; } + (void)load { @@ -70,8 +70,10 @@ + (void)load { } + (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(BugsnagConfiguration *)config - andMazeAddress:(NSString *)mazeAddress { + withConfig:(BugsnagConfiguration *)config { + + NSLog(@"SKW createScenarioNamed: %@", className); + Class class = NSClassFromString(className) ?: NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); @@ -79,46 +81,37 @@ + (Scenario *)createScenarioNamed:(NSString *)className [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; } - return (theScenario = [(Scenario *)[class alloc] initWithConfig:config - andMazeAddress:mazeAddress]); + return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); } + (Scenario *)currentScenario { return theScenario; } -- (NSString *)readMazeRunnerAddress { - // TODO Read from disk - return NULL; -} - -- (NSURL *)mazeRunnerURL { - - return NULL; - - // NSData to read from disk - // NSJsonSerialzation ++ (NSString *)baseMazeAddress { + return theBaseMazeAddress; } -- (NSString *)baseMazeAddress { - - return NULL; - - // NSData - // NSJsonSerialzation ++ (void)setBaseMazeAddress:(NSString *)baseMazeAddress { + theBaseMazeAddress = baseMazeAddress; } +//- (NSString *)baseMazeAddress { +// +// return NULL; +// +// // NSData +// // NSJsonSerialzation +//} -- (instancetype)initWithConfig:(BugsnagConfiguration *)config - andMazeAddress:(NSString *) mazeAddress { +- (instancetype)initWithConfig:(BugsnagConfiguration *)config { if (self = [super init]) { - _baseMazeAddress = mazeAddress; if (config) { _config = config; } else { _config = [[BugsnagConfiguration alloc] initWithApiKey:@"12312312312312312312312312312312"]; - _config.endpoints.notify = [NSString stringWithFormat:@"%@/notify", _baseMazeAddress]; - _config.endpoints.sessions = [NSString stringWithFormat:@"%@/sessions", _baseMazeAddress]; + _config.endpoints.notify = [NSString stringWithFormat:@"%@/notify", theBaseMazeAddress]; + _config.endpoints.sessions = [NSString stringWithFormat:@"%@/sessions", theBaseMazeAddress]; } #if !TARGET_OS_WATCH _config.enabledErrorTypes.ooms = NO; @@ -236,10 +229,10 @@ + (void)clearPersistentData { #endif } -- (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { ++ (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { NSLog(@"%s", __PRETTY_FUNCTION__); NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - NSString *commandEndpoint = [NSString stringWithFormat:@"%@/command", _baseMazeAddress]; + NSString *commandEndpoint = [NSString stringWithFormat:@"%@/command", theBaseMazeAddress]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:commandEndpoint]]; [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (![response isKindOfClass:[NSHTTPURLResponse class]] || [(NSHTTPURLResponse *)response statusCode] != 200) { @@ -280,24 +273,22 @@ - (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioN }] resume]; } -- (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { ++ (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); [self startBugsnagForScenario:scenarioName eventMode:eventMode]; - NSLog(@"Running scenario \"%@\"", NSStringFromClass([theScenario class])); [theScenario run]; } -- (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { ++ (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); theScenario = [Scenario createScenarioNamed:scenarioName - withConfig:nil - andMazeAddress:_baseMazeAddress]; + withConfig:nil]; theScenario.eventMode = eventMode; - NSLog(@"Starting scenario \"%@\"", NSStringFromClass([theScenario class])); + NSLog(@"Starting scenario \"%@\"", NSStringFromClass([self class])); [theScenario startBugsnag]; } diff --git a/features/fixtures/shared/scenarios/ScenarioFactory.swift b/features/fixtures/shared/scenarios/ScenarioFactory.swift new file mode 100644 index 000000000..2ff69ff75 --- /dev/null +++ b/features/fixtures/shared/scenarios/ScenarioFactory.swift @@ -0,0 +1,13 @@ +// +// ScenarioFactory.swift +// iOSTestApp +// +// Created by Steve Kirkland-Walton on 23/01/2023. +// Copyright © 2023 Bugsnag. All rights reserved. +// + +import Foundation + +class ScenarioFactory { + +} diff --git a/features/support/env.rb b/features/support/env.rb index 46c4fcb43..cef22f075 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -121,12 +121,6 @@ def skip_below(os, version) FileUtils.mv '/tmp/kscrash.log', path end when 'ios' - # get_log can be slow (1 or 2 seconds) on device farms - if scenario.failed? || Maze.config.farm == :local - File.open(File.join(path, 'syslog.log'), 'wb') do |file| - Maze.driver.get_log('syslog').each { |entry| file.puts entry.message } - end - end begin data = Maze.driver.pull_file '@com.bugsnag.iOSTestApp/Documents/kscrash.log' File.open(File.join(path, 'kscrash.log'), 'wb') { |file| file << data } From b8dbff28b3399a7aaadfa293051f1ac3217bb3cb Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 23 Jan 2023 18:09:00 +0000 Subject: [PATCH 07/37] No need for document server --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- features/breadcrumbs.feature | 7 +++---- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index dc2da16a5..1a275120b 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gem 'cocoapods' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.10.1' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.13.0' # Use a specific branch #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'master' diff --git a/Gemfile.lock b/Gemfile.lock index f6d21057c..7e8eacc74 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: c361875c7cbd9c4b37d56ccaa1493d4c42a0f0b6 - tag: v7.10.1 + revision: 1fc7dff0864a35a086532b9c0326821a6d682f60 + tag: v7.13.0 specs: - bugsnag-maze-runner (7.10.1) + bugsnag-maze-runner (7.13.0) appium_lib (~> 12.0.0) appium_lib_core (~> 5.4.0) bugsnag (~> 6.24) @@ -43,7 +43,7 @@ GEM faye-websocket (~> 0.11.0) selenium-webdriver (~> 4.2, < 4.6) atomos (0.1.3) - bugsnag (6.25.0) + bugsnag (6.25.1) concurrent-ruby (~> 1.0) builder (3.2.4) childprocess (4.1.0) @@ -146,23 +146,23 @@ GEM mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2022.0105) - mini_portile2 (2.8.0) + mini_portile2 (2.8.1) minitest (5.16.1) molinillo (0.8.0) multi_test (0.1.2) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nokogiri (1.13.10) + nokogiri (1.14.0) mini_portile2 (~> 2.8.0) racc (~> 1.4) optimist (3.0.1) os (1.0.1) - power_assert (2.0.2) + power_assert (2.0.3) public_suffix (4.0.7) - racc (1.6.1) + racc (1.6.2) rake (12.3.3) - regexp_parser (2.6.1) + regexp_parser (2.6.2) rexml (3.2.5) ruby-macho (2.5.1) rubyzip (2.3.2) diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 2ecf471a5..5cd15995f 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -57,14 +57,13 @@ Feature: Attaching a series of notable events leading up to errors @watchos Scenario: Network breadcrumbs - When I start the document server - And I run "NetworkBreadcrumbsScenario" + When I run "NetworkBreadcrumbsScenario" Then I wait to receive an error And the event "breadcrumbs.0.timestamp" is a timestamp And the event "breadcrumbs.0.name" equals "NSURLSession request failed" And the event "breadcrumbs.0.type" equals "request" And the event "breadcrumbs.0.metaData.method" equals "GET" - And the event "breadcrumbs.0.metaData.url" matches "http://.*:9340/reflect/" + And the event "breadcrumbs.0.metaData.url" matches "http://.*:9339/reflect/" And the event "breadcrumbs.0.metaData.urlParams.status" equals "444" And the event "breadcrumbs.0.metaData.urlParams.password" equals "[REDACTED]" And the event "breadcrumbs.0.metaData.status" equals 444 @@ -75,7 +74,7 @@ Feature: Attaching a series of notable events leading up to errors And the event "breadcrumbs.1.name" equals "NSURLSession request succeeded" And the event "breadcrumbs.1.type" equals "request" And the event "breadcrumbs.1.metaData.method" equals "GET" - And the event "breadcrumbs.1.metaData.url" matches "http://.*:9340/reflect/" + And the event "breadcrumbs.1.metaData.url" matches "http://.*:9339/reflect/" And the event "breadcrumbs.1.metaData.urlParams.delay_ms" equals "3000" And the event "breadcrumbs.1.metaData.status" equals 200 And the event "breadcrumbs.1.metaData.duration" is greater than 0 From 5206c90d3abcf45c797c0654c8e709dcdcf074a9 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 23 Jan 2023 20:07:14 +0000 Subject: [PATCH 08/37] Fixes --- features/fixtures/macos/macOSTestApp/MainWindowController.m | 1 + .../fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 51c67ae00..e38469677 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -54,6 +54,7 @@ - (IBAction)runScenario:(id)sender { // Cater for multiple calls to -run if (!Scenario.currentScenario) { + Scenario.baseMazeAddress = @"http://localhost:9339"; [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index 777159f46..97330e1e8 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -12,8 +12,7 @@ @implementation FooError @implementation ManyConcurrentNotifyScenario -- (instancetype)initWithConfig:(BugsnagConfiguration *)config - andMazeAddress:(NSString *)mazeAddress { +- (instancetype)initWithConfig:(BugsnagConfiguration *)config { if (self = [super initWithConfig:config]) { _queue1 = dispatch_queue_create("Log Queue 1", DISPATCH_QUEUE_CONCURRENT); _queue2 = dispatch_queue_create("Log Queue 2", DISPATCH_QUEUE_CONCURRENT); From 75c75343188e65c9ba04f67b675b60682a1aab84 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 24 Jan 2023 18:08:32 +0000 Subject: [PATCH 09/37] Tidy-up --- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 4 ---- .../ios/iOSTestApp/ViewController.swift | 22 ------------------- .../macos/macOSTestApp/MainWindowController.m | 7 ++---- features/fixtures/shared/scenarios/Scenario.m | 21 ++++-------------- .../shared/scenarios/ScenarioFactory.swift | 13 ----------- 5 files changed, 6 insertions(+), 61 deletions(-) delete mode 100644 features/fixtures/shared/scenarios/ScenarioFactory.swift diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 85b5df743..bb7ae576a 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -121,7 +121,6 @@ A1117E5B2536036400014FDA /* OOMSessionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E5A2536036400014FDA /* OOMSessionScenario.swift */; }; AA6ACD1C2773E0B3006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD202773E3F0006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */; }; - AAC12335297ECF650011146B /* ScenarioFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAC12334297ECF650011146B /* ScenarioFactory.swift */; }; AAFEF9EA26EB533800980A10 /* NetworkBreadcrumbsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */; }; CB0AE1F1287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0AE1F0287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */; }; CB42FF9026F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB42FF8F26F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a */; }; @@ -317,7 +316,6 @@ A1117E5A2536036400014FDA /* OOMSessionScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMSessionScenario.swift; sourceTree = ""; }; AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; - AAC12334297ECF650011146B /* ScenarioFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScenarioFactory.swift; sourceTree = ""; }; AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBreadcrumbsScenario.swift; sourceTree = ""; }; AAFEFA0226EB6B5A00980A10 /* BugsnagNetworkRequestPlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BugsnagNetworkRequestPlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CB0AE1F0287D89C90079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnSendErrorCallbackFeatureFlagsScenario.swift; sourceTree = ""; }; @@ -642,7 +640,6 @@ 010BAAF22833CE570003FF36 /* UserPersistencePersistUserClientScenario.m */, 010BAAF82833CE570003FF36 /* UserPersistencePersistUserScenario.m */, E700EE49247D1164008CFFB6 /* UserSessionOverrideScenario.swift */, - AAC12334297ECF650011146B /* ScenarioFactory.swift */, ); name = scenarios; path = ../shared/scenarios; @@ -787,7 +784,6 @@ 8A38C5D124094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift in Sources */, 010BAB292833D08F0003FF36 /* BareboneTestUnhandledErrorScenario.swift in Sources */, 01F6B75F2832757F00B75C5D /* OversizedHandledErrorScenario.swift in Sources */, - AAC12335297ECF650011146B /* ScenarioFactory.swift in Sources */, 010BAB002833CE570003FF36 /* DisableNSExceptionScenario.m in Sources */, 8AEEBBD020FC9E1D00C60763 /* AutoSessionMixedEventsScenario.m in Sources */, 010BAB052833CE570003FF36 /* DisableAllExceptManualExceptionsAndCrashScenario.m in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index d563ff406..dfbe39034 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -21,39 +21,23 @@ class ViewController: UIViewController { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))) NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) apiKeyField.text = UserDefaults.standard.string(forKey: "apiKey") - - log("SKW 1") - Scenario.baseMazeAddress = loadMazeRunnerAddress() } @IBAction func runTestScenario() { - - log("SKW 2") - // Cater for multiple calls to run() if Scenario.current == nil { - - log("SKW 3") - prepareScenario() - log("SKW 4") - log("Starting Bugsnag for scenario: \(Scenario.current!)") Scenario.current!.startBugsnag() } - log("SKW 5") - log("Running scenario: \(Scenario.current!)") Scenario.current!.run() } @IBAction func startBugsnag() { - - log("SKW 6") - prepareScenario() log("Starting Bugsnag for scenario: \(Scenario.current!)") @@ -61,7 +45,6 @@ class ViewController: UIViewController { } @IBAction func clearPersistentData(_ sender: Any) { - log("SKW 7") Scenario.clearPersistentData() } @@ -71,8 +54,6 @@ class ViewController: UIViewController { } internal func prepareScenario() { - log("SKW 9") - var config: BugsnagConfiguration? if (apiKeyField.text!.count > 0) { // Manual testing mode - use the real dashboard and the API key provided @@ -81,7 +62,6 @@ class ViewController: UIViewController { UserDefaults.standard.setValue(apiKey, forKey: "apiKey") config = BugsnagConfiguration(apiKeyField.text!) } - Scenario.createScenarioNamed(scenarioNameField.text!, withConfig: config) @@ -89,8 +69,6 @@ class ViewController: UIViewController { } @IBAction func executeCommand(_ sender: Any) { - log("SKW 10") - Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in self.scenarioNameField.text = scenarioName self.scenarioMetaDataField.text = eventMode diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index e38469677..02080b306 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -55,8 +55,7 @@ - (IBAction)runScenario:(id)sender { // Cater for multiple calls to -run if (!Scenario.currentScenario) { Scenario.baseMazeAddress = @"http://localhost:9339"; - [Scenario createScenarioNamed:self.scenarioName - withConfig:[self configuration]]; + [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -76,8 +75,7 @@ - (IBAction)runScenario:(id)sender { - (IBAction)startBugsnag:(id)sender { BSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - [Scenario createScenarioNamed:self.scenarioName - withConfig:[self configuration]]; + [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); @@ -95,7 +93,6 @@ - (IBAction)useDashboardEndpoints:(id)sender { } - (IBAction)executeMazeRunnerCommand:(id)sender { - [Scenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ self.scenarioName = scenarioName; self.scenarioMetadata = eventMode; diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 941bc09b8..32af23ba5 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -69,11 +69,7 @@ + (void)load { }]; } -+ (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(BugsnagConfiguration *)config { - - NSLog(@"SKW createScenarioNamed: %@", className); - ++ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { Class class = NSClassFromString(className) ?: NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); @@ -81,7 +77,7 @@ + (Scenario *)createScenarioNamed:(NSString *)className [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; } - return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); + return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); } + (Scenario *)currentScenario { @@ -96,14 +92,6 @@ + (void)setBaseMazeAddress:(NSString *)baseMazeAddress { theBaseMazeAddress = baseMazeAddress; } -//- (NSString *)baseMazeAddress { -// -// return NULL; -// -// // NSData -// // NSJsonSerialzation -//} - - (instancetype)initWithConfig:(BugsnagConfiguration *)config { if (self = [super init]) { if (config) { @@ -254,7 +242,7 @@ + (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioN } if ([[command objectForKey:@"reset_data"] isEqual:@YES]) { - [Scenario clearPersistentData]; + [self clearPersistentData]; } if (preHandler) { @@ -284,8 +272,7 @@ + (void)runScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { + (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); - theScenario = [Scenario createScenarioNamed:scenarioName - withConfig:nil]; + theScenario = [Scenario createScenarioNamed:scenarioName withConfig:nil]; theScenario.eventMode = eventMode; NSLog(@"Starting scenario \"%@\"", NSStringFromClass([self class])); diff --git a/features/fixtures/shared/scenarios/ScenarioFactory.swift b/features/fixtures/shared/scenarios/ScenarioFactory.swift deleted file mode 100644 index 2ff69ff75..000000000 --- a/features/fixtures/shared/scenarios/ScenarioFactory.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// ScenarioFactory.swift -// iOSTestApp -// -// Created by Steve Kirkland-Walton on 23/01/2023. -// Copyright © 2023 Bugsnag. All rights reserved. -// - -import Foundation - -class ScenarioFactory { - -} From a75610f4567303f32e0083a766c3f19a8042b6f9 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 24 Jan 2023 18:39:16 +0000 Subject: [PATCH 10/37] Use latest Maze Runner --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 1a275120b..63184fddd 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gem 'cocoapods' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.13.0' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.13.1' # Use a specific branch #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'master' diff --git a/Gemfile.lock b/Gemfile.lock index 7e8eacc74..b9f324d0c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: 1fc7dff0864a35a086532b9c0326821a6d682f60 - tag: v7.13.0 + revision: 121e7396c663d6484646674e633d84ac790962fe + tag: v7.13.1 specs: - bugsnag-maze-runner (7.13.0) + bugsnag-maze-runner (7.13.1) appium_lib (~> 12.0.0) appium_lib_core (~> 5.4.0) bugsnag (~> 6.24) From 2fb816ecc6470c5a439bc48ca8034f71f269d318 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 2 Feb 2023 00:03:27 +0000 Subject: [PATCH 11/37] Read Maze Runner address from file --- .../ios/iOSTestApp/ViewController.swift | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index dfbe39034..11dc7f76d 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -9,6 +9,10 @@ import UIKit import os +class FixtureConfig: Codable { + var maze_address: String +} + class ViewController: UIViewController { @IBOutlet var scenarioNameField : UITextField! @@ -21,7 +25,6 @@ class ViewController: UIViewController { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))) NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil) apiKeyField.text = UserDefaults.standard.string(forKey: "apiKey") - Scenario.baseMazeAddress = loadMazeRunnerAddress() } @IBAction func runTestScenario() { @@ -49,7 +52,32 @@ class ViewController: UIViewController { } func loadMazeRunnerAddress() -> String { - // TODO Load dynamically from file + + for _ in 1...60 { + let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + + log("Reading Maze Runner address from fixture_config.json") + do { + let fileUrl = URL(fileURLWithPath: "fixture_config", + relativeTo: documentsUrl).appendingPathExtension("json") + let savedData = try Data(contentsOf: fileUrl) + if let contents = String(data: savedData, encoding: .utf8) { + let decoder = JSONDecoder() + let jsonData = contents.data(using: .utf8) + let config = try decoder.decode(FixtureConfig.self, from: jsonData!) + let address = "http://" + config.maze_address + log("Using Maze Runner address: " + address) + return address + } + } + catch let error as NSError { + log("Failed to read fixture_config.json: \(error)") + } + log("Waiting for fixture_config.json to appear") + sleep(1) + } + + log("Unable to read from fixture_config.json, defaulting to BrowserStack environment") return "http://bs-local.com:9339"; } @@ -69,6 +97,9 @@ class ViewController: UIViewController { } @IBAction func executeCommand(_ sender: Any) { + + // TODO Only if nil? + Scenario.baseMazeAddress = loadMazeRunnerAddress() Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in self.scenarioNameField.text = scenarioName self.scenarioMetaDataField.text = eventMode From 640c3934b25959c6c3eada13693ef450855179a3 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 2 Feb 2023 00:03:40 +0000 Subject: [PATCH 12/37] Use latest Maze Runner --- Gemfile | 4 ++-- Gemfile.lock | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 63184fddd..a1192e57c 100644 --- a/Gemfile +++ b/Gemfile @@ -4,10 +4,10 @@ gem 'cocoapods' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.13.1' +#gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.17.0' # Use a specific branch #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'master' # Locally, you can run against Maze Runner branches and uncommitted changes: -#gem 'bugsnag-maze-runner', path: '../maze-runner' +gem 'bugsnag-maze-runner', path: '../maze-runner' diff --git a/Gemfile.lock b/Gemfile.lock index b9f324d0c..aa1a019a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,7 @@ -GIT - remote: https://github.com/bugsnag/maze-runner - revision: 121e7396c663d6484646674e633d84ac790962fe - tag: v7.13.1 +PATH + remote: ../maze-runner specs: - bugsnag-maze-runner (7.13.1) + bugsnag-maze-runner (7.17.0) appium_lib (~> 12.0.0) appium_lib_core (~> 5.4.0) bugsnag (~> 6.24) @@ -13,6 +11,7 @@ GIT json_schemer (~> 0.2.24) optimist (~> 3.0.1) os (~> 1.0.0) + rack (~> 2.2) rake (~> 12.3.3) rubyzip (~> 2.3.2) selenium-webdriver (~> 4.0) @@ -153,7 +152,7 @@ GEM nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nokogiri (1.14.0) + nokogiri (1.14.1) mini_portile2 (~> 2.8.0) racc (~> 1.4) optimist (3.0.1) @@ -161,6 +160,7 @@ GEM power_assert (2.0.3) public_suffix (4.0.7) racc (1.6.2) + rack (2.2.6.2) rake (12.3.3) regexp_parser (2.6.2) rexml (3.2.5) From ab89b58ea9ba8c2d2d1f5258acb50cc78bb285e2 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 2 Feb 2023 00:07:57 +0000 Subject: [PATCH 13/37] Correction --- Gemfile | 4 ++-- Gemfile.lock | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index a1192e57c..7b7d53bc3 100644 --- a/Gemfile +++ b/Gemfile @@ -4,10 +4,10 @@ gem 'cocoapods' # A reference to Maze Runner is only needed for running tests locally and if committed it must be # portable for CI, e.g. a specific release. However, leaving it commented out would mean quicker CI. -#gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.17.0' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v7.17.0' # Use a specific branch #gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', branch: 'master' # Locally, you can run against Maze Runner branches and uncommitted changes: -gem 'bugsnag-maze-runner', path: '../maze-runner' +#gem 'bugsnag-maze-runner', path: '../maze-runner' diff --git a/Gemfile.lock b/Gemfile.lock index aa1a019a1..865c301b3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,5 +1,7 @@ -PATH - remote: ../maze-runner +GIT + remote: https://github.com/bugsnag/maze-runner + revision: 01596b90f65a11759e4bd307242e46b1f4c240b2 + tag: v7.17.0 specs: bugsnag-maze-runner (7.17.0) appium_lib (~> 12.0.0) From 80f2be5ded454d366b247be0fd4cec58828e232a Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 2 Feb 2023 09:05:03 +0000 Subject: [PATCH 14/37] =?UTF-8?q?Only=20read=20fixture=20config=20file=20o?= =?UTF-8?q?n=20iOS=2012=20and=20above=20(Appium=201.15=20or=20later=20is?= =?UTF-8?q?=20need=20to=20push=20the=20config=20file=20on=20the=20device?= =?UTF-8?q?=20and=20that=20doesn=E2=80=99t=20work=20for=20iOS=20<=2012).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- features/fixtures/ios/iOSTestApp/ViewController.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 11dc7f76d..35a827fe2 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -52,6 +52,13 @@ class ViewController: UIViewController { } func loadMazeRunnerAddress() -> String { + + let bsAddress = "http://bs-local.com:9339" + + // Only iOS 12 and above will run on BitBat for now + if #available(iOS 12.0, *) {} else { + return bsAddress; + } for _ in 1...60 { let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] @@ -78,7 +85,7 @@ class ViewController: UIViewController { } log("Unable to read from fixture_config.json, defaulting to BrowserStack environment") - return "http://bs-local.com:9339"; + return bsAddress; } internal func prepareScenario() { From 0c226d0fdd2e881b8ed267cd5501579eecc87c0c Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 2 Feb 2023 17:43:16 +0000 Subject: [PATCH 15/37] Tidy-up --- features/fixtures/ios/iOSTestApp/ViewController.swift | 2 -- features/fixtures/macos/macOSTestApp/MainWindowController.m | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 35a827fe2..e53919d9f 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -104,8 +104,6 @@ class ViewController: UIViewController { } @IBAction func executeCommand(_ sender: Any) { - - // TODO Only if nil? Scenario.baseMazeAddress = loadMazeRunnerAddress() Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in self.scenarioNameField.text = scenarioName diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 02080b306..5b88b35e8 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -54,7 +54,6 @@ - (IBAction)runScenario:(id)sender { // Cater for multiple calls to -run if (!Scenario.currentScenario) { - Scenario.baseMazeAddress = @"http://localhost:9339"; [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; Scenario.currentScenario.eventMode = self.scenarioMetadata; @@ -93,6 +92,7 @@ - (IBAction)useDashboardEndpoints:(id)sender { } - (IBAction)executeMazeRunnerCommand:(id)sender { + Scenario.baseMazeAddress = @"http://localhost:9339"; [Scenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ self.scenarioName = scenarioName; self.scenarioMetadata = eventMode; From f918a40fa8d2462b2e27a20ea7443773b89e9fc7 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 10:52:54 +0000 Subject: [PATCH 16/37] No need to upload app again --- .buildkite/pipeline.bs.yml | 0 .buildkite/pipeline.full.yml | 8 ++++---- .buildkite/pipline.bb.yml | 0 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 .buildkite/pipeline.bs.yml create mode 100644 .buildkite/pipline.bb.yml diff --git a/.buildkite/pipeline.bs.yml b/.buildkite/pipeline.bs.yml new file mode 100644 index 000000000..e69de29bb diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index 65055ddef..c6294f68b 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -73,13 +73,13 @@ steps: queue: opensource plugins: artifacts#v1.5.0: - download: ["features/fixtures/ios/output/iOSTestApp.ipa"] + download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" docker-compose#v3.7.0: pull: cocoa-maze-runner run: cocoa-maze-runner command: - - "--app=/app/build/iOSTestApp.ipa" + - "--app=@build/ipa_url.txt" - "--farm=bs" - "--device=IOS_14" - "--appium-version=1.21.0" @@ -102,13 +102,13 @@ steps: queue: opensource plugins: artifacts#v1.5.0: - download: ["features/fixtures/ios/output/iOSTestApp.ipa"] + download: "features/fixtures/ios/output/ipa_url.txt" upload: "maze_output/failed/**/*" docker-compose#v3.7.0: pull: cocoa-maze-runner run: cocoa-maze-runner command: - - "--app=/app/build/iOSTestApp.ipa" + - "--app=@build/ipa_url.txt" - "--farm=bs" - "--device=IOS_14" - "--appium-version=1.21.0" diff --git a/.buildkite/pipline.bb.yml b/.buildkite/pipline.bb.yml new file mode 100644 index 000000000..e69de29bb From 4bfc2949102148eae61a676844e90b319201590f Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 10:53:03 +0000 Subject: [PATCH 17/37] Typo --- features/fixtures/ios/iOSTestApp/ViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index e53919d9f..b2ed6df4e 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -55,7 +55,7 @@ class ViewController: UIViewController { let bsAddress = "http://bs-local.com:9339" - // Only iOS 12 and above will run on BitBat for now + // Only iOS 12 and above will run on BitBar for now if #available(iOS 12.0, *) {} else { return bsAddress; } From 85e7ddcd1cff9035b047c50f6d95494f99f9b5a5 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 10:53:31 +0000 Subject: [PATCH 18/37] Move basic build BS steps into a separate file --- .buildkite/pipeline.bs.yml | 123 +++++++++++++++++++++++++++++++++ .buildkite/pipeline.yml | 122 -------------------------------- .buildkite/pipeline_trigger.sh | 12 ++++ 3 files changed, 135 insertions(+), 122 deletions(-) diff --git a/.buildkite/pipeline.bs.yml b/.buildkite/pipeline.bs.yml index e69de29bb..9eb3122b1 100644 --- a/.buildkite/pipeline.bs.yml +++ b/.buildkite/pipeline.bs.yml @@ -0,0 +1,123 @@ +steps: + + ############################################################################## + # + # Barebones E2E tests + # + + - label: 'iOS 16 E2E tests batch 1' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url.txt" + upload: "maze_output/failed/**/*" + docker-compose#v3.7.0: + pull: cocoa-maze-runner + run: cocoa-maze-runner + command: + - "--app=@build/ipa_url.txt" + - "--farm=bs" + - "--device=IOS_16" + - "--appium-version=1.21.0" + - "--fail-fast" + - "--exclude=features/[e-z].*.feature$" + - "--order=random" + concurrency: 24 + concurrency_group: browserstack-app + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + + - label: 'iOS 16 E2E tests batch 2' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url.txt" + upload: "maze_output/failed/**/*" + docker-compose#v3.7.0: + pull: cocoa-maze-runner + run: cocoa-maze-runner + command: + - "--app=@build/ipa_url.txt" + - "--farm=bs" + - "--device=IOS_16" + - "--appium-version=1.21.0" + - "--fail-fast" + - "--exclude=features/[a-d].*.feature$" + - "--order=random" + concurrency: 24 + concurrency_group: browserstack-app + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + + - label: 'iOS 10 E2E tests batch 1' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url.txt" + upload: "maze_output/failed/**/*" + docker-compose#v3.7.0: + pull: cocoa-maze-runner-legacy + run: cocoa-maze-runner-legacy + command: + - "--app=@build/ipa_url.txt" + - "--farm=bs" + - "--device=IOS_10" + - "--appium-version=1.8.0" + - "--capabilities={\"browserstack.networkLogs\":\"true\"}" + - "--fail-fast" + - "--exclude=features/[e-z].*.feature$" + - "--order=random" + concurrency: 24 + concurrency_group: browserstack-app + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + + - label: 'iOS 10 E2E tests batch 2' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url.txt" + upload: "maze_output/failed/**/*" + docker-compose#v3.7.0: + pull: cocoa-maze-runner-legacy + run: cocoa-maze-runner-legacy + command: + - "--app=@build/ipa_url.txt" + - "--farm=bs" + - "--device=IOS_10" + - "--appium-version=1.8.0" + - "--fail-fast" + - "--exclude=features/[a-d].*.feature$" + - "--order=random" + concurrency: 24 + concurrency_group: browserstack-app + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 1130aa3ec..3d2d8fae0 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -201,128 +201,6 @@ steps: artifact_paths: - logs/* - ############################################################################## - # - # Barebones E2E tests - # - - - label: 'iOS 16 E2E tests batch 1' - depends_on: - - cocoa_fixture - timeout_in_minutes: 60 - agents: - queue: opensource - plugins: - artifacts#v1.5.0: - download: "features/fixtures/ios/output/ipa_url.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner - run: cocoa-maze-runner - command: - - "--app=@build/ipa_url.txt" - - "--farm=bs" - - "--device=IOS_16" - - "--appium-version=1.21.0" - - "--fail-fast" - - "--exclude=features/[e-z].*.feature$" - - "--order=random" - concurrency: 24 - concurrency_group: browserstack-app - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 - - - label: 'iOS 16 E2E tests batch 2' - depends_on: - - cocoa_fixture - timeout_in_minutes: 60 - agents: - queue: opensource - plugins: - artifacts#v1.5.0: - download: "features/fixtures/ios/output/ipa_url.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner - run: cocoa-maze-runner - command: - - "--app=@build/ipa_url.txt" - - "--farm=bs" - - "--device=IOS_16" - - "--appium-version=1.21.0" - - "--fail-fast" - - "--exclude=features/[a-d].*.feature$" - - "--order=random" - concurrency: 24 - concurrency_group: browserstack-app - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 - - - label: 'iOS 10 E2E tests batch 1' - depends_on: - - cocoa_fixture - timeout_in_minutes: 60 - agents: - queue: opensource - plugins: - artifacts#v1.5.0: - download: "features/fixtures/ios/output/ipa_url.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner-legacy - run: cocoa-maze-runner-legacy - command: - - "--app=@build/ipa_url.txt" - - "--farm=bs" - - "--device=IOS_10" - - "--appium-version=1.8.0" - - "--capabilities={\"browserstack.networkLogs\":\"true\"}" - - "--fail-fast" - - "--exclude=features/[e-z].*.feature$" - - "--order=random" - concurrency: 24 - concurrency_group: browserstack-app - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 - - - label: 'iOS 10 E2E tests batch 2' - depends_on: - - cocoa_fixture - timeout_in_minutes: 60 - agents: - queue: opensource - plugins: - artifacts#v1.5.0: - download: "features/fixtures/ios/output/ipa_url.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner-legacy - run: cocoa-maze-runner-legacy - command: - - "--app=@build/ipa_url.txt" - - "--farm=bs" - - "--device=IOS_10" - - "--appium-version=1.8.0" - - "--fail-fast" - - "--exclude=features/[a-d].*.feature$" - - "--order=random" - concurrency: 24 - concurrency_group: browserstack-app - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 - - label: 'macOS 10.13 barebones E2E tests' depends_on: - cocoa_fixture diff --git a/.buildkite/pipeline_trigger.sh b/.buildkite/pipeline_trigger.sh index c3929d24f..f0e306d20 100755 --- a/.buildkite/pipeline_trigger.sh +++ b/.buildkite/pipeline_trigger.sh @@ -10,3 +10,15 @@ else echo "Running basic build" buildkite-agent pipeline upload .buildkite/block.full.yml fi + +# Run BrowserStack steps unless instructed not to +if [[ "$BUILDKITE_MESSAGE" != *"[nobs]"* && + "$DEVICE_FARM" != *"NO_BS"* ]]; then + buildkite-agent pipeline upload .buildkite/pipeline.bs.yml +fi + +# Run BitBar steps if instructed to +if [[ "$BUILDKITE_MESSAGE" == *"[bb]"* || + "$DEVICE_FARM" == *"BB"* ]]; then + buildkite-agent pipeline upload .buildkite/pipeline.bb.yml +fi From e000cf50fc5ec62325764880a7a5786714e12b2c Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 11:35:40 +0000 Subject: [PATCH 19/37] Selectively run tests on bitbar [bb][nobs] --- .buildkite/pipeline.bb.yml | 45 ++++++++++++++++++++++++++++++++++++++ .buildkite/pipline.bb.yml | 0 docker-compose.yml | 28 ++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 .buildkite/pipeline.bb.yml delete mode 100644 .buildkite/pipline.bb.yml diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml new file mode 100644 index 000000000..b6cb6b399 --- /dev/null +++ b/.buildkite/pipeline.bb.yml @@ -0,0 +1,45 @@ +steps: + + - label: 'BitBar Poc - iOS 15 - batch 1' + depends_on: "cocoa_fixture" + timeout_in_minutes: 60 + plugins: + artifacts#v1.9.0: + download: "features/fixtures/ios/output/iOSTestApp.ipa" + upload: "maze_output/failed/**/*" + docker-compose#v4.7.0: + pull: cocoa-maze-runner-bitbar + run: cocoa-maze-runner-bitbar + service-ports: true + command: + - "features" + - "--app=/app/build/iOSTestApp.ipa" + - "--farm=bb" + - "--device=IOS_15" + - "--no-tunnel" + - "--aws-public-ip" + concurrency: 5 + concurrency_group: 'bitbar-ios-15' + concurrency_method: eager + + - label: 'BitBar Poc - iOS 15 - batch 2' + depends_on: "cocoa_fixture" + timeout_in_minutes: 60 + plugins: + artifacts#v1.9.0: + download: "features/fixtures/ios/output/iOSTestApp.ipa" + upload: "maze_output/failed/**/*" + docker-compose#v4.7.0: + pull: cocoa-maze-runner-bitbar + run: cocoa-maze-runner-bitbar + service-ports: true + command: + - "features" + - "--app=/app/build/iOSTestApp.ipa" + - "--farm=bb" + - "--device=ANDROID_10" + - "--no-tunnel" + - "--aws-public-ip" + concurrency: 5 + concurrency_group: 'bitbar-ios-15' + concurrency_method: eager diff --git a/.buildkite/pipline.bb.yml b/.buildkite/pipline.bb.yml deleted file mode 100644 index e69de29bb..000000000 diff --git a/docker-compose.yml b/docker-compose.yml index c38575871..485c5e4f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,34 @@ services: - ./features/:/app/features/ - ./maze_output:/app/maze_output + cocoa-maze-runner-bitbar: + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:integration-bitbar-improvements-cli + environment: + DEBUG: + VERBOSE: + BUILDKITE: + BUILDKITE_BRANCH: + BUILDKITE_BUILD_CREATOR: + BUILDKITE_BUILD_NUMBER: + BUILDKITE_BUILD_URL: + BUILDKITE_LABEL: + BUILDKITE_MESSAGE: + BUILDKITE_PIPELINE_NAME: + BUILDKITE_REPO: + BUILDKITE_RETRY_COUNT: + BUILDKITE_STEP_KEY: + MAZE_BUGSNAG_API_KEY: + MAZE_REPEATER_API_KEY: + BITBAR_USERNAME: + BITBAR_ACCESS_KEY: + ports: + - "9000-9499:9339" + volumes: + - ./features/fixtures/ios/output:/app/build + - ./features/:/app/features/ + - ./maze_output:/app/maze_output + - /var/run/docker.sock:/var/run/docker.sock + cocoa-maze-runner-legacy: image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v7-cli-legacy environment: From efd4a412e6c1f4a8b434a32cd7edf94e6202f41e Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 12:15:03 +0000 Subject: [PATCH 20/37] Use EC2 [bb][nobs] --- .buildkite/pipeline.bb.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml index b6cb6b399..090f765f8 100644 --- a/.buildkite/pipeline.bb.yml +++ b/.buildkite/pipeline.bb.yml @@ -3,6 +3,8 @@ steps: - label: 'BitBar Poc - iOS 15 - batch 1' depends_on: "cocoa_fixture" timeout_in_minutes: 60 + agents: + queue: opensource plugins: artifacts#v1.9.0: download: "features/fixtures/ios/output/iOSTestApp.ipa" @@ -25,6 +27,8 @@ steps: - label: 'BitBar Poc - iOS 15 - batch 2' depends_on: "cocoa_fixture" timeout_in_minutes: 60 + agents: + queue: opensource plugins: artifacts#v1.9.0: download: "features/fixtures/ios/output/iOSTestApp.ipa" From ecad6702e3002a05a1680a12f5863b9c6f61ebc8 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 12:31:46 +0000 Subject: [PATCH 21/37] Correction [bb][nobs] --- .buildkite/pipeline.bb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml index 090f765f8..dc8008c0a 100644 --- a/.buildkite/pipeline.bb.yml +++ b/.buildkite/pipeline.bb.yml @@ -41,7 +41,7 @@ steps: - "features" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bb" - - "--device=ANDROID_10" + - "--device=IOS_15" - "--no-tunnel" - "--aws-public-ip" concurrency: 5 From 523c6e83a740ced4eb9016895068b492e95d76a9 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 22:53:17 +0000 Subject: [PATCH 22/37] Just check values against a regex --- features/app_and_device_attributes.feature | 3 +- features/app_hangs.feature | 2 +- features/barebone_tests.feature | 6 ++-- features/session_tracking.feature | 2 +- features/steps/cocoa_steps.rb | 39 ---------------------- 5 files changed, 7 insertions(+), 45 deletions(-) diff --git a/features/app_and_device_attributes.feature b/features/app_and_device_attributes.feature index 8f8352667..7e850ec81 100644 --- a/features/app_and_device_attributes.feature +++ b/features/app_and_device_attributes.feature @@ -19,7 +19,8 @@ Feature: App and Device attributes present And the error payload field "events.0.device.manufacturer" equals "Apple" And the error payload field "events.0.device.locale" is not null And the error payload field "events.0.device.id" is not null - And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.modelNumber" equals the platform-dependent string: | ios | @not_null | | macos | @null | diff --git a/features/app_hangs.feature b/features/app_hangs.feature index dfa7f6044..f011f8ea5 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -46,7 +46,7 @@ Feature: App hangs And the error payload field "events.0.device.manufacturer" equals "Apple" And the error payload field "events.0.device.locale" is not null And the error payload field "events.0.device.id" is not null - And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" And the error payload field "events.0.device.modelNumber" equals the platform-dependent string: | ios | @not_null | | macos | @null | diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index 8ed471a0f..0b3a99e52 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -110,7 +110,7 @@ Feature: Barebone tests And the error payload field "events.0.app.durationInForeground" is a number And the error payload field "events.0.device.freeDisk" is an integer And the error payload field "events.0.device.freeMemory" is an integer - And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" And the error payload field "events.0.device.totalMemory" is an integer And the error payload field "events.0.threads" is an array with 0 elements And the "isPC" of stack frame 0 is null @@ -234,7 +234,7 @@ Feature: Barebone tests And the error payload field "events.0.app.durationInForeground" is a number And the error payload field "events.0.device.freeDisk" is an integer And the error payload field "events.0.device.freeMemory" is an integer - And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" And the error payload field "events.0.device.totalMemory" is an integer And on !watchOS, the error payload field "events.0.threads" is a non-empty array And on !watchOS, the error payload field "events.0.threads.1" is not null @@ -334,5 +334,5 @@ Feature: Barebone tests And the error payload field "events.0.app.duration" is null And the error payload field "events.0.app.durationInForeground" is null And the error payload field "events.0.device.freeDisk" is null - And the error payload field "events.0.device.model" matches the test device model + And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" And the error payload field "events.0.threads" is an array with 0 elements diff --git a/features/session_tracking.feature b/features/session_tracking.feature index 8712c1205..1e90ede35 100644 --- a/features/session_tracking.feature +++ b/features/session_tracking.feature @@ -17,7 +17,7 @@ Feature: Session Tracking And the session payload field "device.osName" equals the platform-dependent string: | ios | iOS | | macos | Mac OS | - And the session payload field "device.model" matches the test device model + And the session payload field "device.model" matches the regex "iPhone1?\d,\d" And the session payload field "sessions.0.id" is a UUID And the session payload field "sessions.0.startedAt" is a parsable timestamp in seconds diff --git a/features/steps/cocoa_steps.rb b/features/steps/cocoa_steps.rb index d84417bac..e94c3f054 100644 --- a/features/steps/cocoa_steps.rb +++ b/features/steps/cocoa_steps.rb @@ -44,45 +44,6 @@ def request_fields_are_equal(key, index_a, index_b) ) end -def check_device_model(field, list) - internal_names = { - 'iPhone 6' => %w[iPhone7,2], - 'iPhone 6 Plus' => %w[iPhone7,1], - 'iPhone 6S' => %w[iPhone8,1], - 'iPhone 7' => %w[iPhone9,1 iPhone9,2 iPhone9,3 iPhone9,4], - 'iPhone 8' => %w[iPhone10,1 iPhone10,4], - 'iPhone 8 Plus' => %w[iPhone10,2 iPhone10,5], - 'iPhone 11' => %w[iPhone12,1], - 'iPhone 11 Pro' => %w[iPhone12,3], - 'iPhone 11 Pro Max' => %w[iPhone12,5], - 'iPhone 14' => %w[iPhone14,7], - 'iPhone 14 Plus' => %w[iPhone14,8], - 'iPhone 14 Pro' => %w[iPhone15,2], - 'iPhone 14 Pro Max' => %w[iPhone15,3], - 'iPhone X' => %w[iPhone10,3 iPhone10,6], - 'iPhone XR' => %w[iPhone11,8], - 'iPhone XS' => %w[iPhone11,2 iPhone11,4 iPhone11,8] - } - $logger.info Maze.config.capabilities - - expected_model = Maze.config.capabilities['deviceName'] - msg = "Model '#{expected_model}' found by Appium not present in internal_names hash" - Maze.check.true(internal_names.has_key?(expected_model), msg) - - valid_models = internal_names[expected_model] - device_model = Maze::Helper.read_key_path(list.current[:body], field) - Maze.check.true(valid_models.include?(device_model), - "'#{device_model}' did not match any of the list of valid models #{valid_models}") -end - -Then('the error payload field {string} matches the test device model') do |field| - check_device_model field, Maze::Server.errors if Maze::Helper.get_current_platform.eql?('ios') -end - -Then('the session payload field {string} matches the test device model') do |field| - check_device_model field, Maze::Server.sessions if Maze::Helper.get_current_platform.eql?('ios') -end - Then('the error is valid for the error reporting API') do platform = Maze::Helper.get_current_platform case platform From c5bf2a5a3f56b5b0c28bcc689240d6f66e1eabe0 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Fri, 3 Feb 2023 22:53:30 +0000 Subject: [PATCH 23/37] Use dev branch [bb][nobs] --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 485c5e4f9..7911bc1c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: - ./maze_output:/app/maze_output cocoa-maze-runner-bitbar: - image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:integration-bitbar-improvements-cli + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:tms-device-fixes-cli environment: DEBUG: VERBOSE: From 5ad706d0edb66d5aa0c0fe1c34e7985fe18a1852 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Sat, 4 Feb 2023 22:45:46 +0000 Subject: [PATCH 24/37] Run in two batches --- .buildkite/pipeline.bb.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml index dc8008c0a..fedf2e3b1 100644 --- a/.buildkite/pipeline.bb.yml +++ b/.buildkite/pipeline.bb.yml @@ -14,7 +14,7 @@ steps: run: cocoa-maze-runner-bitbar service-ports: true command: - - "features" + - "--exclude=features/[e-z].*.feature$" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bb" - "--device=IOS_15" @@ -38,7 +38,7 @@ steps: run: cocoa-maze-runner-bitbar service-ports: true command: - - "features" + - "--exclude=features/[a-d].*.feature$" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bb" - "--device=IOS_15" From 26b627ba3671969ae655fc05802dfb0ef5dd1222 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Sat, 4 Feb 2023 22:46:10 +0000 Subject: [PATCH 25/37] Corrections and skips pending investigation [bb][nobs] --- features/app_and_device_attributes.feature | 5 ++++- features/app_hangs.feature | 4 +++- features/barebone_tests.feature | 6 +++--- features/breadcrumbs.feature | 4 ++-- features/config_from_plist.feature | 4 ++++ features/support/env.rb | 4 ++++ 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/features/app_and_device_attributes.feature b/features/app_and_device_attributes.feature index 7e850ec81..17a43f810 100644 --- a/features/app_and_device_attributes.feature +++ b/features/app_and_device_attributes.feature @@ -19,7 +19,7 @@ Feature: App and Device attributes present And the error payload field "events.0.device.manufacturer" equals "Apple" And the error payload field "events.0.device.locale" is not null And the error payload field "events.0.device.id" is not null - And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the error payload field "events.0.device.modelNumber" equals the platform-dependent string: | ios | @not_null | @@ -91,6 +91,9 @@ Feature: App and Device attributes present And the error payload field "events.0.device.manufacturer" equals "Nokia" And the error payload field "events.0.device.modelNumber" equals "0898" + + # PLAT-9615 + @skip_bitbar Scenario: Info.plist settings are used when calling startWithApiKey When I run "AppAndDeviceAttributesStartWithApiKeyScenario" And I wait to receive an error diff --git a/features/app_hangs.feature b/features/app_hangs.feature index f011f8ea5..709c5188f 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -7,6 +7,8 @@ Feature: App hangs When I run "AppHangDefaultConfigScenario" Then I should receive no errors + # PLAT-9616 + @skip_bitbar Scenario: App hangs above the threshold should be reported When I set the app to "2.1" mode And I run "AppHangScenario" @@ -46,7 +48,7 @@ Feature: App hangs And the error payload field "events.0.device.manufacturer" equals "Apple" And the error payload field "events.0.device.locale" is not null And the error payload field "events.0.device.id" is not null - And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the error payload field "events.0.device.modelNumber" equals the platform-dependent string: | ios | @not_null | | macos | @null | diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index 0b3a99e52..8fae762ed 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -110,7 +110,7 @@ Feature: Barebone tests And the error payload field "events.0.app.durationInForeground" is a number And the error payload field "events.0.device.freeDisk" is an integer And the error payload field "events.0.device.freeMemory" is an integer - And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the error payload field "events.0.device.totalMemory" is an integer And the error payload field "events.0.threads" is an array with 0 elements And the "isPC" of stack frame 0 is null @@ -234,7 +234,7 @@ Feature: Barebone tests And the error payload field "events.0.app.durationInForeground" is a number And the error payload field "events.0.device.freeDisk" is an integer And the error payload field "events.0.device.freeMemory" is an integer - And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the error payload field "events.0.device.totalMemory" is an integer And on !watchOS, the error payload field "events.0.threads" is a non-empty array And on !watchOS, the error payload field "events.0.threads.1" is not null @@ -334,5 +334,5 @@ Feature: Barebone tests And the error payload field "events.0.app.duration" is null And the error payload field "events.0.app.durationInForeground" is null And the error payload field "events.0.device.freeDisk" is null - And the error payload field "events.0.device.model" matches the regex "iPhone1?\d,\d" + And the error payload field "events.0.device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the error payload field "events.0.threads" is an array with 0 elements diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 5cd15995f..7c09fab46 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -63,7 +63,7 @@ Feature: Attaching a series of notable events leading up to errors And the event "breadcrumbs.0.name" equals "NSURLSession request failed" And the event "breadcrumbs.0.type" equals "request" And the event "breadcrumbs.0.metaData.method" equals "GET" - And the event "breadcrumbs.0.metaData.url" matches "http://.*:9339/reflect/" + And the event "breadcrumbs.0.metaData.url" matches "http://.*:9\d{3}/reflect/" And the event "breadcrumbs.0.metaData.urlParams.status" equals "444" And the event "breadcrumbs.0.metaData.urlParams.password" equals "[REDACTED]" And the event "breadcrumbs.0.metaData.status" equals 444 @@ -74,7 +74,7 @@ Feature: Attaching a series of notable events leading up to errors And the event "breadcrumbs.1.name" equals "NSURLSession request succeeded" And the event "breadcrumbs.1.type" equals "request" And the event "breadcrumbs.1.metaData.method" equals "GET" - And the event "breadcrumbs.1.metaData.url" matches "http://.*:9339/reflect/" + And the event "breadcrumbs.1.metaData.url" matches "http://.*:9\d{3}/reflect/" And the event "breadcrumbs.1.metaData.urlParams.delay_ms" equals "3000" And the event "breadcrumbs.1.metaData.status" equals 200 And the event "breadcrumbs.1.metaData.duration" is greater than 0 diff --git a/features/config_from_plist.feature b/features/config_from_plist.feature index 17038da06..62f4fdc4f 100644 --- a/features/config_from_plist.feature +++ b/features/config_from_plist.feature @@ -5,6 +5,8 @@ Feature: Loading Bugsnag configuration from Info.plist Background: Given I clear all persistent data + # PLAT-9615 + @skip_bitbar Scenario: Specifying config in Info.plist When I run "LoadConfigFromFileScenario" And I wait to receive a session @@ -19,6 +21,8 @@ Feature: Loading Bugsnag configuration from Info.plist | macos | macOSTestApp.LaunchError | And the event "app.releaseStage" equals "beta2" + # PLAT-9615 + @skip_bitbar Scenario: Calling Bugsnag.start() with no configuration When I run "LoadConfigFromFileAutoScenario" And I wait to receive a session diff --git a/features/support/env.rb b/features/support/env.rb index cef22f075..9f9924a09 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -61,6 +61,10 @@ def enable_malloc_scribble $app_env.merge! env end +Before('@skip_bitbar') do |scenario| + skip_this_scenario("Skipping scenario") if Maze.config.farm == :bb +end + def skip_below(os, version) skip_this_scenario("Skipping scenario") if Maze::Helper.get_current_platform == os and Maze.config.os_version < version end From 02b2ecd0937e98b58da3956cd9c1463e8593df9b Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 6 Feb 2023 14:21:08 +0000 Subject: [PATCH 26/37] Run tests in a single batch for now --- .buildkite/pipeline.bb.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml index fedf2e3b1..308fab151 100644 --- a/.buildkite/pipeline.bb.yml +++ b/.buildkite/pipeline.bb.yml @@ -2,7 +2,7 @@ steps: - label: 'BitBar Poc - iOS 15 - batch 1' depends_on: "cocoa_fixture" - timeout_in_minutes: 60 + timeout_in_minutes: 90 agents: queue: opensource plugins: @@ -14,7 +14,8 @@ steps: run: cocoa-maze-runner-bitbar service-ports: true command: - - "--exclude=features/[e-z].*.feature$" +# TODO: Run in a single batch until PLAT-9603 is done +# - "--exclude=features/[e-z].*.feature$" - "--app=/app/build/iOSTestApp.ipa" - "--farm=bb" - "--device=IOS_15" @@ -25,8 +26,10 @@ steps: concurrency_method: eager - label: 'BitBar Poc - iOS 15 - batch 2' + # TODO: Run in a single batch until PLAT-9603 is done + skip: Pending PLAT-9603 depends_on: "cocoa_fixture" - timeout_in_minutes: 60 + timeout_in_minutes: 90 agents: queue: opensource plugins: From 9bf0cec6bceccc6efcdec7d6cdb316ebc2e0976d Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 6 Feb 2023 14:21:17 +0000 Subject: [PATCH 27/37] Allow for macOS [full ci] --- features/session_tracking.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/session_tracking.feature b/features/session_tracking.feature index 1e90ede35..941727950 100644 --- a/features/session_tracking.feature +++ b/features/session_tracking.feature @@ -17,7 +17,7 @@ Feature: Session Tracking And the session payload field "device.osName" equals the platform-dependent string: | ios | iOS | | macos | Mac OS | - And the session payload field "device.model" matches the regex "iPhone1?\d,\d" + And the session payload field "device.model" matches the regex "[Macmini|iPhone]1?\d,\d" And the session payload field "sessions.0.id" is a UUID And the session payload field "sessions.0.startedAt" is a parsable timestamp in seconds From 0c4a6007a8ea0167f0bff98ac087138ed0380c5d Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 6 Feb 2023 16:53:37 +0000 Subject: [PATCH 28/37] Just need a new commit id --- .buildkite/pipeline.bb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.buildkite/pipeline.bb.yml b/.buildkite/pipeline.bb.yml index 308fab151..9d8549636 100644 --- a/.buildkite/pipeline.bb.yml +++ b/.buildkite/pipeline.bb.yml @@ -50,3 +50,4 @@ steps: concurrency: 5 concurrency_group: 'bitbar-ios-15' concurrency_method: eager + From b5e7902b54ce483ba8fd0a9abc95916e8872699d Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 23 Feb 2023 23:21:37 +0000 Subject: [PATCH 29/37] Enable features --- features/app_and_device_attributes.feature | 11 --- features/config_from_plist.feature | 78 ++++++++++--------- .../ios/iOSTestApp/BugsnagWrapper.swift | 9 +++ 3 files changed, 50 insertions(+), 48 deletions(-) create mode 100644 features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift diff --git a/features/app_and_device_attributes.feature b/features/app_and_device_attributes.feature index 17a43f810..1829bccd1 100644 --- a/features/app_and_device_attributes.feature +++ b/features/app_and_device_attributes.feature @@ -91,17 +91,6 @@ Feature: App and Device attributes present And the error payload field "events.0.device.manufacturer" equals "Nokia" And the error payload field "events.0.device.modelNumber" equals "0898" - - # PLAT-9615 - @skip_bitbar - Scenario: Info.plist settings are used when calling startWithApiKey - When I run "AppAndDeviceAttributesStartWithApiKeyScenario" - And I wait to receive an error - Then the error is valid for the error reporting API - And the error "Bugsnag-API-Key" header equals "12312312312312312312312312312312" - - And the error payload field "events.0.app.releaseStage" equals "beta2" - Scenario: Duration value increments as expected When I run "AppDurationScenario" And I wait to receive 3 errors diff --git a/features/config_from_plist.feature b/features/config_from_plist.feature index 62f4fdc4f..85aefc0ec 100644 --- a/features/config_from_plist.feature +++ b/features/config_from_plist.feature @@ -2,40 +2,44 @@ Feature: Loading Bugsnag configuration from Info.plist Configuration options can be specified in build at build time to avoid writing code for those options. - Background: - Given I clear all persistent data - - # PLAT-9615 - @skip_bitbar - Scenario: Specifying config in Info.plist - When I run "LoadConfigFromFileScenario" - And I wait to receive a session - And I wait to receive an error - - Then the session "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" - And the session payload field "sessions" is not null - - And the error "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" - And the event "metaData.nserror.domain" equals the platform-dependent string: - | ios | iOSTestApp.LaunchError | - | macos | macOSTestApp.LaunchError | - And the event "app.releaseStage" equals "beta2" - - # PLAT-9615 - @skip_bitbar - Scenario: Calling Bugsnag.start() with no configuration - When I run "LoadConfigFromFileAutoScenario" - And I wait to receive a session - And I wait to receive an error - - Then the session "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" - And the session payload field "sessions" is not null - - And the error "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" - And the error payload field "notifier.name" equals the platform-dependent string: - | ios | iOS Bugsnag Notifier | - | macos | OSX Bugsnag Notifier | - And the event "metaData.nserror.domain" equals the platform-dependent string: - | ios | iOSTestApp.LoadConfigFromFileAutoScenarioError | - | macos | macOSTestApp.LoadConfigFromFileAutoScenarioError | - And the event "app.releaseStage" equals "beta2" + Background: + Given I clear all persistent data + + Scenario: Specifying config in Info.plist + When I run "LoadConfigFromFileScenario" + And I wait to receive a session + And I wait to receive an error + + Then the session "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" + And the session payload field "sessions" is not null + + And the error "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" + And the event "metaData.nserror.domain" equals the platform-dependent string: + | ios | iOSTestApp.LaunchError | + | macos | macOSTestApp.LaunchError | + And the event "app.releaseStage" equals "beta2" + + Scenario: Calling Bugsnag.start() with no configuration + When I run "LoadConfigFromFileAutoScenario" + And I wait to receive a session + And I wait to receive an error + + Then the session "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" + And the session payload field "sessions" is not null + + And the error "Bugsnag-API-Key" header equals "0192837465afbecd0192837465afbecd" + And the error payload field "notifier.name" equals the platform-dependent string: + | ios | iOS Bugsnag Notifier | + | macos | OSX Bugsnag Notifier | + And the event "metaData.nserror.domain" equals the platform-dependent string: + | ios | iOSTestApp.LoadConfigFromFileAutoScenarioError | + | macos | macOSTestApp.LoadConfigFromFileAutoScenarioError | + And the event "app.releaseStage" equals "beta2" + + Scenario: Info.plist settings are used when calling startWithApiKey + When I run "AppAndDeviceAttributesStartWithApiKeyScenario" + And I wait to receive an error + Then the error is valid for the error reporting API + And the error "Bugsnag-API-Key" header equals "12312312312312312312312312312312" + + And the error payload field "events.0.app.releaseStage" equals "beta2" diff --git a/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift b/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift new file mode 100644 index 000000000..bd7dd0030 --- /dev/null +++ b/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift @@ -0,0 +1,9 @@ +// +// BugsnagWrapper.swift +// iOSTestApp +// +// Created by Steve Kirkland-Walton on 23/02/2023. +// Copyright © 2023 Bugsnag. All rights reserved. +// + +import Foundation From 262e3ccde95a3bb0ad02b66d17f0ed42fe1440d6 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 23 Feb 2023 23:22:21 +0000 Subject: [PATCH 30/37] Basically working for iOS --- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 4 +++ .../ios/iOSTestApp/BugsnagWrapper.swift | 28 +++++++++++++++++++ features/fixtures/ios/iOSTestApp/Info.plist | 4 +-- ...iceAttributesStartWithApiKeyScenario.swift | 2 +- .../LoadConfigFromFileAutoScenario.swift | 2 +- .../LoadConfigFromFileScenario.swift | 2 +- 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index bb7ae576a..ac3d2942c 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ A1117E572535B22300014FDA /* OOMAutoDetectErrorsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E562535B22300014FDA /* OOMAutoDetectErrorsScenario.swift */; }; A1117E592535B29800014FDA /* OOMEnabledErrorTypesScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */; }; A1117E5B2536036400014FDA /* OOMSessionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E5A2536036400014FDA /* OOMSessionScenario.swift */; }; + AA5646F629A7EE9D00CE8629 /* BugsnagWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */; }; AA6ACD1C2773E0B3006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD202773E3F0006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */; }; AAFEF9EA26EB533800980A10 /* NetworkBreadcrumbsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */; }; @@ -314,6 +315,7 @@ 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 = ""; }; + AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugsnagWrapper.swift; sourceTree = ""; }; AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBreadcrumbsScenario.swift; sourceTree = ""; }; @@ -438,6 +440,7 @@ 8AB8866C20404DD30003E444 /* LaunchScreen.storyboard */, 8AB8866520404DD30003E444 /* ViewController.swift */, 8AB8866720404DD30003E444 /* Main.storyboard */, + AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */, ); path = iOSTestApp; sourceTree = ""; @@ -783,6 +786,7 @@ E700EE65247D6C08008CFFB6 /* OnSendCallbackRemovalScenario.m in Sources */, 8A38C5D124094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift in Sources */, 010BAB292833D08F0003FF36 /* BareboneTestUnhandledErrorScenario.swift in Sources */, + AA5646F629A7EE9D00CE8629 /* BugsnagWrapper.swift in Sources */, 01F6B75F2832757F00B75C5D /* OversizedHandledErrorScenario.swift in Sources */, 010BAB002833CE570003FF36 /* DisableNSExceptionScenario.m in Sources */, 8AEEBBD020FC9E1D00C60763 /* AutoSessionMixedEventsScenario.m in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift b/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift index bd7dd0030..cd51e3d49 100644 --- a/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift +++ b/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift @@ -7,3 +7,31 @@ // import Foundation + +class BugsnagWrapper : Bugsnag { + + static var plistNotifyEndpoint: String = ""; + static var plistSessionsEndpoint: String = ""; + + override class func start(with configuration: BugsnagConfiguration) -> BugsnagClient { + + // Store the endpoint values read from the plist + plistNotifyEndpoint = configuration.endpoints.notify; + plistSessionsEndpoint = configuration.endpoints.sessions; + + NSLog("Plist notify endpoint: %@", plistNotifyEndpoint); + NSLog("Plist sessions endpoint: %@", plistSessionsEndpoint); + + if (plistNotifyEndpoint != "http://example.com/notify" + || plistSessionsEndpoint != "http://example.com/sessions") { + + fatalError("Endpoint configuration read from plist is not as expected"); + } + + // Overwrite the configuration endpoints + configuration.endpoints.notify = String(format: "%@/notify", Scenario.baseMazeAddress); + configuration.endpoints.sessions = String(format: "%@/sessions", Scenario.baseMazeAddress); + + return Bugsnag.start(with: configuration); + } +} diff --git a/features/fixtures/ios/iOSTestApp/Info.plist b/features/fixtures/ios/iOSTestApp/Info.plist index 272ced950..b24cc7ebe 100644 --- a/features/fixtures/ios/iOSTestApp/Info.plist +++ b/features/fixtures/ios/iOSTestApp/Info.plist @@ -57,9 +57,9 @@ endpoints notify - http://bs-local.com:9339/notify + http://example.com/notify sessions - http://bs-local.com:9339/sessions + http://example.com/sessions releaseStage beta2 diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift index 5ba622ec5..b3f4a3363 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift @@ -4,7 +4,7 @@ class AppAndDeviceAttributesStartWithApiKeyScenario: Scenario { override func startBugsnag() { - Bugsnag.start(withApiKey: "12312312312312312312312312312312") + BugsnagWrapper.start(withApiKey: "12312312312312312312312312312312") super.startBugsnag() } diff --git a/features/fixtures/shared/scenarios/LoadConfigFromFileAutoScenario.swift b/features/fixtures/shared/scenarios/LoadConfigFromFileAutoScenario.swift index 027b8ad3b..98824c5c4 100644 --- a/features/fixtures/shared/scenarios/LoadConfigFromFileAutoScenario.swift +++ b/features/fixtures/shared/scenarios/LoadConfigFromFileAutoScenario.swift @@ -10,7 +10,7 @@ class LoadConfigFromFileAutoScenarioError : Error { @objc class LoadConfigFromFileAutoScenario: Scenario { override func startBugsnag() { - Bugsnag.start() + BugsnagWrapper.start() } override func run() { diff --git a/features/fixtures/shared/scenarios/LoadConfigFromFileScenario.swift b/features/fixtures/shared/scenarios/LoadConfigFromFileScenario.swift index 6f086548d..3edc16da4 100644 --- a/features/fixtures/shared/scenarios/LoadConfigFromFileScenario.swift +++ b/features/fixtures/shared/scenarios/LoadConfigFromFileScenario.swift @@ -11,7 +11,7 @@ class LaunchError : Error { override func startBugsnag() { config = BugsnagConfiguration.loadConfig() - Bugsnag.start(with: config) + _ = BugsnagWrapper.start(with: config) } override func run() { From 2227d2a51bdd330c3362fb9fcc706b3e69dddd33 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 28 Feb 2023 21:16:52 +0000 Subject: [PATCH 31/37] Refactor and apply to macOS fixture --- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 17 +++++++++++++---- .../macOSTestApp.xcodeproj/project.pbxproj | 13 +++++++++++++ features/fixtures/macos/macOSTestApp/Info.plist | 4 ++-- .../utils}/BugsnagWrapper.swift | 0 4 files changed, 28 insertions(+), 6 deletions(-) rename features/fixtures/{ios/iOSTestApp => shared/utils}/BugsnagWrapper.swift (100%) diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index ac3d2942c..4ac2ec75e 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -119,7 +119,7 @@ A1117E572535B22300014FDA /* OOMAutoDetectErrorsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E562535B22300014FDA /* OOMAutoDetectErrorsScenario.swift */; }; A1117E592535B29800014FDA /* OOMEnabledErrorTypesScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */; }; A1117E5B2536036400014FDA /* OOMSessionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E5A2536036400014FDA /* OOMSessionScenario.swift */; }; - AA5646F629A7EE9D00CE8629 /* BugsnagWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */; }; + AA4C7F1529AEA0C4009B09A9 /* BugsnagWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4C7F1429AEA0C4009B09A9 /* BugsnagWrapper.swift */; }; AA6ACD1C2773E0B3006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD202773E3F0006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */; }; AAFEF9EA26EB533800980A10 /* NetworkBreadcrumbsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */; }; @@ -315,7 +315,7 @@ 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 = ""; }; - AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugsnagWrapper.swift; sourceTree = ""; }; + AA4C7F1429AEA0C4009B09A9 /* BugsnagWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BugsnagWrapper.swift; sourceTree = ""; }; AA6ACD1B2773E0B3006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1F2773E3F0006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; AAFEF9E926EB533800980A10 /* NetworkBreadcrumbsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkBreadcrumbsScenario.swift; sourceTree = ""; }; @@ -416,6 +416,7 @@ 8AB8865720404DD30003E444 = { isa = PBXGroup; children = ( + AA4C7F1329AEA060009B09A9 /* utils */, 8AB8866220404DD30003E444 /* iOSTestApp */, F42953DE2BB41023C0B07F41 /* scenarios */, 8AB8866120404DD30003E444 /* Products */, @@ -440,11 +441,19 @@ 8AB8866C20404DD30003E444 /* LaunchScreen.storyboard */, 8AB8866520404DD30003E444 /* ViewController.swift */, 8AB8866720404DD30003E444 /* Main.storyboard */, - AA5646F529A7EE9D00CE8629 /* BugsnagWrapper.swift */, ); path = iOSTestApp; sourceTree = ""; }; + AA4C7F1329AEA060009B09A9 /* utils */ = { + isa = PBXGroup; + children = ( + AA4C7F1429AEA0C4009B09A9 /* BugsnagWrapper.swift */, + ); + name = utils; + path = ../shared/utils; + sourceTree = ""; + }; D018DCE3210BCB4D0B8FA6D2 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -786,7 +795,7 @@ E700EE65247D6C08008CFFB6 /* OnSendCallbackRemovalScenario.m in Sources */, 8A38C5D124094D7B00BC4463 /* DiscardedBreadcrumbTypeScenario.swift in Sources */, 010BAB292833D08F0003FF36 /* BareboneTestUnhandledErrorScenario.swift in Sources */, - AA5646F629A7EE9D00CE8629 /* BugsnagWrapper.swift in Sources */, + AA4C7F1529AEA0C4009B09A9 /* BugsnagWrapper.swift in Sources */, 01F6B75F2832757F00B75C5D /* OversizedHandledErrorScenario.swift in Sources */, 010BAB002833CE570003FF36 /* DisableNSExceptionScenario.m in Sources */, 8AEEBBD020FC9E1D00C60763 /* AutoSessionMixedEventsScenario.m in Sources */, diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index ee3c69a6b..48008bdea 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -184,6 +184,7 @@ 01FA9EC626D64FFF0059FF4A /* AppHangInTerminationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA9EC526D64FFF0059FF4A /* AppHangInTerminationScenario.swift */; }; 8A096DF827C7E63A00DB6ECC /* CxxUnexpectedScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF727C7E63A00DB6ECC /* CxxUnexpectedScenario.mm */; }; 8A096DFA27C7E6D800DB6ECC /* CxxBareThrowScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF927C7E6D800DB6ECC /* CxxBareThrowScenario.mm */; }; + AA4C7F1829AEA31D009B09A9 /* BugsnagWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */; }; AA6ACD1A2773E098006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD192773E098006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD1E2773E39C006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1D2773E39C006464C4 /* UserInfoScenario.swift */; }; CB0AE1F3287DBA380079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0AE1F2287DBA380079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */; }; @@ -390,6 +391,7 @@ 5C65BFC9838298CFA8A35072 /* Pods_macOSTestApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_macOSTestApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8A096DF727C7E63A00DB6ECC /* CxxUnexpectedScenario.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxUnexpectedScenario.mm; sourceTree = ""; }; 8A096DF927C7E6D800DB6ECC /* CxxBareThrowScenario.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxBareThrowScenario.mm; sourceTree = ""; }; + AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BugsnagWrapper.swift; sourceTree = ""; }; AA6ACD192773E098006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1D2773E39C006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; CB0AE1F2287DBA380079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnSendErrorCallbackFeatureFlagsScenario.swift; sourceTree = ""; }; @@ -607,6 +609,7 @@ 0176C09F254AE81B0066E0F3 = { isa = PBXGroup; children = ( + AA4C7F1629AEA1F2009B09A9 /* utils */, 0176C0AA254AE81B0066E0F3 /* macOSTestApp */, 01452234254AFCD600D436AA /* scenarios */, 0176C0A9254AE81B0066E0F3 /* Products */, @@ -648,6 +651,15 @@ path = Pods; sourceTree = ""; }; + AA4C7F1629AEA1F2009B09A9 /* utils */ = { + isa = PBXGroup; + children = ( + AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */, + ); + name = utils; + path = ../shared/utils; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -921,6 +933,7 @@ 01F47D20254B1B3100B184AD /* ObjCMsgSendScenario.m in Sources */, 01F47CD4254B1B3100B184AD /* AsyncSafeThreadScenario.m in Sources */, 01ECBCF525A7522000FC0678 /* OnErrorOverwriteUnhandledTrueScenario.swift in Sources */, + AA4C7F1829AEA31D009B09A9 /* BugsnagWrapper.swift in Sources */, 01DCB82D279868160048640A /* ConcurrentCrashesScenario.mm in Sources */, 0123189C275921590007EFD7 /* RecrashScenarios.mm in Sources */, CB0AE1F3287DBA380079B28E /* OnSendErrorCallbackFeatureFlagsScenario.swift in Sources */, diff --git a/features/fixtures/macos/macOSTestApp/Info.plist b/features/fixtures/macos/macOSTestApp/Info.plist index cfd84f3e1..0389cd5ea 100644 --- a/features/fixtures/macos/macOSTestApp/Info.plist +++ b/features/fixtures/macos/macOSTestApp/Info.plist @@ -9,9 +9,9 @@ endpoints notify - http://bs-local.com:9339/notify + http://example.com/notify sessions - http://bs-local.com:9339/sessions + http://example.com/sessions releaseStage beta2 diff --git a/features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift b/features/fixtures/shared/utils/BugsnagWrapper.swift similarity index 100% rename from features/fixtures/ios/iOSTestApp/BugsnagWrapper.swift rename to features/fixtures/shared/utils/BugsnagWrapper.swift From b388b5f09a975e5824a24992c1243c1d6b163e82 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 1 Mar 2023 12:47:25 +0100 Subject: [PATCH 32/37] Fix race condition from BG task calling BSGRunContextUpdateMemory while the main thread is also calling it --- Bugsnag/Helpers/BSGHardware.h | 4 +- Bugsnag/Helpers/BSGInternalErrorReporter.m | 2 +- Bugsnag/Helpers/BSGRunContext.h | 8 +-- Bugsnag/Helpers/BSGRunContext.m | 70 +++++++++++++------ .../KSCrash/Recording/BSG_KSCrashState.m | 2 +- .../KSCrash/Recording/BSG_KSSystemInfo.m | 4 +- .../KSCrash/Recording/Tools/BSG_KSMach.c | 2 +- .../Recording/Tools/BSG_KSMachHeaders.c | 10 +-- Bugsnag/Payload/BugsnagThread.m | 4 +- .../include/Bugsnag/BSG_KSCrashReportWriter.h | 2 +- 10 files changed, 68 insertions(+), 40 deletions(-) diff --git a/Bugsnag/Helpers/BSGHardware.h b/Bugsnag/Helpers/BSGHardware.h index 2bb1c0292..ce3c5b95a 100644 --- a/Bugsnag/Helpers/BSGHardware.h +++ b/Bugsnag/Helpers/BSGHardware.h @@ -18,11 +18,11 @@ #pragma mark Device #if TARGET_OS_IOS -static inline UIDevice *BSGGetDevice() { +static inline UIDevice *BSGGetDevice(void) { return [UIDEVICE currentDevice]; } #elif TARGET_OS_WATCH -static inline WKInterfaceDevice *BSGGetDevice() { +static inline WKInterfaceDevice *BSGGetDevice(void) { return [WKInterfaceDevice currentDevice]; } #endif diff --git a/Bugsnag/Helpers/BSGInternalErrorReporter.m b/Bugsnag/Helpers/BSGInternalErrorReporter.m index ec28e9d8e..fa00f2a4c 100644 --- a/Bugsnag/Helpers/BSGInternalErrorReporter.m +++ b/Bugsnag/Helpers/BSGInternalErrorReporter.m @@ -283,7 +283,7 @@ - (void)sendEvent:(nonnull BugsnagEvent *)event { // Intentionally differs from +[BSG_KSSystemInfo deviceAndAppHash] // See ROAD-1488 -static NSString * DeviceId() { +static NSString * DeviceId(void) { CC_SHA1_CTX ctx; CC_SHA1_Init(&ctx); diff --git a/Bugsnag/Helpers/BSGRunContext.h b/Bugsnag/Helpers/BSGRunContext.h index 496cdeb8c..0b6d2db55 100644 --- a/Bugsnag/Helpers/BSGRunContext.h +++ b/Bugsnag/Helpers/BSGRunContext.h @@ -77,7 +77,7 @@ void BSGRunContextUpdateTimestamp(void); #pragma mark - #ifdef FOUNDATION_EXTERN -static inline bool BSGRunContextWasCriticalThermalState() { +static inline bool BSGRunContextWasCriticalThermalState(void) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunguarded-availability-new" return bsg_lastRunContext && bsg_lastRunContext->thermalState == NSProcessInfoThermalStateCritical; @@ -89,16 +89,16 @@ static inline bool BSGRunContextWasCriticalThermalState() { bool BSGRunContextWasKilled(void); #endif -static inline bool BSGRunContextWasLaunching() { +static inline bool BSGRunContextWasLaunching(void) { return bsg_lastRunContext && bsg_lastRunContext->isLaunching; } #if BSG_HAVE_OOM_DETECTION -static inline bool BSGRunContextWasMemoryWarning() { +static inline bool BSGRunContextWasMemoryWarning(void) { return bsg_lastRunContext && bsg_lastRunContext->memoryPressure > DISPATCH_MEMORYPRESSURE_NORMAL; } #endif -static inline bool BSGRunContextWasTerminating() { +static inline bool BSGRunContextWasTerminating(void) { return bsg_lastRunContext && bsg_lastRunContext->isTerminating; } diff --git a/Bugsnag/Helpers/BSGRunContext.m b/Bugsnag/Helpers/BSGRunContext.m index 9dc6b81c8..a5245ae44 100644 --- a/Bugsnag/Helpers/BSGRunContext.m +++ b/Bugsnag/Helpers/BSGRunContext.m @@ -45,7 +45,7 @@ #pragma mark - Initial setup /// Populates `bsg_runContext` -static void InitRunContext() { +static void InitRunContext(void) { bsg_runContext->isDebuggerAttached = bsg_ksmachisBeingTraced(); bsg_runContext->isLaunching = YES; @@ -74,19 +74,19 @@ static void InitRunContext() { } BSGRunContextUpdateTimestamp(); - InstallTimer(); - BSGRunContextUpdateMemory(); if (!bsg_runContext->memoryLimit) { bsg_log_debug(@"Cannot query `memoryLimit` on this device"); } + + InstallTimer(); // Set `structVersion` last so that BSGRunContextLoadLast() will reject data // that is not fully initialised. bsg_runContext->structVersion = BSGRUNCONTEXT_VERSION; } -static uint64_t GetBootTime() { +static uint64_t GetBootTime(void) { struct timeval tv; size_t len = sizeof(tv); int ret = sysctl((int[]){CTL_KERN, KERN_BOOTTIME}, 2, &tv, &len, NULL, 0); @@ -94,7 +94,7 @@ static uint64_t GetBootTime() { return (uint64_t)tv.tv_sec * USEC_PER_SEC + (uint64_t)tv.tv_usec; } -static bool GetIsActive() { +static bool GetIsActive(void) { #if TARGET_OS_OSX return GetIsForeground(); #endif @@ -110,7 +110,7 @@ static bool GetIsActive() { #endif } -static bool GetIsForeground() { +static bool GetIsForeground(void) { #if TARGET_OS_OSX return [[NSAPPLICATION sharedApplication] isActive]; #endif @@ -182,7 +182,7 @@ static bool GetIsForeground() { #endif -static void InstallTimer() { +static void InstallTimer(void) { static dispatch_source_t timer; dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0); @@ -206,27 +206,43 @@ static void InstallTimer() { #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_OSX -static void NoteAppActive() { +static void NoteAppActive(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->isActive = YES; bsg_runContext->isForeground = YES; BSGRunContextUpdateTimestamp(); } -static void NoteAppBackground() { +static void NoteAppBackground(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->isActive = NO; bsg_runContext->isForeground = NO; BSGRunContextUpdateTimestamp(); } #if !TARGET_OS_OSX -static void NoteAppInactive() { +static void NoteAppInactive(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->isActive = NO; bsg_runContext->isForeground = YES; BSGRunContextUpdateTimestamp(); } #endif -static void NoteAppWillTerminate() { +static void NoteAppWillTerminate(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->isTerminating = YES; BSGRunContextUpdateTimestamp(); } @@ -235,15 +251,27 @@ static void NoteAppWillTerminate() { #if TARGET_OS_IOS -static void NoteBatteryLevel() { +static void NoteBatteryLevel(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->batteryLevel = BSGGetDevice().batteryLevel; } -static void NoteBatteryState() { +static void NoteBatteryState(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { bsg_runContext->batteryState = BSGGetDevice().batteryState; } -static void NoteOrientation() { +static void NoteOrientation(__unused CFNotificationCenterRef center, + __unused void *observer, + __unused CFNotificationName name, + __unused const void *object, + __unused CFDictionaryRef userInfo) { UIDeviceOrientation orientation = [UIDEVICE currentDevice].orientation; if (orientation != UIDeviceOrientationUnknown) { bsg_runContext->lastKnownOrientation = orientation; @@ -272,7 +300,7 @@ static void NoteThermalState(__unused CFNotificationCenterRef center, #if BSG_HAVE_OOM_DETECTION -static void ObserveMemoryPressure() { +static void ObserveMemoryPressure(void) { // DISPATCH_SOURCE_TYPE_MEMORYPRESSURE arrives slightly sooner than // UIApplicationDidReceiveMemoryWarningNotification dispatch_source_t source = @@ -293,7 +321,7 @@ static void ObserveMemoryPressure() { #endif -static void AddObservers() { +static void AddObservers(void) { CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter(); #define OBSERVE(name, function) CFNotificationCenterAddObserver(\ @@ -341,11 +369,11 @@ static void AddObservers() { #pragma mark - Misc -void BSGRunContextUpdateTimestamp() { +void BSGRunContextUpdateTimestamp(void) { ATOMIC_SET(bsg_runContext->timestamp, CFAbsoluteTimeGetCurrent()); } -static void UpdateHostMemory() { +static void UpdateHostMemory(void) { static _Atomic mach_port_t host_atomic = 0; mach_port_t host = atomic_load(&host_atomic); if (!host) { @@ -366,7 +394,7 @@ static void UpdateHostMemory() { bsg_runContext->hostMemoryFree = hostMemoryFree; } -static void UpdateTaskMemory() { +static void UpdateTaskMemory(void) { task_vm_info_data_t task_vm = {0}; mach_msg_type_number_t count = TASK_VM_INFO_COUNT; kern_return_t kr = task_info(current_task(), TASK_VM_INFO, @@ -391,7 +419,7 @@ static void UpdateTaskMemory() { #endif } -void BSGRunContextUpdateMemory() { +void BSGRunContextUpdateMemory(void) { UpdateTaskMemory(); UpdateHostMemory(); } @@ -401,7 +429,7 @@ void BSGRunContextUpdateMemory() { #if !TARGET_OS_WATCH -bool BSGRunContextWasKilled() { +bool BSGRunContextWasKilled(void) { // App extensions have a different lifecycle and the heuristic used for // finding app terminations rooted in fixable code does not apply if ([BSG_KSSystemInfo isRunningInAppExtension]) { diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m index 6b1b6908c..bb849bc46 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m @@ -208,7 +208,7 @@ void bsg_kscrashstate_notifyAppCrash(void) { bsg_kscrashstate_i_saveState(bsg_g_state, bsg_g_stateFilePath); } -void bsg_kscrashstate_updateDurationStats() { +void bsg_kscrashstate_updateDurationStats(void) { uint64_t timeNow = mach_absolute_time(); const double duration = bsg_ksmachtimeDifferenceInSeconds( timeNow, bsg_g_state->lastUpdateDurationsTime ?: bsg_g_state->appLaunchTime); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index e389c78fb..1eeae0796 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -48,7 +48,7 @@ #import #import -static inline bool is_jailbroken() { +static inline bool is_jailbroken(void) { static bool initialized_jb; static bool is_jb; if(!initialized_jb) { @@ -72,7 +72,7 @@ static inline bool is_jailbroken() { * https://opensource.apple.com/source/xnu/xnu-7195.81.3/libsyscall/wrappers/system-version-compat.c.auto.html */ #if !TARGET_OS_SIMULATOR -static NSDictionary * bsg_systemversion() { +static NSDictionary * bsg_systemversion(void) { int fd = -1; char buffer[1024] = {0}; const char *file = "/System/Library/CoreServices/SystemVersion.plist"; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c index c6e279f28..d593948b0 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach.c @@ -152,7 +152,7 @@ bool bsg_ksmachfillState(const thread_t thread, const thread_state_t state, } #endif -thread_t bsg_ksmachthread_self() { +thread_t bsg_ksmachthread_self(void) { thread_t thread_self = mach_thread_self(); mach_port_deallocate(mach_task_self(), thread_self); return thread_self; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c index 52f1b85ee..20a36236f 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c @@ -50,7 +50,7 @@ static dispatch_queue_t bsg_g_serial_queue; static BSG_Mach_Header_Info *g_self_image; -BSG_Mach_Header_Info *bsg_mach_headers_get_images() { +BSG_Mach_Header_Info *bsg_mach_headers_get_images(void) { if (!bsg_g_mach_headers_images_head) { bsg_mach_headers_initialize(); bsg_mach_headers_register_dyld_images(); @@ -59,7 +59,7 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_images() { return bsg_g_mach_headers_images_head; } -BSG_Mach_Header_Info *bsg_mach_headers_get_main_image() { +BSG_Mach_Header_Info *bsg_mach_headers_get_main_image(void) { for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) { if (img->header->filetype == MH_EXECUTE) { return img; @@ -73,7 +73,7 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_self_image(void) { return g_self_image; } -void bsg_mach_headers_initialize() { +void bsg_mach_headers_initialize(void) { // Clear any existing headers to reset the head/tail pointers for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; ) { @@ -88,7 +88,7 @@ void bsg_mach_headers_initialize() { g_self_image = NULL; } -static void bsg_mach_headers_register_dyld_images() { +static void bsg_mach_headers_register_dyld_images(void) { // /usr/lib/dyld's mach header is is not exposed via the _dyld APIs, so to be able to include information // about stack frames in dyld`start (for example) we need to acess "_dyld_all_image_infos" task_dyld_info_data_t dyld_info = {0}; @@ -114,7 +114,7 @@ static void bsg_mach_headers_register_dyld_images() { } } -static void bsg_mach_headers_register_for_changes() { +static void bsg_mach_headers_register_for_changes(void) { // Register for binary images being loaded and unloaded. dyld calls the add function once // for each library that has already been loaded and then keeps this cache up-to-date // with future changes diff --git a/Bugsnag/Payload/BugsnagThread.m b/Bugsnag/Payload/BugsnagThread.m index 1ed5d6307..6bd77c08b 100644 --- a/Bugsnag/Payload/BugsnagThread.m +++ b/Bugsnag/Payload/BugsnagThread.m @@ -26,12 +26,12 @@ // Protect access to thread-unsafe bsg_kscrashsentry_suspendThreads() static pthread_mutex_t bsg_suspend_threads_mutex = PTHREAD_MUTEX_INITIALIZER; -static void suspend_threads() { +static void suspend_threads(void) { pthread_mutex_lock(&bsg_suspend_threads_mutex); bsg_kscrashsentry_suspendThreads(); } -static void resume_threads() { +static void resume_threads(void) { bsg_kscrashsentry_resumeThreads(); pthread_mutex_unlock(&bsg_suspend_threads_mutex); } diff --git a/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h b/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h index f4f563c82..610eb20f1 100644 --- a/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h +++ b/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h @@ -190,7 +190,7 @@ typedef struct BSG_KSCrashReportWriter { * * @param name The name to give this element. * - * @param value A pointer to the JSON data. + * @param jsonElement A pointer to the JSON data. */ void (*addJSONElement)(const struct BSG_KSCrashReportWriter *writer, const char *name, const char *jsonElement); From 840b62fbeae22ba88ab345825aefbc01b56e97f9 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 2 Mar 2023 13:15:22 +0100 Subject: [PATCH 33/37] Fix race condition accessing bsg_runContext->hostMemoryFree --- Bugsnag/Helpers/BSGRunContext.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bugsnag/Helpers/BSGRunContext.m b/Bugsnag/Helpers/BSGRunContext.m index a5245ae44..9c2cf5a32 100644 --- a/Bugsnag/Helpers/BSGRunContext.m +++ b/Bugsnag/Helpers/BSGRunContext.m @@ -391,7 +391,7 @@ static void UpdateHostMemory(void) { } size_t hostMemoryFree = host_vm.free_count * vm_kernel_page_size; - bsg_runContext->hostMemoryFree = hostMemoryFree; + ATOMIC_SET(bsg_runContext->hostMemoryFree, hostMemoryFree); } static void UpdateTaskMemory(void) { From 9b0135dc20e4e98b52f910ba4048b6e116bae97e Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Tue, 7 Mar 2023 11:14:03 +0100 Subject: [PATCH 34/37] Disable OOM test which flakes a lot on ios 16 --- features/barebone_tests.feature | 1 + features/support/env.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index 8fae762ed..7467fdccf 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -243,6 +243,7 @@ Feature: Barebone tests And the "isLR" of stack frame 0 is null @skip_macos + @skip_ios_16 # https://smartbear.atlassian.net/browse/PLAT-9724 Scenario: Barebone test: Out Of Memory When I run "OOMScenario" diff --git a/features/support/env.rb b/features/support/env.rb index 9f9924a09..e18ed88f9 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -69,6 +69,14 @@ def skip_below(os, version) skip_this_scenario("Skipping scenario") if Maze::Helper.get_current_platform == os and Maze.config.os_version < version end +def skip_between(os, version_lo, version_hi) + skip_this_scenario("Skipping scenario") if Maze::Helper.get_current_platform == os and Maze.config.os_version >= version_lo and Maze.config.os_version <= version_hi +end + +Before('@skip_ios_16') do |scenario| + skip_between('ios', 16, 16.99) +end + Before('@skip_below_ios_11') do |scenario| skip_below('ios', 11) end From 845f2e275e201efc136261a9ab5e3a75dc6ea592 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 3 Mar 2023 16:11:48 +0100 Subject: [PATCH 35/37] Fix race condition in KSMachHeaders --- .../Source/KSCrash/Recording/BSG_KSCrashC.c | 2 + .../KSCrash/Recording/BSG_KSCrashReport.c | 2 +- .../Recording/Tools/BSG_KSMachHeaders.c | 165 ++++++++++-------- .../Recording/Tools/BSG_KSMachHeaders.h | 31 ++-- CHANGELOG.md | 3 + Tests/KSCrashTests/BSG_KSMachHeadersTests.m | 74 ++++---- 6 files changed, 157 insertions(+), 120 deletions(-) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c index f7f1d40a7..3d7483eeb 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashC.c @@ -109,6 +109,8 @@ BSG_KSCrashType bsg_kscrash_install(const char *const crashReportFilePath, return context->config.handlingCrashTypes; } bsg_g_installed = 1; + + bsg_mach_headers_initialize(); bsg_kscrash_reinstall(crashReportFilePath, recrashReportFilePath, stateFilePath, crashID); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 275811563..c36109511 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -844,7 +844,7 @@ void bsg_kscrw_i_writeBinaryImages(const BSG_KSCrashReportWriter *const writer, { writer->beginArray(writer, key); { - for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) { + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = atomic_load(&img->next)) { if (img->inCrashReport) { bsg_kscrw_i_writeBinaryImage(writer, NULL, img); } diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c index 20a36236f..5a92664c9 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.c @@ -34,33 +34,43 @@ struct crashreporter_annotations_t { uint64_t abort_cause; // unsigned int }; -static void bsg_mach_headers_register_dyld_images(void); -static void bsg_mach_headers_register_for_changes(void); -static intptr_t bsg_mach_headers_compute_slide(const struct mach_header *header); -static bool bsg_mach_headers_contains_address(BSG_Mach_Header_Info *image, vm_address_t address); -static const char * bsg_mach_headers_get_path(const struct mach_header *header); +static void add_image(const struct mach_header *header, intptr_t slide); +static void remove_image(const struct mach_header *header, intptr_t slide); +static void register_dyld_images(void); +static void register_for_changes(void); +static intptr_t compute_slide(const struct mach_header *header); +static bool contains_address(BSG_Mach_Header_Info *image, vm_address_t address); +static const char * get_path(const struct mach_header *header); static const struct dyld_all_image_infos *g_all_image_infos; // MARK: - Mach Header Linked List -static BSG_Mach_Header_Info *bsg_g_mach_headers_images_head; -static BSG_Mach_Header_Info *bsg_g_mach_headers_images_tail; -static dispatch_queue_t bsg_g_serial_queue; - +// The list head is implemented as a dummy entry to simplify the algorithm. +// We fetch g_head_dummy.next to get the real head of the list. +static BSG_Mach_Header_Info g_head_dummy; +static _Atomic(BSG_Mach_Header_Info *) g_images_tail = &g_head_dummy; static BSG_Mach_Header_Info *g_self_image; -BSG_Mach_Header_Info *bsg_mach_headers_get_images(void) { - if (!bsg_g_mach_headers_images_head) { - bsg_mach_headers_initialize(); - bsg_mach_headers_register_dyld_images(); - bsg_mach_headers_register_for_changes(); +static _Atomic(bool) is_mach_headers_initialized; + +void bsg_mach_headers_initialize(void) { + bool expected = false; + if (!atomic_compare_exchange_strong(&is_mach_headers_initialized, &expected, true)) { + // Already called + return; } - return bsg_g_mach_headers_images_head; + + register_dyld_images(); + register_for_changes(); +} + +BSG_Mach_Header_Info *bsg_mach_headers_get_images(void) { + return atomic_load(&g_head_dummy.next); } BSG_Mach_Header_Info *bsg_mach_headers_get_main_image(void) { - for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = img->next) { + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = atomic_load(&img->next)) { if (img->header->filetype == MH_EXECUTE) { return img; } @@ -69,26 +79,10 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_main_image(void) { } BSG_Mach_Header_Info *bsg_mach_headers_get_self_image(void) { - (void)bsg_mach_headers_get_images(); return g_self_image; } -void bsg_mach_headers_initialize(void) { - - // Clear any existing headers to reset the head/tail pointers - for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; ) { - BSG_Mach_Header_Info *imgToDelete = img; - img = img->next; - free(imgToDelete); - } - - bsg_g_mach_headers_images_head = NULL; - bsg_g_mach_headers_images_tail = NULL; - bsg_g_serial_queue = dispatch_queue_create("com.bugsnag.mach-headers", DISPATCH_QUEUE_SERIAL); - g_self_image = NULL; -} - -static void bsg_mach_headers_register_dyld_images(void) { +static void register_dyld_images(void) { // /usr/lib/dyld's mach header is is not exposed via the _dyld APIs, so to be able to include information // about stack frames in dyld`start (for example) we need to acess "_dyld_all_image_infos" task_dyld_info_data_t dyld_info = {0}; @@ -97,8 +91,8 @@ static void bsg_mach_headers_register_dyld_images(void) { if (kr == KERN_SUCCESS && dyld_info.all_image_info_addr) { g_all_image_infos = (const void *)dyld_info.all_image_info_addr; - intptr_t dyldImageSlide = bsg_mach_headers_compute_slide(g_all_image_infos->dyldImageLoadAddress); - bsg_mach_headers_add_image(g_all_image_infos->dyldImageLoadAddress, dyldImageSlide); + intptr_t dyldImageSlide = compute_slide(g_all_image_infos->dyldImageLoadAddress); + add_image(g_all_image_infos->dyldImageLoadAddress, dyldImageSlide); #if TARGET_OS_SIMULATOR // Get the mach header for `dyld_sim` which is not exposed via the _dyld APIs @@ -106,7 +100,7 @@ static void bsg_mach_headers_register_dyld_images(void) { if (g_all_image_infos->infoArray && strstr(g_all_image_infos->infoArray->imageFilePath, "/usr/lib/dyld_sim")) { const struct mach_header *header = g_all_image_infos->infoArray->imageLoadAddress; - bsg_mach_headers_add_image(header, bsg_mach_headers_compute_slide(header)); + add_image(header, compute_slide(header)); } #endif } else { @@ -114,12 +108,12 @@ static void bsg_mach_headers_register_dyld_images(void) { } } -static void bsg_mach_headers_register_for_changes(void) { +static void register_for_changes(void) { // Register for binary images being loaded and unloaded. dyld calls the add function once // for each library that has already been loaded and then keeps this cache up-to-date // with future changes - _dyld_register_func_for_add_image(&bsg_mach_headers_add_image); - _dyld_register_func_for_remove_image(&bsg_mach_headers_remove_image); + _dyld_register_func_for_add_image(&add_image); + _dyld_register_func_for_remove_image(&remove_image); } /** @@ -142,7 +136,7 @@ bool bsg_mach_headers_populate_info(const struct mach_header *header, intptr_t s } // 2. The image doesn't have a name. Note: running with a debugger attached causes this condition to match. - const char *imageName = bsg_mach_headers_get_path(header); + const char *imageName = get_path(header); if (!imageName) { BSG_KSLOG_ERROR("Could not find name for mach header @ %p", header); return false; @@ -195,43 +189,41 @@ bool bsg_mach_headers_populate_info(const struct mach_header *header, intptr_t s info->name = imageName; info->slide = slide; info->unloaded = FALSE; - info->next = NULL; + atomic_store(&info->next, NULL); return true; } -void bsg_mach_headers_add_image(const struct mach_header *header, intptr_t slide) { +static void add_image(const struct mach_header *header, intptr_t slide) { BSG_Mach_Header_Info *newImage = calloc(1, sizeof(BSG_Mach_Header_Info)); - if (newImage != NULL) { - if (bsg_mach_headers_populate_info(header, slide, newImage)) { - dispatch_sync(bsg_g_serial_queue, ^{ - if (bsg_g_mach_headers_images_head == NULL) { - bsg_g_mach_headers_images_head = newImage; - } else { - bsg_g_mach_headers_images_tail->next = newImage; - } - bsg_g_mach_headers_images_tail = newImage; - if (header == &__dso_handle) { - g_self_image = newImage; - } - }); - } else { - free(newImage); - } + if (newImage == NULL) { + return; + } + + if (!bsg_mach_headers_populate_info(header, slide, newImage)) { + free(newImage); + return; + } + + BSG_Mach_Header_Info *oldTail = atomic_exchange(&g_images_tail, newImage); + atomic_store(&oldTail->next, newImage); + + if (header == &__dso_handle) { + g_self_image = newImage; } } -/** - * To avoid a destructive operation that could lead thread safety problems, we maintain the - * image record, but mark it as unloaded - */ -void bsg_mach_headers_remove_image(const struct mach_header *header, intptr_t slide) { +static void remove_image(const struct mach_header *header, intptr_t slide) { BSG_Mach_Header_Info existingImage = { 0 }; - if (bsg_mach_headers_populate_info(header, slide, &existingImage)) { - for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; img = img->next) { - if (img->imageVmAddr == existingImage.imageVmAddr) { - img->unloaded = true; - } + if (!bsg_mach_headers_populate_info(header, slide, &existingImage)) { + return; + } + + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = atomic_load(&img->next)) { + if (img->imageVmAddr == existingImage.imageVmAddr) { + // To avoid a destructive operation that could lead thread safety problems, + // we maintain the image record, but mark it as unloaded + img->unloaded = true; } } } @@ -240,7 +232,7 @@ BSG_Mach_Header_Info *bsg_mach_headers_image_named(const char *const imageName, if (imageName != NULL) { - for (BSG_Mach_Header_Info *img = bsg_g_mach_headers_images_head; img != NULL; img = img->next) { + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = atomic_load(&img->next)) { if (img->name == NULL) { continue; // name is null if the index is out of range per dyld(3) } else if (img->unloaded == true) { @@ -261,8 +253,8 @@ BSG_Mach_Header_Info *bsg_mach_headers_image_named(const char *const imageName, } BSG_Mach_Header_Info *bsg_mach_headers_image_at_address(const uintptr_t address) { - for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img; img = img->next) { - if (bsg_mach_headers_contains_address(img, address)) { + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img; img = atomic_load(&img->next)) { + if (contains_address(img, address)) { return img; } } @@ -350,7 +342,7 @@ const char *bsg_mach_headers_get_crash_info_message(const BSG_Mach_Header_Info * return NULL; } -static intptr_t bsg_mach_headers_compute_slide(const struct mach_header *header) { +static intptr_t compute_slide(const struct mach_header *header) { uintptr_t cmdPtr = bsg_mach_headers_first_cmd_after_header(header); if (!cmdPtr) { return 0; @@ -376,7 +368,7 @@ static intptr_t bsg_mach_headers_compute_slide(const struct mach_header *header) return 0; } -static bool bsg_mach_headers_contains_address(BSG_Mach_Header_Info *img, vm_address_t address) { +static bool contains_address(BSG_Mach_Header_Info *img, vm_address_t address) { if (img->unloaded) { return false; } @@ -384,7 +376,7 @@ static bool bsg_mach_headers_contains_address(BSG_Mach_Header_Info *img, vm_addr return address >= imageStart && address < (imageStart + img->imageSize); } -static const char * bsg_mach_headers_get_path(const struct mach_header *header) { +static const char * get_path(const struct mach_header *header) { Dl_info DlInfo = {0}; dladdr(header, &DlInfo); if (DlInfo.dli_fname) { @@ -403,3 +395,28 @@ static const char * bsg_mach_headers_get_path(const struct mach_header *header) #endif return NULL; } + +void bsg_test_support_mach_headers_reset(void) { + // Erase all current images + BSG_Mach_Header_Info *next = NULL; + for (BSG_Mach_Header_Info *img = bsg_mach_headers_get_images(); img != NULL; img = next) { + next = atomic_load(&img->next); + free(img); + } + + // Reset cached data + atomic_store(&g_head_dummy.next, NULL); + atomic_store(&g_images_tail, &g_head_dummy); + g_self_image = NULL; + + // Force bsg_mach_headers_initialize to run again when requested. + atomic_store(&is_mach_headers_initialized, false); +} + +void bsg_test_support_mach_headers_add_image(const struct mach_header *header, intptr_t slide) { + add_image(header, slide); +} + +void bsg_test_support_mach_headers_remove_image(const struct mach_header *header, intptr_t slide) { + remove_image(header, slide); +} diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h index 05e2471d5..c69159c96 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMachHeaders.h @@ -11,6 +11,7 @@ #include #include +#include /* Maintaining our own list of framework Mach headers means that we avoid potential * deadlock situations where we try and suspend lock-holding threads prior to @@ -52,13 +53,14 @@ typedef struct bsg_mach_image { bool inCrashReport; /// The next image in the linked list - struct bsg_mach_image *next; + _Atomic(struct bsg_mach_image *) next; } BSG_Mach_Header_Info; // MARK: - Operations /** - * Resets mach header data + * Initialize the headers management system. + * This MUST be called before calling anything else. */ void bsg_mach_headers_initialize(void); @@ -77,16 +79,6 @@ BSG_Mach_Header_Info *bsg_mach_headers_get_main_image(void); */ BSG_Mach_Header_Info *bsg_mach_headers_get_self_image(void); -/** - * Called when a binary image is loaded. - */ -void bsg_mach_headers_add_image(const struct mach_header *mh, intptr_t slide); - -/** - * Called when a binary image is unloaded. - */ -void bsg_mach_headers_remove_image(const struct mach_header *mh, intptr_t slide); - /** * Find the loaded binary image that contains the specified instruction address. */ @@ -120,4 +112,19 @@ uintptr_t bsg_mach_headers_first_cmd_after_header(const struct mach_header *head */ const char *bsg_mach_headers_get_crash_info_message(const BSG_Mach_Header_Info *header); +/** + * Resets mach header data (for unit tests). + */ +void bsg_test_support_mach_headers_reset(void); + +/** + * Add a binary image (for unit tests). + */ +void bsg_test_support_mach_headers_add_image(const struct mach_header *mh, intptr_t slide); + +/** + * Remove a binary image (for unit tests). + */ +void bsg_test_support_mach_headers_remove_image(const struct mach_header *mh, intptr_t slide); + #endif /* BSG_KSMachHeaders_h */ diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c0eada0..17855a270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Changelog ## Bug fixes +* Fixed some race conditions that could cause issues in rare cases. + [1529](https://github.com/bugsnag/bugsnag-cocoa/pull/1529) + * onError blocked from running if releaseStage not in enabledReleaseStages. [1518](https://github.com/bugsnag/bugsnag-cocoa/pull/1518) diff --git a/Tests/KSCrashTests/BSG_KSMachHeadersTests.m b/Tests/KSCrashTests/BSG_KSMachHeadersTests.m index f1c972f90..3d5370417 100644 --- a/Tests/KSCrashTests/BSG_KSMachHeadersTests.m +++ b/Tests/KSCrashTests/BSG_KSMachHeadersTests.m @@ -13,8 +13,6 @@ #import #import -void bsg_mach_headers_add_image(const struct mach_header *mh, intptr_t slide); - const struct mach_header header1 = { .magic = MH_MAGIC, .cputype = 0, @@ -25,7 +23,11 @@ .flags = 0 }; const struct segment_command command1 = { - LC_SEGMENT,0,SEG_TEXT,111,10,0,0,0,0,0,0 + .cmd = LC_SEGMENT, + .cmdsize = 0, + .segname = SEG_TEXT, + .vmaddr = 111, + .vmsize = 10, }; const struct mach_header header2 = { @@ -38,7 +40,11 @@ .flags = 0 }; const struct segment_command command2 = { - LC_SEGMENT,0,SEG_TEXT,222,10,0,0,0,0,0,0 + .cmd = LC_SEGMENT, + .cmdsize = 0, + .segname = SEG_TEXT, + .vmaddr = 222, + .vmsize = 10, }; @interface BSG_KSMachHeadersTests : XCTestCase @@ -50,54 +56,56 @@ - (void)setUp { bsg_mach_headers_initialize(); } -- (void)testAddRemoveHeaders { - bsg_mach_headers_add_image(&header1, 0); - - BSG_Mach_Header_Info *listTail; +static BSG_Mach_Header_Info *get_tail(BSG_Mach_Header_Info *head) { + BSG_Mach_Header_Info *current = head; + for (; current->next != NULL; current = current->next) { + } + return current; +} + +- (void)testAddRemove { + bsg_test_support_mach_headers_reset(); + + bsg_test_support_mach_headers_add_image(&header1, 0); - listTail = bsg_mach_headers_get_images(); - XCTAssertEqual(listTail->imageVmAddr, 111); + BSG_Mach_Header_Info *listTail = get_tail(bsg_mach_headers_get_images()); + XCTAssertEqual(listTail->imageVmAddr, command1.vmaddr); XCTAssert(listTail->unloaded == FALSE); - bsg_mach_headers_add_image(&header2, 0); - - listTail = bsg_mach_headers_get_images(); - XCTAssertEqual(listTail->imageVmAddr, 111); + bsg_test_support_mach_headers_add_image(&header2, 0); + + XCTAssertEqual(listTail->imageVmAddr, command1.vmaddr); XCTAssert(listTail->unloaded == FALSE); - XCTAssertEqual(listTail->next->imageVmAddr, 222); + XCTAssertEqual(listTail->next->imageVmAddr, command2.vmaddr); XCTAssert(listTail->next->unloaded == FALSE); - - bsg_mach_headers_remove_image(&header1, 0); - listTail = bsg_mach_headers_get_images(); - XCTAssertEqual(listTail->imageVmAddr, 111); + bsg_test_support_mach_headers_remove_image(&header1, 0); + + XCTAssertEqual(listTail->imageVmAddr, command1.vmaddr); XCTAssert(listTail->unloaded == TRUE); - XCTAssertEqual(listTail->next->imageVmAddr, 222); + XCTAssertEqual(listTail->next->imageVmAddr, command2.vmaddr); XCTAssert(listTail->next->unloaded == FALSE); - - bsg_mach_headers_remove_image(&header2, 0); - listTail = bsg_mach_headers_get_images(); - XCTAssertEqual(listTail->imageVmAddr, 111); + bsg_test_support_mach_headers_remove_image(&header2, 0); + + XCTAssertEqual(listTail->imageVmAddr, command1.vmaddr); XCTAssert(listTail->unloaded == TRUE); - XCTAssertEqual(listTail->next->imageVmAddr, 222); + XCTAssertEqual(listTail->next->imageVmAddr, command2.vmaddr); XCTAssert(listTail->next->unloaded == TRUE); - - bsg_mach_headers_initialize(); } - (void)testFindImageAtAddress { - bsg_mach_headers_add_image(&header1, 0); - bsg_mach_headers_add_image(&header2, 0); + bsg_test_support_mach_headers_reset(); + + bsg_test_support_mach_headers_add_image(&header1, 0); + bsg_test_support_mach_headers_add_image(&header2, 0); BSG_Mach_Header_Info *item; item = bsg_mach_headers_image_at_address((uintptr_t)&header1); - XCTAssertEqual(item->imageVmAddr, 111); + XCTAssertEqual(item->imageVmAddr, command1.vmaddr); item = bsg_mach_headers_image_at_address((uintptr_t)&header2); - XCTAssertEqual(item->imageVmAddr, 222); - - bsg_mach_headers_initialize(); + XCTAssertEqual(item->imageVmAddr, command2.vmaddr); } - (void) testGetImageNameNULL From 44856443fdd6a6b81bc0a6527a91e0ec01e4e5c2 Mon Sep 17 00:00:00 2001 From: robert-smartbear <126675445+robert-smartbear@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:10:31 +0100 Subject: [PATCH 36/37] [PLAT-8309] Add isStarted to client (#1528) * Added isStarted method to Bugsnag * Added isStarted to Changelog Changes requested in code review * Fixed failing tests * Fixed failing tests * Removed waiting for session from internal workings scenario * Added a missing scenario to macOS tests * Small improvements * Naming changes --- Bugsnag/Bugsnag.m | 78 ++++++++++--------- Bugsnag/BugsnagInternals.h | 2 +- Bugsnag/Client/BugsnagClient+Private.h | 6 +- Bugsnag/Client/BugsnagClient.m | 3 +- Bugsnag/include/Bugsnag/Bugsnag.h | 6 ++ CHANGELOG.md | 8 +- Tests/BugsnagTests/BugsnagClientMirrorTest.m | 12 ++- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 4 + .../macOSTestApp.xcodeproj/project.pbxproj | 4 + .../scenarios/InternalWorkingsScenario.swift | 36 +++++++++ features/internal_workings.feature | 10 +++ 11 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 features/fixtures/shared/scenarios/InternalWorkingsScenario.swift create mode 100644 features/internal_workings.feature diff --git a/Bugsnag/Bugsnag.m b/Bugsnag/Bugsnag.m index fbe714dc3..da9e7a344 100644 --- a/Bugsnag/Bugsnag.m +++ b/Bugsnag/Bugsnag.m @@ -62,6 +62,10 @@ + (BugsnagClient *_Nonnull)startWithConfiguration:(BugsnagConfiguration *_Nonnul } } ++ (BOOL)isStarted { + return bsg_g_bugsnag_client.isStarted; +} + /** * Purge the global client so that it will be regenerated on the next call to start. * This is only used by the unit tests. @@ -75,51 +79,51 @@ + (BugsnagClient *)client { } + (BOOL)appDidCrashLastLaunch { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [self.client appDidCrashLastLaunch]; } return NO; } + (BugsnagLastRunInfo *)lastRunInfo { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return self.client.lastRunInfo; } return nil; } + (void)markLaunchCompleted { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client markLaunchCompleted]; } } + (void)notify:(NSException *)exception { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client notify:exception]; } } + (void)notify:(NSException *)exception block:(BugsnagOnErrorBlock)block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client notify:exception block:block]; } } + (void)notifyError:(NSError *)error { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client notifyError:error]; } } + (void)notifyError:(NSError *)error block:(BugsnagOnErrorBlock)block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client notifyError:error block:block]; } } -+ (BOOL)bugsnagStarted { - if (!self.client.started) { ++ (BOOL)bugsnagReadyForInternalCalls { + if (!self.client.readyForInternalCalls) { bsg_log_err(@"Ensure you have started Bugsnag with startWithApiKey: " @"before calling any other Bugsnag functions."); @@ -129,14 +133,14 @@ + (BOOL)bugsnagStarted { } + (void)leaveBreadcrumbWithMessage:(NSString *)message { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client leaveBreadcrumbWithMessage:message]; } } + (void)leaveBreadcrumbForNotificationName: (NSString *_Nonnull)notificationName { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client leaveBreadcrumbForNotificationName:notificationName]; } } @@ -145,7 +149,7 @@ + (void)leaveBreadcrumbWithMessage:(NSString *_Nonnull)message metadata:(NSDictionary *_Nullable)metadata andType:(BSGBreadcrumbType)type { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client leaveBreadcrumbWithMessage:message metadata:metadata andType:type]; @@ -154,13 +158,13 @@ + (void)leaveBreadcrumbWithMessage:(NSString *_Nonnull)message + (void)leaveNetworkRequestBreadcrumbForTask:(NSURLSessionTask *)task metrics:(NSURLSessionTaskMetrics *)metrics { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client leaveNetworkRequestBreadcrumbForTask:task metrics:metrics]; } } + (NSArray *_Nonnull)breadcrumbs { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return self.client.breadcrumbs; } else { return @[]; @@ -168,19 +172,19 @@ + (void)leaveNetworkRequestBreadcrumbForTask:(NSURLSessionTask *)task } + (void)startSession { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client startSession]; } } + (void)pauseSession { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client pauseSession]; } } + (BOOL)resumeSession { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [self.client resumeSession]; } else { return false; @@ -192,31 +196,31 @@ + (BOOL)resumeSession { // ============================================================================= + (void)addFeatureFlagWithName:(NSString *)name variant:(nullable NSString *)variant { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client addFeatureFlagWithName:name variant:variant]; } } + (void)addFeatureFlagWithName:(NSString *)name { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client addFeatureFlagWithName:name]; } } + (void)addFeatureFlags:(NSArray *)featureFlags { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client addFeatureFlags:featureFlags]; } } + (void)clearFeatureFlagWithName:(NSString *)name { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client clearFeatureFlagWithName:name]; } } + (void)clearFeatureFlags { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client clearFeatureFlags]; } } @@ -237,7 +241,7 @@ + (void)addMetadata:(id _Nullable)metadata withKey:(NSString *_Nonnull)key toSection:(NSString *_Nonnull)section { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client addMetadata:metadata withKey:key toSection:section]; @@ -247,7 +251,7 @@ + (void)addMetadata:(id _Nullable)metadata + (void)addMetadata:(id _Nonnull)metadata toSection:(NSString *_Nonnull)section { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client addMetadata:metadata toSection:section]; } @@ -255,7 +259,7 @@ + (void)addMetadata:(id _Nonnull)metadata + (NSMutableDictionary *)getMetadataFromSection:(NSString *)section { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [[self.client getMetadataFromSection:section] mutableCopy]; } return nil; @@ -264,7 +268,7 @@ + (NSMutableDictionary *)getMetadataFromSection:(NSString *)section + (id _Nullable )getMetadataFromSection:(NSString *_Nonnull)section withKey:(NSString *_Nonnull)key { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [[self.client getMetadataFromSection:section withKey:key] mutableCopy]; } return nil; @@ -272,7 +276,7 @@ + (id _Nullable )getMetadataFromSection:(NSString *_Nonnull)section + (void)clearMetadataFromSection:(NSString *)section { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client clearMetadataFromSection:section]; } } @@ -280,7 +284,7 @@ + (void)clearMetadataFromSection:(NSString *)section + (void)clearMetadataFromSection:(NSString *_Nonnull)sectionName withKey:(NSString *_Nonnull)key { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client clearMetadataFromSection:sectionName withKey:key]; } @@ -289,13 +293,13 @@ + (void)clearMetadataFromSection:(NSString *_Nonnull)sectionName // MARK: - + (void)setContext:(NSString *_Nullable)context { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client setContext:context]; } } + (NSString *_Nullable)context { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return self.client.context; } return nil; @@ -308,13 +312,13 @@ + (BugsnagUser *)user { + (void)setUser:(NSString *_Nullable)userId withEmail:(NSString *_Nullable)email andName:(NSString *_Nullable)name { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client setUser:userId withEmail:email andName:name]; } } + (nonnull BugsnagOnSessionRef)addOnSessionBlock:(nonnull BugsnagOnSessionBlock)block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [self.client addOnSessionBlock:block]; } else { // We need to return something from this nonnull method; simulate what would have happened. @@ -323,14 +327,14 @@ + (nonnull BugsnagOnSessionRef)addOnSessionBlock:(nonnull BugsnagOnSessionBlock) } + (void)removeOnSession:(nonnull BugsnagOnSessionRef)callback { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client removeOnSession:callback]; } } + (void)removeOnSessionBlock:(BugsnagOnSessionBlock _Nonnull )block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client removeOnSessionBlock:block]; } } @@ -340,7 +344,7 @@ + (void)removeOnSessionBlock:(BugsnagOnSessionBlock _Nonnull )block // ============================================================================= + (nonnull BugsnagOnBreadcrumbRef)addOnBreadcrumbBlock:(nonnull BugsnagOnBreadcrumbBlock)block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { return [self.client addOnBreadcrumbBlock:block]; } else { // We need to return something from this nonnull method; simulate what would have happened. @@ -349,13 +353,13 @@ + (nonnull BugsnagOnBreadcrumbRef)addOnBreadcrumbBlock:(nonnull BugsnagOnBreadcr } + (void)removeOnBreadcrumb:(nonnull BugsnagOnBreadcrumbRef)callback { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client removeOnBreadcrumb:callback]; } } + (void)removeOnBreadcrumbBlock:(BugsnagOnBreadcrumbBlock _Nonnull)block { - if ([self bugsnagStarted]) { + if ([self bugsnagReadyForInternalCalls]) { [self.client removeOnBreadcrumbBlock:block]; } } diff --git a/Bugsnag/BugsnagInternals.h b/Bugsnag/BugsnagInternals.h index ebb6e5251..7a71c091e 100644 --- a/Bugsnag/BugsnagInternals.h +++ b/Bugsnag/BugsnagInternals.h @@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @interface Bugsnag () -@property (class, readonly, nonatomic) BOOL bugsnagStarted; +@property (class, readonly, nonatomic) BOOL bugsnagReadyForInternalCalls; @property (class, readonly, nonatomic) BugsnagClient *client; diff --git a/Bugsnag/Client/BugsnagClient+Private.h b/Bugsnag/Client/BugsnagClient+Private.h index ef5e1827c..fd3c836fd 100644 --- a/Bugsnag/Client/BugsnagClient+Private.h +++ b/Bugsnag/Client/BugsnagClient+Private.h @@ -40,7 +40,11 @@ BSG_OBJC_DIRECT_MEMBERS @property (nonatomic) NSMutableDictionary *extraRuntimeInfo; -@property (nonatomic) BOOL started; +@property (atomic) BOOL isStarted; + +/// YES if BugsnagClient is ready to handle some internal method calls. +/// It does not mean that it is fully started and ready to receive method calls from outside of the library. +@property (atomic) BOOL readyForInternalCalls; /// State related metadata /// diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 3efd52a86..4c6cd9464 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -284,7 +284,7 @@ - (void)start { #endif object:nil]; - self.started = YES; + self.readyForInternalCalls = YES; id reactNativePlugin = [NSClassFromString(@"BugsnagReactNativePlugin") new]; if (reactNativePlugin) { @@ -326,6 +326,7 @@ - (void)start { // Note: BSGAppHangDetector itself checks configuration.enabledErrorTypes.appHangs [self startAppHangDetector]; #endif + self.isStarted = YES; } - (void)appLaunchTimerFired:(__unused NSTimer *)timer { diff --git a/Bugsnag/include/Bugsnag/Bugsnag.h b/Bugsnag/include/Bugsnag/Bugsnag.h index 26d9b8e47..caab62b46 100644 --- a/Bugsnag/include/Bugsnag/Bugsnag.h +++ b/Bugsnag/include/Bugsnag/Bugsnag.h @@ -107,6 +107,12 @@ BUGSNAG_EXTERN */ + (BOOL)appDidCrashLastLaunch BUGSNAG_DEPRECATED_WITH_REPLACEMENT("lastRunInfo.crashed"); +/** + * @return YES if and only if a Bugsnag.start() has been called + * and Bugsnag has initialized such that any calls to the Bugsnag methods can succeed + */ ++ (BOOL)isStarted; + /** * Information about the last run of the app, and whether it crashed. */ diff --git a/CHANGELOG.md b/CHANGELOG.md index 17855a270..744b12c36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ Changelog ========= + ## TBD -## Bug fixes +### Enhancements + +* Added isStarted to Bugsnag and BugsnagClient + [1528](https://github.com/bugsnag/bugsnag-cocoa/pull/1528) + +### Bug fixes * Fixed some race conditions that could cause issues in rare cases. [1529](https://github.com/bugsnag/bugsnag-cocoa/pull/1529) diff --git a/Tests/BugsnagTests/BugsnagClientMirrorTest.m b/Tests/BugsnagTests/BugsnagClientMirrorTest.m index 25e31d6b4..e31ff1a27 100644 --- a/Tests/BugsnagTests/BugsnagClientMirrorTest.m +++ b/Tests/BugsnagTests/BugsnagClientMirrorTest.m @@ -76,6 +76,8 @@ - (void)setUp { @"orientationDidChange: v24@0:8@16", @"pluginClient @16@0:8", @"populateEventData: v24@0:8@16", + @"readyForInternalCalls B16@0:8", + @"readyForInternalCalls c16@0:8", @"sendBreadcrumbForControlNotification: v24@0:8@16", @"sendBreadcrumbForMenuItemNotification: v24@0:8@16", @"sendBreadcrumbForNotification: v24@0:8@16", @@ -107,9 +109,11 @@ - (void)setUp { @"setNotifier: v24@0:8@16", @"setObserver: v24@0:8@?16", @"setPluginClient: v24@0:8@16", + @"setReadyForInternalCalls: v20@0:8B16", + @"setReadyForInternalCalls: v20@0:8c16", @"setSessionTracker: v24@0:8@16", - @"setStarted: v20@0:8B16", - @"setStarted: v20@0:8c16", + @"setIsStarted: v20@0:8B16", + @"setIsStarted: v20@0:8c16", @"setState: v24@0:8@16", @"setStateEventBlocks: v24@0:8@16", @"setStateMetadataFromLastLaunch: v24@0:8@16", @@ -130,8 +134,8 @@ - (void)setUp { // the following methods are implemented on Bugsnag but do not need to // be mirrored on BugsnagClient self.bugsnagMethodsNotRequiredOnClient = [NSSet setWithArray:@[ - @"bugsnagStarted B16@0:8", - @"bugsnagStarted c16@0:8", + @"bugsnagReadyForInternalCalls B16@0:8", + @"bugsnagReadyForInternalCalls c16@0:8", @"client @16@0:8", @"getContext @16@0:8", @"purge v16@0:8", diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 4ac2ec75e..118ada868 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ 8AF6FD7A225E3FA00056EF9E /* ResumeSessionOOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AF6FD79225E3FA00056EF9E /* ResumeSessionOOMScenario.m */; }; 8AF8FCAC22BD1E5400A967CA /* UnhandledInternalNotifyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF8FCAB22BD1E5400A967CA /* UnhandledInternalNotifyScenario.swift */; }; 8AF8FCAE22BD23BA00A967CA /* HandledInternalNotifyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF8FCAD22BD23BA00A967CA /* HandledInternalNotifyScenario.swift */; }; + 967F6F1229B2236A0054EED8 /* InternalWorkingsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967F6F1129B2236A0054EED8 /* InternalWorkingsScenario.swift */; }; A1117E552535A59100014FDA /* OOMLoadScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E542535A59100014FDA /* OOMLoadScenario.swift */; }; A1117E572535B22300014FDA /* OOMAutoDetectErrorsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E562535B22300014FDA /* OOMAutoDetectErrorsScenario.swift */; }; A1117E592535B29800014FDA /* OOMEnabledErrorTypesScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */; }; @@ -311,6 +312,7 @@ 8AF6FD79225E3FA00056EF9E /* ResumeSessionOOMScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ResumeSessionOOMScenario.m; sourceTree = ""; }; 8AF8FCAB22BD1E5400A967CA /* UnhandledInternalNotifyScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnhandledInternalNotifyScenario.swift; sourceTree = ""; }; 8AF8FCAD22BD23BA00A967CA /* HandledInternalNotifyScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandledInternalNotifyScenario.swift; sourceTree = ""; }; + 967F6F1129B2236A0054EED8 /* InternalWorkingsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalWorkingsScenario.swift; sourceTree = ""; }; A1117E542535A59100014FDA /* OOMLoadScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMLoadScenario.swift; sourceTree = ""; }; A1117E562535B22300014FDA /* OOMAutoDetectErrorsScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMAutoDetectErrorsScenario.swift; sourceTree = ""; }; A1117E582535B29800014FDA /* OOMEnabledErrorTypesScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OOMEnabledErrorTypesScenario.swift; sourceTree = ""; }; @@ -652,6 +654,7 @@ 010BAAF22833CE570003FF36 /* UserPersistencePersistUserClientScenario.m */, 010BAAF82833CE570003FF36 /* UserPersistencePersistUserScenario.m */, E700EE49247D1164008CFFB6 /* UserSessionOverrideScenario.swift */, + 967F6F1129B2236A0054EED8 /* InternalWorkingsScenario.swift */, ); name = scenarios; path = ../shared/scenarios; @@ -756,6 +759,7 @@ files = ( 01221E55282E5538008095C3 /* MaxPersistedSessionsScenario.m in Sources */, E700EE7B247D7A1F008CFFB6 /* SIGSEGVScenario.m in Sources */, + 967F6F1229B2236A0054EED8 /* InternalWorkingsScenario.swift in Sources */, 010BAB0D2833CE570003FF36 /* OOMWillTerminateScenario.m in Sources */, 010BAB252833D0070003FF36 /* AppHangDisabledScenario.swift in Sources */, E75040A02478019D005D33BD /* AutoDetectFalseHandledScenario.swift in Sources */, diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index 48008bdea..dc8b700aa 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -184,6 +184,7 @@ 01FA9EC626D64FFF0059FF4A /* AppHangInTerminationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA9EC526D64FFF0059FF4A /* AppHangInTerminationScenario.swift */; }; 8A096DF827C7E63A00DB6ECC /* CxxUnexpectedScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF727C7E63A00DB6ECC /* CxxUnexpectedScenario.mm */; }; 8A096DFA27C7E6D800DB6ECC /* CxxBareThrowScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF927C7E6D800DB6ECC /* CxxBareThrowScenario.mm */; }; + 967F6F1629B767CE0054EED8 /* InternalWorkingsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 967F6F1529B767CE0054EED8 /* InternalWorkingsScenario.swift */; }; AA4C7F1829AEA31D009B09A9 /* BugsnagWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */; }; AA6ACD1A2773E098006464C4 /* UserFromConfigScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD192773E098006464C4 /* UserFromConfigScenario.swift */; }; AA6ACD1E2773E39C006464C4 /* UserInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6ACD1D2773E39C006464C4 /* UserInfoScenario.swift */; }; @@ -391,6 +392,7 @@ 5C65BFC9838298CFA8A35072 /* Pods_macOSTestApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_macOSTestApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8A096DF727C7E63A00DB6ECC /* CxxUnexpectedScenario.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxUnexpectedScenario.mm; sourceTree = ""; }; 8A096DF927C7E6D800DB6ECC /* CxxBareThrowScenario.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxBareThrowScenario.mm; sourceTree = ""; }; + 967F6F1529B767CE0054EED8 /* InternalWorkingsScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InternalWorkingsScenario.swift; sourceTree = ""; }; AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BugsnagWrapper.swift; sourceTree = ""; }; AA6ACD192773E098006464C4 /* UserFromConfigScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserFromConfigScenario.swift; sourceTree = ""; }; AA6ACD1D2773E39C006464C4 /* UserInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoScenario.swift; sourceTree = ""; }; @@ -487,6 +489,7 @@ 010BAB4B2833D34A0003FF36 /* HandledErrorValidReleaseStageScenario.swift */, 01F47C28254B1B2C00B184AD /* HandledExceptionScenario.swift */, 01F47C55254B1B2E00B184AD /* HandledInternalNotifyScenario.swift */, + 967F6F1529B767CE0054EED8 /* InternalWorkingsScenario.swift */, 01847DCC26443DF000ADA4C7 /* InvalidCrashReportScenario.m */, 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenario.swift */, 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */, @@ -953,6 +956,7 @@ 017D9D002833C81100B0AA87 /* DisableSignalsExceptionScenario.m in Sources */, 01F47CF7254B1B3100B184AD /* OnSendErrorCallbackCrashScenario.swift in Sources */, 01F47CDF254B1B3100B184AD /* AbortScenario.m in Sources */, + 967F6F1629B767CE0054EED8 /* InternalWorkingsScenario.swift in Sources */, 010BAB672833D34A0003FF36 /* HandledErrorValidReleaseStageScenario.swift in Sources */, 01F47CD1254B1B3100B184AD /* ManualContextClientScenario.swift in Sources */, 010BAB732833D34A0003FF36 /* AppAndDeviceAttributesStartWithApiKeyScenario.swift in Sources */, diff --git a/features/fixtures/shared/scenarios/InternalWorkingsScenario.swift b/features/fixtures/shared/scenarios/InternalWorkingsScenario.swift new file mode 100644 index 000000000..e5d9e7847 --- /dev/null +++ b/features/fixtures/shared/scenarios/InternalWorkingsScenario.swift @@ -0,0 +1,36 @@ +// +// IsStartedScenario.swift +// iOSTestApp +// +// Created by Robert B on 03/03/2023. +// Copyright © 2023 Bugsnag. All rights reserved. +// + +import Foundation + +@objc class InternalWorkingsScenario: Scenario { + + override func startBugsnag() { + verifyBugsnagIsNotStarted() + super.startBugsnag() + } + + override func run() { + verifyBugsnagIsStarted() + reportStatusOk() + } + + private func verifyBugsnagIsStarted() { + assert(Bugsnag.isStarted(), "Bugsnag should be started") + } + + private func verifyBugsnagIsNotStarted() { + assert(!Bugsnag.isStarted(), "Bugsnag should not be started initially") + } + + private func reportStatusOk() { + Bugsnag.notify(NSException(name: NSExceptionName("InternalWorkingsScenario"), + reason: "All Clear!", + userInfo: nil)) + } +} diff --git a/features/internal_workings.feature b/features/internal_workings.feature new file mode 100644 index 000000000..8d2ba683b --- /dev/null +++ b/features/internal_workings.feature @@ -0,0 +1,10 @@ +Feature: Bugsnag's internal workings + + Background: + Given I clear all persistent data + + Scenario: Bugsnag library works as it should internally + When I run "InternalWorkingsScenario" + And I wait to receive a session + And I wait to receive an error + And the exception "message" equals "All Clear!" From db99e5e204b5504097d4d29ef38257aba32ca2d1 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 8 Mar 2023 12:11:04 +0100 Subject: [PATCH 37/37] Release v6.26.0 --- .jazzy.yaml | 4 ++-- Bugsnag.podspec.json | 4 ++-- Bugsnag/Payload/BugsnagNotifier.m | 2 +- BugsnagNetworkRequestPlugin.podspec.json | 6 +++--- CHANGELOG.md | 2 +- Framework/Info.plist | 2 +- Tests/BugsnagTests/Info.plist | 2 +- Tests/TestHost-iOS/Info.plist | 2 +- VERSION | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index 9f5fa22b2..a0f1294aa 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -2,11 +2,11 @@ author_url: "https://www.bugsnag.com" author: "Bugsnag Inc" clean: false # avoid deleting docs/.git framework_root: "Bugsnag" -github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.25.2/Bugsnag" +github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.26.0/Bugsnag" github_url: "https://github.com/bugsnag/bugsnag-cocoa" hide_documentation_coverage: true module: "Bugsnag" -module_version: "6.25.2" +module_version: "6.26.0" objc: true output: "docs" readme: "README.md" diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index b4acfe478..649de4f17 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.25.2", + "version": "6.26.0", "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.25.2" + "tag": "v6.26.0" }, "ios": { "frameworks": [ diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index dfe101c8a..bcbf16c50 100644 --- a/Bugsnag/Payload/BugsnagNotifier.m +++ b/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else _name = @"Bugsnag Objective-C"; #endif - _version = @"6.25.2"; + _version = @"6.26.0"; _url = @"https://github.com/bugsnag/bugsnag-cocoa"; _dependencies = @[]; } diff --git a/BugsnagNetworkRequestPlugin.podspec.json b/BugsnagNetworkRequestPlugin.podspec.json index 62323d3bd..d0e51f071 100644 --- a/BugsnagNetworkRequestPlugin.podspec.json +++ b/BugsnagNetworkRequestPlugin.podspec.json @@ -1,16 +1,16 @@ { "name": "BugsnagNetworkRequestPlugin", - "version": "6.25.2", + "version": "6.26.0", "summary": "Network request monitoring support for Bugsnag.", "homepage": "https://bugsnag.com", "license": "MIT", "authors": { "Bugsnag": "notifiers@bugsnag.com" }, - "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.25.2/BugsnagNetworkRequestPlugin/README.md", + "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.26.0/BugsnagNetworkRequestPlugin/README.md", "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.25.2" + "tag": "v6.26.0" }, "dependencies": { "Bugsnag": "~> 6.13" diff --git a/CHANGELOG.md b/CHANGELOG.md index 744b12c36..2d1005c0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Changelog ========= -## TBD +## 6.26.0 (2023-03-08) ### Enhancements diff --git a/Framework/Info.plist b/Framework/Info.plist index 4ba85157b..37a72f1b7 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.25.2 + 6.26.0 CFBundleVersion 1 diff --git a/Tests/BugsnagTests/Info.plist b/Tests/BugsnagTests/Info.plist index 3900ec36e..2558e2828 100644 --- a/Tests/BugsnagTests/Info.plist +++ b/Tests/BugsnagTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.25.2 + 6.26.0 CFBundleVersion 1 diff --git a/Tests/TestHost-iOS/Info.plist b/Tests/TestHost-iOS/Info.plist index 422122c22..98e58351d 100644 --- a/Tests/TestHost-iOS/Info.plist +++ b/Tests/TestHost-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.25.2 + 6.26.0 CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/VERSION b/VERSION index aec9b1056..4c6a35fb6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.25.2 +6.26.0