From 28d38a2e3197e4d7ee453f14aeb260a29a6f223b Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Thu, 13 Jan 2022 10:08:12 +0000 Subject: [PATCH 1/8] Use Maze Runner GET /command endpoint --- Gemfile | 2 +- Gemfile.lock | 19 +- .../ios/iOSTestApp/Base.lproj/Main.storyboard | 177 ++++++++++-------- .../ios/iOSTestApp/ViewController.swift | 52 +++-- .../macos/macOSTestApp/MainWindowController.m | 39 ++-- .../macOSTestApp/MainWindowController.xib | 52 +++-- features/fixtures/shared/scenarios/Scenario.h | 11 +- features/fixtures/shared/scenarios/Scenario.m | 141 +++++++------- features/steps/app_steps.rb | 26 +-- features/steps/cocoa_steps.rb | 36 ++-- 10 files changed, 279 insertions(+), 276 deletions(-) diff --git a/Gemfile b/Gemfile index 233eee75a..4fb37895e 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ gem 'xcpretty' # 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: 'v6.8.0' +gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v6.9.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 66943fc47..17e474a13 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/bugsnag/maze-runner - revision: fe12189f83aad154f54221ee0fcd41b483d3c0d1 - tag: v6.8.0 + revision: 4979ecccf0b3f21fa4f9cb439191081993ae5704 + tag: v6.9.0 specs: - bugsnag-maze-runner (6.8.0) + bugsnag-maze-runner (6.9.0) appium_lib (~> 11.2.0) bugsnag (~> 6.24) cucumber (~> 7.1) @@ -119,10 +119,9 @@ GEM cucumber-messages (~> 17.1, >= 17.1.0) cucumber-messages (17.1.1) cucumber-tag-expressions (4.1.0) - cucumber-wire (6.2.0) + cucumber-wire (6.2.1) cucumber-core (~> 10.1, >= 10.1.0) cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) curb (0.9.11) danger (8.4.2) claide (~> 1.0) @@ -137,7 +136,7 @@ GEM no_proxy_fix octokit (~> 4.7) terminal-table (>= 1, < 4) - diff-lcs (1.4.4) + diff-lcs (1.5.0) escape (0.0.4) ethon (0.15.0) ffi (>= 1.15.0) @@ -193,8 +192,8 @@ GEM liferaft (0.0.6) mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2021.1115) - mini_portile2 (2.6.1) + mime-types-data (3.2022.0105) + mini_portile2 (2.7.1) minitest (5.15.0) molinillo (0.8.0) multi_test (0.1.2) @@ -204,8 +203,8 @@ GEM nap (1.1.0) netrc (0.11.0) no_proxy_fix (0.1.2) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) + nokogiri (1.13.0) + mini_portile2 (~> 2.7.0) racc (~> 1.4) octokit (4.21.0) faraday (>= 0.9) diff --git a/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard b/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard index 646db4529..5e37b3c22 100644 --- a/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard +++ b/features/fixtures/ios/iOSTestApp/Base.lproj/Main.storyboard @@ -1,14 +1,14 @@ - + - + - + @@ -16,91 +16,86 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -109,6 +104,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 689e9068c..694f12841 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -15,8 +15,6 @@ class ViewController: UIViewController { @IBOutlet var scenarioMetaDataField : UITextField! @IBOutlet var apiKeyField: UITextField! - var scenario : Scenario? - override func viewDidLoad() { super.viewDidLoad() self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))) @@ -25,29 +23,31 @@ class ViewController: UIViewController { } @IBAction func runTestScenario() { - scenario = prepareScenario() + // Cater for multiple calls to run() + if Scenario.current == nil { + prepareScenario() + + log("Starting Bugsnag for scenario: \(Scenario.current!)") + Scenario.current!.startBugsnag() + } - log("Starting Bugsnag for scenario: \(String(describing: scenario))") - scenario?.startBugsnag() - log("Running scenario: \(String(describing: scenario))") - scenario?.run() + log("Running scenario: \(Scenario.current!)") + Scenario.current!.run() } @IBAction func startBugsnag() { - scenario = prepareScenario() - log("Starting Bugsnag for scenario: \(String(describing: scenario))") - scenario?.startBugsnag() + prepareScenario() + + log("Starting Bugsnag for scenario: \(Scenario.current!)") + Scenario.current!.startBugsnag() } @IBAction func clearPersistentData(_ sender: Any) { Scenario.clearPersistentData() } - internal func prepareScenario() -> Scenario { - let eventType : String! = scenarioNameField.text - let eventMode : String! = scenarioMetaDataField.text - - let config: BugsnagConfiguration + internal func prepareScenario() { + var config: BugsnagConfiguration? if (apiKeyField.text!.count > 0) { // Manual testing mode - use the real dashboard and the API key provided let apiKey = apiKeyField.text! @@ -55,24 +55,20 @@ class ViewController: UIViewController { UserDefaults.standard.setValue(apiKey, forKey: "apiKey") config = BugsnagConfiguration(apiKeyField.text!) } - else { - // Automation mode - config = BugsnagConfiguration("12312312312312312312312312312312") - config.endpoints = BugsnagEndpointConfiguration(notify: "http://bs-local.com:9339/notify", sessions: "http://bs-local.com:9339/sessions") - } - - let allowedErrorTypes = BugsnagErrorTypes() - allowedErrorTypes.ooms = false - config.enabledErrorTypes = allowedErrorTypes - let scenario = Scenario.createScenarioNamed(eventType, withConfig: config) - scenario.eventMode = eventMode - return scenario + Scenario.createScenarioNamed(scenarioNameField.text!, withConfig: config) + Scenario.current!.eventMode = scenarioMetaDataField.text } + @IBAction func executeCommand(_ sender: Any) { + Scenario.executeMazeRunnerCommand { _, scenarioName, eventMode in + self.scenarioNameField.text = scenarioName + self.scenarioMetaDataField.text = eventMode + } + } @objc func didEnterBackgroundNotification() { - scenario?.didEnterBackgroundNotification() + Scenario.current?.didEnterBackgroundNotification() } } diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index d048de430..acf60b13e 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -24,8 +24,6 @@ @interface MainWindowController () @property (copy) NSString *scenarioName; @property (copy) NSString *sessionEndpoint; -@property Scenario *scenario; - @end #pragma mark - @@ -48,39 +46,39 @@ - (BugsnagConfiguration *)configuration { if (self.sessionEndpoint) { configuration.endpoints.sessions = self.sessionEndpoint; } - configuration.enabledErrorTypes.ooms = NO; return configuration; } - (IBAction)runScenario:(id)sender { BSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - - if (!self.scenario) { - self.scenario = [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; - self.scenario.eventMode = self.scenarioMetadata; - - BSLog(@"Starting Bugsnag for scenario: %@", self.scenario); - [self.scenario startBugsnag]; + + // Cater for multiple calls to -run + if (!Scenario.currentScenario) { + [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; + Scenario.currentScenario.eventMode = self.scenarioMetadata; + + BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); + [Scenario.currentScenario startBugsnag]; } - BSLog(@"Will run scenario: %@", self.scenario); + BSLog(@"Will run scenario: %@", Scenario.currentScenario); // Using dispatch_async to prevent AppleEvents swallowing exceptions. // For more info see https://www.chimehq.com/blog/sad-state-of-exceptions // 0.1s delay allows accessibility APIs to finish handling the mouse click and returns control to the tests framework. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - BSLog(@"Running scenario: %@", self.scenario); - [self.scenario run]; + BSLog(@"Running scenario: %@", Scenario.currentScenario); + [Scenario.currentScenario run]; }); } - (IBAction)startBugsnag:(id)sender { BSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - self.scenario = [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; - self.scenario.eventMode = self.scenarioMetadata; + [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; + Scenario.currentScenario.eventMode = self.scenarioMetadata; - BSLog(@"Starting Bugsnag for scenario: %@", self.scenario); - [self.scenario startBugsnag]; + BSLog(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); + [Scenario.currentScenario startBugsnag]; } - (IBAction)clearPersistentData:(id)sender { @@ -92,6 +90,13 @@ - (IBAction)useDashboardEndpoints:(id)sender { self.sessionEndpoint = @"https://sessions.bugsnag.com"; } +- (IBAction)executeMazeRunnerCommand:(id)sender { + [Scenario executeMazeRunnerCommand:^(NSString *action, NSString *scenarioName, NSString *eventMode){ + self.scenarioName = scenarioName; + self.scenarioMetadata = eventMode; + }]; +} + @end diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.xib b/features/fixtures/macos/macOSTestApp/MainWindowController.xib index 1f5dddd89..4799024a7 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.xib +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -15,14 +15,14 @@ - - + + - + - + @@ -31,7 +31,7 @@ - + @@ -48,7 +48,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -122,7 +122,7 @@ - + @@ -139,7 +139,7 @@ - + @@ -148,7 +148,7 @@ - + @@ -165,7 +165,7 @@ - + @@ -186,7 +186,7 @@ - + @@ -202,12 +202,24 @@ + - + diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index d0647f4ef..31d75c373 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -19,14 +19,11 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; -+ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config; ++ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(nullable BugsnagConfiguration *)config; -- (instancetype)initWithConfig:(BugsnagConfiguration *)config; +@property (class, readonly, nullable) Scenario *currentScenario; -/** - * Blocks the calling thread until network connectivity to the notify endpoint has been verified. - */ -- (void)waitForNetworkConnectivity; +- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config; /** * Executes the test case @@ -43,6 +40,8 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); + (void)clearPersistentData; ++ (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; + @end NS_ASSUME_NONNULL_END diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index cc2326f7e..9d49702d4 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -2,10 +2,10 @@ // Created by Jamie Lynch on 23/03/2018. // Copyright (c) 2018 Bugsnag. All rights reserved. // -#import - #import "Scenario.h" +#import + extern void bsg_kscrash_setPrintTraceToStdout(bool printTraceToStdout); extern bool bsg_kslog_setLogFilename(const char *filename, bool overwrite); @@ -30,77 +30,34 @@ @implementation Scenario { dispatch_block_t _onEventDelivery; } -+ (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(BugsnagConfiguration *)config { - - NSString *fullClassName = [NSString stringWithFormat:@"%@Scenario", className]; - Class clz = NSClassFromString(fullClassName); - -#if TARGET_OS_IPHONE - NSString *swiftPrefix = @"iOSTestApp."; -#elif TARGET_OS_OSX - NSString *swiftPrefix = @"macOSTestApp."; -#endif - - if (!clz) { // Case-insensitive class lookup because AppiumForMac is a bit unreliable at entering uppercase characters. - unsigned int classCount = 0; - Class *classes = objc_copyClassList(&classCount); - for (unsigned int i = 0; i < classCount; i++) { - NSString *name = NSStringFromClass(classes[i]); - if ([name hasPrefix:swiftPrefix]) { - name = [name substringFromIndex:swiftPrefix.length]; - } - if ([name caseInsensitiveCompare:fullClassName] == NSOrderedSame) { - clz = classes[i]; - break; - } - } - free(classes); - } ++ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { + Class class = NSClassFromString(className) ?: + NSClassFromString([@"iOSTestApp." stringByAppendingString:className]) ?: + NSClassFromString([@"macOSTestApp." stringByAppendingString:className]); - if (!clz) { - [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", fullClassName]; + if (!class) { + [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; } - id obj = [clz alloc]; - - NSAssert([obj isKindOfClass:[Scenario class]], @"Class '%@' is not a subclass of Scenario", fullClassName); - - theScenario = obj; + return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); +} - return [(Scenario *)obj initWithConfig:config]; ++ (Scenario *)currentScenario { + return theScenario; } - (instancetype)initWithConfig:(BugsnagConfiguration *)config { if (self = [super init]) { - self.config = config; - } - return self; -} - -- (void)waitForNetworkConnectivity { - NSDictionary *proxySettings = (__bridge_transfer NSDictionary *)CFNetworkCopySystemProxySettings(); - NSLog(@"*** Proxy settings = %@", proxySettings); - - // This check uses HTTP rather than sockets because connectivity is commonly provided via an HTTP proxy. - - NSURL *url = [NSURL URLWithString:self.config.endpoints.notify]; - NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:3]; - NSLog(@"*** Checking for connectivity to %@", url); - while (1) { - NSURLResponse *response = nil; - NSError *error = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; -#pragma clang diagnostic pop - if ([response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode / 100 == 2) { - NSLog(@"*** Received response from notify endpoint."); - break; + if (config) { + _config = config; + } else { + _config = [[BugsnagConfiguration alloc] initWithApiKey:@"12312312312312312312312312312312"]; + _config.endpoints.notify = @"http://bs-local.com:9339/notify"; + _config.endpoints.sessions = @"http://bs-local.com:9339/sessions"; } - NSLog(@"*** No response from notify endpoint, retrying in 1 second..."); - [NSThread sleepForTimeInterval:1]; + _config.enabledErrorTypes.ooms = NO; } + return self; } - (void)run { @@ -109,8 +66,6 @@ - (void)run { } - (void)startBugsnag { - // TODO: PLAT-5827 - // [self waitForNetworkConnectivity]; // Disabled for now because MR v4 does not listen on / [Bugsnag startWithConfiguration:self.config]; bsg_kscrash_setPrintTraceToStdout(true); @@ -199,4 +154,60 @@ + (void)clearPersistentData { bsg_kslog_setLogFilename(ksLogPath, true); } ++ (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { + NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; + + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://bs-local.com:9339/command"]]; + [[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); + return; + } + NSLog(@"%s response body: %@", __PRETTY_FUNCTION__, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); + NSDictionary *command = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + + NSString *action = [command objectForKey:@"action"]; + NSParameterAssert([action isKindOfClass:[NSString class]]); + + NSString *scenarioName = [command objectForKey:@"scenario_name"]; + NSParameterAssert([scenarioName isKindOfClass:[NSString class]]); + + NSString *eventMode = [command objectForKey:@"scenario_mode"]; + if ([eventMode isKindOfClass:[NSNull class]]) { + eventMode = nil; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + preHandler(action, scenarioName, eventMode); + }); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if ([action isEqualToString:@"run_scenario"]) { + [self runScenario:scenarioName eventMode:eventMode]; + } else if ([action isEqualToString:@"start_bugsnag"]) { + [self startBugsnagForScenario:scenarioName eventMode:eventMode]; + } + }); + }] resume]; +} + ++ (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 { + NSLog(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); + + theScenario = [Scenario createScenarioNamed:scenarioName withConfig:nil]; + theScenario.eventMode = eventMode; + + NSLog(@"Starting scenario \"%@\"", NSStringFromClass([theScenario class])); + [theScenario startBugsnag]; +} + @end diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index f439e707f..04e383ec3 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -55,31 +55,9 @@ end # -# Setting scenario and mode +# Setting scenario mode # -When('I set the app to {string} scenario') do |scenario| - case Maze::Helper.get_current_platform - when 'macos' - mac_set_value('scenarioName', scenario) - else - steps %(When I send the keys "#{scenario}" to the element "scenario_name") - end -end - When('I set the app to {string} mode') do |mode| - case Maze::Helper.get_current_platform - when 'macos' - mac_set_value('scenarioMetadata', mode) - else - steps %(When I send the keys "#{mode}" to the element "scenario_metadata") - end -end - -def mac_set_value(key, value) - # Using find_element to ensure app is ready for input - Maze.driver.find_element(:id, 'scenario_name') - # Using 'open location' because it is one of the few AppleScript commands that does not require privacy approval - location = "macOSTestApp:///mainWindowController?#{key}=#{value}" - system("osascript -e 'tell application \"macOSTestApp\" to open location \"#{location}\"'", exception: true) + $scenario_mode = mode end diff --git a/features/steps/cocoa_steps.rb b/features/steps/cocoa_steps.rb index bb054cde5..fbed395ea 100644 --- a/features/steps/cocoa_steps.rb +++ b/features/steps/cocoa_steps.rb @@ -1,17 +1,5 @@ -def short_scenario_name(scenario) - # A "Scenario" suffix is removed by the test harness and re-added uniformly by - # the test fixture. This reduces the time that Appium spends entering text. - unless scenario.end_with? 'Scenario' - raise 'All scenario names must end with "Scenario".' - end - scenario.delete_suffix 'Scenario' -end - -When('I run {string}') do |event_type| - steps %( - When I set the app to "#{short_scenario_name event_type}" scenario - And I click the element "run_scenario" - ) +When('I run {string}') do |scenario_name| + execute_command :run_scenario, scenario_name end When("I run {string} and relaunch the crashed app") do |event_type| @@ -57,11 +45,8 @@ def click_if_present(element) false end -When('I configure Bugsnag for {string}') do |event_type| - steps %( - When I set the app to "#{short_scenario_name event_type}" scenario - And I click the element "start_bugsnag" - ) +When('I configure Bugsnag for {string}') do |scenario_name| + execute_command :start_bugsnag, scenario_name end When('I clear the error queue') do @@ -274,8 +259,13 @@ def wait_for_true raise 'Assertion not passed in 30s' unless assertion_passed end -def send_keys_to_element(element_id, text) - element = find_element(@element_locator, element_id) - element.clear() - element.set_value(text) +def execute_command(action, scenario_name) + command = { action: action, scenario_name: scenario_name, scenario_mode: $scenario_mode } + Maze::Server.commands.add command + Maze.driver.click_element :execute_command + $scenario_mode = nil + # Ensure fixture has read the command + count = 100 + sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 + raise 'Test fixture did not GET /command' unless Maze::Server.commands.remaining.empty? end From 2d5af6f8de185ab33a46abdb5640bfaebbb4f0de Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Thu, 13 Jan 2022 10:09:00 +0000 Subject: [PATCH 2/8] Rename LastRunInfoScenario --- .../fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj | 8 ++++---- .../macos/macOSTestApp.xcodeproj/project.pbxproj | 8 ++++---- ...unInfoScenarios.swift => LastRunInfoScenario.swift} | 4 ++-- features/last_run_info.feature | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) rename features/fixtures/shared/scenarios/{LastRunInfoScenarios.swift => LastRunInfoScenario.swift} (90%) diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 63d56a774..268a9f652 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ 017B4134276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 017B4133276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m */; }; 01847DD626453D4E00ADA4C7 /* InvalidCrashReportScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DD526453D4E00ADA4C7 /* InvalidCrashReportScenario.m */; }; 01AF6A53258A112F00FFC803 /* BareboneTestScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */; }; - 01B6BB7525D5748800FC4DE6 /* LastRunInfoScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenarios.swift */; }; + 01B6BB7525D5748800FC4DE6 /* LastRunInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenario.swift */; }; 01B6BBB625DA82B800FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; }; 01CD103926690463007FA5F0 /* libBugsnagStatic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01CD103826690463007FA5F0 /* libBugsnagStatic.a */; }; 01DE903826CE99B800455213 /* CriticalThermalStateScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DE903726CE99B800455213 /* CriticalThermalStateScenario.swift */; }; @@ -183,7 +183,7 @@ 017B4133276B8D9B0054C91D /* OnSendErrorPersistenceScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnSendErrorPersistenceScenario.m; sourceTree = ""; }; 01847DD526453D4E00ADA4C7 /* InvalidCrashReportScenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InvalidCrashReportScenario.m; sourceTree = ""; }; 01AF6A52258A112F00FFC803 /* BareboneTestScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BareboneTestScenarios.swift; sourceTree = ""; }; - 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenarios.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LastRunInfoScenarios.swift; sourceTree = ""; }; + 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LastRunInfoScenario.swift; sourceTree = ""; }; 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendLaunchCrashesSynchronouslyScenario.swift; sourceTree = ""; }; 01CD103826690463007FA5F0 /* libBugsnagStatic.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libBugsnagStatic.a; sourceTree = BUILT_PRODUCTS_DIR; }; 01DE903726CE99B800455213 /* CriticalThermalStateScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CriticalThermalStateScenario.swift; sourceTree = ""; }; @@ -604,7 +604,7 @@ 8AB1081823301FE600672818 /* ReleaseStageScenarios.swift */, F4295ABA693D273D52AA9F6B /* Scenario.h */, F42954E8B66F3FB7F5333CF7 /* Scenario.m */, - 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenarios.swift */, + 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenario.swift */, CBB7878F2578FC0C0071BDE4 /* MarkUnhandledHandledScenario.h */, CBB787902578FC0C0071BDE4 /* MarkUnhandledHandledScenario.m */, 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */, @@ -881,7 +881,7 @@ 01018BA025E40ADD000312C6 /* AsyncSafeMallocScenario.m in Sources */, 8A98400320FD11BF0023ECD1 /* AutoSessionCustomVersionScenario.m in Sources */, 8A3B5F292407F66700CE4A3A /* ModifyBreadcrumbScenario.swift in Sources */, - 01B6BB7525D5748800FC4DE6 /* LastRunInfoScenarios.swift in Sources */, + 01B6BB7525D5748800FC4DE6 /* LastRunInfoScenario.swift in Sources */, E75040A2247801A8005D33BD /* AutoDetectFalseNSExceptionScenario.swift in Sources */, E700EE7E247D7A61008CFFB6 /* SIGSYSScenario.m in Sources */, E75040A42478052D005D33BD /* AutoDetectFalseAbortScenario.swift in Sources */, diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index ce5e1b561..20ac62fe3 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -21,7 +21,7 @@ 01847DCD26443DF000ADA4C7 /* InvalidCrashReportScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01847DCC26443DF000ADA4C7 /* InvalidCrashReportScenario.m */; }; 01AF6A50258A00DE00FFC803 /* BareboneTestScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AF6A4F258A00DE00FFC803 /* BareboneTestScenarios.swift */; }; 01AF6A84258BB38A00FFC803 /* DispatchCrashScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01AF6A83258BB38A00FFC803 /* DispatchCrashScenario.swift */; }; - 01B6BB7225D56CBF00FC4DE6 /* LastRunInfoScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenarios.swift */; }; + 01B6BB7225D56CBF00FC4DE6 /* LastRunInfoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenario.swift */; }; 01B6BBA225DA774C00FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBA125DA774C00FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; }; 01DE903A26CEAD1200455213 /* CriticalThermalStateScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DE903926CEAD1200455213 /* CriticalThermalStateScenario.swift */; }; 01E0DB0625E8E95700A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */; }; @@ -177,7 +177,7 @@ 01847DCC26443DF000ADA4C7 /* InvalidCrashReportScenario.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InvalidCrashReportScenario.m; sourceTree = ""; }; 01AF6A4F258A00DE00FFC803 /* BareboneTestScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BareboneTestScenarios.swift; sourceTree = ""; }; 01AF6A83258BB38A00FFC803 /* DispatchCrashScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DispatchCrashScenario.swift; sourceTree = ""; }; - 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastRunInfoScenarios.swift; sourceTree = ""; }; + 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastRunInfoScenario.swift; sourceTree = ""; }; 01B6BBA125DA774C00FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendLaunchCrashesSynchronouslyScenario.swift; sourceTree = ""; }; 01DE903926CEAD1200455213 /* CriticalThermalStateScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CriticalThermalStateScenario.swift; sourceTree = ""; }; 01E0DB0425E8E90500A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = ""; }; @@ -436,7 +436,7 @@ 01F47C28254B1B2C00B184AD /* HandledExceptionScenario.swift */, 01F47C55254B1B2E00B184AD /* HandledInternalNotifyScenario.swift */, 01847DCC26443DF000ADA4C7 /* InvalidCrashReportScenario.m */, - 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenarios.swift */, + 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenario.swift */, 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */, 01F47C94254B1B2F00B184AD /* LoadConfigFromFileScenario.swift */, 01F47C3B254B1B2D00B184AD /* ManualContextClientScenario.swift */, @@ -853,7 +853,7 @@ 01F47CF4254B1B3100B184AD /* OOMSessionScenario.swift in Sources */, 01F47D21254B1B3100B184AD /* SIGFPEScenario.m in Sources */, 01F47CEB254B1B3100B184AD /* AutoSessionHandledEventsScenario.m in Sources */, - 01B6BB7225D56CBF00FC4DE6 /* LastRunInfoScenarios.swift in Sources */, + 01B6BB7225D56CBF00FC4DE6 /* LastRunInfoScenario.swift in Sources */, 01F47CF7254B1B3100B184AD /* OnSendErrorCallbackCrashScenario.swift in Sources */, 01F47CDF254B1B3100B184AD /* AbortScenario.m in Sources */, 01F47CD1254B1B3100B184AD /* ManualContextClientScenario.swift in Sources */, diff --git a/features/fixtures/shared/scenarios/LastRunInfoScenarios.swift b/features/fixtures/shared/scenarios/LastRunInfoScenario.swift similarity index 90% rename from features/fixtures/shared/scenarios/LastRunInfoScenarios.swift rename to features/fixtures/shared/scenarios/LastRunInfoScenario.swift index 0b33429fa..c511cca43 100644 --- a/features/fixtures/shared/scenarios/LastRunInfoScenarios.swift +++ b/features/fixtures/shared/scenarios/LastRunInfoScenario.swift @@ -1,12 +1,12 @@ // -// LastRunInfoScenarios.swift +// LastRunInfoScenario.swift // macOSTestApp // // Created by Nick Dowell on 11/02/2021. // Copyright © 2021 Bugsnag Inc. All rights reserved. // -class LastRunInfoConsecutiveLaunchCrashesScenario: Scenario { +class LastRunInfoScenario: Scenario { override func startBugsnag() { config.launchDurationMillis = 0 diff --git a/features/last_run_info.feature b/features/last_run_info.feature index 05791693f..2b63e6d27 100644 --- a/features/last_run_info.feature +++ b/features/last_run_info.feature @@ -4,8 +4,8 @@ Feature: Launch detection Given I clear all persistent data Scenario: LastRunInfo consecutiveLaunchCrashes increments when isLaunching is true - When I run "LastRunInfoConsecutiveLaunchCrashesScenario" and relaunch the crashed app - And I configure Bugsnag for "LastRunInfoConsecutiveLaunchCrashesScenario" + When I run "LastRunInfoScenario" and relaunch the crashed app + And I configure Bugsnag for "LastRunInfoScenario" And I wait to receive an error And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 1 And the event "metaData.lastRunInfo.crashed" is true @@ -13,19 +13,19 @@ Feature: Launch detection And I discard the oldest error And I run the configured scenario and relaunch the crashed app - And I configure Bugsnag for "LastRunInfoConsecutiveLaunchCrashesScenario" + And I configure Bugsnag for "LastRunInfoScenario" And I wait to receive an error And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 2 And I discard the oldest error And I run the configured scenario and relaunch the crashed app - And I configure Bugsnag for "LastRunInfoConsecutiveLaunchCrashesScenario" + And I configure Bugsnag for "LastRunInfoScenario" And I wait to receive an error And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 3 And I discard the oldest error And I run the configured scenario and relaunch the crashed app - And I configure Bugsnag for "LastRunInfoConsecutiveLaunchCrashesScenario" + And I configure Bugsnag for "LastRunInfoScenario" And I wait to receive an error And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 0 And the event "metaData.lastRunInfo.crashed" is true From 93f1de88f179bfe65e0db62ad926c42760cf7cc3 Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Wed, 12 Jan 2022 15:28:18 +0000 Subject: [PATCH 3/8] KSCrashReport buffered output --- Bugsnag.xcodeproj/project.pbxproj | 26 ++++++++ ...B9A70F85-2A05-489B-A5BA-1129C478B0A1.plist | 2 +- ...D90C8D39-16DA-4CCB-8283-66F4A3B10BA2.plist | 2 +- ...7E1A1286-DD8C-4E45-9E88-4335CD976176.plist | 2 +- .../KSCrash/Recording/BSG_KSCrashReport.c | 22 +++++-- .../Source/KSCrash/Recording/BSG_KSFile.c | 59 +++++++++++++++++ .../Source/KSCrash/Recording/BSG_KSFile.h | 25 ++++++++ CHANGELOG.md | 7 ++ Tests/KSCrashTests/BSG_KSCrashReportTests.m | 32 +++++----- Tests/KSCrashTests/BSG_KSFileTests.m | 64 +++++++++++++++++++ 10 files changed, 217 insertions(+), 24 deletions(-) create mode 100644 Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.c create mode 100644 Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.h create mode 100644 Tests/KSCrashTests/BSG_KSFileTests.m diff --git a/Bugsnag.xcodeproj/project.pbxproj b/Bugsnag.xcodeproj/project.pbxproj index cb11b3ac4..b569d5367 100644 --- a/Bugsnag.xcodeproj/project.pbxproj +++ b/Bugsnag.xcodeproj/project.pbxproj @@ -696,6 +696,9 @@ 01B14C57251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B14C58251CE55F00118748 /* report-react-native-promise-rejection.json in Resources */ = {isa = PBXBuildFile; fileRef = 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */; }; 01B6BB7E25D5777F00FC4DE6 /* BugsnagSwiftPublicAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008966B02486D43500DC48C2 /* BugsnagSwiftPublicAPITests.swift */; }; + 01B74E9C27903326004B9765 /* BSG_KSFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B74E9B27903326004B9765 /* BSG_KSFileTests.m */; }; + 01B74E9D27903326004B9765 /* BSG_KSFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B74E9B27903326004B9765 /* BSG_KSFileTests.m */; }; + 01B74E9E27903326004B9765 /* BSG_KSFileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B74E9B27903326004B9765 /* BSG_KSFileTests.m */; }; 01B79DA9267CC4A000C8CC5E /* BSGUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGUtils.h */; }; 01B79DAA267CC4A000C8CC5E /* BSGUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGUtils.h */; }; 01B79DAB267CC4A000C8CC5E /* BSGUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 01B79DA7267CC4A000C8CC5E /* BSGUtils.h */; }; @@ -712,6 +715,13 @@ 01C17AE72542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; 01C17AE82542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; 01C17AE92542ED7F00C102C9 /* KSCrashReportWriterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */; }; + 01CB95BF278F0C830077744A /* BSG_KSFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CB95BD278F0C830077744A /* BSG_KSFile.h */; }; + 01CB95C0278F0C830077744A /* BSG_KSFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CB95BD278F0C830077744A /* BSG_KSFile.h */; }; + 01CB95C1278F0C830077744A /* BSG_KSFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CB95BD278F0C830077744A /* BSG_KSFile.h */; }; + 01CB95C2278F0C830077744A /* BSG_KSFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 01CB95BE278F0C830077744A /* BSG_KSFile.c */; }; + 01CB95C3278F0C830077744A /* BSG_KSFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 01CB95BE278F0C830077744A /* BSG_KSFile.c */; }; + 01CB95C4278F0C830077744A /* BSG_KSFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 01CB95BE278F0C830077744A /* BSG_KSFile.c */; }; + 01CB95C5278F0C830077744A /* BSG_KSFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 01CB95BE278F0C830077744A /* BSG_KSFile.c */; }; 01CCAEEA25D414D60057268D /* BugsnagLastRunInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CCAEE825D414D60057268D /* BugsnagLastRunInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 01CCAEEB25D414D60057268D /* BugsnagLastRunInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CCAEE825D414D60057268D /* BugsnagLastRunInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 01CCAEEC25D414D60057268D /* BugsnagLastRunInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 01CCAEE825D414D60057268D /* BugsnagLastRunInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1378,6 +1388,7 @@ 01A617842733D15C00024A0B /* BugsnagNetworkRequestPlugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BugsnagNetworkRequestPlugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 01ABD8782786DF9A009A5CA2 /* BSG_KSCrashReportTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSG_KSCrashReportTests.m; sourceTree = ""; }; 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "report-react-native-promise-rejection.json"; sourceTree = ""; }; + 01B74E9B27903326004B9765 /* BSG_KSFileTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSG_KSFileTests.m; sourceTree = ""; }; 01B79DA7267CC4A000C8CC5E /* BSGUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGUtils.h; sourceTree = ""; }; 01B79DA8267CC4A000C8CC5E /* BSGUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGUtils.m; sourceTree = ""; }; 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadKSCrashReportOperationTests.m; sourceTree = ""; }; @@ -1385,6 +1396,8 @@ 01C17AE62542ED7F00C102C9 /* KSCrashReportWriterTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSCrashReportWriterTests.m; sourceTree = ""; }; 01C2769B2601F44D006901EA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 01C2769C2601F455006901EA /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = ""; }; + 01CB95BD278F0C830077744A /* BSG_KSFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSG_KSFile.h; sourceTree = ""; }; + 01CB95BE278F0C830077744A /* BSG_KSFile.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = BSG_KSFile.c; sourceTree = ""; }; 01CCAEE825D414D60057268D /* BugsnagLastRunInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagLastRunInfo.h; sourceTree = ""; }; 01CCAEE925D414D60057268D /* BugsnagLastRunInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagLastRunInfo.m; sourceTree = ""; }; 01CCAEFF25D4151C0057268D /* BugsnagLastRunInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagLastRunInfo+Private.h"; sourceTree = ""; }; @@ -1535,6 +1548,7 @@ isa = PBXGroup; children = ( 01ABD8782786DF9A009A5CA2 /* BSG_KSCrashReportTests.m */, + 01B74E9B27903326004B9765 /* BSG_KSFileTests.m */, 008966D82486D43700DC48C2 /* BSG_KSMachHeadersTests.m */, 008966EA2486D43700DC48C2 /* BSG_KSMachTests.m */, 008966D62486D43700DC48C2 /* FileBasedTestCase.h */, @@ -1628,6 +1642,8 @@ 008969292486DAD000DC48C2 /* BSG_KSCrashState.m */, 0089692B2486DAD000DC48C2 /* BSG_KSCrashType.c */, 008969482486DAD000DC48C2 /* BSG_KSCrashType.h */, + 01CB95BE278F0C830077744A /* BSG_KSFile.c */, + 01CB95BD278F0C830077744A /* BSG_KSFile.h */, 0089692F2486DAD000DC48C2 /* BSG_KSSystemInfo.h */, 0089694C2486DAD000DC48C2 /* BSG_KSSystemInfo.m */, 008969312486DAD000DC48C2 /* BSG_KSSystemInfoC.h */, @@ -2146,6 +2162,7 @@ 010FF28425ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008967F42486DA4500DC48C2 /* BSGSessionUploader.h in Headers */, 0126DF1B257A92860031A70C /* BugsnagSession+Private.h in Headers */, + 01CB95BF278F0C830077744A /* BSG_KSFile.h in Headers */, 008969E12486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, 00896A0E2486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, 008969D22486DAD100DC48C2 /* BSG_KSCrashContext.h in Headers */, @@ -2252,6 +2269,7 @@ 008967F52486DA4500DC48C2 /* BSGSessionUploader.h in Headers */, 010FF28525ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E22486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, + 01CB95C0278F0C830077744A /* BSG_KSFile.h in Headers */, 0126DF1C257A92860031A70C /* BugsnagSession+Private.h in Headers */, 00896A0F2486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, 008969D32486DAD100DC48C2 /* BSG_KSCrashContext.h in Headers */, @@ -2358,6 +2376,7 @@ 008967F62486DA4500DC48C2 /* BSGSessionUploader.h in Headers */, 010FF28625ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E32486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, + 01CB95C1278F0C830077744A /* BSG_KSFile.h in Headers */, 0126DF1D257A92860031A70C /* BugsnagSession+Private.h in Headers */, 00896A102486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, 008969D42486DAD100DC48C2 /* BSG_KSCrashContext.h in Headers */, @@ -2741,6 +2760,7 @@ 01840B7225DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968C32486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBAB4DD82510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, + 01CB95C2278F0C830077744A /* BSG_KSFile.c in Sources */, 008968A72486DA9600DC48C2 /* BugsnagSession.m in Sources */, 0089683A2486DA6C00DC48C2 /* BugsnagMetadata.m in Sources */, 013D9CD426C5262F0077F0AD /* UISceneStub.m in Sources */, @@ -2844,6 +2864,7 @@ 008967752486D43700DC48C2 /* XCTestCase+KSCrash.m in Sources */, CB9103642502320A00E9D1E2 /* BugsnagApiClientTest.m in Sources */, 008967602486D43700DC48C2 /* BugsnagBreadcrumbsTest.m in Sources */, + 01B74E9C27903326004B9765 /* BSG_KSFileTests.m in Sources */, CB10E53F250BA8DE00AF5824 /* BugsnagKVStoreTest.m in Sources */, CB6419AB25A73E8C00613D25 /* BSGStorageMigratorTests.m in Sources */, E701FAA72490EF77008D842F /* ClientApiValidationTest.m in Sources */, @@ -2898,6 +2919,7 @@ 008968BA2486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, 00896A152486DAD100DC48C2 /* BSG_KSCrashSentry_Signal.c in Sources */, 01468F5625876DC1002B0519 /* BSGNotificationBreadcrumbs.m in Sources */, + 01CB95C3278F0C830077744A /* BSG_KSFile.c in Sources */, 008967BF2486DA1900DC48C2 /* BugsnagClient.m in Sources */, 008968962486DA9600DC48C2 /* BugsnagHandledState.m in Sources */, 008969C42486DAD100DC48C2 /* BSG_KSObjC.c in Sources */, @@ -3009,6 +3031,7 @@ 008967A02486D43700DC48C2 /* KSCrashSentry_Tests.m in Sources */, 008967432486D43700DC48C2 /* BugsnagSessionTrackerStopTest.m in Sources */, 008967972486D43700DC48C2 /* KSCrashState_Tests.m in Sources */, + 01B74E9D27903326004B9765 /* BSG_KSFileTests.m in Sources */, 008967762486D43700DC48C2 /* XCTestCase+KSCrash.m in Sources */, 01E8765F256684E700F4B70A /* URLSessionMock.m in Sources */, 008967312486D43700DC48C2 /* BSGClientObserverTests.m in Sources */, @@ -3088,6 +3111,7 @@ 01840B7425DC26E200F95648 /* BSGEventUploader.m in Sources */, 008968C52486DA9600DC48C2 /* BugsnagUser.m in Sources */, CBAB4DDA2510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, + 01CB95C4278F0C830077744A /* BSG_KSFile.c in Sources */, 008968A92486DA9600DC48C2 /* BugsnagSession.m in Sources */, 0089683C2486DA6C00DC48C2 /* BugsnagMetadata.m in Sources */, 013D9CD626C5262F0077F0AD /* UISceneStub.m in Sources */, @@ -3182,6 +3206,7 @@ 008967322486D43700DC48C2 /* BSGClientObserverTests.m in Sources */, CBA2249D251E429C00B87416 /* TestSupport.m in Sources */, 01847DAE26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m in Sources */, + 01B74E9E27903326004B9765 /* BSG_KSFileTests.m in Sources */, 004E35372487AFF2007FBAE4 /* BugsnagHandledStateTest.m in Sources */, 016875C8258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */, 01935AE3275E68E9007498B3 /* UIApplicationStub.m in Sources */, @@ -3261,6 +3286,7 @@ 008967DD2486DA2D00DC48C2 /* BugsnagConfiguration.m in Sources */, 008968E42486DAA700DC48C2 /* BugsnagPluginClient.m in Sources */, 01840B7525DC26E200F95648 /* BSGEventUploader.m in Sources */, + 01CB95C5278F0C830077744A /* BSG_KSFile.c in Sources */, 008968BC2486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, 008968242486DA5600DC48C2 /* BugsnagKeys.m in Sources */, 008967B72486D9D800DC48C2 /* BugsnagBreadcrumbs.m in Sources */, diff --git a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/B9A70F85-2A05-489B-A5BA-1129C478B0A1.plist b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/B9A70F85-2A05-489B-A5BA-1129C478B0A1.plist index e066634d3..b7e07688b 100644 --- a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/B9A70F85-2A05-489B-A5BA-1129C478B0A1.plist +++ b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/B9A70F85-2A05-489B-A5BA-1129C478B0A1.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.065700 + 0.034600 baselineIntegrationDisplayName Local Baseline diff --git a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/D90C8D39-16DA-4CCB-8283-66F4A3B10BA2.plist b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/D90C8D39-16DA-4CCB-8283-66F4A3B10BA2.plist index 6406c2649..8f62ee943 100644 --- a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/D90C8D39-16DA-4CCB-8283-66F4A3B10BA2.plist +++ b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1C7A24869B0E00A27979.xcbaseline/D90C8D39-16DA-4CCB-8283-66F4A3B10BA2.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.468000 + 0.195000 baselineIntegrationDisplayName Local Baseline diff --git a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist index abeac13f9..ee9597963 100644 --- a/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist +++ b/Bugsnag.xcodeproj/xcshareddata/xcbaselines/00AD1CB424869C1200A27979.xcbaseline/7E1A1286-DD8C-4E45-9E88-4335CD976176.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 0.091900 + 0.042200 baselineIntegrationDisplayName Local Baseline diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index fa8b1d60e..092e3f56e 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -29,6 +29,7 @@ #include "BSG_KSBacktrace_Private.h" #include "BSG_KSCrashReportFields.h" #include "BSG_KSCrashReportVersion.h" +#include "BSG_KSFile.h" #include "BSG_KSFileUtils.h" #include "BSG_KSJSONCodec.h" #include "BSG_KSMach.h" @@ -308,8 +309,7 @@ void bsg_kscrw_i_endContainer(const BSG_KSCrashReportWriter *const writer) { int bsg_kscrw_i_addJSONData(const char *const data, const size_t length, void *const userData) { - const int fd = *((int *)userData); - const bool success = bsg_ksfuwriteBytesToFD(fd, data, (ssize_t)length); + bool success = BSG_KSFileWrite(userData, data, length); return success ? BSG_KSJSON_OK : BSG_KSJSON_ERROR_CANNOT_ADD_DATA; } @@ -1502,14 +1502,18 @@ void bsg_kscrashreport_writeMinimalReport( bsg_kscrw_i_updateStackOverflowStatus(crashContext); + BSG_KSFile file; + char buffer[512]; + BSG_KSFileInit(&file, fd, buffer, sizeof(buffer) / sizeof(*buffer)); + BSG_KSJSONEncodeContext jsonContext; - jsonContext.userData = &fd; + jsonContext.userData = &file; BSG_KSCrashReportWriter concreteWriter; BSG_KSCrashReportWriter *writer = &concreteWriter; bsg_kscrw_i_prepareReportWriter(writer, &jsonContext); bsg_ksjsonbeginEncode(bsg_getJsonContext(writer), false, - bsg_kscrw_i_addJSONData, &fd); + bsg_kscrw_i_addJSONData, &file); writer->beginObject(writer, BSG_KSCrashField_Report); { @@ -1541,6 +1545,7 @@ void bsg_kscrashreport_writeMinimalReport( bsg_ksjsonendEncode(bsg_getJsonContext(writer)); + BSG_KSFileFlush(&file); close(fd); } @@ -1557,14 +1562,18 @@ void bsg_kscrashreport_writeStandardReport( bsg_kscrw_i_updateStackOverflowStatus(crashContext); + BSG_KSFile file; + char buffer[4096]; + BSG_KSFileInit(&file, fd, buffer, sizeof(buffer) / sizeof(*buffer)); + BSG_KSJSONEncodeContext jsonContext; - jsonContext.userData = &fd; + jsonContext.userData = &file; BSG_KSCrashReportWriter concreteWriter; BSG_KSCrashReportWriter *writer = &concreteWriter; bsg_kscrw_i_prepareReportWriter(writer, &jsonContext); bsg_ksjsonbeginEncode(bsg_getJsonContext(writer), false, - bsg_kscrw_i_addJSONData, &fd); + bsg_kscrw_i_addJSONData, &file); writer->beginObject(writer, BSG_KSCrashField_Report); { @@ -1588,6 +1597,7 @@ void bsg_kscrashreport_writeStandardReport( bsg_ksjsonendEncode(bsg_getJsonContext(writer)); + BSG_KSFileFlush(&file); close(fd); } diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.c new file mode 100644 index 000000000..353fc201e --- /dev/null +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.c @@ -0,0 +1,59 @@ +// +// BSG_KSFile.c +// Bugsnag +// +// Created by Nick Dowell on 12/01/2022. +// Copyright © 2022 Bugsnag Inc. All rights reserved. +// + +#include "BSG_KSFile.h" + +#include "BSG_KSFileUtils.h" + +#include +#include + +static inline bool bsg_write(const int fd, const char *bytes, size_t length) { + return bsg_ksfuwriteBytesToFD(fd, bytes, (ssize_t)length); +} + +void BSG_KSFileInit(BSG_KSFile *file, int fd, char *buffer, size_t length) { + file->fd = fd; + file->buffer = buffer; + file->bufferSize = length; + file->bufferUsed = 0; +} + +bool BSG_KSFileWrite(BSG_KSFile *file, const char *data, size_t length) { + const size_t bytesCopied = MIN(file->bufferSize - file->bufferUsed, length); + memcpy(file->buffer + file->bufferUsed, data, bytesCopied); + file->bufferUsed += bytesCopied; + data += bytesCopied; + length -= bytesCopied; + + if (file->bufferUsed == file->bufferSize) { + if (!BSG_KSFileFlush(file)) { + return false; + } + } + + if (!length) { + return true; + } + + if (length >= file->bufferSize) { + return bsg_write(file->fd, data, length); + } + + memcpy(file->buffer, data, length); + file->bufferUsed = length; + return true; +} + +bool BSG_KSFileFlush(BSG_KSFile *file) { + if (!bsg_write(file->fd, file->buffer, file->bufferUsed)) { + return false; + } + file->bufferUsed = 0; + return true; +} diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.h new file mode 100644 index 000000000..96f0306fa --- /dev/null +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSFile.h @@ -0,0 +1,25 @@ +// +// BSG_KSFile.h +// Bugsnag +// +// Created by Nick Dowell on 12/01/2022. +// Copyright © 2022 Bugsnag Inc. All rights reserved. +// + +#pragma once + +#include +#include + +typedef struct { + int fd; + char *buffer; + size_t bufferSize; + size_t bufferUsed; +} BSG_KSFile; + +void BSG_KSFileInit(BSG_KSFile *file, int fd, char *buffer, size_t length); + +bool BSG_KSFileWrite(BSG_KSFile *file, const char *data, size_t length); + +bool BSG_KSFileFlush(BSG_KSFile *file); diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b65c155..4592612e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Bug fixes + +* Improve crash report writing performance with buffered output. + [#1281](https://github.com/bugsnag/bugsnag-cocoa/pull/1281) + ## 6.16.0 (2022-01-12) ### Enhancements diff --git a/Tests/KSCrashTests/BSG_KSCrashReportTests.m b/Tests/KSCrashTests/BSG_KSCrashReportTests.m index ebe9887f3..d24a466c8 100644 --- a/Tests/KSCrashTests/BSG_KSCrashReportTests.m +++ b/Tests/KSCrashTests/BSG_KSCrashReportTests.m @@ -22,10 +22,9 @@ @interface BSG_KSCrashReportTests : XCTestCase @implementation BSG_KSCrashReportTests - (void)testBinaryImages { - NSString *directory = NSTemporaryDirectory(); - NSString *crashReportFilePath = [directory stringByAppendingPathComponent:@"crash_report"]; - NSString *recrashReportFilePath = [directory stringByAppendingPathComponent:@"recrash_report"]; - NSString *stateFilePath = [directory stringByAppendingPathComponent:@"kscrash_state"]; + NSString *crashReportFilePath = [self temporaryFile:@"crash_report.json"]; + NSString *recrashReportFilePath = [self temporaryFile:@"recrash_report"]; + NSString *stateFilePath = [self temporaryFile:@"kscrash_state"]; NSString *crashID = [[NSUUID UUID] UUIDString]; bsg_kscrash_init(); @@ -62,17 +61,12 @@ - (void)testBinaryImages { [backtraceImageAddrs removeObject:[NSNull null]]; XCTAssertEqualObjects(binaryImageAddrs, backtraceImageAddrs); - - [[NSFileManager defaultManager] removeItemAtPath:crashReportFilePath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:recrashReportFilePath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:stateFilePath error:nil]; } - (void)testWriteStandardReportPerformance { - NSString *directory = NSTemporaryDirectory(); - NSString *crashReportFilePath = [directory stringByAppendingPathComponent:@"crash_report"]; - NSString *recrashReportFilePath = [directory stringByAppendingPathComponent:@"recrash_report"]; - NSString *stateFilePath = [directory stringByAppendingPathComponent:@"kscrash_state"]; + NSString *crashReportFilePath = [self temporaryFile:@"crash_report"]; + NSString *recrashReportFilePath = [self temporaryFile:@"recrash_report"]; + NSString *stateFilePath = [self temporaryFile:@"kscrash_state"]; NSString *crashID = [[NSUUID UUID] UUIDString]; bsg_kscrash_init(); @@ -111,11 +105,19 @@ - (void)testWriteStandardReportPerformance { } [self stopMeasuring]; + NSDictionary *report = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:crashReportFilePath] options:0 error:nil]; + XCTAssert([report isKindOfClass:[NSDictionary class]], @"%@", report); [[NSFileManager defaultManager] removeItemAtPath:crashReportFilePath error:nil]; }]; - - [[NSFileManager defaultManager] removeItemAtPath:recrashReportFilePath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:stateFilePath error:nil]; +} + +- (NSString *)temporaryFile:(NSString *)fileName { + NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:fileName]; + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + [self addTeardownBlock:^{ + [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; + }]; + return path; } @end diff --git a/Tests/KSCrashTests/BSG_KSFileTests.m b/Tests/KSCrashTests/BSG_KSFileTests.m new file mode 100644 index 000000000..622563519 --- /dev/null +++ b/Tests/KSCrashTests/BSG_KSFileTests.m @@ -0,0 +1,64 @@ +// +// BSG_KSFileTests.m +// Bugsnag +// +// Created by Nick Dowell on 13/01/2022. +// Copyright © 2022 Bugsnag Inc. All rights reserved. +// + +#import + +#import "BSG_KSFile.h" + +@interface BSG_KSFileTests : XCTestCase + +@property NSString *filePath; +@property int fileDescriptor; + +@end + +@implementation BSG_KSFileTests + +- (void)setUp { + self.filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[self description]]; + self.fileDescriptor = open(self.filePath.fileSystemRepresentation, O_RDWR | O_CREAT | O_EXCL, 0644); +} + +- (void)tearDown { + close(self.fileDescriptor); + unlink(self.filePath.fileSystemRepresentation); +} + +- (void)testFileWrite { + BSG_KSFile file; + const size_t bufferSize = 8; + char buffer[bufferSize]; + + BSG_KSFileInit(&file, self.fileDescriptor, buffer, bufferSize); + XCTAssertEqual(file.bufferSize, bufferSize); + XCTAssertEqual(file.bufferUsed, 0); + + BSG_KSFileWrite(&file, "Someone", 7); + XCTAssertEqual(file.bufferUsed, 7, @"The buffer should not be flushed until filled"); + BSG_KSFileWrite(&file, " ", 1); + XCTAssertEqual(file.bufferUsed, 0, @"The buffer should be flushed once filled"); + + BSG_KSFileWrite(&file, "says", 4); + BSG_KSFileWrite(&file, ": ", 2); + XCTAssertEqual(file.bufferUsed, 6, @"The buffer should not be flushed until filled"); + + BSG_KSFileWrite(&file, "Hello, ", 7); + XCTAssertEqual(file.bufferUsed, (6 + 7) % bufferSize); + + BSG_KSFileWrite(&file, "Supercalifragilisticexpialidocious", 34); + XCTAssertEqual(file.bufferUsed, 0, @"Large writes should flush the buffer and leave it empty"); + + BSG_KSFileFlush(&file); + XCTAssertEqualObjects([self fileContentsAsString], @"Someone says: Hello, Supercalifragilisticexpialidocious"); +} + +- (NSString *)fileContentsAsString { + return [NSString stringWithContentsOfFile:self.filePath encoding:NSUTF8StringEncoding error:nil]; +} + +@end From 099c5a761d264a95d6d73b0351b4873c8af20eba Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Wed, 12 Jan 2022 15:42:14 +0000 Subject: [PATCH 4/8] KSCrashState buffered output --- .../Source/KSCrash/Recording/BSG_KSCrashState.m | 12 ++++++++---- Tests/KSCrashTests/KSCrashState_Tests.m | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m index dff9b7231..3b96c6047 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashState.m @@ -26,7 +26,7 @@ #include "BSG_KSCrashState.h" -#include "BSG_KSFileUtils.h" +#include "BSG_KSFile.h" #include "BSG_KSJSONCodec.h" #include "BSG_KSJSONCodecObjC.h" #include "BSG_KSMach.h" @@ -154,8 +154,7 @@ int bsg_kscrashstate_i_onEndData(__unused void *const userData) { */ int bsg_kscrashstate_i_addJSONData(const char *const data, const size_t length, void *const userData) { - const int fd = *((int *)userData); - const bool success = bsg_ksfuwriteBytesToFD(fd, data, (ssize_t)length); + bool success = BSG_KSFileWrite(userData, data, length); return success ? BSG_KSJSON_OK : BSG_KSJSON_ERROR_CANNOT_ADD_DATA; } @@ -227,9 +226,13 @@ bool bsg_kscrashstate_i_saveState(const BSG_KSCrash_State *const state, return false; } + BSG_KSFile file; + char buffer[256]; + BSG_KSFileInit(&file, fd, buffer, sizeof(buffer) / sizeof(*buffer)); + BSG_KSJSONEncodeContext JSONContext; bsg_ksjsonbeginEncode(&JSONContext, false, bsg_kscrashstate_i_addJSONData, - &fd); + &file); int result; if ((result = bsg_ksjsonbeginObject(&JSONContext, NULL)) != BSG_KSJSON_OK) { @@ -269,6 +272,7 @@ bool bsg_kscrashstate_i_saveState(const BSG_KSCrash_State *const state, result = bsg_ksjsonendEncode(&JSONContext); done: + BSG_KSFileFlush(&file); close(fd); if (result != BSG_KSJSON_OK) { diff --git a/Tests/KSCrashTests/KSCrashState_Tests.m b/Tests/KSCrashTests/KSCrashState_Tests.m index f269a41db..e659edbff 100755 --- a/Tests/KSCrashTests/KSCrashState_Tests.m +++ b/Tests/KSCrashTests/KSCrashState_Tests.m @@ -483,4 +483,20 @@ - (void)testBGFGCrash XCTAssertTrue(context.crashedLastLaunch, @""); } +- (void)testPersistence +{ + NSString *file = [self.tempPath stringByAppendingPathComponent:@"state.json"]; + + BSG_KSCrash_State state = {0}; + bsg_kscrashstate_init([file fileSystemRepresentation], &state); + + id json = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:file] options:0 error:nil]; + XCTAssertEqualObjects([json objectForKey:@"crashedLastLaunch"], @NO); + + bsg_kscrashstate_notifyAppCrash(); + + json = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:file] options:0 error:nil]; + XCTAssertEqualObjects([json objectForKey:@"crashedLastLaunch"], @YES); +} + @end From 2af1e27609d04f72eb499b0f12c3a9421c476f6b Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Thu, 13 Jan 2022 15:22:40 +0000 Subject: [PATCH 5/8] Fix setting custom notifier info --- Bugsnag/Configuration/BugsnagConfiguration.m | 1 + Tests/BugsnagTests/BugsnagConfigurationTests.m | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Bugsnag/Configuration/BugsnagConfiguration.m b/Bugsnag/Configuration/BugsnagConfiguration.m index 66a0ae6ef..811bef83d 100644 --- a/Bugsnag/Configuration/BugsnagConfiguration.m +++ b/Bugsnag/Configuration/BugsnagConfiguration.m @@ -97,6 +97,7 @@ - (nonnull id)copyWithZone:(nullable __attribute__((unused)) NSZone *)zone { [copy setMaxPersistedEvents:self.maxPersistedEvents]; [copy setMaxPersistedSessions:self.maxPersistedSessions]; [copy setMaxBreadcrumbs:self.maxBreadcrumbs]; + [copy setNotifier:self.notifier]; [copy setFeatureFlagStore:self.featureFlagStore]; [copy setMetadata:self.metadata]; [copy setEndpoints:self.endpoints]; diff --git a/Tests/BugsnagTests/BugsnagConfigurationTests.m b/Tests/BugsnagTests/BugsnagConfigurationTests.m index ad75679b9..5e7231fdc 100644 --- a/Tests/BugsnagTests/BugsnagConfigurationTests.m +++ b/Tests/BugsnagTests/BugsnagConfigurationTests.m @@ -10,6 +10,7 @@ #import "BugsnagCrashSentry.h" #import "BugsnagEndpointConfiguration.h" #import "BugsnagErrorTypes.h" +#import "BugsnagNotifier.h" #import "BugsnagSessionTracker.h" #import "BugsnagTestConstants.h" @@ -855,6 +856,10 @@ - (void)testNSCopying { [config setAutoDetectErrors:YES]; [config setContext:@"context1"]; [config setAppType:@"The most amazing app, a brilliant app, the app to end all apps"]; + [config setNotifier:[[BugsnagNotifier alloc] initWithName:@"Example" + version:@"0.0.0" + url:@"https://example.com" + dependencies:@[[[BugsnagNotifier alloc] init]]]]; [config setPersistUser:YES]; [config setSendThreads:BSGThreadSendPolicyUnhandledOnly]; BugsnagOnSendErrorBlock onSendBlock1 = ^BOOL(BugsnagEvent * _Nonnull event) { return true; }; @@ -901,6 +906,10 @@ - (void)testNSCopying { XCTAssertEqual(config.onSendBlocks[0], clone.onSendBlocks[0]); [clone setOnSendBlocks:[@[ onSendBlock2 ] mutableCopy]]; XCTAssertNotEqual(config.onSendBlocks[0], clone.onSendBlocks[0]); + + XCTAssertEqualObjects(clone.notifier.name, config.notifier.name); + XCTAssertEqualObjects(clone.notifier.version, config.notifier.version); + XCTAssertEqualObjects(clone.notifier.url, config.notifier.url); } - (void)testMetadataMutability { From 9aed5a1e6d229680af12be90c7af22daa24ada37 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Tue, 18 Jan 2022 14:10:36 -0800 Subject: [PATCH 6/8] Add missing imports when not using clang modules If `CLANG_ENABLE_MODULES=NO` these imports are missing leading to compiler errors. --- Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashNames.c | 1 + Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashNames.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashNames.c index 94b339027..c9548cede 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashNames.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashNames.c @@ -8,6 +8,7 @@ #include "BSG_KSCrashNames.h" #include +#include static const char* thread_state_names[] = { // Defined in mach/thread_info.h diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h index 2992d09ce..7ef0910a7 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSFileUtils.h @@ -35,6 +35,7 @@ extern "C" { #endif #include +#include #include /** Get the last entry in a file path. Assumes UNIX style separators. From 2059be55644528b9434fa484826303216bb9bd3a Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Wed, 19 Jan 2022 08:37:11 +0000 Subject: [PATCH 7/8] Build iOSTestApp with CLANG_ENABLE_MODULES=NO --- CHANGELOG.md | 7 +++++++ features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj | 4 ++++ features/scripts/export_ios_app.sh | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13b65c155..69fb9f7bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Bug fixes + +* Fix missing imports when building with `CLANG_ENABLE_MODULES=NO` + [#1284](https://github.com/bugsnag/bugsnag-cocoa/pull/1284) + ## 6.16.0 (2022-01-12) ### Enhancements diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 63d56a774..ea97f80ba 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 01B6BBB625DA82B800FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */; }; 01CD103926690463007FA5F0 /* libBugsnagStatic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01CD103826690463007FA5F0 /* libBugsnagStatic.a */; }; 01DE903826CE99B800455213 /* CriticalThermalStateScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DE903726CE99B800455213 /* CriticalThermalStateScenario.swift */; }; + 01DF442C2798044E00C31104 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01DF442B2798044E00C31104 /* SystemConfiguration.framework */; }; 01E0DB0B25E8EBD100A740ED /* AppDurationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */; }; 01E356C026CD5B6A00BE3F64 /* ThermalStateBreadcrumbScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E356BF26CD5B6A00BE3F64 /* ThermalStateBreadcrumbScenario.swift */; }; 01E5EAD225B713990066EA8A /* OOMScenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E5EAD125B713990066EA8A /* OOMScenario.m */; }; @@ -187,6 +188,7 @@ 01B6BBB525DA82B700FC4DE6 /* SendLaunchCrashesSynchronouslyScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendLaunchCrashesSynchronouslyScenario.swift; sourceTree = ""; }; 01CD103826690463007FA5F0 /* libBugsnagStatic.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libBugsnagStatic.a; sourceTree = BUILT_PRODUCTS_DIR; }; 01DE903726CE99B800455213 /* CriticalThermalStateScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CriticalThermalStateScenario.swift; sourceTree = ""; }; + 01DF442B2798044E00C31104 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 01E0DB0A25E8EBD100A740ED /* AppDurationScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDurationScenario.swift; sourceTree = ""; }; 01E356BF26CD5B6A00BE3F64 /* ThermalStateBreadcrumbScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThermalStateBreadcrumbScenario.swift; sourceTree = ""; }; 01E5EAD025B713990066EA8A /* OOMScenario.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OOMScenario.h; sourceTree = ""; }; @@ -365,6 +367,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 01DF442C2798044E00C31104 /* SystemConfiguration.framework in Frameworks */, CB42FF9026F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a in Frameworks */, 01CD103926690463007FA5F0 /* libBugsnagStatic.a in Frameworks */, ); @@ -433,6 +436,7 @@ D018DCE3210BCB4D0B8FA6D2 /* Frameworks */ = { isa = PBXGroup; children = ( + 01DF442B2798044E00C31104 /* SystemConfiguration.framework */, CB42FF8F26F1EDB500E8D5D2 /* libBugsnagNetworkRequestPluginStatic.a */, AAFEFA0226EB6B5A00980A10 /* BugsnagNetworkRequestPlugin.framework */, 01CD103826690463007FA5F0 /* libBugsnagStatic.a */, diff --git a/features/scripts/export_ios_app.sh b/features/scripts/export_ios_app.sh index ce596fad5..d1af19b8f 100755 --- a/features/scripts/export_ios_app.sh +++ b/features/scripts/export_ios_app.sh @@ -6,6 +6,11 @@ cd features/fixtures/ios echo "--- iOSTestApp: xcodebuild archive" +# +# Using CLANG_ENABLE_MODULES=NO to surface build errors +# https://github.com/bugsnag/bugsnag-cocoa/pull/1284 +# + xcrun xcodebuild \ -scheme iOSTestApp \ -workspace iOSTestApp.xcworkspace \ @@ -15,6 +20,7 @@ xcrun xcodebuild \ -allowProvisioningUpdates \ -quiet \ archive \ + CLANG_ENABLE_MODULES=NO \ GCC_PREPROCESSOR_DEFINITIONS='$(inherited) BSG_LOG_LEVEL=BSG_LOGLEVEL_DEBUG' echo "--- iOSTestApp: xcodebuild -exportArchive" From c1d2184cf4eeee8c92b4add3aee4c8449fca6a79 Mon Sep 17 00:00:00 2001 From: Nick Dowell Date: Wed, 19 Jan 2022 10:43:51 +0000 Subject: [PATCH 8/8] Release v6.16.1 --- .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 c8ba12bd7..5d0040b46 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.16.0/Bugsnag" +github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.16.1/Bugsnag" github_url: "https://github.com/bugsnag/bugsnag-cocoa" hide_documentation_coverage: true module: "Bugsnag" -module_version: "6.16.0" +module_version: "6.16.1" objc: true output: "docs" readme: "README.md" diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index 38d69f0bc..5edf5124d 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.16.0", + "version": "6.16.1", "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.16.0" + "tag": "v6.16.1" }, "frameworks": [ "Foundation", diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index 911bcca23..70ada58cf 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.16.0"; + _version = @"6.16.1"; _url = @"https://github.com/bugsnag/bugsnag-cocoa"; _dependencies = @[]; } diff --git a/BugsnagNetworkRequestPlugin.podspec.json b/BugsnagNetworkRequestPlugin.podspec.json index eb8b9f256..c867da7f9 100644 --- a/BugsnagNetworkRequestPlugin.podspec.json +++ b/BugsnagNetworkRequestPlugin.podspec.json @@ -1,16 +1,16 @@ { "name": "BugsnagNetworkRequestPlugin", - "version": "6.16.0", + "version": "6.16.1", "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.16.0/BugsnagNetworkRequestPlugin/README.md", + "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.16.1/BugsnagNetworkRequestPlugin/README.md", "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.16.0" + "tag": "v6.16.1" }, "dependencies": { "Bugsnag": "~> 6.13" diff --git a/CHANGELOG.md b/CHANGELOG.md index 947ee1ce1..95b357c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Changelog ========= -## TBD +## 6.16.1 (2022-01-19) ### Bug fixes diff --git a/Framework/Info.plist b/Framework/Info.plist index a7200eaff..7b015f9cc 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.16.0 + 6.16.1 CFBundleVersion 1 diff --git a/Tests/BugsnagTests/Info.plist b/Tests/BugsnagTests/Info.plist index c02667c7a..ecb1f932b 100644 --- a/Tests/BugsnagTests/Info.plist +++ b/Tests/BugsnagTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.16.0 + 6.16.1 CFBundleVersion 1 diff --git a/Tests/TestHost-iOS/Info.plist b/Tests/TestHost-iOS/Info.plist index 7bc6100db..2f2f224cc 100644 --- a/Tests/TestHost-iOS/Info.plist +++ b/Tests/TestHost-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.16.0 + 6.16.1 CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/VERSION b/VERSION index bfec95bf0..954ea4957 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.16.0 +6.16.1