From a1a8239928027deb93d5a355950ae082b52b2cea Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 1 Mar 2024 13:05:25 +0100 Subject: [PATCH 01/51] Fix an off-by-1 error when fetching arm64 register values that could potentially run off the end of the array --- .../Recording/Tools/BSG_KSMach_Arm64.c | 26 ++++++++++++++----- CHANGELOG.md | 7 +++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach_Arm64.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach_Arm64.c index 78795c079..5c399f054 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach_Arm64.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Tools/BSG_KSMach_Arm64.c @@ -36,7 +36,7 @@ static const char *bsg_g_registerNames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", - "x27", "x28", "x29", "fp", "lr", "sp", "pc", "cpsr"}; + "x27", "x28", "fp", "lr", "sp", "pc", "cpsr"}; static const int bsg_g_registerNamesCount = sizeof(bsg_g_registerNames) / sizeof(*bsg_g_registerNames); @@ -100,20 +100,32 @@ const char *bsg_ksmachregisterName(const int regNumber) { uint64_t bsg_ksmachregisterValue(const BSG_STRUCT_MCONTEXT_L *const machineContext, const int regNumber) { - if (regNumber <= 29) { +// _structs.h: +// _STRUCT_ARM_THREAD_STATE64 +// { +// __uint64_t __x[29]; /* General purpose registers x0-x28 */ +// __uint64_t __fp; /* Frame pointer x29 */ +// __uint64_t __lr; /* Link register x30 */ +// __uint64_t __sp; /* Stack pointer x31 */ +// __uint64_t __pc; /* Program counter */ +// __uint32_t __cpsr; /* Current program status register */ +// __uint32_t __pad; /* Same size for 32-bit or 64-bit clients */ +// }; + + if (regNumber <= 28) { return machineContext->__ss.__x[regNumber]; } switch (regNumber) { - case 30: + case 29: return machineContext->__ss.__fp; - case 31: + case 30: return machineContext->__ss.__lr; - case 32: + case 31: return machineContext->__ss.__sp; - case 33: + case 32: return machineContext->__ss.__pc; - case 34: + case 33: return machineContext->__ss.__cpsr; } diff --git a/CHANGELOG.md b/CHANGELOG.md index f4edb9565..cb65bc1d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## TBD + +### Bug fixes + +* Fix off-by-1 error when fetching register values on arm64 that could potentially run off the array. + [1635](https://github.com/bugsnag/bugsnag-cocoa/pull/1635) + ## 6.28.1 (2024-02-28) ### Bug fixes From dc56671fafd1bb4fe0c82f352e4edf80f1ece7f1 Mon Sep 17 00:00:00 2001 From: Alex Moinet Date: Tue, 12 Mar 2024 15:28:21 +0000 Subject: [PATCH 02/51] Pass-through api key to enable sending bugsnags on test failure (#1636) --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 6ee1dc6b1..42c6716e3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ x-common-environment: &common-environment BUILDKITE_STEP_KEY: MAZE_BUGSNAG_API_KEY: MAZE_REPEATER_API_KEY: + MAZE_SCENARIO_BUGSNAG_API_KEY: services: cocoa-maze-runner: From ed32567f00cc2afe3a160f88c38cb089cab91524 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Tue, 12 Mar 2024 10:22:34 +0100 Subject: [PATCH 03/51] Introduce new Fixture class and new Scenario structure to use the idempotent mazerunner API --- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 16 +- .../ios/iOSTestApp/CommandReaderThread.swift | 147 +++++++++++------ .../fixtures/ios/iOSTestApp/Fixture.swift | 153 ++++++++++++++++++ .../ios/iOSTestApp/FixtureConfig.swift | 29 ++++ .../ios/iOSTestApp/MazeRunnerCommand.swift | 31 ++++ .../ios/iOSTestApp/ViewController.swift | 26 +-- features/fixtures/shared/scenarios/Scenario.m | 1 + 7 files changed, 327 insertions(+), 76 deletions(-) create mode 100644 features/fixtures/ios/iOSTestApp/Fixture.swift create mode 100644 features/fixtures/ios/iOSTestApp/FixtureConfig.swift create mode 100644 features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index af196dd77..f3662387d 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -88,6 +88,9 @@ 01FA9EC426D63BB20059FF4A /* AppHangInTerminationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA9EC326D63BB20059FF4A /* AppHangInTerminationScenario.swift */; }; 095E095A2AF3BE8D00273F1F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095E09592AF3BE8D00273F1F /* Logging.swift */; }; 095E095D2AF3BFDA00273F1F /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 095E095C2AF3BFDA00273F1F /* Logging.m */; }; + 09F024F82B9F3971007D9F73 /* FixtureConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */; }; + 09F024FA2B9F3ACD007D9F73 /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024F92B9F3ACD007D9F73 /* Fixture.swift */; }; + 09F024FC2B9F3B16007D9F73 /* MazeRunnerCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */; }; 6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */; }; 8A096DF627C7E56C00DB6ECC /* CxxUnexpectedScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */; }; 8A096DFC27C7E77600DB6ECC /* CxxBareThrowScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */; }; @@ -288,6 +291,9 @@ 095E09592AF3BE8D00273F1F /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 095E095B2AF3BFDA00273F1F /* Logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 095E095C2AF3BFDA00273F1F /* Logging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = ""; }; + 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixtureConfig.swift; sourceTree = ""; }; + 09F024F92B9F3ACD007D9F73 /* Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fixture.swift; sourceTree = ""; }; + 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MazeRunnerCommand.swift; sourceTree = ""; }; 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = ""; }; 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxUnexpectedScenario.mm; sourceTree = ""; }; 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxBareThrowScenario.mm; sourceTree = ""; }; @@ -446,11 +452,14 @@ children = ( 8AB8866320404DD30003E444 /* AppDelegate.swift */, 8AB8866A20404DD30003E444 /* Assets.xcassets */, + AA8BAEE22A4DD90E00A8BEA7 /* CommandReaderThread.swift */, + 09F024F92B9F3ACD007D9F73 /* Fixture.swift */, + 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */, 8AB8866F20404DD30003E444 /* Info.plist */, 8AB8866C20404DD30003E444 /* LaunchScreen.storyboard */, - 8AB8866520404DD30003E444 /* ViewController.swift */, 8AB8866720404DD30003E444 /* Main.storyboard */, - AA8BAEE22A4DD90E00A8BEA7 /* CommandReaderThread.swift */, + 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */, + 8AB8866520404DD30003E444 /* ViewController.swift */, ); path = iOSTestApp; sourceTree = ""; @@ -778,6 +787,7 @@ 017BA42428A1558A00CB985E /* OversizedBreadcrumbsScenario.swift in Sources */, 8AB8866620404DD30003E444 /* ViewController.swift in Sources */, 010BAB0E2833CE570003FF36 /* UnhandledMachExceptionOverrideScenario.m in Sources */, + 09F024FA2B9F3ACD007D9F73 /* Fixture.swift in Sources */, 8AB8866420404DD30003E444 /* AppDelegate.swift in Sources */, 00507A64242BFE5600EF1B87 /* EnabledBreadcrumbTypesIsNilScenario.swift in Sources */, 010BAB082833CE570003FF36 /* UnhandledErrorThreadSendAlwaysScenario.m in Sources */, @@ -823,6 +833,7 @@ 010BAB312833D1930003FF36 /* SendLaunchCrashesSynchronouslyLaunchCompletedScenario.swift in Sources */, 6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */, F4295A94DD2D131A594A212C /* HandledErrorScenario.swift in Sources */, + 09F024FC2B9F3B16007D9F73 /* MazeRunnerCommand.swift in Sources */, F4295A7AA9B4A18992A2F020 /* HandledErrorOverrideScenario.swift in Sources */, 01E0DB0B25E8EBD100A740ED /* AppDurationScenario.swift in Sources */, E700EE62247D4D42008CFFB6 /* OnCrashHandlerScenario.m in Sources */, @@ -933,6 +944,7 @@ E7B79CD8247FD7810039FB88 /* AutoContextNSExceptionScenario.swift in Sources */, 010BAB3B2833D2280003FF36 /* UnhandledErrorValidReleaseStageScenario.swift in Sources */, 010BDFB92885562D007025F9 /* ReportBackgroundAppHangScenario.swift in Sources */, + 09F024F82B9F3971007D9F73 /* FixtureConfig.swift in Sources */, 01E356C026CD5B6A00BE3F64 /* ThermalStateBreadcrumbScenario.swift in Sources */, E7B79CD2247FD66E0039FB88 /* ManualContextClientScenario.swift in Sources */, E7B79CD6247FD7750039FB88 /* AutoContextNSErrorScenario.swift in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/CommandReaderThread.swift b/features/fixtures/ios/iOSTestApp/CommandReaderThread.swift index 85f5e2a8a..00be4cf45 100644 --- a/features/fixtures/ios/iOSTestApp/CommandReaderThread.swift +++ b/features/fixtures/ios/iOSTestApp/CommandReaderThread.swift @@ -6,76 +6,119 @@ // Copyright © 2023 Bugsnag. All rights reserved. // -import UIKit -import os +import Foundation class CommandReaderThread: Thread { - - var action: (String, String) -> Void - init(action: @escaping (String, String) -> Void) { - self.action = action + var fixtureConfig: FixtureConfig + var commandReceiver: CommandReceiver + var lastCommandID: String = "" + + init(fixtureConfig: FixtureConfig, commandReceiver: CommandReceiver) { + self.fixtureConfig = fixtureConfig + self.commandReceiver = commandReceiver } - - override func main() { - if Scenario.baseMazeAddress.isEmpty { - Scenario.baseMazeAddress = self.loadMazeRunnerAddress() - } - - var isRunningCommand = false + override func main() { while true { - if isRunningCommand { - logInfo("A command fetch is already in progress, waiting 1 second more...") + if self.commandReceiver.canReceiveCommand() { + receiveNextCommand() } else { - isRunningCommand = true - Scenario.executeMazeRunnerCommand() { scenarioName, eventMode in - if (!scenarioName.isEmpty) { - self.action(scenarioName, eventMode) - } - isRunningCommand = false - } + logDebug("A command is already in progress, waiting 1 second more...") } Thread.sleep(forTimeInterval: 1) } } - - func loadMazeRunnerAddress() -> String { - let bsAddress = "http://bs-local.com:9339" + func newStartedFetchTask() -> CommandFetchTask { + let fetchTask = CommandFetchTask(url: fixtureConfig.commandURL, afterCommandID: lastCommandID) + fetchTask.start() + return fetchTask + } + + func receiveNextCommand() { + let maxWaitTime = 5.0 + let pollingInterval = 1.0 - // Only iOS 12 and above will run on BitBar for now - if #available(iOS 12.0, *) {} else { - return bsAddress; + var fetchTask = newStartedFetchTask() + let startTime = Date() + + while true { + Thread.sleep(forTimeInterval: pollingInterval) + switch fetchTask.state { + case CommandFetchState.success: + logDebug("Command fetch: Request succeeded") + let command = fetchTask.command! + if (command.uuid != "") { + lastCommandID = command.uuid + } + commandReceiver.receiveCommand(command: command) + return + case CommandFetchState.fetching: + let duration = Date() - startTime + if duration < maxWaitTime { + logDebug("Command fetch: Server hasn't responded in \(duration)s (max \(maxWaitTime)). Waiting \(pollingInterval)s more...") + } else { + fetchTask.cancel() + logInfo("Command fetch: Server hasn't responded in \(duration)s (max \(maxWaitTime)). Trying again...") + fetchTask = newStartedFetchTask() + } + break + case CommandFetchState.failed: + logInfo("Command fetch: Request failed. Trying again...") + fetchTask = newStartedFetchTask() + break + } } - - for n in 1...30 { - let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + } +} + +extension Date { + static func - (lhs: Date, rhs: Date) -> TimeInterval { + return lhs.timeIntervalSinceReferenceDate - rhs.timeIntervalSinceReferenceDate + } +} + +enum CommandFetchState { + case failed, fetching, success +} - logInfo("Reading Maze Runner address from fixture_config.json") - do { - let fileUrl = URL(fileURLWithPath: "fixture_config", - relativeTo: documentsUrl).appendingPathExtension("json") +class CommandFetchTask { + var url: URL + var state = CommandFetchState.failed + var command: MazeRunnerCommand? + var task: URLSessionTask? - let savedData = try Data(contentsOf: fileUrl) + init(url: URL, afterCommandID: String) { + self.url = URL(string: "\(url.absoluteString)?after=\(afterCommandID)")! + } + + func cancel() { + task?.cancel() + } - if let contents = String(data: savedData, encoding: .utf8) { - logInfo(String(format: "Found fixture_config.json after %d seconds", n)) - let decoder = JSONDecoder() - let jsonData = contents.data(using: .utf8) - let config = try decoder.decode(FixtureConfig.self, from: jsonData!) - let address = "http://" + config.maze_address - logInfo("Using Maze Runner address: " + address) - return address + func start() { + logInfo("Fetching next command from \(url)") + state = CommandFetchState.fetching + let request = URLRequest(url: url) + task = URLSession.shared.dataTask(with: request) { data, response, error in + if let data = data { + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + do { + let command = try decoder.decode(MazeRunnerCommand.self, from: data) + logInfo("Command fetched and decoded") + self.command = command; + self.state = CommandFetchState.success + } catch { + self.state = CommandFetchState.failed + let dataAsString = String(data: data, encoding: .utf8) + logError("Failed to fetch command: Invalid Response from \(String(describing: self.url)): [\(String(describing: dataAsString))]: Error is: \(error)") } + } else if let error = error { + self.state = CommandFetchState.failed + logError("Failed to fetch command: HTTP Request to \(String(describing: self.url)) failed: \(error)") } - catch let error as NSError { - logWarn("Failed to read fixture_config.json: \(error)") - } - logInfo("Waiting for fixture_config.json to appear") - Thread.sleep(forTimeInterval: 1) } - - logError("Unable to read from fixture_config.json, defaulting to BrowserStack environment") - return bsAddress; + task?.resume() } } diff --git a/features/fixtures/ios/iOSTestApp/Fixture.swift b/features/fixtures/ios/iOSTestApp/Fixture.swift new file mode 100644 index 000000000..21a2eca2a --- /dev/null +++ b/features/fixtures/ios/iOSTestApp/Fixture.swift @@ -0,0 +1,153 @@ +// +// Fixture.swift +// iOSTestApp +// +// Created by Karl Stenerud on 11.03.24. +// Copyright © 2024 Bugsnag. All rights reserved. +// + +import Foundation + +protocol CommandReceiver { + func canReceiveCommand() -> Bool + func receiveCommand(command: MazeRunnerCommand) +} + +class Fixture: NSObject, CommandReceiver { + static let defaultMazeRunnerURL = URL(string: "http://bs-local.com:9339")! + + var readyToReceiveCommand = false + var commandReaderThread: CommandReaderThread? + var fixtureConfig: FixtureConfig = FixtureConfig(mazeRunnerBaseAddress: defaultMazeRunnerURL) + var scenario: Scenario? = nil + + func start() { + DispatchQueue.global(qos: .userInitiated).async { + self.loadMazeRunnerAddress { address in + self.fixtureConfig = FixtureConfig(mazeRunnerBaseAddress: address) + self.beginReceivingCommands(fixtureConfig: self.fixtureConfig) + } + } + } + + func beginReceivingCommands(fixtureConfig: FixtureConfig) { + readyToReceiveCommand = true + commandReaderThread = CommandReaderThread(fixtureConfig: fixtureConfig, commandReceiver: self) + commandReaderThread!.start() + } + + func canReceiveCommand() -> Bool { + return readyToReceiveCommand + } + + func receiveCommand(command: MazeRunnerCommand) { + readyToReceiveCommand = false + var isReady = true + DispatchQueue.main.async { + logInfo("Executing command [\(command.action)] with args \(command.args)") + switch command.action { + case "run_scenario": + self.runScenario(scenarioName: command.args[0], completion: { + self.readyToReceiveCommand = true + }) + isReady = false; + break + case "invoke_method": + self.invokeMethod(methodName: command.args[0], args: Array(command.args[1...])) + break + case "noop": + break + default: + assertionFailure("\(command.action): Unknown command") + } + if isReady { + self.readyToReceiveCommand = true + } + } + } + + private func runScenario(scenarioName: String, completion: @escaping () -> ()) { + logInfo("========== Running scenario \(scenarioName) ==========") + let scenarioClass: AnyClass = NSClassFromString("Fixture.\(scenarioName)")! + logInfo("Loaded scenario class: \(scenarioClass)") + scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig) as Scenario? + logInfo("Configuring scenario in class \(scenarioClass)") + scenario!.configure() + logInfo("Clearing persistent data") + scenario!.clearPersistentData() + logInfo("Starting bugsnag performance") + scenario!.startBugsnag() + logInfo("Starting scenario in class \(scenarioClass)") + scenario!.run() + logInfo("========== Completed scenario \(scenarioName) ==========") + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + self.scenario!.reportMeasurements() + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + completion() + } + } + } + + private func invokeMethod(methodName: String, args: Array) { + logInfo("Invoking method \(methodName) with args \(args) on \(String(describing: scenario!.self))") + + let sel = NSSelectorFromString(methodName) + if (!scenario!.responds(to: sel)) { + fatalError("\(String(describing: scenario!.self)) does not respond to \(methodName). Did you set the @objcMembers annotation on \(String(describing: scenario!.self))?") + } + + switch args.count { + case 0: + scenario!.perform(sel) + case 1: + // Note: Parameter must accept a string + scenario!.perform(sel, with: args[0]) + default: + fatalError("invoking \(methodName) with args \(args): Fixture currently only supports up to 1 argument") + } + } + + func loadMazeRunnerAddress(completion: (URL)->()) { + let defaultUrl = Fixture.defaultMazeRunnerURL + + // Only iOS 12 and above will run on BitBar for now + if #available(iOS 12.0, *) {} else { + completion(defaultUrl) + return + } + + for n in 1...60 { + let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + + logInfo("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) { + logInfo(String(format: "Found fixture_config.json after %d seconds", n)) + let decoder = JSONDecoder() + let jsonData = contents.data(using: .utf8) + let config = try decoder.decode(FixtureConfigJSON.self, from: jsonData!) + let address = "http://" + config.maze_address + logInfo("Using Maze Runner address: \(address)") + completion(URL(string: address)!) + return + } + } + catch let error as NSError { + logWarn("Failed to read fixture_config.json: \(error)") + } + logInfo("Waiting for fixture_config.json to appear") + sleep(1) + } + + logError("Unable to read from fixture_config.json, defaulting to BrowserStack environment") + completion(defaultUrl) + return + } + + private struct FixtureConfigJSON: Decodable { + var maze_address: String + } +} diff --git a/features/fixtures/ios/iOSTestApp/FixtureConfig.swift b/features/fixtures/ios/iOSTestApp/FixtureConfig.swift new file mode 100644 index 000000000..f2029141d --- /dev/null +++ b/features/fixtures/ios/iOSTestApp/FixtureConfig.swift @@ -0,0 +1,29 @@ +// +// FixtureConfig.swift +// iOSTestApp +// +// Created by Karl Stenerud on 11.03.24. +// Copyright © 2024 Bugsnag. All rights reserved. +// + +import Foundation + +@objc class FixtureConfig: NSObject { + let mazeRunnerURL: URL + let tracesURL: URL + let commandURL: URL + let metricsURL: URL + let reflectURL: URL + let notifyURL: URL + let sessionsURL: URL + + init(mazeRunnerBaseAddress: URL) { + mazeRunnerURL = mazeRunnerBaseAddress + tracesURL = mazeRunnerBaseAddress.appendingPathComponent("traces") + commandURL = mazeRunnerBaseAddress.appendingPathComponent("command") + metricsURL = mazeRunnerBaseAddress.appendingPathComponent("metrics") + notifyURL = mazeRunnerBaseAddress.appendingPathComponent("notify") + sessionsURL = mazeRunnerBaseAddress.appendingPathComponent("sessions") + reflectURL = mazeRunnerBaseAddress.appendingPathComponent("reflect") + } +} diff --git a/features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift b/features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift new file mode 100644 index 000000000..608a7b29b --- /dev/null +++ b/features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift @@ -0,0 +1,31 @@ +// +// MazeRunnerCommand.swift +// iOSTestApp +// +// Created by Karl Stenerud on 11.03.24. +// Copyright © 2024 Bugsnag. All rights reserved. +// + +import Foundation + +class MazeRunnerCommand: Codable { + let message: String + let action: String + let uuid: String + let args: Array + + init(uuid: String, action: String, args: Array, message: String) { + self.uuid = uuid + self.message = message + self.action = action + self.args = args + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.uuid = try container.decodeIfPresent(String.self, forKey: .uuid) ?? "" + self.message = try container.decodeIfPresent(String.self, forKey: .message) ?? "" + self.action = try container.decodeIfPresent(String.self, forKey: .action) ?? "" + self.args = try container.decodeIfPresent(Array.self, forKey: .args) ?? [] + } +} diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index ab408a0da..b5587915a 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -9,34 +9,16 @@ import UIKit import os -class FixtureConfig: Codable { - var maze_address: String -} - class ViewController: UIViewController { @IBOutlet var scenarioNameField : UITextField! @IBOutlet var scenarioMetaDataField : UITextField! @IBOutlet var apiKeyField: UITextField! - var mazeRunnerAddress: String = "" - - override func viewDidLoad() { - super.viewDidLoad() - 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") - logInfo("Read API key from UserDefaults: \(apiKeyField.text!)") + var fixture: Fixture = Fixture() - // Poll for commands to run - if #available(iOS 10.0, *) { - let uiUpdater = { (scenarioName: String, eventMode: String) in - self.scenarioNameField.text = scenarioName - self.scenarioMetaDataField.text = eventMode - } - - let thread = CommandReaderThread(action: uiUpdater) - thread.start() - } + required init?(coder: NSCoder) { + super.init(coder: coder) + fixture.start() } @IBAction func runTestScenario() { diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 7f2f20fae..3b59f4146 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -4,6 +4,7 @@ // #import "Scenario.h" #import "Logging.h" +#import "FixtureConfig-Swift.h" #import From 00af03a138d764543317d9e6157e3f1e3ef85eee Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 14 Mar 2024 14:06:49 +0100 Subject: [PATCH 04/51] Update scenarios to use new scenario structure --- Gemfile | 2 +- Gemfile.lock | 55 +++---- docker-compose.yml | 6 +- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 20 +-- .../ios/iOSTestApp/ViewController.swift | 45 ++---- .../macOSTestApp.xcodeproj/project.pbxproj | 20 ++- .../macos/macOSTestApp/MainWindowController.m | 43 +++--- .../shared/scenarios/AbortOverrideScenario.m | 4 +- .../fixtures/shared/scenarios/AbortScenario.m | 4 +- .../scenarios/AccessNonObjectScenario.m | 4 +- ...ceAttributesCallbackOverrideScenario.swift | 5 +- ...viceAttributesConfigOverrideScenario.swift | 5 +- ...ibutesInfiniteLaunchDurationScenario.swift | 4 +- .../AppAndDeviceAttributesScenario.swift | 4 +- ...iceAttributesStartWithApiKeyScenario.swift | 2 - ...nhandledExceptionAfterLaunchScenario.swift | 4 +- ...handledExceptionDuringLaunchScenario.swift | 4 +- .../scenarios/AppDurationScenario.swift | 4 +- .../AppHangDidBecomeActiveScenario.swift | 4 +- .../scenarios/AppHangDisabledScenario.swift | 4 +- .../AppHangFatalDisabledScenario.swift | 4 +- .../scenarios/AppHangFatalOnlyScenario.swift | 4 +- .../AppHangInTerminationScenario.swift | 4 +- .../shared/scenarios/AppHangScenario.swift | 6 +- .../scenarios/AsyncSafeThreadScenario.m | 4 +- .../AttemptDeliveryOnCrashScenario.swift | 7 +- .../shared/scenarios/AutoCaptureRunScenario.m | 4 +- .../AutoContextNSErrorScenario.swift | 4 +- .../AutoContextNSExceptionScenario.swift | 4 +- .../AutoDetectFalseAbortScenario.swift | 8 +- .../AutoDetectFalseHandledScenario.swift | 8 +- .../AutoDetectFalseNSExceptionScenario.swift | 8 +- .../AutoSessionCustomVersionScenario.m | 4 +- .../AutoSessionMixedEventsScenario.m | 12 +- .../scenarios/AutoSessionUnhandledScenario.m | 8 +- .../scenarios/AutoSessionWithUserScenario.m | 4 +- .../BareboneTestHandledScenario.swift | 4 +- .../BareboneTestUnhandledErrorScenario.swift | 6 +- .../BreadcrumbCallbackCrashScenario.swift | 4 +- .../BreadcrumbCallbackDiscardScenario.swift | 4 +- .../BreadcrumbCallbackOrderScenario.swift | 4 +- .../BreadcrumbCallbackOverrideScenario.swift | 4 +- .../BreadcrumbCallbackRemovalScenario.m | 5 +- .../shared/scenarios/BuiltinTrapScenario.m | 4 +- .../scenarios/ConcurrentCrashesScenario.mm | 4 +- .../CriticalThermalStateScenario.swift | 39 +++-- .../CustomPluginNotifierDescriptionScenario.m | 4 +- .../scenarios/CxxExceptionOverrideScenario.mm | 4 +- .../shared/scenarios/CxxExceptionScenario.mm | 4 +- ...llExceptManualExceptionsAndCrashScenario.m | 16 +- .../scenarios/DisableMachExceptionScenario.m | 12 +- .../scenarios/DisableNSExceptionScenario.m | 4 +- .../DisableSignalsExceptionScenario.m | 12 +- ...abledReleaseStageAutoSessionScenario.swift | 4 +- ...ledReleaseStageManualSessionScenario.swift | 4 +- .../DisabledSessionTrackingScenario.m | 4 +- ...ClassesHandledExceptionRegexScenario.swift | 4 +- ...DiscardClassesUnhandledCrashScenario.swift | 6 +- ...ardClassesUnhandledExceptionScenario.swift | 6 +- .../DiscardedBreadcrumbTypeScenario.swift | 4 +- .../scenarios/DispatchCrashScenario.swift | 4 +- .../EnabledBreadcrumbTypesIsNilScenario.swift | 5 +- .../scenarios/EnabledErrorTypesCxxScenario.mm | 14 +- ...abledReleaseStageAutoSessionScenario.swift | 4 +- ...ledReleaseStageManualSessionScenario.swift | 4 +- ...dledErrorInvalidReleaseStageScenario.swift | 4 +- .../HandledErrorOverrideScenario.swift | 6 +- .../scenarios/HandledErrorScenario.swift | 6 +- .../HandledErrorThreadSendAlwaysScenario.m | 4 +- ...dledErrorThreadSendUnhandledOnlyScenario.m | 4 +- ...andledErrorValidReleaseStageScenario.swift | 4 +- .../scenarios/HandledExceptionScenario.swift | 6 +- .../scenarios/InvalidCrashReportScenario.m | 6 +- .../scenarios/LastRunInfoScenario.swift | 4 +- .../ManualContextClientScenario.swift | 4 +- .../ManualContextConfigurationScenario.swift | 4 +- .../ManualContextOnErrorScenario.swift | 4 +- .../shared/scenarios/ManualSessionScenario.m | 4 +- .../scenarios/ManualSessionWithUserScenario.m | 4 +- .../scenarios/ManyConcurrentNotifyScenario.m | 8 +- .../scenarios/MarkUnhandledHandledScenario.m | 4 +- .../scenarios/MaxPersistedSessionsScenario.m | 23 ++- .../scenarios/MetadataMergeScenario.swift | 4 +- .../MetadataRedactionDefaultScenario.swift | 6 +- .../MetadataRedactionNestedScenario.swift | 8 +- .../MetadataRedactionRegexScenario.swift | 10 +- .../ModifyBreadcrumbInNotifyScenario.swift | 4 +- .../scenarios/ModifyBreadcrumbScenario.swift | 4 +- .../scenarios/NSExceptionShiftScenario.m | 4 +- .../NetworkBreadcrumbsScenario.swift | 9 +- .../shared/scenarios/NewSessionScenario.swift | 5 +- .../scenarios/NonExistentMethodScenario.m | 4 +- .../NotifyCallbackCrashScenario.swift | 4 +- .../shared/scenarios/NullPointerScenario.m | 4 +- .../OOMAutoDetectErrorsScenario.swift | 5 +- .../OOMEnabledErrorTypesScenario.swift | 5 +- .../scenarios/OOMInactiveScenario.swift | 4 +- .../shared/scenarios/OOMLoadScenario.swift | 4 +- .../fixtures/shared/scenarios/OOMScenario.m | 4 +- .../shared/scenarios/OOMSessionScenario.swift | 4 +- .../shared/scenarios/OOMSessionlessScenario.m | 4 +- .../scenarios/OOMWillTerminateScenario.m | 4 +- .../scenarios/ObjCExceptionOverrideScenario.m | 4 +- .../shared/scenarios/ObjCExceptionScenario.m | 4 +- .../shared/scenarios/ObjCMsgSendScenario.m | 4 +- .../scenarios/OldCrashReportScenario.swift | 4 +- .../scenarios/OldHandledErrorScenario.swift | 4 +- .../shared/scenarios/OldSessionScenario.m | 8 +- .../shared/scenarios/OnCrashHandlerScenario.m | 4 +- .../scenarios/OnErrorOverwriteScenario.swift | 5 +- ...ErrorOverwriteUnhandledFalseScenario.swift | 5 +- ...nErrorOverwriteUnhandledTrueScenario.swift | 5 +- .../OnSendCallbackOrderScenario.swift | 4 +- .../scenarios/OnSendCallbackRemovalScenario.m | 4 +- .../OnSendErrorCallbackCrashScenario.swift | 4 +- ...endErrorCallbackFeatureFlagsScenario.swift | 4 +- .../OnSendErrorPersistenceScenario.m | 5 +- .../scenarios/OnSendOverwriteScenario.swift | 5 +- .../OriginalErrorNSErrorScenario.swift | 5 +- .../OriginalErrorNSExceptionScenario.swift | 5 +- .../OversizedCrashReportScenario.swift | 4 +- .../OversizedHandledErrorScenario.swift | 4 +- .../scenarios/OverwriteLinkRegisterScenario.m | 4 +- .../scenarios/PrivilegedInstructionScenario.m | 4 +- .../scenarios/ReadGarbagePointerScenario.m | 4 +- .../shared/scenarios/ReadOnlyPageScenario.m | 4 +- .../shared/scenarios/RecrashScenarios.mm | 4 +- .../shared/scenarios/ReleasedObjectScenario.m | 6 +- .../ReportBackgroundAppHangScenario.swift | 4 +- .../scenarios/ResumeSessionOOMScenario.m | 4 +- .../scenarios/ResumedSessionScenario.swift | 5 +- .../shared/scenarios/SIGBUSScenario.m | 4 +- .../shared/scenarios/SIGFPEScenario.m | 4 +- .../shared/scenarios/SIGILLScenario.m | 4 +- .../shared/scenarios/SIGPIPEIgnoredScenario.m | 4 +- .../shared/scenarios/SIGPIPEScenario.m | 4 +- .../shared/scenarios/SIGSEGVScenario.m | 4 +- .../shared/scenarios/SIGSYSScenario.m | 4 +- .../shared/scenarios/SIGTRAPScenario.m | 4 +- features/fixtures/shared/scenarios/Scenario.h | 24 ++- features/fixtures/shared/scenarios/Scenario.m | 141 +++--------------- ...nchCrashesSynchronouslyFalseScenario.swift | 4 +- ...SynchronouslyLaunchCompletedScenario.swift | 2 +- ...ndLaunchCrashesSynchronouslyScenario.swift | 8 +- .../SessionCallbackCrashScenario.swift | 4 +- .../SessionCallbackDiscardScenario.swift | 4 +- .../SessionCallbackOrderScenario.swift | 4 +- .../SessionCallbackOverrideScenario.swift | 4 +- .../SessionCallbackRemovalScenario.m | 4 +- .../shared/scenarios/SessionOOMScenario.m | 4 +- .../shared/scenarios/StackOverflowScenario.m | 4 +- .../shared/scenarios/StopSessionOOMScenario.m | 4 +- .../scenarios/StoppedSessionScenario.swift | 5 +- .../scenarios/SwiftAssertionScenario.swift | 7 +- .../shared/scenarios/SwiftCrashScenario.swift | 7 +- .../TelemetryUsageDisabledScenario.swift | 4 +- .../ThermalStateBreadcrumbScenario.swift | 5 +- .../scenarios/UndefinedInstructionScenario.m | 4 +- ...rorChangeInvalidReleaseStageScenario.swift | 6 +- ...ErrorChangeValidReleaseStageScenario.swift | 6 +- ...dledErrorInvalidReleaseStageScenario.swift | 4 +- .../UnhandledErrorThreadSendAlwaysScenario.m | 4 +- .../UnhandledErrorThreadSendNeverScenario.m | 4 +- ...andledErrorValidReleaseStageScenario.swift | 4 +- .../UnhandledMachExceptionOverrideScenario.m | 4 +- .../UnhandledMachExceptionScenario.m | 4 +- .../scenarios/UserEventOverrideScenario.swift | 4 +- .../scenarios/UserFromClientScenario.swift | 4 +- .../scenarios/UserFromConfigScenario.swift | 4 +- .../shared/scenarios/UserInfoScenario.swift | 6 +- .../shared/scenarios/UserNilScenario.swift | 6 +- .../UserPersistenceDontPersistUserScenario.m | 4 +- .../scenarios/UserPersistenceNoUserScenario.m | 4 - ...UserPersistencePersistUserClientScenario.m | 4 +- .../UserPersistencePersistUserScenario.m | 4 +- .../UserSessionOverrideScenario.swift | 4 +- .../shared/utils/BugsnagWrapper.swift | 4 +- .../utils}/CommandReaderThread.swift | 0 .../iOSTestApp => shared/utils}/Fixture.swift | 96 ++++++++---- .../utils}/FixtureConfig.swift | 10 +- .../shared/{scenarios => utils}/Logging.h | 0 .../shared/{scenarios => utils}/Logging.m | 0 .../utils}/MazeRunnerCommand.swift | 0 features/steps/app_steps.rb | 19 +-- 184 files changed, 650 insertions(+), 696 deletions(-) rename features/fixtures/{ios/iOSTestApp => shared/utils}/CommandReaderThread.swift (100%) rename features/fixtures/{ios/iOSTestApp => shared/utils}/Fixture.swift (57%) rename features/fixtures/{ios/iOSTestApp => shared/utils}/FixtureConfig.swift (79%) rename features/fixtures/shared/{scenarios => utils}/Logging.h (100%) rename features/fixtures/shared/{scenarios => utils}/Logging.m (100%) rename features/fixtures/{ios/iOSTestApp => shared/utils}/MazeRunnerCommand.swift (100%) diff --git a/Gemfile b/Gemfile index 1ac230de5..fdd9471c2 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', '~> 8.0' +gem 'bugsnag-maze-runner', '~> 9.0' gem 'cocoapods' gem 'xcpretty' diff --git a/Gemfile.lock b/Gemfile.lock index a1573dab1..3d4f6bc1e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,11 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.6) + CFPropertyList (3.0.7) + base64 + nkf rexml - activesupport (7.1.2) + activesupport (7.1.3.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -13,7 +15,7 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.5) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) @@ -27,10 +29,10 @@ GEM selenium-webdriver (~> 4.2, < 4.6) atomos (0.1.3) base64 (0.2.0) - bigdecimal (3.1.4) - bugsnag (6.26.0) + bigdecimal (3.1.7) + bugsnag (6.26.3) concurrent-ruby (~> 1.0) - bugsnag-maze-runner (8.13.0) + bugsnag-maze-runner (9.5.0) appium_lib (~> 12.0.0) appium_lib_core (~> 5.4.0) bugsnag (~> 6.24) @@ -50,10 +52,10 @@ GEM builder (3.2.4) childprocess (4.1.0) claide (1.1.0) - cocoapods (1.14.3) + cocoapods (1.15.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.3) + cocoapods-core (= 1.15.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -68,7 +70,7 @@ GEM nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.3) + cocoapods-core (1.15.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -88,7 +90,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.2.2) + concurrent-ruby (1.2.3) connection_pool (2.4.1) cucumber (7.1.0) builder (~> 3.2, >= 3.2.4) @@ -122,10 +124,9 @@ GEM cucumber-core (~> 10.1, >= 10.1.0) cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) curb (0.9.11) - diff-lcs (1.5.0) + diff-lcs (1.5.1) dogstatsd-ruby (5.5.0) - drb (2.2.0) - ruby2_keywords + drb (2.2.1) ecma-re-validator (0.4.0) regexp_parser (~> 2.2) escape (0.0.4) @@ -141,40 +142,41 @@ GEM gh_inspector (1.1.3) hana (1.3.7) httpclient (2.8.3) - i18n (1.14.1) + i18n (1.14.4) concurrent-ruby (~> 1.0) - json (2.6.3) + json (2.7.1) json_schemer (0.2.25) ecma-re-validator (~> 0.3) hana (~> 1.3) regexp_parser (~> 2.0) simpleidn (~> 0.2) uri_template (~> 0.7) - mime-types (3.5.1) + mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2023.1003) - minitest (5.20.0) + mime-types-data (3.2024.0305) + minitest (5.22.3) molinillo (0.8.0) multi_test (0.1.2) mutex_m (0.2.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nokogiri (1.15.5-arm64-darwin) + nkf (0.2.0) + nokogiri (1.16.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.15.5-x86_64-darwin) + nokogiri (1.16.2-x86_64-darwin) racc (~> 1.4) optimist (3.0.1) os (1.0.1) power_assert (2.0.3) public_suffix (4.0.7) racc (1.7.3) - rack (2.2.8) + rack (2.2.8.1) rake (12.3.3) - regexp_parser (2.8.2) + regexp_parser (2.9.0) rexml (3.2.6) + rouge (2.0.7) ruby-macho (2.5.1) - ruby2_keywords (0.0.5) rubyzip (2.3.2) selenium-webdriver (4.5.0) childprocess (>= 0.5, < 5.0) @@ -201,21 +203,24 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - xcodeproj (1.23.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (~> 3.2.4) + xcpretty (0.3.0) + rouge (~> 2.0.7) PLATFORMS arm64-darwin-22 x86_64-darwin-19 DEPENDENCIES - bugsnag-maze-runner (~> 8.0) + bugsnag-maze-runner (~> 9.0) cocoapods + xcpretty BUNDLED WITH 2.4.8 diff --git a/docker-compose.yml b/docker-compose.yml index 6ee1dc6b1..62c1de71f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ x-common-environment: &common-environment services: cocoa-maze-runner: - image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v8-cli + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v9-cli environment: <<: *common-environment BROWSER_STACK_USERNAME: @@ -32,7 +32,7 @@ services: - ./maze_output:/app/maze_output cocoa-maze-runner-bitbar: - image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v8-cli + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v9-cli environment: <<: *common-environment BITBAR_USERNAME: @@ -46,7 +46,7 @@ services: - /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-v8-cli-legacy + image: 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner-releases:latest-v9-cli-legacy environment: <<: *common-environment BROWSER_STACK_USERNAME: diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index f3662387d..447cbd258 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -88,9 +88,9 @@ 01FA9EC426D63BB20059FF4A /* AppHangInTerminationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA9EC326D63BB20059FF4A /* AppHangInTerminationScenario.swift */; }; 095E095A2AF3BE8D00273F1F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095E09592AF3BE8D00273F1F /* Logging.swift */; }; 095E095D2AF3BFDA00273F1F /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 095E095C2AF3BFDA00273F1F /* Logging.m */; }; - 09F024F82B9F3971007D9F73 /* FixtureConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */; }; 09F024FA2B9F3ACD007D9F73 /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024F92B9F3ACD007D9F73 /* Fixture.swift */; }; 09F024FC2B9F3B16007D9F73 /* MazeRunnerCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */; }; + 09F0250B2BA1E640007D9F73 /* FixtureConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F0250A2BA1E640007D9F73 /* FixtureConfig.swift */; }; 6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */; }; 8A096DF627C7E56C00DB6ECC /* CxxUnexpectedScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */; }; 8A096DFC27C7E77600DB6ECC /* CxxBareThrowScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */; }; @@ -291,9 +291,9 @@ 095E09592AF3BE8D00273F1F /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 095E095B2AF3BFDA00273F1F /* Logging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 095E095C2AF3BFDA00273F1F /* Logging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = ""; }; - 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixtureConfig.swift; sourceTree = ""; }; 09F024F92B9F3ACD007D9F73 /* Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fixture.swift; sourceTree = ""; }; 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MazeRunnerCommand.swift; sourceTree = ""; }; + 09F0250A2BA1E640007D9F73 /* FixtureConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixtureConfig.swift; sourceTree = ""; }; 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = ""; }; 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxUnexpectedScenario.mm; sourceTree = ""; }; 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxBareThrowScenario.mm; sourceTree = ""; }; @@ -431,8 +431,8 @@ 8AB8865720404DD30003E444 = { isa = PBXGroup; children = ( - AA4C7F1329AEA060009B09A9 /* utils */, 8AB8866220404DD30003E444 /* iOSTestApp */, + AA4C7F1329AEA060009B09A9 /* utils */, F42953DE2BB41023C0B07F41 /* scenarios */, 8AB8866120404DD30003E444 /* Products */, D018DCE3210BCB4D0B8FA6D2 /* Frameworks */, @@ -452,13 +452,9 @@ children = ( 8AB8866320404DD30003E444 /* AppDelegate.swift */, 8AB8866A20404DD30003E444 /* Assets.xcassets */, - AA8BAEE22A4DD90E00A8BEA7 /* CommandReaderThread.swift */, - 09F024F92B9F3ACD007D9F73 /* Fixture.swift */, - 09F024F72B9F3971007D9F73 /* FixtureConfig.swift */, 8AB8866F20404DD30003E444 /* Info.plist */, 8AB8866C20404DD30003E444 /* LaunchScreen.storyboard */, 8AB8866720404DD30003E444 /* Main.storyboard */, - 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */, 8AB8866520404DD30003E444 /* ViewController.swift */, ); path = iOSTestApp; @@ -468,7 +464,13 @@ isa = PBXGroup; children = ( AA4C7F1429AEA0C4009B09A9 /* BugsnagWrapper.swift */, + AA8BAEE22A4DD90E00A8BEA7 /* CommandReaderThread.swift */, + 09F024F92B9F3ACD007D9F73 /* Fixture.swift */, + 09F0250A2BA1E640007D9F73 /* FixtureConfig.swift */, + 095E095B2AF3BFDA00273F1F /* Logging.h */, + 095E095C2AF3BFDA00273F1F /* Logging.m */, 095E09592AF3BE8D00273F1F /* Logging.swift */, + 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */, ); name = utils; path = ../shared/utils; @@ -567,8 +569,6 @@ 01B6BB7425D5748800FC4DE6 /* LastRunInfoScenario.swift */, 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */, 8AB65FCB22DC77CB001200AB /* LoadConfigFromFileScenario.swift */, - 095E095B2AF3BFDA00273F1F /* Logging.h */, - 095E095C2AF3BFDA00273F1F /* Logging.m */, E7B79CD1247FD66E0039FB88 /* ManualContextClientScenario.swift */, E7B79CCF247FD6660039FB88 /* ManualContextConfigurationScenario.swift */, E7B79CD3247FD6760039FB88 /* ManualContextOnErrorScenario.swift */, @@ -900,6 +900,7 @@ A1117E552535A59100014FDA /* OOMLoadScenario.swift in Sources */, 8A840FBA21AF5C450041DBFA /* SwiftAssertionScenario.swift in Sources */, E753F24824927412001FB671 /* OnSendErrorCallbackCrashScenario.swift in Sources */, + 09F0250B2BA1E640007D9F73 /* FixtureConfig.swift in Sources */, 01847DD626453D4E00ADA4C7 /* InvalidCrashReportScenario.m in Sources */, 001E5502243B8FDA0009E31D /* AutoCaptureRunScenario.m in Sources */, 0104085F258CA0A100933C60 /* DispatchCrashScenario.swift in Sources */, @@ -944,7 +945,6 @@ E7B79CD8247FD7810039FB88 /* AutoContextNSExceptionScenario.swift in Sources */, 010BAB3B2833D2280003FF36 /* UnhandledErrorValidReleaseStageScenario.swift in Sources */, 010BDFB92885562D007025F9 /* ReportBackgroundAppHangScenario.swift in Sources */, - 09F024F82B9F3971007D9F73 /* FixtureConfig.swift in Sources */, 01E356C026CD5B6A00BE3F64 /* ThermalStateBreadcrumbScenario.swift in Sources */, E7B79CD2247FD66E0039FB88 /* ManualContextClientScenario.swift in Sources */, E7B79CD6247FD7750039FB88 /* AutoContextNSErrorScenario.swift in Sources */, diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index b5587915a..9c8f9f691 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -7,7 +7,6 @@ // import UIKit -import os class ViewController: UIViewController { @@ -18,49 +17,33 @@ class ViewController: UIViewController { required init?(coder: NSCoder) { super.init(coder: coder) + apiKeyField.text = Fixture.defaultApiKey fixture.start() } @IBAction func runTestScenario() { - // Cater for multiple calls to run() - if Scenario.current == nil { - prepareScenario() + let apiKey = apiKeyField.text! + let scenarioName = scenarioNameField.text! + let args = scenarioMetaDataField.text!.count > 0 ? [scenarioMetaDataField.text!] : [] - logInfo("Starting Bugsnag for scenario: \(Scenario.current!)") - Scenario.current!.startBugsnag() - } - - logInfo("Running scenario: \(Scenario.current!)") - Scenario.current!.run() + fixture.setApiKey(apiKey: apiKey) + fixture.runScenario(scenarioName: scenarioName, args: args) {} } @IBAction func startBugsnag() { - prepareScenario() - - logInfo("Starting Bugsnag for scenario: \(Scenario.current!)") - Scenario.current!.startBugsnag() + let apiKey = apiKeyField.text! + let scenarioName = scenarioNameField.text! + let args = scenarioMetaDataField.text!.count > 0 ? [scenarioMetaDataField.text!] : [] + + fixture.setApiKey(apiKey: apiKey) + fixture.startBugsnagForScenario(scenarioName: scenarioName, args: args) {} } @IBAction func clearPersistentData(_ sender: Any) { - Scenario.clearPersistentData() - } - - 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! - logInfo("Running in manual mode with API key: \(apiKey)") - UserDefaults.standard.setValue(apiKey, forKey: "apiKey") - config = BugsnagConfiguration(apiKeyField.text!) - } - - Scenario.createScenarioNamed(scenarioNameField.text!, - withConfig: config) - Scenario.current!.eventMode = scenarioMetaDataField.text + fixture.clearPersistentData() } @objc func didEnterBackgroundNotification() { - Scenario.current?.didEnterBackgroundNotification() + fixture.didEnterBackgroundNotification() } } diff --git a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj index 4bba25555..f67ad192c 100644 --- a/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/macos/macOSTestApp.xcodeproj/project.pbxproj @@ -184,6 +184,10 @@ 01FA9EC626D64FFF0059FF4A /* AppHangInTerminationScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FA9EC526D64FFF0059FF4A /* AppHangInTerminationScenario.swift */; }; 095E095F2AF3C98F00273F1F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095E095E2AF3C98F00273F1F /* Logging.swift */; }; 095E09622AF3C9A500273F1F /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 095E09612AF3C9A500273F1F /* Logging.m */; }; + 09F0250E2BA301FD007D9F73 /* FixtureConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F0250C2BA301FD007D9F73 /* FixtureConfig.swift */; }; + 09F0250F2BA301FD007D9F73 /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F0250D2BA301FD007D9F73 /* Fixture.swift */; }; + 09F025112BA30225007D9F73 /* MazeRunnerCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F025102BA30225007D9F73 /* MazeRunnerCommand.swift */; }; + 09F025132BA30240007D9F73 /* CommandReaderThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F025122BA30240007D9F73 /* CommandReaderThread.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 */; }; @@ -393,6 +397,10 @@ 095E095E2AF3C98F00273F1F /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 095E09602AF3C9A500273F1F /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = ""; }; 095E09612AF3C9A500273F1F /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = ""; }; + 09F0250C2BA301FD007D9F73 /* FixtureConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FixtureConfig.swift; sourceTree = ""; }; + 09F0250D2BA301FD007D9F73 /* Fixture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixture.swift; sourceTree = ""; }; + 09F025102BA30225007D9F73 /* MazeRunnerCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MazeRunnerCommand.swift; sourceTree = ""; }; + 09F025122BA30240007D9F73 /* CommandReaderThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandReaderThread.swift; sourceTree = ""; }; 2C49722B331FF4B0DC477462 /* Pods-macOSTestApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-macOSTestApp.release.xcconfig"; path = "Target Support Files/Pods-macOSTestApp/Pods-macOSTestApp.release.xcconfig"; sourceTree = ""; }; 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 = ""; }; @@ -499,8 +507,6 @@ 01B6BB7125D56CBF00FC4DE6 /* LastRunInfoScenario.swift */, 01F47C23254B1B2C00B184AD /* LoadConfigFromFileAutoScenario.swift */, 01F47C94254B1B2F00B184AD /* LoadConfigFromFileScenario.swift */, - 095E09602AF3C9A500273F1F /* Logging.h */, - 095E09612AF3C9A500273F1F /* Logging.m */, 01F47C3B254B1B2D00B184AD /* ManualContextClientScenario.swift */, 01F47C47254B1B2D00B184AD /* ManualContextConfigurationScenario.swift */, 01F47C82254B1B2F00B184AD /* ManualContextOnErrorScenario.swift */, @@ -665,7 +671,13 @@ isa = PBXGroup; children = ( AA4C7F1729AEA31D009B09A9 /* BugsnagWrapper.swift */, + 09F025122BA30240007D9F73 /* CommandReaderThread.swift */, + 09F0250D2BA301FD007D9F73 /* Fixture.swift */, + 09F0250C2BA301FD007D9F73 /* FixtureConfig.swift */, + 095E09602AF3C9A500273F1F /* Logging.h */, + 095E09612AF3C9A500273F1F /* Logging.m */, 095E095E2AF3C98F00273F1F /* Logging.swift */, + 09F025102BA30225007D9F73 /* MazeRunnerCommand.swift */, ); name = utils; path = ../shared/utils; @@ -802,6 +814,7 @@ 01F47D31254B1B3100B184AD /* OverwriteLinkRegisterScenario.m in Sources */, 01F47D08254B1B3100B184AD /* AutoSessionCustomVersionScenario.m in Sources */, 01F47CC6254B1B3100B184AD /* HandledExceptionScenario.swift in Sources */, + 09F0250E2BA301FD007D9F73 /* FixtureConfig.swift in Sources */, 01F47D0D254B1B3100B184AD /* LoadConfigFromFileScenario.swift in Sources */, 095E09622AF3C9A500273F1F /* Logging.m in Sources */, 01F47CF5254B1B3100B184AD /* StoppedSessionScenario.swift in Sources */, @@ -849,6 +862,7 @@ 0184DBDE28C6317C006AF50B /* CouldNotCreateDirectoryScenario.swift in Sources */, 01F47CCC254B1B3100B184AD /* OnSendCallbackOrderScenario.swift in Sources */, 017D9D042833C81100B0AA87 /* HandledErrorThreadSendAlwaysScenario.m in Sources */, + 09F025132BA30240007D9F73 /* CommandReaderThread.swift in Sources */, 01F47D04254B1B3100B184AD /* ModifyBreadcrumbScenario.swift in Sources */, 01F47CD3254B1B3100B184AD /* EnabledBreadcrumbTypesIsNilScenario.swift in Sources */, 01F47CC4254B1B3100B184AD /* OriginalErrorNSExceptionScenario.swift in Sources */, @@ -940,6 +954,7 @@ 010BAB752833D34A0003FF36 /* SendLaunchCrashesSynchronouslyFalseScenario.swift in Sources */, 01F47D1D254B1B3100B184AD /* DiscardedBreadcrumbTypeScenario.swift in Sources */, 01F47CC5254B1B3100B184AD /* LoadConfigFromFileAutoScenario.swift in Sources */, + 09F0250F2BA301FD007D9F73 /* Fixture.swift in Sources */, 01F47CCB254B1B3100B184AD /* BreadcrumbCallbackCrashScenario.swift in Sources */, 01F47D02254B1B3100B184AD /* CustomPluginNotifierDescriptionScenario.m in Sources */, 01F47D2C254B1B3100B184AD /* SIGSYSScenario.m in Sources */, @@ -969,6 +984,7 @@ 967F6F1629B767CE0054EED8 /* InternalWorkingsScenario.swift in Sources */, 010BAB672833D34A0003FF36 /* HandledErrorValidReleaseStageScenario.swift in Sources */, 01F47CD1254B1B3100B184AD /* ManualContextClientScenario.swift in Sources */, + 09F025112BA30225007D9F73 /* MazeRunnerCommand.swift in Sources */, 010BAB732833D34A0003FF36 /* AppAndDeviceAttributesStartWithApiKeyScenario.swift in Sources */, 01BB5D2628A1463C00A7F322 /* OversizedBreadcrumbsScenario.swift in Sources */, 01F47CC9254B1B3100B184AD /* BuiltinTrapScenario.m in Sources */, diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 16fa42131..bb277b973 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -10,6 +10,7 @@ #import "Scenario.h" #import "Logging.h" +#import "macOSTestApp-Swift.h" #import @@ -21,6 +22,7 @@ @interface MainWindowController () @property (copy) NSString *scenarioMetadata; @property (copy) NSString *scenarioName; @property (copy) NSString *sessionEndpoint; +@property (nonatomic,strong) Fixture *fixture; @end @@ -34,6 +36,7 @@ - (void)windowDidLoad { self.apiKey = @"12312312312312312312312312312312"; self.notifyEndpoint = @"http://localhost:9339/notify"; self.sessionEndpoint = @"http://localhost:9339/sessions"; + self.fixture = [[Fixture alloc] init]; } - (BugsnagConfiguration *)configuration { @@ -50,38 +53,33 @@ - (BugsnagConfiguration *)configuration { - (IBAction)runScenario:(id)sender { logDebug(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - // Cater for multiple calls to -run - if (!Scenario.currentScenario) { - [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; - Scenario.currentScenario.eventMode = self.scenarioMetadata; - - logInfo(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); - [Scenario.currentScenario startBugsnag]; - } - - logInfo(@"Will run scenario: %@", Scenario.currentScenario); + [self.fixture setApiKeyWithApiKey:self.apiKey]; + [self.fixture setNotifyEndpointWithEndpoint:self.notifyEndpoint]; + [self.fixture setSessionEndpointWithEndpoint:self.sessionEndpoint]; + NSString *scenarioName = self.scenarioName; + NSArray *args = @[self.scenarioMetadata]; + // 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(), ^{ - logInfo(@"Running scenario: %@", Scenario.currentScenario); - [Scenario.currentScenario run]; + logInfo(@"Running scenario: %@", scenarioName); + [self.fixture runScenarioWithScenarioName:scenarioName args:args completion:^{}]; }); } - (IBAction)startBugsnag:(id)sender { logDebug(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); - [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; - Scenario.currentScenario.eventMode = self.scenarioMetadata; + NSString *scenarioName = self.scenarioName; + NSArray *args = @[self.scenarioMetadata]; - logInfo(@"Starting Bugsnag for scenario: %@", Scenario.currentScenario); - [Scenario.currentScenario startBugsnag]; + [self.fixture startBugsnagForScenarioWithScenarioName:scenarioName args:args completion:^{}]; } - (IBAction)clearPersistentData:(id)sender { logInfo(@"Clearing persistent data"); - [Scenario clearPersistentData]; + [self.fixture clearPersistentData]; } - (IBAction)useDashboardEndpoints:(id)sender { @@ -90,11 +88,12 @@ - (IBAction)useDashboardEndpoints:(id)sender { } - (IBAction)executeMazeRunnerCommand:(id)sender { - Scenario.baseMazeAddress = @"http://localhost:9339"; - [Scenario executeMazeRunnerCommand:^(NSString *scenarioName, NSString *eventMode){ - self.scenarioName = scenarioName; - self.scenarioMetadata = eventMode; - }]; + NSLog(@"WARNING: executeMazeRunnerCommand has been DISABLED."); +// Scenario.baseMazeAddress = @"http://localhost:9339"; +// [Scenario executeMazeRunnerCommand:^(NSString *scenarioName, NSString *eventMode){ +// self.scenarioName = scenarioName; +// self.scenarioMetadata = eventMode; +// }]; } @end diff --git a/features/fixtures/shared/scenarios/AbortOverrideScenario.m b/features/fixtures/shared/scenarios/AbortOverrideScenario.m index 1f1e3d347..863df9bde 100644 --- a/features/fixtures/shared/scenarios/AbortOverrideScenario.m +++ b/features/fixtures/shared/scenarios/AbortOverrideScenario.m @@ -32,9 +32,9 @@ @interface AbortOverrideScenario : MarkUnhandledHandledScenario @implementation AbortOverrideScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/AbortScenario.m b/features/fixtures/shared/scenarios/AbortScenario.m index 3c5a2d3e1..eebb038f2 100644 --- a/features/fixtures/shared/scenarios/AbortScenario.m +++ b/features/fixtures/shared/scenarios/AbortScenario.m @@ -32,9 +32,9 @@ @interface AbortScenario : Scenario @implementation AbortScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/AccessNonObjectScenario.m b/features/fixtures/shared/scenarios/AccessNonObjectScenario.m index 0927824ef..287050227 100644 --- a/features/fixtures/shared/scenarios/AccessNonObjectScenario.m +++ b/features/fixtures/shared/scenarios/AccessNonObjectScenario.m @@ -35,9 +35,9 @@ @interface AccessNonObjectScenario : Scenario @implementation AccessNonObjectScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesCallbackOverrideScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesCallbackOverrideScenario.swift index 463b1d912..1bd0c3f59 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesCallbackOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesCallbackOverrideScenario.swift @@ -1,6 +1,7 @@ class AppAndDeviceAttributesCallbackOverrideScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.addOnSendError { (event) -> Bool in @@ -13,8 +14,6 @@ class AppAndDeviceAttributesCallbackOverrideScenario: Scenario { return true } - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesConfigOverrideScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesConfigOverrideScenario.swift index 4d4969ad9..6ed8eff75 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesConfigOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesConfigOverrideScenario.swift @@ -3,15 +3,14 @@ */ class AppAndDeviceAttributesConfigOverrideScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.appType = "iLeet" self.config.bundleVersion = "12345" self.config.context = "myContext" self.config.releaseStage = "secondStage" - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesInfiniteLaunchDurationScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesInfiniteLaunchDurationScenario.swift index e39c38a10..caa8e6527 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesInfiniteLaunchDurationScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesInfiniteLaunchDurationScenario.swift @@ -1,9 +1,9 @@ class AppAndDeviceAttributesInfiniteLaunchDurationScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.launchDurationMillis = 0 - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesScenario.swift index a567ec0a6..70359e8ce 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesScenario.swift @@ -8,10 +8,10 @@ */ class AppAndDeviceAttributesScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.launchDurationMillis = 1000 - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift index b3f4a3363..c943b3508 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesStartWithApiKeyScenario.swift @@ -5,8 +5,6 @@ class AppAndDeviceAttributesStartWithApiKeyScenario: Scenario { override func startBugsnag() { BugsnagWrapper.start(withApiKey: "12312312312312312312312312312312") - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario.swift index d285a8d4f..8656915f9 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario.swift @@ -1,8 +1,8 @@ class AppAndDeviceAttributesUnhandledExceptionAfterLaunchScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario.swift b/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario.swift index 87b12f835..dfa16670b 100644 --- a/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario.swift +++ b/features/fixtures/shared/scenarios/AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario.swift @@ -1,8 +1,8 @@ class AppAndDeviceAttributesUnhandledExceptionDuringLaunchScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppDurationScenario.swift b/features/fixtures/shared/scenarios/AppDurationScenario.swift index fc5745e82..dacafe569 100644 --- a/features/fixtures/shared/scenarios/AppDurationScenario.swift +++ b/features/fixtures/shared/scenarios/AppDurationScenario.swift @@ -12,12 +12,12 @@ class AppDurationScenario: Scenario { var startDate: Date! var startTime: DispatchTime! - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.sendThreads = .never startDate = Date() startTime = .now() - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangDidBecomeActiveScenario.swift b/features/fixtures/shared/scenarios/AppHangDidBecomeActiveScenario.swift index b54f0781e..b2b60b201 100644 --- a/features/fixtures/shared/scenarios/AppHangDidBecomeActiveScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangDidBecomeActiveScenario.swift @@ -2,10 +2,10 @@ import UIKit class AppHangDidBecomeActiveScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.appHangThresholdMillis = 2_000 self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangDisabledScenario.swift b/features/fixtures/shared/scenarios/AppHangDisabledScenario.swift index 513b2df18..bb047dee8 100644 --- a/features/fixtures/shared/scenarios/AppHangDisabledScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangDisabledScenario.swift @@ -1,8 +1,8 @@ class AppHangDisabledScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.enabledErrorTypes.appHangs = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift b/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift index a5b08b688..ef591412e 100644 --- a/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift @@ -1,9 +1,9 @@ class AppHangFatalDisabledScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.enabledErrorTypes.appHangs = false config.enabledErrorTypes.ooms = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift b/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift index c5071c426..0c68629d0 100644 --- a/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift @@ -1,11 +1,11 @@ class AppHangFatalOnlyScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.appHangThresholdMillis = BugsnagAppHangThresholdFatalOnly // Sending synchronously causes an immediate retry upon failure, which creates flakes. config.sendLaunchCrashesSynchronously = false config.addFeatureFlag(name: "Testing") - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangInTerminationScenario.swift b/features/fixtures/shared/scenarios/AppHangInTerminationScenario.swift index 63e7b8942..58710fe25 100644 --- a/features/fixtures/shared/scenarios/AppHangInTerminationScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangInTerminationScenario.swift @@ -14,9 +14,9 @@ import AppKit class AppHangInTerminationScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.appHangThresholdMillis = 2_000 - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AppHangScenario.swift b/features/fixtures/shared/scenarios/AppHangScenario.swift index 5f3474afc..283b5b780 100644 --- a/features/fixtures/shared/scenarios/AppHangScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangScenario.swift @@ -8,16 +8,16 @@ class AppHangScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.appHangThresholdMillis = 2_000 config.enabledBreadcrumbTypes = [.user] config.addFeatureFlag(name: "Testing") - super.startBugsnag() } override func run() { Bugsnag.setContext("App Hang Scenario") - let timeInterval = TimeInterval(eventMode!)! + let timeInterval = TimeInterval(args[0])! logDebug("Simulating an app hang of \(timeInterval) seconds...") if timeInterval > 2 { Thread.sleep(forTimeInterval: 1.5) diff --git a/features/fixtures/shared/scenarios/AsyncSafeThreadScenario.m b/features/fixtures/shared/scenarios/AsyncSafeThreadScenario.m index 21469d72e..ab5b547b9 100644 --- a/features/fixtures/shared/scenarios/AsyncSafeThreadScenario.m +++ b/features/fixtures/shared/scenarios/AsyncSafeThreadScenario.m @@ -37,9 +37,9 @@ @interface AsyncSafeThreadScenario : Scenario @implementation AsyncSafeThreadScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/AttemptDeliveryOnCrashScenario.swift b/features/fixtures/shared/scenarios/AttemptDeliveryOnCrashScenario.swift index 5dd2f1b03..db209d7fc 100644 --- a/features/fixtures/shared/scenarios/AttemptDeliveryOnCrashScenario.swift +++ b/features/fixtures/shared/scenarios/AttemptDeliveryOnCrashScenario.swift @@ -8,19 +8,18 @@ class AttemptDeliveryOnCrashScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() BSGCrashSentryDeliveryTimeout = 15 config.attemptDeliveryOnCrash = true config.addOnSendError { event in event.context = "OnSendError" return true } - super.startBugsnag() } override func run() { - guard let eventMode = eventMode else { return } - switch eventMode { + switch args[0] { case "BadAccess": if let ptr = UnsafePointer(bitPattern: 42) { strlen(ptr) diff --git a/features/fixtures/shared/scenarios/AutoCaptureRunScenario.m b/features/fixtures/shared/scenarios/AutoCaptureRunScenario.m index dc720615c..69b34bae6 100644 --- a/features/fixtures/shared/scenarios/AutoCaptureRunScenario.m +++ b/features/fixtures/shared/scenarios/AutoCaptureRunScenario.m @@ -14,9 +14,9 @@ @interface AutoCaptureRunScenario : Scenario @implementation AutoCaptureRunScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = YES; - [super startBugsnag]; } - (void)run {} diff --git a/features/fixtures/shared/scenarios/AutoContextNSErrorScenario.swift b/features/fixtures/shared/scenarios/AutoContextNSErrorScenario.swift index 78e390125..6a9c4e724 100644 --- a/features/fixtures/shared/scenarios/AutoContextNSErrorScenario.swift +++ b/features/fixtures/shared/scenarios/AutoContextNSErrorScenario.swift @@ -10,9 +10,9 @@ import Foundation class AutoContextNSErrorScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AutoContextNSExceptionScenario.swift b/features/fixtures/shared/scenarios/AutoContextNSExceptionScenario.swift index 83776a007..5957373c6 100644 --- a/features/fixtures/shared/scenarios/AutoContextNSExceptionScenario.swift +++ b/features/fixtures/shared/scenarios/AutoContextNSExceptionScenario.swift @@ -10,9 +10,9 @@ import Foundation class AutoContextNSExceptionScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/AutoDetectFalseAbortScenario.swift b/features/fixtures/shared/scenarios/AutoDetectFalseAbortScenario.swift index 53b2b09ad..d4b51cc57 100644 --- a/features/fixtures/shared/scenarios/AutoDetectFalseAbortScenario.swift +++ b/features/fixtures/shared/scenarios/AutoDetectFalseAbortScenario.swift @@ -13,10 +13,10 @@ import Foundation */ internal class AutoDetectFalseAbortScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false - self.config.autoDetectErrors = false - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false + self.config.autoDetectErrors = false } override func run() { diff --git a/features/fixtures/shared/scenarios/AutoDetectFalseHandledScenario.swift b/features/fixtures/shared/scenarios/AutoDetectFalseHandledScenario.swift index b5e037af0..a1517e17d 100644 --- a/features/fixtures/shared/scenarios/AutoDetectFalseHandledScenario.swift +++ b/features/fixtures/shared/scenarios/AutoDetectFalseHandledScenario.swift @@ -13,10 +13,10 @@ import Foundation */ internal class AutoDetectFalseHandledScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false - self.config.autoDetectErrors = false - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false + self.config.autoDetectErrors = false } override func run() { diff --git a/features/fixtures/shared/scenarios/AutoDetectFalseNSExceptionScenario.swift b/features/fixtures/shared/scenarios/AutoDetectFalseNSExceptionScenario.swift index c7a4c1d47..882cb02bb 100644 --- a/features/fixtures/shared/scenarios/AutoDetectFalseNSExceptionScenario.swift +++ b/features/fixtures/shared/scenarios/AutoDetectFalseNSExceptionScenario.swift @@ -13,10 +13,10 @@ import Foundation */ internal class AutoDetectFalseNSExceptionScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false - self.config.autoDetectErrors = false - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false + self.config.autoDetectErrors = false } override func run() { diff --git a/features/fixtures/shared/scenarios/AutoSessionCustomVersionScenario.m b/features/fixtures/shared/scenarios/AutoSessionCustomVersionScenario.m index a5ff35082..56e99ecf4 100644 --- a/features/fixtures/shared/scenarios/AutoSessionCustomVersionScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionCustomVersionScenario.m @@ -14,12 +14,12 @@ @interface AutoSessionCustomVersionScenario : Scenario @implementation AutoSessionCustomVersionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; [self.config addOnSessionBlock:^BOOL(BugsnagSession * _Nonnull session) { session.app.version = @"2.0.14"; return true; }]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m index 09bd7a1f5..6d8dd7a96 100644 --- a/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionMixedEventsScenario.m @@ -31,13 +31,15 @@ @implementation AutoSessionMixedEventsScenario - (void)run { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self performBlockAndWaitForEventDelivery:^{ + [self waitForEventDelivery:^{ [Bugsnag notifyError:[FirstErr errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + } andThen:^{ + [self waitForEventDelivery:^{ + [Bugsnag notifyError:[SecondErr errorWithDomain:@"com.bugsnag" code:831 userInfo:nil]]; + } andThen:^{ + @throw [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; + }]; }]; - [self performBlockAndWaitForEventDelivery:^{ - [Bugsnag notifyError:[SecondErr errorWithDomain:@"com.bugsnag" code:831 userInfo:nil]]; - }]; - @throw [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; }); } diff --git a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m index bbfd91200..352a0b0a9 100644 --- a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m @@ -14,17 +14,17 @@ @interface AutoSessionUnhandledScenario : Scenario @implementation AutoSessionUnhandledScenario -- (void)startBugsnag { - if ([self.eventMode isEqualToString:@"noevent"]) { +- (void)configure { + [super configure]; + if ([self.args[0] isEqualToString:@"noevent"]) { self.config.autoTrackSessions = NO; } else { self.config.autoTrackSessions = YES; } - [super startBugsnag]; } - (void)run { - if (![self.eventMode isEqualToString:@"noevent"]) { + if (![self.args[0] isEqualToString:@"noevent"]) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ NSException *ex = [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; diff --git a/features/fixtures/shared/scenarios/AutoSessionWithUserScenario.m b/features/fixtures/shared/scenarios/AutoSessionWithUserScenario.m index 4e951eaef..1ae170b66 100644 --- a/features/fixtures/shared/scenarios/AutoSessionWithUserScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionWithUserScenario.m @@ -14,9 +14,9 @@ @interface AutoSessionWithUserScenario : Scenario @implementation AutoSessionWithUserScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; [self.config setUser:@"123" withEmail:@"joe@example.com" andName:@"Joe Bloggs"]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift b/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift index ae7e85198..fc417b064 100644 --- a/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift +++ b/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift @@ -14,7 +14,8 @@ class BareboneTestHandledScenario: Scenario { var afterSendErrorBlock: (() -> Void)? - override func startBugsnag() { + override func configure() { + super.configure() config.addOnBreadcrumb { NSLog("OnBreadcrumb: \"\($0.message)\"") self.onBreadcrumbCount += 1 @@ -57,7 +58,6 @@ class BareboneTestHandledScenario: Scenario { ]) config.appVersion = "12.3" config.bundleVersion = "12301" - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift b/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift index 689505e39..3657a164a 100644 --- a/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift +++ b/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift @@ -2,8 +2,9 @@ class BareboneTestUnhandledErrorScenario: Scenario { private var payload: Payload! - override func startBugsnag() { - if eventMode == "report" { + override func configure() { + super.configure() + if args[0] == "report" { // The version of the app at report time. config.appVersion = "23.4" config.bundleVersion = "23401" @@ -26,7 +27,6 @@ class BareboneTestUnhandledErrorScenario: Scenario { config.context = "Something" config.setUser("barfoo", withEmail: "barfoo@example.com", andName: "Bar Foo") } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BreadcrumbCallbackCrashScenario.swift b/features/fixtures/shared/scenarios/BreadcrumbCallbackCrashScenario.swift index 382fc0839..6ababb9de 100644 --- a/features/fixtures/shared/scenarios/BreadcrumbCallbackCrashScenario.swift +++ b/features/fixtures/shared/scenarios/BreadcrumbCallbackCrashScenario.swift @@ -10,7 +10,8 @@ import Foundation class BreadcrumbCallbackCrashScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = [] @@ -29,7 +30,6 @@ class BreadcrumbCallbackCrashScenario : Scenario { crumb.metadata["secondCallback"] = true return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BreadcrumbCallbackDiscardScenario.swift b/features/fixtures/shared/scenarios/BreadcrumbCallbackDiscardScenario.swift index b1c9532d9..4d781c193 100644 --- a/features/fixtures/shared/scenarios/BreadcrumbCallbackDiscardScenario.swift +++ b/features/fixtures/shared/scenarios/BreadcrumbCallbackDiscardScenario.swift @@ -10,7 +10,8 @@ import Foundation class BreadcrumbCallbackDiscardScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = [] @@ -18,7 +19,6 @@ class BreadcrumbCallbackDiscardScenario : Scenario { crumb.metadata["addedVal"] = true return crumb.message == "Hello World" } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BreadcrumbCallbackOrderScenario.swift b/features/fixtures/shared/scenarios/BreadcrumbCallbackOrderScenario.swift index 3a90f4a37..f5a2422ca 100644 --- a/features/fixtures/shared/scenarios/BreadcrumbCallbackOrderScenario.swift +++ b/features/fixtures/shared/scenarios/BreadcrumbCallbackOrderScenario.swift @@ -10,7 +10,8 @@ import Foundation class BreadcrumbCallbackOrderScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = [] @@ -26,7 +27,6 @@ class BreadcrumbCallbackOrderScenario : Scenario { count += 1 return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BreadcrumbCallbackOverrideScenario.swift b/features/fixtures/shared/scenarios/BreadcrumbCallbackOverrideScenario.swift index fa8159d53..52f67b2ec 100644 --- a/features/fixtures/shared/scenarios/BreadcrumbCallbackOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/BreadcrumbCallbackOverrideScenario.swift @@ -10,7 +10,8 @@ import Foundation class BreadcrumbCallbackOverrideScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = .log @@ -20,7 +21,6 @@ class BreadcrumbCallbackOverrideScenario : Scenario { crumb.metadata["foo"] = "wham" return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/BreadcrumbCallbackRemovalScenario.m b/features/fixtures/shared/scenarios/BreadcrumbCallbackRemovalScenario.m index 7fd294638..51c5dec50 100644 --- a/features/fixtures/shared/scenarios/BreadcrumbCallbackRemovalScenario.m +++ b/features/fixtures/shared/scenarios/BreadcrumbCallbackRemovalScenario.m @@ -14,7 +14,8 @@ @interface BreadcrumbCallbackRemovalScenario : Scenario @implementation BreadcrumbCallbackRemovalScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = BSGEnabledBreadcrumbTypeUser; @@ -31,8 +32,6 @@ - (void)startBugsnag { }; BugsnagOnBreadcrumbRef onBreadcrumb = [self.config addOnBreadcrumbBlock:block]; [self.config removeOnBreadcrumb:onBreadcrumb]; - - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/BuiltinTrapScenario.m b/features/fixtures/shared/scenarios/BuiltinTrapScenario.m index 2018d978d..eadbe96ef 100644 --- a/features/fixtures/shared/scenarios/BuiltinTrapScenario.m +++ b/features/fixtures/shared/scenarios/BuiltinTrapScenario.m @@ -16,9 +16,9 @@ @interface BuiltinTrapScenario : Scenario @implementation BuiltinTrapScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } diff --git a/features/fixtures/shared/scenarios/ConcurrentCrashesScenario.mm b/features/fixtures/shared/scenarios/ConcurrentCrashesScenario.mm index c4b7f069a..b354134db 100644 --- a/features/fixtures/shared/scenarios/ConcurrentCrashesScenario.mm +++ b/features/fixtures/shared/scenarios/ConcurrentCrashesScenario.mm @@ -18,9 +18,9 @@ @interface ConcurrentCrashesScenario : Scenario @implementation ConcurrentCrashesScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } static volatile bool ready; diff --git a/features/fixtures/shared/scenarios/CriticalThermalStateScenario.swift b/features/fixtures/shared/scenarios/CriticalThermalStateScenario.swift index 39d6fd48d..cf38a892a 100644 --- a/features/fixtures/shared/scenarios/CriticalThermalStateScenario.swift +++ b/features/fixtures/shared/scenarios/CriticalThermalStateScenario.swift @@ -11,30 +11,29 @@ import Foundation @available(iOS 11.0, tvOS 11.0, *) class CriticalThermalStateScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false - - super.startBugsnag() - - performBlockAndWaitForSessionDelivery { - Bugsnag.startSession() - } } - + override func run() { - let mockThermalState: @convention(block) () -> ProcessInfo.ThermalState = { .critical } + self.wait(forSessionDelivery: { + Bugsnag.startSession() + }, andThen: { + let mockThermalState: @convention(block) () -> ProcessInfo.ThermalState = { .critical } - method_setImplementation( - class_getInstanceMethod(ProcessInfo.self, #selector(getter:ProcessInfo.thermalState))!, - imp_implementationWithBlock(mockThermalState) - ) - - NotificationCenter.default.post( - name: ProcessInfo.thermalStateDidChangeNotification, object: ProcessInfo.processInfo) - - after(.seconds(3)) { - kill(getpid(), SIGKILL) - } + method_setImplementation( + class_getInstanceMethod(ProcessInfo.self, #selector(getter:ProcessInfo.thermalState))!, + imp_implementationWithBlock(mockThermalState) + ) + + NotificationCenter.default.post( + name: ProcessInfo.thermalStateDidChangeNotification, object: ProcessInfo.processInfo) + + after(.seconds(3)) { + kill(getpid(), SIGKILL) + } + }) } } diff --git a/features/fixtures/shared/scenarios/CustomPluginNotifierDescriptionScenario.m b/features/fixtures/shared/scenarios/CustomPluginNotifierDescriptionScenario.m index 0310e8832..a859006e0 100644 --- a/features/fixtures/shared/scenarios/CustomPluginNotifierDescriptionScenario.m +++ b/features/fixtures/shared/scenarios/CustomPluginNotifierDescriptionScenario.m @@ -27,10 +27,10 @@ - (void)unload {} @implementation CustomPluginNotifierDescriptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; [self.config addPlugin:[DescriptionPlugin new]]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/CxxExceptionOverrideScenario.mm b/features/fixtures/shared/scenarios/CxxExceptionOverrideScenario.mm index cc487d97f..939a2fe1f 100644 --- a/features/fixtures/shared/scenarios/CxxExceptionOverrideScenario.mm +++ b/features/fixtures/shared/scenarios/CxxExceptionOverrideScenario.mm @@ -38,9 +38,9 @@ @interface CxxExceptionOverrideScenario : MarkUnhandledHandledScenario @implementation CxxExceptionOverrideScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/CxxExceptionScenario.mm b/features/fixtures/shared/scenarios/CxxExceptionScenario.mm index e9b6a362d..23718ba56 100644 --- a/features/fixtures/shared/scenarios/CxxExceptionScenario.mm +++ b/features/fixtures/shared/scenarios/CxxExceptionScenario.mm @@ -38,9 +38,9 @@ @interface CxxExceptionScenario : Scenario @implementation CxxExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/DisableAllExceptManualExceptionsAndCrashScenario.m b/features/fixtures/shared/scenarios/DisableAllExceptManualExceptionsAndCrashScenario.m index 254075afd..89736d39c 100644 --- a/features/fixtures/shared/scenarios/DisableAllExceptManualExceptionsAndCrashScenario.m +++ b/features/fixtures/shared/scenarios/DisableAllExceptManualExceptionsAndCrashScenario.m @@ -20,7 +20,8 @@ @interface DisableAllExceptManualExceptionsAndCrashScenario : Scenario @implementation DisableAllExceptManualExceptionsAndCrashScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; errorTypes.cppExceptions = false; errorTypes.machExceptions = false; @@ -29,18 +30,17 @@ - (void)startBugsnag { errorTypes.ooms = false; self.config.enabledErrorTypes = errorTypes; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { - // Notify error so that mazerunner sees something - [self performBlockAndWaitForEventDelivery:^{ + [self waitForEventDelivery:^{ + // Notify error so that mazerunner sees something [Bugsnag notifyError:[NSError errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + } andThen:^{ + // From null ptr scenario + volatile char *ptr = NULL; + (void) *ptr; }]; - - // From null prt scenario - volatile char *ptr = NULL; - (void) *ptr; } @end diff --git a/features/fixtures/shared/scenarios/DisableMachExceptionScenario.m b/features/fixtures/shared/scenarios/DisableMachExceptionScenario.m index a4401ec8c..a0f201198 100644 --- a/features/fixtures/shared/scenarios/DisableMachExceptionScenario.m +++ b/features/fixtures/shared/scenarios/DisableMachExceptionScenario.m @@ -16,7 +16,8 @@ @interface DisableMachExceptionScenario : Scenario @implementation DisableMachExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; errorTypes.machExceptions = false; errorTypes.ooms = false; @@ -26,18 +27,17 @@ - (void)startBugsnag { // Suppress the SIGSEGV caused by EXC_BAD_ACCESS (https://flylib.com/books/en/3.126.1.110/1/) return ![@"SIGSEGV" isEqualToString:event.errors[0].errorClass]; }]; - [super startBugsnag]; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winvalid-noreturn" - (void)run __attribute__((noreturn)) { - // Notify error so that mazerunner sees something - [self performBlockAndWaitForEventDelivery:^{ + [self waitForEventDelivery:^{ + // Notify error so that mazerunner sees something [Bugsnag notifyError:[NSError errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + } andThen:^{ + strcmp(0, ""); // Generate EXC_BAD_ACCESS (see e.g. https://stackoverflow.com/q/22488358/2431627) }]; - - strcmp(0, ""); // Generate EXC_BAD_ACCESS (see e.g. https://stackoverflow.com/q/22488358/2431627) } #pragma clang diagnostic pop diff --git a/features/fixtures/shared/scenarios/DisableNSExceptionScenario.m b/features/fixtures/shared/scenarios/DisableNSExceptionScenario.m index e5cdf9be7..e03986b44 100644 --- a/features/fixtures/shared/scenarios/DisableNSExceptionScenario.m +++ b/features/fixtures/shared/scenarios/DisableNSExceptionScenario.m @@ -16,13 +16,13 @@ @interface DisableNSExceptionScenario : Scenario @implementation DisableNSExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; errorTypes.unhandledExceptions = false; errorTypes.ooms = false; self.config.enabledErrorTypes = errorTypes; self.config.autoTrackSessions = NO; - [super startBugsnag]; } // Suppress the warning. The async confuses the compiler. diff --git a/features/fixtures/shared/scenarios/DisableSignalsExceptionScenario.m b/features/fixtures/shared/scenarios/DisableSignalsExceptionScenario.m index 22d3ff90b..306139d52 100644 --- a/features/fixtures/shared/scenarios/DisableSignalsExceptionScenario.m +++ b/features/fixtures/shared/scenarios/DisableSignalsExceptionScenario.m @@ -16,24 +16,24 @@ @interface DisableSignalsExceptionScenario : Scenario @implementation DisableSignalsExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; errorTypes.signals = false; errorTypes.ooms = false; self.config.enabledErrorTypes = errorTypes; self.config.autoTrackSessions = NO; - [super startBugsnag]; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winvalid-noreturn" - (void)run __attribute__((noreturn)) { - // Notify error so that mazerunner sees something - [self performBlockAndWaitForEventDelivery:^{ + [self waitForEventDelivery:^{ + // Notify error so that mazerunner sees something [Bugsnag notifyError:[NSError errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + } andThen:^{ + raise(SIGINT); }]; - - raise(SIGINT); } #pragma clang pop diff --git a/features/fixtures/shared/scenarios/DisabledReleaseStageAutoSessionScenario.swift b/features/fixtures/shared/scenarios/DisabledReleaseStageAutoSessionScenario.swift index fde6f6006..1be2e8bae 100644 --- a/features/fixtures/shared/scenarios/DisabledReleaseStageAutoSessionScenario.swift +++ b/features/fixtures/shared/scenarios/DisabledReleaseStageAutoSessionScenario.swift @@ -1,9 +1,9 @@ class DisabledReleaseStageAutoSessionScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.releaseStage = "beta" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/DisabledReleaseStageManualSessionScenario.swift b/features/fixtures/shared/scenarios/DisabledReleaseStageManualSessionScenario.swift index 2be693772..3b029007e 100644 --- a/features/fixtures/shared/scenarios/DisabledReleaseStageManualSessionScenario.swift +++ b/features/fixtures/shared/scenarios/DisabledReleaseStageManualSessionScenario.swift @@ -1,10 +1,10 @@ class DisabledReleaseStageManualSessionScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.releaseStage = "beta" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/DisabledSessionTrackingScenario.m b/features/fixtures/shared/scenarios/DisabledSessionTrackingScenario.m index cc87e35db..ab34225b4 100644 --- a/features/fixtures/shared/scenarios/DisabledSessionTrackingScenario.m +++ b/features/fixtures/shared/scenarios/DisabledSessionTrackingScenario.m @@ -11,9 +11,9 @@ @interface DisabledSessionTrackingScenario : Scenario @implementation DisabledSessionTrackingScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/DiscardClassesHandledExceptionRegexScenario.swift b/features/fixtures/shared/scenarios/DiscardClassesHandledExceptionRegexScenario.swift index 8e8334827..020b27038 100644 --- a/features/fixtures/shared/scenarios/DiscardClassesHandledExceptionRegexScenario.swift +++ b/features/fixtures/shared/scenarios/DiscardClassesHandledExceptionRegexScenario.swift @@ -16,10 +16,10 @@ extension NSExceptionName { class DiscardClassesHandledExceptionRegexScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.discardClasses = try! [NSRegularExpression(pattern: #"NS\w+Exception"#)] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/DiscardClassesUnhandledCrashScenario.swift b/features/fixtures/shared/scenarios/DiscardClassesUnhandledCrashScenario.swift index fe7d029b8..d7c4c3821 100644 --- a/features/fixtures/shared/scenarios/DiscardClassesUnhandledCrashScenario.swift +++ b/features/fixtures/shared/scenarios/DiscardClassesUnhandledCrashScenario.swift @@ -1,12 +1,16 @@ class DiscardClassesUnhandledCrashScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.discardClasses = ["SIGABRT"] config.addOnSendError { precondition(!$0.unhandled, "OnSendError should not be called for discarded errors (SIGABRT)") return true } + } + + override func startBugsnag() { super.startBugsnag() if Bugsnag.lastRunInfo?.crashed == true { diff --git a/features/fixtures/shared/scenarios/DiscardClassesUnhandledExceptionScenario.swift b/features/fixtures/shared/scenarios/DiscardClassesUnhandledExceptionScenario.swift index 6c34c8ebf..b46eba415 100644 --- a/features/fixtures/shared/scenarios/DiscardClassesUnhandledExceptionScenario.swift +++ b/features/fixtures/shared/scenarios/DiscardClassesUnhandledExceptionScenario.swift @@ -1,12 +1,16 @@ class DiscardClassesUnhandledExceptionScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.discardClasses = [NSExceptionName.rangeException.rawValue] config.addOnSendError { precondition(!$0.unhandled, "OnSendError should not be called for discarded errors (NSRangeException)") return true } + } + + override func startBugsnag() { super.startBugsnag() if Bugsnag.lastRunInfo?.crashed == true { diff --git a/features/fixtures/shared/scenarios/DiscardedBreadcrumbTypeScenario.swift b/features/fixtures/shared/scenarios/DiscardedBreadcrumbTypeScenario.swift index 7bca16b85..9538ee4f0 100644 --- a/features/fixtures/shared/scenarios/DiscardedBreadcrumbTypeScenario.swift +++ b/features/fixtures/shared/scenarios/DiscardedBreadcrumbTypeScenario.swift @@ -2,10 +2,10 @@ import Foundation class DiscardedBreadcrumbTypeScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = [.error, .process]; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/DispatchCrashScenario.swift b/features/fixtures/shared/scenarios/DispatchCrashScenario.swift index d3b6168e4..de1efaecf 100644 --- a/features/fixtures/shared/scenarios/DispatchCrashScenario.swift +++ b/features/fixtures/shared/scenarios/DispatchCrashScenario.swift @@ -8,9 +8,9 @@ class DispatchCrashScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/EnabledBreadcrumbTypesIsNilScenario.swift b/features/fixtures/shared/scenarios/EnabledBreadcrumbTypesIsNilScenario.swift index 77fc44890..9f35aba24 100644 --- a/features/fixtures/shared/scenarios/EnabledBreadcrumbTypesIsNilScenario.swift +++ b/features/fixtures/shared/scenarios/EnabledBreadcrumbTypesIsNilScenario.swift @@ -9,10 +9,11 @@ import Foundation class EnabledBreadcrumbTypesIsNilScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.enabledBreadcrumbTypes = []; // aka .none - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/EnabledErrorTypesCxxScenario.mm b/features/fixtures/shared/scenarios/EnabledErrorTypesCxxScenario.mm index f09a10a01..2f80181b7 100644 --- a/features/fixtures/shared/scenarios/EnabledErrorTypesCxxScenario.mm +++ b/features/fixtures/shared/scenarios/EnabledErrorTypesCxxScenario.mm @@ -20,7 +20,8 @@ @interface EnabledErrorTypesCxxScenario : Scenario @implementation EnabledErrorTypesCxxScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; errorTypes.cppExceptions = false; errorTypes.ooms = false; @@ -30,20 +31,19 @@ - (void)startBugsnag { // std::exception terminates with abort() by default, therefore discard SIGABRT return ![@"SIGABRT" isEqualToString:event.errors[0].errorClass]; }]; - [super startBugsnag]; } - (void)run { [self crash]; } -- (void)crash __attribute__((noreturn)) { - // Notify error so that mazerunner sees something - [self performBlockAndWaitForEventDelivery:^{ +- (void)crash { + [self waitForEventDelivery:^{ + // Notify error so that mazerunner sees something [Bugsnag notifyError:[NSError errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; + } andThen:^{ + throw new disabled_cxx_reporting_kaboom_exception; }]; - - throw new disabled_cxx_reporting_kaboom_exception; } @end diff --git a/features/fixtures/shared/scenarios/EnabledReleaseStageAutoSessionScenario.swift b/features/fixtures/shared/scenarios/EnabledReleaseStageAutoSessionScenario.swift index d0b3ee96e..b96cc6927 100644 --- a/features/fixtures/shared/scenarios/EnabledReleaseStageAutoSessionScenario.swift +++ b/features/fixtures/shared/scenarios/EnabledReleaseStageAutoSessionScenario.swift @@ -8,10 +8,10 @@ class EnabledReleaseStageAutoSessionScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.releaseStage = "prod" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/EnabledReleaseStageManualSessionScenario.swift b/features/fixtures/shared/scenarios/EnabledReleaseStageManualSessionScenario.swift index ba10bf8c9..d5f96a744 100644 --- a/features/fixtures/shared/scenarios/EnabledReleaseStageManualSessionScenario.swift +++ b/features/fixtures/shared/scenarios/EnabledReleaseStageManualSessionScenario.swift @@ -1,10 +1,10 @@ class EnabledReleaseStageManualSessionScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.releaseStage = "prod" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/HandledErrorInvalidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/HandledErrorInvalidReleaseStageScenario.swift index c20437c41..f4383b306 100644 --- a/features/fixtures/shared/scenarios/HandledErrorInvalidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/HandledErrorInvalidReleaseStageScenario.swift @@ -1,10 +1,10 @@ class HandledErrorInvalidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.releaseStage = "dev" self.config.enabledReleaseStages = ["prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/HandledErrorOverrideScenario.swift b/features/fixtures/shared/scenarios/HandledErrorOverrideScenario.swift index 71bee00a1..f53334e11 100644 --- a/features/fixtures/shared/scenarios/HandledErrorOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/HandledErrorOverrideScenario.swift @@ -10,9 +10,9 @@ import Foundation */ class HandledErrorOverrideScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } fileprivate func logError(_ error: Error) { diff --git a/features/fixtures/shared/scenarios/HandledErrorScenario.swift b/features/fixtures/shared/scenarios/HandledErrorScenario.swift index 041963d04..27bfee86a 100644 --- a/features/fixtures/shared/scenarios/HandledErrorScenario.swift +++ b/features/fixtures/shared/scenarios/HandledErrorScenario.swift @@ -10,9 +10,9 @@ import Foundation */ class HandledErrorScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/HandledErrorThreadSendAlwaysScenario.m b/features/fixtures/shared/scenarios/HandledErrorThreadSendAlwaysScenario.m index 30b657b4b..4fca60fb6 100644 --- a/features/fixtures/shared/scenarios/HandledErrorThreadSendAlwaysScenario.m +++ b/features/fixtures/shared/scenarios/HandledErrorThreadSendAlwaysScenario.m @@ -14,9 +14,9 @@ @interface HandledErrorThreadSendAlwaysScenario : Scenario @implementation HandledErrorThreadSendAlwaysScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/HandledErrorThreadSendUnhandledOnlyScenario.m b/features/fixtures/shared/scenarios/HandledErrorThreadSendUnhandledOnlyScenario.m index 63432ec7a..c9b8ae643 100644 --- a/features/fixtures/shared/scenarios/HandledErrorThreadSendUnhandledOnlyScenario.m +++ b/features/fixtures/shared/scenarios/HandledErrorThreadSendUnhandledOnlyScenario.m @@ -14,10 +14,10 @@ @interface HandledErrorThreadSendUnhandledOnlyScenario : Scenario @implementation HandledErrorThreadSendUnhandledOnlyScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; self.config.sendThreads = BSGThreadSendPolicyUnhandledOnly; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/HandledErrorValidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/HandledErrorValidReleaseStageScenario.swift index df39a8b1b..cbe1602ee 100644 --- a/features/fixtures/shared/scenarios/HandledErrorValidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/HandledErrorValidReleaseStageScenario.swift @@ -2,11 +2,11 @@ class MagicError : NSError {} class HandledErrorValidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.releaseStage = "prod" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/HandledExceptionScenario.swift b/features/fixtures/shared/scenarios/HandledExceptionScenario.swift index 4976725fa..880835b3d 100644 --- a/features/fixtures/shared/scenarios/HandledExceptionScenario.swift +++ b/features/fixtures/shared/scenarios/HandledExceptionScenario.swift @@ -10,9 +10,9 @@ import Foundation */ class HandledExceptionScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/InvalidCrashReportScenario.m b/features/fixtures/shared/scenarios/InvalidCrashReportScenario.m index 19c3118c8..0e74d7d0a 100644 --- a/features/fixtures/shared/scenarios/InvalidCrashReportScenario.m +++ b/features/fixtures/shared/scenarios/InvalidCrashReportScenario.m @@ -19,13 +19,13 @@ static void CrashHandler(const BSG_KSCrashReportWriter *writer) { @implementation InvalidCrashReportScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; self.config.onCrashHandler = CrashHandler; - if ([self.eventMode isEqualToString:@"internalErrorsDisabled"]) { + if ([self.args[0] isEqualToString:@"internalErrorsDisabled"]) { self.config.telemetry &= ~BSGTelemetryInternalErrors; } - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/LastRunInfoScenario.swift b/features/fixtures/shared/scenarios/LastRunInfoScenario.swift index 4995e7b7e..255c9b547 100644 --- a/features/fixtures/shared/scenarios/LastRunInfoScenario.swift +++ b/features/fixtures/shared/scenarios/LastRunInfoScenario.swift @@ -8,7 +8,8 @@ class LastRunInfoScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.launchDurationMillis = 0 config.sendLaunchCrashesSynchronously = false config.addOnSendError { @@ -21,7 +22,6 @@ class LastRunInfoScenario: Scenario { } return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ManualContextClientScenario.swift b/features/fixtures/shared/scenarios/ManualContextClientScenario.swift index d83bd11ba..1be5c70ec 100644 --- a/features/fixtures/shared/scenarios/ManualContextClientScenario.swift +++ b/features/fixtures/shared/scenarios/ManualContextClientScenario.swift @@ -10,9 +10,9 @@ import Foundation class ManualContextClientScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ManualContextConfigurationScenario.swift b/features/fixtures/shared/scenarios/ManualContextConfigurationScenario.swift index 65ac379c9..246325ca1 100644 --- a/features/fixtures/shared/scenarios/ManualContextConfigurationScenario.swift +++ b/features/fixtures/shared/scenarios/ManualContextConfigurationScenario.swift @@ -10,10 +10,10 @@ import Foundation class ManualContextConfigurationScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.context = "contextFromConfig" - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ManualContextOnErrorScenario.swift b/features/fixtures/shared/scenarios/ManualContextOnErrorScenario.swift index 04123ef16..ce15a0ad1 100644 --- a/features/fixtures/shared/scenarios/ManualContextOnErrorScenario.swift +++ b/features/fixtures/shared/scenarios/ManualContextOnErrorScenario.swift @@ -10,13 +10,13 @@ import Foundation class ManualContextOnErrorScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError { (event) -> Bool in event.context = "OnErrorContext" return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ManualSessionScenario.m b/features/fixtures/shared/scenarios/ManualSessionScenario.m index 26c912d2e..14fafbcd4 100644 --- a/features/fixtures/shared/scenarios/ManualSessionScenario.m +++ b/features/fixtures/shared/scenarios/ManualSessionScenario.m @@ -14,9 +14,9 @@ @interface ManualSessionScenario : Scenario @implementation ManualSessionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ManualSessionWithUserScenario.m b/features/fixtures/shared/scenarios/ManualSessionWithUserScenario.m index 842a028b9..f53dd0bf2 100644 --- a/features/fixtures/shared/scenarios/ManualSessionWithUserScenario.m +++ b/features/fixtures/shared/scenarios/ManualSessionWithUserScenario.m @@ -14,10 +14,10 @@ @interface ManualSessionWithUserScenario : Scenario @implementation ManualSessionWithUserScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; [self.config setUser:@"123" withEmail:@"joe@example.com" andName:@"Joe Bloggs"]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index a4904b226..37520a539 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -13,8 +13,8 @@ @implementation FooError @implementation ManyConcurrentNotifyScenario -- (instancetype)initWithConfig:(BugsnagConfiguration *)config { - if (self = [super initWithConfig:config]) { +- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args { + if (self = [super initWithFixtureConfig:config args:args]) { _queue1 = dispatch_queue_create("Log Queue 1", DISPATCH_QUEUE_CONCURRENT); _queue2 = dispatch_queue_create("Log Queue 2", DISPATCH_QUEUE_CONCURRENT); } @@ -39,9 +39,9 @@ - (void)logError:(NSError *)error { }); } -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } @end diff --git a/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m index 83e4cb409..be60701df 100644 --- a/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m +++ b/features/fixtures/shared/scenarios/MarkUnhandledHandledScenario.m @@ -11,9 +11,9 @@ @implementation MarkUnhandledHandledScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.onCrashHandler = markErrorHandledCallback; - [super startBugsnag]; } @end diff --git a/features/fixtures/shared/scenarios/MaxPersistedSessionsScenario.m b/features/fixtures/shared/scenarios/MaxPersistedSessionsScenario.m index f39155546..c002f9bbd 100644 --- a/features/fixtures/shared/scenarios/MaxPersistedSessionsScenario.m +++ b/features/fixtures/shared/scenarios/MaxPersistedSessionsScenario.m @@ -14,27 +14,26 @@ @interface MaxPersistedSessionsScenario : Scenario @implementation MaxPersistedSessionsScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; self.config.maxPersistedSessions = 1; +} - [super startBugsnag]; +- (void)run { + [self waitForSessionDelivery:^{ + [Bugsnag setUser:[self nextUserId] withEmail:nil andName:nil]; + [Bugsnag startSession]; + } andThen:^{ + // Filesystem timestamps have a resolution of 1 second, so wait to ensure + // that the first persisted session will have an older file creation date. + [NSThread sleepForTimeInterval:1]; - [self performBlockAndWaitForSessionDelivery:^{ [Bugsnag setUser:[self nextUserId] withEmail:nil andName:nil]; [Bugsnag startSession]; }]; } -- (void)run { - // Filesystem timestamps have a resolution of 1 second, so wait to ensure - // that the first persisted session will have an older file creation date. - [NSThread sleepForTimeInterval:1]; - - [Bugsnag setUser:[self nextUserId] withEmail:nil andName:nil]; - [Bugsnag startSession]; -} - - (NSString *)nextUserId { NSString *key = @"sessionCounter"; NSInteger sessionCounter = [NSUserDefaults.standardUserDefaults integerForKey:key] + 1; diff --git a/features/fixtures/shared/scenarios/MetadataMergeScenario.swift b/features/fixtures/shared/scenarios/MetadataMergeScenario.swift index c91c7252b..6c4e298a4 100644 --- a/features/fixtures/shared/scenarios/MetadataMergeScenario.swift +++ b/features/fixtures/shared/scenarios/MetadataMergeScenario.swift @@ -10,9 +10,9 @@ import Foundation class MetadataMergeScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/MetadataRedactionDefaultScenario.swift b/features/fixtures/shared/scenarios/MetadataRedactionDefaultScenario.swift index 4659668e7..184540ae4 100644 --- a/features/fixtures/shared/scenarios/MetadataRedactionDefaultScenario.swift +++ b/features/fixtures/shared/scenarios/MetadataRedactionDefaultScenario.swift @@ -13,9 +13,9 @@ import Foundation */ class MetadataRedactionDefaultScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/MetadataRedactionNestedScenario.swift b/features/fixtures/shared/scenarios/MetadataRedactionNestedScenario.swift index d39da1a7a..7a505a870 100644 --- a/features/fixtures/shared/scenarios/MetadataRedactionNestedScenario.swift +++ b/features/fixtures/shared/scenarios/MetadataRedactionNestedScenario.swift @@ -13,10 +13,10 @@ import Foundation */ class MetadataRedactionNestedScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - self.config.redactedKeys = ["name", "age"] - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; + self.config.redactedKeys = ["name", "age"] } override func run() { diff --git a/features/fixtures/shared/scenarios/MetadataRedactionRegexScenario.swift b/features/fixtures/shared/scenarios/MetadataRedactionRegexScenario.swift index ee5170b78..16b6c33bc 100644 --- a/features/fixtures/shared/scenarios/MetadataRedactionRegexScenario.swift +++ b/features/fixtures/shared/scenarios/MetadataRedactionRegexScenario.swift @@ -13,11 +13,11 @@ import Foundation */ class MetadataRedactionRegexScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - let regex = try! NSRegularExpression(pattern: "[a-z]at") - self.config.redactedKeys = [regex] - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; + let regex = try! NSRegularExpression(pattern: "[a-z]at") + self.config.redactedKeys = [regex] } override func run() { diff --git a/features/fixtures/shared/scenarios/ModifyBreadcrumbInNotifyScenario.swift b/features/fixtures/shared/scenarios/ModifyBreadcrumbInNotifyScenario.swift index 5a3b4bb57..a3307837c 100644 --- a/features/fixtures/shared/scenarios/ModifyBreadcrumbInNotifyScenario.swift +++ b/features/fixtures/shared/scenarios/ModifyBreadcrumbInNotifyScenario.swift @@ -2,9 +2,9 @@ import Foundation class ModifyBreadcrumbInNotifyScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ModifyBreadcrumbScenario.swift b/features/fixtures/shared/scenarios/ModifyBreadcrumbScenario.swift index 9e8ebebed..8253390ac 100644 --- a/features/fixtures/shared/scenarios/ModifyBreadcrumbScenario.swift +++ b/features/fixtures/shared/scenarios/ModifyBreadcrumbScenario.swift @@ -2,7 +2,8 @@ import Foundation class ModifyBreadcrumbScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError(block: { event in @@ -13,7 +14,6 @@ class ModifyBreadcrumbScenario: Scenario { }) return true }) - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/NSExceptionShiftScenario.m b/features/fixtures/shared/scenarios/NSExceptionShiftScenario.m index e1b0d059b..1da665965 100644 --- a/features/fixtures/shared/scenarios/NSExceptionShiftScenario.m +++ b/features/fixtures/shared/scenarios/NSExceptionShiftScenario.m @@ -6,9 +6,9 @@ @interface NSExceptionShiftScenario : Scenario @implementation NSExceptionShiftScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift index 099b9eba6..06fb15343 100644 --- a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift +++ b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift @@ -11,15 +11,14 @@ import Foundation @available(iOS 10.0, macOS 10.12, *) class NetworkBreadcrumbsScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false; config.add(BugsnagNetworkRequestPlugin()) config.addOnBreadcrumb { let url = $0.metadata["url"] as? String ?? "" - return url.hasPrefix(Scenario.baseMazeAddress) && url.contains("/reflect") + return url.hasPrefix(self.fixtureConfig.mazeRunnerURL.absoluteString) && url.contains("/reflect") } - - super.startBugsnag() } override func run() { @@ -33,7 +32,7 @@ class NetworkBreadcrumbsScenario : Scenario { } func query(string: String) { - let url = URL(string: Scenario.baseMazeAddress + string)! + let url = URL(string: fixtureConfig.mazeRunnerURL.absoluteString + string)! let semaphore = DispatchSemaphore(value: 0) let task = URLSession.shared.dataTask(with: url) {(data, response, error) in diff --git a/features/fixtures/shared/scenarios/NewSessionScenario.swift b/features/fixtures/shared/scenarios/NewSessionScenario.swift index ffb207569..9f1b91c6e 100644 --- a/features/fixtures/shared/scenarios/NewSessionScenario.swift +++ b/features/fixtures/shared/scenarios/NewSessionScenario.swift @@ -9,9 +9,10 @@ import Foundation internal class NewSessionScenario: Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/NonExistentMethodScenario.m b/features/fixtures/shared/scenarios/NonExistentMethodScenario.m index 8990d003c..392b2b0ca 100644 --- a/features/fixtures/shared/scenarios/NonExistentMethodScenario.m +++ b/features/fixtures/shared/scenarios/NonExistentMethodScenario.m @@ -14,9 +14,9 @@ @interface NonExistentMethodScenario : Scenario @implementation NonExistentMethodScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/NotifyCallbackCrashScenario.swift b/features/fixtures/shared/scenarios/NotifyCallbackCrashScenario.swift index 7ab135580..c9f0bb222 100644 --- a/features/fixtures/shared/scenarios/NotifyCallbackCrashScenario.swift +++ b/features/fixtures/shared/scenarios/NotifyCallbackCrashScenario.swift @@ -10,9 +10,9 @@ import Foundation class NotifyCallbackCrashScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/NullPointerScenario.m b/features/fixtures/shared/scenarios/NullPointerScenario.m index 81318f063..0636dbc30 100644 --- a/features/fixtures/shared/scenarios/NullPointerScenario.m +++ b/features/fixtures/shared/scenarios/NullPointerScenario.m @@ -36,9 +36,9 @@ @interface NullPointerScenario : Scenario @implementation NullPointerScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OOMAutoDetectErrorsScenario.swift b/features/fixtures/shared/scenarios/OOMAutoDetectErrorsScenario.swift index f695863cc..d64dee02f 100644 --- a/features/fixtures/shared/scenarios/OOMAutoDetectErrorsScenario.swift +++ b/features/fixtures/shared/scenarios/OOMAutoDetectErrorsScenario.swift @@ -10,12 +10,11 @@ import Foundation class OOMAutoDetectErrorsScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.enabledErrorTypes.ooms = true self.config.autoDetectErrors = false - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OOMEnabledErrorTypesScenario.swift b/features/fixtures/shared/scenarios/OOMEnabledErrorTypesScenario.swift index 3bcdfba84..c4257af81 100644 --- a/features/fixtures/shared/scenarios/OOMEnabledErrorTypesScenario.swift +++ b/features/fixtures/shared/scenarios/OOMEnabledErrorTypesScenario.swift @@ -10,12 +10,11 @@ import Foundation class OOMEnabledErrorTypesScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false self.config.enabledErrorTypes.ooms = false self.config.autoDetectErrors = true - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OOMInactiveScenario.swift b/features/fixtures/shared/scenarios/OOMInactiveScenario.swift index a9b496b36..1dac703be 100644 --- a/features/fixtures/shared/scenarios/OOMInactiveScenario.swift +++ b/features/fixtures/shared/scenarios/OOMInactiveScenario.swift @@ -12,9 +12,9 @@ import UIKit class OOMInactiveScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.enabledErrorTypes.ooms = true - Bugsnag.start(with: config) } override func run() { diff --git a/features/fixtures/shared/scenarios/OOMLoadScenario.swift b/features/fixtures/shared/scenarios/OOMLoadScenario.swift index 273a556cf..fce64d06d 100644 --- a/features/fixtures/shared/scenarios/OOMLoadScenario.swift +++ b/features/fixtures/shared/scenarios/OOMLoadScenario.swift @@ -10,12 +10,12 @@ import Foundation class OOMLoadScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = true config.enabledErrorTypes.ooms = true config.addMetadata(["bar": "foo"], section: "custom") config.setUser("foobar", withEmail: "foobar@example.com", andName: "Foo Bar") - Bugsnag.start(with: config) } override func run() { diff --git a/features/fixtures/shared/scenarios/OOMScenario.m b/features/fixtures/shared/scenarios/OOMScenario.m index e5f0622f6..835a7113a 100644 --- a/features/fixtures/shared/scenarios/OOMScenario.m +++ b/features/fixtures/shared/scenarios/OOMScenario.m @@ -28,7 +28,8 @@ void onCrashHandler(const BSG_KSCrashReportWriter *writer) { assert(!"onCrashHandler should not be called for OOMs"); } -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = YES; self.config.enabledErrorTypes.ooms = YES; self.config.onCrashHandler = onCrashHandler; @@ -51,7 +52,6 @@ - (void)startBugsnag { } return true; }]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OOMSessionScenario.swift b/features/fixtures/shared/scenarios/OOMSessionScenario.swift index 9f5b5f958..b95cb4eca 100644 --- a/features/fixtures/shared/scenarios/OOMSessionScenario.swift +++ b/features/fixtures/shared/scenarios/OOMSessionScenario.swift @@ -10,10 +10,10 @@ import Foundation class OOMSessionScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.enabledErrorTypes.ooms = true config.autoTrackSessions = false - Bugsnag.start(with: config) } override func run() { diff --git a/features/fixtures/shared/scenarios/OOMSessionlessScenario.m b/features/fixtures/shared/scenarios/OOMSessionlessScenario.m index eff7e4904..8b4377404 100644 --- a/features/fixtures/shared/scenarios/OOMSessionlessScenario.m +++ b/features/fixtures/shared/scenarios/OOMSessionlessScenario.m @@ -15,10 +15,10 @@ @interface OOMSessionlessScenario : Scenario @implementation OOMSessionlessScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; self.config.enabledErrorTypes.ooms = YES; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OOMWillTerminateScenario.m b/features/fixtures/shared/scenarios/OOMWillTerminateScenario.m index 4c8e0eedf..70223785c 100644 --- a/features/fixtures/shared/scenarios/OOMWillTerminateScenario.m +++ b/features/fixtures/shared/scenarios/OOMWillTerminateScenario.m @@ -9,9 +9,9 @@ @interface OOMWillTerminateScenario : Scenario @implementation OOMWillTerminateScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ObjCExceptionOverrideScenario.m b/features/fixtures/shared/scenarios/ObjCExceptionOverrideScenario.m index 5ab2177c4..0aa7ebf90 100644 --- a/features/fixtures/shared/scenarios/ObjCExceptionOverrideScenario.m +++ b/features/fixtures/shared/scenarios/ObjCExceptionOverrideScenario.m @@ -37,9 +37,9 @@ @interface ObjCExceptionOverrideScenario : MarkUnhandledHandledScenario @implementation ObjCExceptionOverrideScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run __attribute__((noreturn)) { diff --git a/features/fixtures/shared/scenarios/ObjCExceptionScenario.m b/features/fixtures/shared/scenarios/ObjCExceptionScenario.m index b7e491c63..ba57b2dc0 100644 --- a/features/fixtures/shared/scenarios/ObjCExceptionScenario.m +++ b/features/fixtures/shared/scenarios/ObjCExceptionScenario.m @@ -37,9 +37,9 @@ @interface ObjCExceptionScenario : Scenario @implementation ObjCExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run __attribute__((noreturn)) { diff --git a/features/fixtures/shared/scenarios/ObjCMsgSendScenario.m b/features/fixtures/shared/scenarios/ObjCMsgSendScenario.m index eb8c9cb06..66d0d9729 100644 --- a/features/fixtures/shared/scenarios/ObjCMsgSendScenario.m +++ b/features/fixtures/shared/scenarios/ObjCMsgSendScenario.m @@ -37,9 +37,9 @@ @interface ObjCMsgSendScenario : Scenario @implementation ObjCMsgSendScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OldCrashReportScenario.swift b/features/fixtures/shared/scenarios/OldCrashReportScenario.swift index 48de226cd..6c6839522 100644 --- a/features/fixtures/shared/scenarios/OldCrashReportScenario.swift +++ b/features/fixtures/shared/scenarios/OldCrashReportScenario.swift @@ -1,10 +1,10 @@ class OldCrashReportScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() modifyEventCreationDate() config.autoTrackSessions = false config.enabledErrorTypes.ooms = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OldHandledErrorScenario.swift b/features/fixtures/shared/scenarios/OldHandledErrorScenario.swift index 499a1995e..d225239db 100644 --- a/features/fixtures/shared/scenarios/OldHandledErrorScenario.swift +++ b/features/fixtures/shared/scenarios/OldHandledErrorScenario.swift @@ -1,10 +1,10 @@ class OldHandledErrorScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() modifyEventCreationDate() config.autoTrackSessions = false config.enabledErrorTypes.ooms = false - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OldSessionScenario.m b/features/fixtures/shared/scenarios/OldSessionScenario.m index 2aca8e73c..fc6f587f7 100644 --- a/features/fixtures/shared/scenarios/OldSessionScenario.m +++ b/features/fixtures/shared/scenarios/OldSessionScenario.m @@ -14,12 +14,12 @@ @interface OldSessionScenario : Scenario @implementation OldSessionScenario -- (void)startBugsnag { - if ([self.eventMode isEqualToString:@"new"]) { +- (void)configure { + [super configure]; + if ([self.args[0] isEqualToString:@"new"]) { [self modifySessionCreationDate]; } - [self.config setUser:self.eventMode withEmail:nil andName:nil]; - [super startBugsnag]; + [self.config setUser:self.args[0] withEmail:nil andName:nil]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OnCrashHandlerScenario.m b/features/fixtures/shared/scenarios/OnCrashHandlerScenario.m index edd7b4c32..70b1d1ebf 100644 --- a/features/fixtures/shared/scenarios/OnCrashHandlerScenario.m +++ b/features/fixtures/shared/scenarios/OnCrashHandlerScenario.m @@ -41,10 +41,10 @@ @interface OnCrashHandlerScenario : Scenario @implementation OnCrashHandlerScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; self.config.onCrashHandler = &HandleCrashedThread; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OnErrorOverwriteScenario.swift b/features/fixtures/shared/scenarios/OnErrorOverwriteScenario.swift index acbb60bb4..bc4bd6d63 100644 --- a/features/fixtures/shared/scenarios/OnErrorOverwriteScenario.swift +++ b/features/fixtures/shared/scenarios/OnErrorOverwriteScenario.swift @@ -12,9 +12,10 @@ import Foundation * Verifies that an OnErrorCallback can overwrite information for a handled error */ class OnErrorOverwriteScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledFalseScenario.swift b/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledFalseScenario.swift index 6c4a9ebf4..f66ab227b 100644 --- a/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledFalseScenario.swift +++ b/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledFalseScenario.swift @@ -12,9 +12,10 @@ import Foundation * Verifies that an OnErrorCallback can overwrite unhandled for a handled error */ class OnErrorOverwriteUnhandledFalseScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledTrueScenario.swift b/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledTrueScenario.swift index f3b6bf64e..eb4b0f07b 100644 --- a/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledTrueScenario.swift +++ b/features/fixtures/shared/scenarios/OnErrorOverwriteUnhandledTrueScenario.swift @@ -12,9 +12,10 @@ import Foundation * Verifies that an OnErrorCallback can overwrite unhandled for a handled error */ class OnErrorOverwriteUnhandledTrueScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnSendCallbackOrderScenario.swift b/features/fixtures/shared/scenarios/OnSendCallbackOrderScenario.swift index 48e5ee6ab..59156dd79 100644 --- a/features/fixtures/shared/scenarios/OnSendCallbackOrderScenario.swift +++ b/features/fixtures/shared/scenarios/OnSendCallbackOrderScenario.swift @@ -15,14 +15,14 @@ class OnSendCallbackOrderScenario : Scenario { var callbackOrder = 0 - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError { (event) -> Bool in event.addMetadata(self.callbackOrder, key: "config", section: "callbacks") self.callbackOrder += 1 return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnSendCallbackRemovalScenario.m b/features/fixtures/shared/scenarios/OnSendCallbackRemovalScenario.m index aa1f3f7ed..c6271e7b4 100644 --- a/features/fixtures/shared/scenarios/OnSendCallbackRemovalScenario.m +++ b/features/fixtures/shared/scenarios/OnSendCallbackRemovalScenario.m @@ -17,7 +17,8 @@ @interface OnSendCallbackRemovalScenario : Scenario @implementation OnSendCallbackRemovalScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; id block = ^BOOL(BugsnagEvent * _Nonnull event) { [event addMetadata:@"this should never happen" withKey:@"config" toSection:@"callbacks"]; return true; @@ -30,7 +31,6 @@ - (void)startBugsnag { return true; }]; [self.config removeOnSendError:block]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OnSendErrorCallbackCrashScenario.swift b/features/fixtures/shared/scenarios/OnSendErrorCallbackCrashScenario.swift index 4742a3e47..523c41815 100644 --- a/features/fixtures/shared/scenarios/OnSendErrorCallbackCrashScenario.swift +++ b/features/fixtures/shared/scenarios/OnSendErrorCallbackCrashScenario.swift @@ -10,7 +10,8 @@ import Foundation class OnSendErrorCallbackCrashScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError { (event) -> Bool in event.addMetadata(true, key: "beforeCrash", section: "callbacks") @@ -27,7 +28,6 @@ class OnSendErrorCallbackCrashScenario : Scenario { event.addMetadata(true, key: "secondCallback", section: "callbacks") return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnSendErrorCallbackFeatureFlagsScenario.swift b/features/fixtures/shared/scenarios/OnSendErrorCallbackFeatureFlagsScenario.swift index b080b00c0..765234373 100644 --- a/features/fixtures/shared/scenarios/OnSendErrorCallbackFeatureFlagsScenario.swift +++ b/features/fixtures/shared/scenarios/OnSendErrorCallbackFeatureFlagsScenario.swift @@ -7,14 +7,14 @@ import Foundation class OnSendErrorCallbackFeatureFlagsScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError { (event) -> Bool in event.addFeatureFlag(name: "fromCallback", variant: event.featureFlags[0].variant) event.clearFeatureFlag(name: "deleteMe") return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OnSendErrorPersistenceScenario.m b/features/fixtures/shared/scenarios/OnSendErrorPersistenceScenario.m index a59c953a5..d08c31a92 100644 --- a/features/fixtures/shared/scenarios/OnSendErrorPersistenceScenario.m +++ b/features/fixtures/shared/scenarios/OnSendErrorPersistenceScenario.m @@ -17,7 +17,8 @@ @interface OnSendErrorPersistenceScenario : Scenario @implementation OnSendErrorPersistenceScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; if (!self.hasNotified) { @@ -26,8 +27,6 @@ - (void)startBugsnag { return YES; }]; } - - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/OnSendOverwriteScenario.swift b/features/fixtures/shared/scenarios/OnSendOverwriteScenario.swift index 82819bd9c..eeb7debcf 100644 --- a/features/fixtures/shared/scenarios/OnSendOverwriteScenario.swift +++ b/features/fixtures/shared/scenarios/OnSendOverwriteScenario.swift @@ -12,7 +12,9 @@ import Foundation * Verifies that an OnSend callback can overwrite information for an unhandled error */ class OnSendOverwriteScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSendError { (event) -> Bool in event.app.id = "customAppId" @@ -23,7 +25,6 @@ class OnSendOverwriteScenario : Scenario { event.setUser("customId", withEmail: "customEmail", andName: "customName") return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OriginalErrorNSErrorScenario.swift b/features/fixtures/shared/scenarios/OriginalErrorNSErrorScenario.swift index 68040248b..2b7a99a8a 100644 --- a/features/fixtures/shared/scenarios/OriginalErrorNSErrorScenario.swift +++ b/features/fixtures/shared/scenarios/OriginalErrorNSErrorScenario.swift @@ -12,9 +12,10 @@ import Foundation * Verifies that the original error property is populated for a handled NSError */ class OriginalErrorNSErrorScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OriginalErrorNSExceptionScenario.swift b/features/fixtures/shared/scenarios/OriginalErrorNSExceptionScenario.swift index 80e9ed535..b7d5b2ffa 100644 --- a/features/fixtures/shared/scenarios/OriginalErrorNSExceptionScenario.swift +++ b/features/fixtures/shared/scenarios/OriginalErrorNSExceptionScenario.swift @@ -12,9 +12,10 @@ import Foundation * Verifies that the original error property is populated for a handled NSException */ class OriginalErrorNSExceptionScenario : Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OversizedCrashReportScenario.swift b/features/fixtures/shared/scenarios/OversizedCrashReportScenario.swift index 57dfd6e78..0d7f65bea 100644 --- a/features/fixtures/shared/scenarios/OversizedCrashReportScenario.swift +++ b/features/fixtures/shared/scenarios/OversizedCrashReportScenario.swift @@ -1,6 +1,7 @@ class OversizedCrashReportScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.enabledErrorTypes.ooms = false config.maxStringValueLength = UInt.max @@ -12,7 +13,6 @@ class OversizedCrashReportScenario: Scenario { $0.addMetadata(data.base64EncodedString(), key: "random", section: "test") return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OversizedHandledErrorScenario.swift b/features/fixtures/shared/scenarios/OversizedHandledErrorScenario.swift index 414e6251c..fa2b6566e 100644 --- a/features/fixtures/shared/scenarios/OversizedHandledErrorScenario.swift +++ b/features/fixtures/shared/scenarios/OversizedHandledErrorScenario.swift @@ -1,6 +1,7 @@ class OversizedHandledErrorScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.enabledErrorTypes.ooms = false config.maxStringValueLength = UInt.max @@ -12,7 +13,6 @@ class OversizedHandledErrorScenario: Scenario { $0.addMetadata(data.base64EncodedString(), key: "random", section: "test") return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/OverwriteLinkRegisterScenario.m b/features/fixtures/shared/scenarios/OverwriteLinkRegisterScenario.m index 6aa1f2284..ab9a6a365 100644 --- a/features/fixtures/shared/scenarios/OverwriteLinkRegisterScenario.m +++ b/features/fixtures/shared/scenarios/OverwriteLinkRegisterScenario.m @@ -43,9 +43,9 @@ - (NSString *)desc { return @""; } -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/PrivilegedInstructionScenario.m b/features/fixtures/shared/scenarios/PrivilegedInstructionScenario.m index 2860f5311..8f17088ae 100644 --- a/features/fixtures/shared/scenarios/PrivilegedInstructionScenario.m +++ b/features/fixtures/shared/scenarios/PrivilegedInstructionScenario.m @@ -37,9 +37,9 @@ @interface PrivilegedInstructionScenario : Scenario @implementation PrivilegedInstructionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ReadGarbagePointerScenario.m b/features/fixtures/shared/scenarios/ReadGarbagePointerScenario.m index 1c50c0509..aad00efe2 100644 --- a/features/fixtures/shared/scenarios/ReadGarbagePointerScenario.m +++ b/features/fixtures/shared/scenarios/ReadGarbagePointerScenario.m @@ -39,9 +39,9 @@ @interface ReadGarbagePointerScenario : Scenario @implementation ReadGarbagePointerScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ReadOnlyPageScenario.m b/features/fixtures/shared/scenarios/ReadOnlyPageScenario.m index 0ebfeb63b..c5ddf29e9 100644 --- a/features/fixtures/shared/scenarios/ReadOnlyPageScenario.m +++ b/features/fixtures/shared/scenarios/ReadOnlyPageScenario.m @@ -37,9 +37,9 @@ @interface ReadOnlyPageScenario : Scenario @implementation ReadOnlyPageScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } static void __attribute__((used)) dummyfunc(void) { diff --git a/features/fixtures/shared/scenarios/RecrashScenarios.mm b/features/fixtures/shared/scenarios/RecrashScenarios.mm index da496aa9b..0f4406ac2 100644 --- a/features/fixtures/shared/scenarios/RecrashScenarios.mm +++ b/features/fixtures/shared/scenarios/RecrashScenarios.mm @@ -31,9 +31,9 @@ @interface NAME : Scenario \ @end \ @implementation NAME \ -- (void)startBugsnag { \ +- (void)configure { \ + [super configure]; \ self.config.onCrashHandler = HANDLER; \ - [super startBugsnag]; \ } \ - (void)run { \ CRASH; \ diff --git a/features/fixtures/shared/scenarios/ReleasedObjectScenario.m b/features/fixtures/shared/scenarios/ReleasedObjectScenario.m index 75a098378..ee5eee0f5 100644 --- a/features/fixtures/shared/scenarios/ReleasedObjectScenario.m +++ b/features/fixtures/shared/scenarios/ReleasedObjectScenario.m @@ -40,12 +40,12 @@ @implementation ReleasedObjectScenario - (NSString *)category { return @"Objective-C"; } - (NSString *)title { return @"Message a released object"; } - (NSString *)desc { return @""; } -- (void)startBugsnag { + +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - - (void)run { #if __i386__ && !TARGET_IPHONE_SIMULATOR diff --git a/features/fixtures/shared/scenarios/ReportBackgroundAppHangScenario.swift b/features/fixtures/shared/scenarios/ReportBackgroundAppHangScenario.swift index cc98ee1f1..902416413 100644 --- a/features/fixtures/shared/scenarios/ReportBackgroundAppHangScenario.swift +++ b/features/fixtures/shared/scenarios/ReportBackgroundAppHangScenario.swift @@ -2,7 +2,8 @@ import UIKit class ReportBackgroundAppHangScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.appHangThresholdMillis = 1_000 self.config.reportBackgroundAppHangs = true self.config.addOnSendError { event in @@ -11,7 +12,6 @@ class ReportBackgroundAppHangScenario: Scenario { stackframe.method == "CABackingStoreCollectBlocking" } } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ResumeSessionOOMScenario.m b/features/fixtures/shared/scenarios/ResumeSessionOOMScenario.m index 9c7aeacc5..4a960bbb4 100644 --- a/features/fixtures/shared/scenarios/ResumeSessionOOMScenario.m +++ b/features/fixtures/shared/scenarios/ResumeSessionOOMScenario.m @@ -6,11 +6,11 @@ @interface ResumeSessionOOMScenario : Scenario @implementation ResumeSessionOOMScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; self.config.enabledErrorTypes = errorTypes; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/ResumedSessionScenario.swift b/features/fixtures/shared/scenarios/ResumedSessionScenario.swift index aec85ccb3..2d4f114b5 100644 --- a/features/fixtures/shared/scenarios/ResumedSessionScenario.swift +++ b/features/fixtures/shared/scenarios/ResumedSessionScenario.swift @@ -9,9 +9,10 @@ import Foundation internal class ResumedSessionScenario: Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SIGBUSScenario.m b/features/fixtures/shared/scenarios/SIGBUSScenario.m index 6d974de38..98346997d 100644 --- a/features/fixtures/shared/scenarios/SIGBUSScenario.m +++ b/features/fixtures/shared/scenarios/SIGBUSScenario.m @@ -14,9 +14,9 @@ @interface SIGBUSScenario : Scenario @implementation SIGBUSScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGFPEScenario.m b/features/fixtures/shared/scenarios/SIGFPEScenario.m index 193cbd9a5..dba7a7f27 100644 --- a/features/fixtures/shared/scenarios/SIGFPEScenario.m +++ b/features/fixtures/shared/scenarios/SIGFPEScenario.m @@ -14,9 +14,9 @@ @interface SIGFPEScenario : Scenario @implementation SIGFPEScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGILLScenario.m b/features/fixtures/shared/scenarios/SIGILLScenario.m index 0b43f8536..e31b69ad2 100644 --- a/features/fixtures/shared/scenarios/SIGILLScenario.m +++ b/features/fixtures/shared/scenarios/SIGILLScenario.m @@ -14,9 +14,9 @@ @interface SIGILLScenario : Scenario @implementation SIGILLScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGPIPEIgnoredScenario.m b/features/fixtures/shared/scenarios/SIGPIPEIgnoredScenario.m index 9a5dd01d5..f199bfaf7 100644 --- a/features/fixtures/shared/scenarios/SIGPIPEIgnoredScenario.m +++ b/features/fixtures/shared/scenarios/SIGPIPEIgnoredScenario.m @@ -15,10 +15,10 @@ @interface SIGPIPEIgnoredScenario : Scenario @implementation SIGPIPEIgnoredScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; sigignore(SIGPIPE); self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGPIPEScenario.m b/features/fixtures/shared/scenarios/SIGPIPEScenario.m index be4696c12..8d5c3360d 100644 --- a/features/fixtures/shared/scenarios/SIGPIPEScenario.m +++ b/features/fixtures/shared/scenarios/SIGPIPEScenario.m @@ -14,9 +14,9 @@ @interface SIGPIPEScenario : Scenario @implementation SIGPIPEScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGSEGVScenario.m b/features/fixtures/shared/scenarios/SIGSEGVScenario.m index 667eb4569..ed7cd06ba 100644 --- a/features/fixtures/shared/scenarios/SIGSEGVScenario.m +++ b/features/fixtures/shared/scenarios/SIGSEGVScenario.m @@ -14,9 +14,9 @@ @interface SIGSEGVScenario : Scenario @implementation SIGSEGVScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGSYSScenario.m b/features/fixtures/shared/scenarios/SIGSYSScenario.m index 607c9919c..92d297b96 100644 --- a/features/fixtures/shared/scenarios/SIGSYSScenario.m +++ b/features/fixtures/shared/scenarios/SIGSYSScenario.m @@ -14,9 +14,9 @@ @interface SIGSYSScenario : Scenario @implementation SIGSYSScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SIGTRAPScenario.m b/features/fixtures/shared/scenarios/SIGTRAPScenario.m index c8ca433a2..bf4f7c3c8 100644 --- a/features/fixtures/shared/scenarios/SIGTRAPScenario.m +++ b/features/fixtures/shared/scenarios/SIGTRAPScenario.m @@ -14,9 +14,9 @@ @interface SIGTRAPScenario : Scenario @implementation SIGTRAPScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 9f05796f9..bc586b4e7 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -13,19 +13,19 @@ void logInternal(const char* level, NSString *format, va_list args); void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); +@class FixtureConfig; + @interface Scenario : NSObject +@property (strong, nonatomic, nonnull) FixtureConfig *fixtureConfig; @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; +@property (strong, nonatomic, nonnull) NSArray *args; -+ (Scenario *)createScenarioNamed:(NSString *)className - withConfig:(nullable BugsnagConfiguration *)config; - -@property (class, readwrite) NSString *baseMazeAddress; -@property (class, readonly, nullable) Scenario *currentScenario; +- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args; -- (instancetype)initWithConfig:(nullable BugsnagConfiguration *)config; +- (void)configure; -/** + /** * Executes the test case */ - (void)run; @@ -34,16 +34,14 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); - (void)didEnterBackgroundNotification; -@property (nonatomic, strong, nullable) NSString *eventMode; - -- (void)performBlockAndWaitForEventDelivery:(dispatch_block_t)block NS_SWIFT_NAME(performBlockAndWaitForEventDelivery(_:)); +// Wait for the next event to be delivered, and then run a block on the main thread. +- (void)waitForEventDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock; -- (void)performBlockAndWaitForSessionDelivery:(dispatch_block_t)block NS_SWIFT_NAME(performBlockAndWaitForSessionDelivery(_:)); +// Wait for the next session to be delivered, and then run a block on the main thread. +- (void)waitForSessionDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock; + (void)clearPersistentData; -+ (void)executeMazeRunnerCommand:(nullable void (^)(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 3b59f4146..38cb34ac7 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -4,14 +4,16 @@ // #import "Scenario.h" #import "Logging.h" -#import "FixtureConfig-Swift.h" #import #if TARGET_OS_IOS #define SWIFT_MODULE "iOSTestApp" +#import +#import "iOSTestApp-Swift.h" #elif TARGET_OS_OSX #define SWIFT_MODULE "macOSTestApp" +#import "macOSTestApp-Swift.h" #elif TARGET_OS_WATCH #import "watchos_maze_host.h" #define SWIFT_MODULE "watchOSTestApp_WatchKit_Extension" @@ -27,13 +29,12 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { // MARK: - -static Scenario *theScenario; -static NSString *theBaseMazeAddress = @""; - #if !TARGET_OS_WATCH static char ksLogPath[PATH_MAX]; #endif +static __weak Scenario *currentScenario; + @implementation Scenario { dispatch_block_t _onEventDelivery; dispatch_block_t _onSessionDelivery; @@ -65,43 +66,22 @@ + (void)load { }]; } -+ (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { - Class class = NSClassFromString(className) ?: - NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); - - if (!class) { - [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; +- (instancetype)initWithFixtureConfig:(FixtureConfig *)fixtureConfig args:(NSArray *)args { + if (self = [super init]) { + _fixtureConfig = fixtureConfig; + _args = args; + currentScenario = self; } - - return (theScenario = [(Scenario *)[class alloc] initWithConfig:config]); -} - -+ (Scenario *)currentScenario { - return theScenario; -} - -+ (NSString *)baseMazeAddress { - return theBaseMazeAddress; -} - -+ (void)setBaseMazeAddress:(NSString *)baseMazeAddress { - theBaseMazeAddress = baseMazeAddress; + return self; } -- (instancetype)initWithConfig:(BugsnagConfiguration *)config { - if (self = [super init]) { - if (config) { - _config = config; - } else { - _config = [[BugsnagConfiguration alloc] initWithApiKey:@"12312312312312312312312312312312"]; - _config.endpoints.notify = [NSString stringWithFormat:@"%@/notify", theBaseMazeAddress]; - _config.endpoints.sessions = [NSString stringWithFormat:@"%@/sessions", theBaseMazeAddress]; - } +- (void)configure { + self.config = [[BugsnagConfiguration alloc] initWithApiKey:self.fixtureConfig.apiKey]; + self.config.endpoints.notify = self.fixtureConfig.notifyURL.absoluteString; + self.config.endpoints.sessions = self.fixtureConfig.sessionsURL.absoluteString; #if !TARGET_OS_WATCH - _config.enabledErrorTypes.ooms = NO; + self.config.enabledErrorTypes.ooms = NO; #endif - } - return self; } - (void)run { @@ -116,22 +96,18 @@ - (void)startBugsnag { - (void)didEnterBackgroundNotification { } -- (void)performBlockAndWaitForEventDelivery:(dispatch_block_t)block { - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); +-(void)waitForEventDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock { _onEventDelivery = ^{ - dispatch_semaphore_signal(semaphore); + dispatch_async(dispatch_get_main_queue(), thenBlock); }; - block(); - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + deliveryBlock(); } -- (void)performBlockAndWaitForSessionDelivery:(dispatch_block_t)block NS_SWIFT_NAME(performBlockAndWaitForSessionDelivery(_:)) { - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); +-(void)waitForSessionDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock { _onSessionDelivery = ^{ - dispatch_semaphore_signal(semaphore); + dispatch_async(dispatch_get_main_queue(), thenBlock); }; - block(); - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + deliveryBlock(); } - (void)requestDidComplete:(NSURLRequest *)request { @@ -157,7 +133,7 @@ - (void)requestDidComplete:(NSURLRequest *)request { return NSURLSession_uploadTaskWithRequest_fromData_completionHandler(session, _cmd, request, fromData, ^(NSData *responseData, NSURLResponse *response, NSError *error) { completionHandler(responseData, response, error); - [theScenario requestDidComplete:request]; + [currentScenario requestDidComplete:request]; }); } @@ -213,75 +189,4 @@ + (void)clearPersistentData { #endif } -+ (void)executeMazeRunnerCommand:(void (^)(NSString *scenarioName, NSString *scenarioMode))preHandler { - logDebug(@"%s", __PRETTY_FUNCTION__); - NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - NSString *commandEndpoint = [NSString stringWithFormat:@"%@/command", theBaseMazeAddress]; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:commandEndpoint]]; - logDebug(@"%s: Sending command request to %@", __PRETTY_FUNCTION__, commandEndpoint); - [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (![response isKindOfClass:[NSHTTPURLResponse class]] || [(NSHTTPURLResponse *)response statusCode] != 200) { - logError(@"%s: command request failed with %@", __PRETTY_FUNCTION__, response ?: error); - preHandler(@"", NULL); - return; - } - logInfo(@"%s: command 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]]); - - if ([action isEqualToString:@"noop"]) { - logInfo(@"%s: No Maze Runner command queued at present", __PRETTY_FUNCTION__); - preHandler(@"", NULL); - return; - } - - NSString *scenarioName = [command objectForKey:@"scenario_name"]; - NSParameterAssert([scenarioName isKindOfClass:[NSString class]]); - - NSString *eventMode = [command objectForKey:@"scenario_mode"]; - if ([eventMode isKindOfClass:[NSNull class]]) { - eventMode = nil; - } - - if ([[command objectForKey:@"reset_data"] isEqual:@YES]) { - [self clearPersistentData]; - } - - // Display the scenario name/data on the UI - if (preHandler) { - dispatch_async(dispatch_get_main_queue(), ^{ - preHandler(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 { - logDebug(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); - - [self startBugsnagForScenario:scenarioName eventMode:eventMode]; - - [theScenario run]; -} - -+ (void)startBugsnagForScenario:(NSString *)scenarioName eventMode:(NSString *)eventMode { - logDebug(@"%s %@ %@", __PRETTY_FUNCTION__, scenarioName, eventMode); - - theScenario = [Scenario createScenarioNamed:scenarioName withConfig:nil]; - theScenario.eventMode = eventMode; - - logInfo(@"%s: Starting scenario \"%@\"", __PRETTY_FUNCTION__, NSStringFromClass([self class])); - [theScenario startBugsnag]; -} - @end diff --git a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyFalseScenario.swift b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyFalseScenario.swift index 5bb500ea3..5205d126f 100644 --- a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyFalseScenario.swift +++ b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyFalseScenario.swift @@ -1,7 +1,7 @@ class SendLaunchCrashesSynchronouslyFalseScenario: SendLaunchCrashesSynchronouslyScenario { - override func startBugsnag() { + override func configure() { + super.configure() config.sendLaunchCrashesSynchronously = false - super.startBugsnag() } } diff --git a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyLaunchCompletedScenario.swift b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyLaunchCompletedScenario.swift index 1c9b1ce3e..24c5d4758 100644 --- a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyLaunchCompletedScenario.swift +++ b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyLaunchCompletedScenario.swift @@ -1,7 +1,7 @@ class SendLaunchCrashesSynchronouslyLaunchCompletedScenario: SendLaunchCrashesSynchronouslyScenario { override func run() { - if eventMode != "report" { + if args[0] != "report" { logDebug(">>> Calling markLaunchCompleted()") Bugsnag.markLaunchCompleted() } diff --git a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyScenario.swift b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyScenario.swift index ff3111a9c..2088495a9 100644 --- a/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyScenario.swift +++ b/features/fixtures/shared/scenarios/SendLaunchCrashesSynchronouslyScenario.swift @@ -10,16 +10,20 @@ class SendLaunchCrashesSynchronouslyScenario: Scenario { var startDuration: CFAbsoluteTime = 0 - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.sendThreads = .never + } + + override func startBugsnag() { let startedAt = CFAbsoluteTimeGetCurrent() super.startBugsnag() startDuration = CFAbsoluteTimeGetCurrent() - startedAt } override func run() { - if eventMode == "report" { + if args[0] == "report" { logDebug(">>> Delaying to allow previous run's crash report to be sent") DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [startDuration] in NSLog(">>> Calling notify() with startDuration = \(startDuration)") diff --git a/features/fixtures/shared/scenarios/SessionCallbackCrashScenario.swift b/features/fixtures/shared/scenarios/SessionCallbackCrashScenario.swift index 0a84566fb..24e34f1af 100644 --- a/features/fixtures/shared/scenarios/SessionCallbackCrashScenario.swift +++ b/features/fixtures/shared/scenarios/SessionCallbackCrashScenario.swift @@ -10,7 +10,8 @@ import Foundation class SessionCallbackCrashScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSession { (session) -> Bool in session.app.id = "someAppId" @@ -30,7 +31,6 @@ class SessionCallbackCrashScenario : Scenario { session.app.id = "customAppId" return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SessionCallbackDiscardScenario.swift b/features/fixtures/shared/scenarios/SessionCallbackDiscardScenario.swift index 67c20d144..4990055a9 100644 --- a/features/fixtures/shared/scenarios/SessionCallbackDiscardScenario.swift +++ b/features/fixtures/shared/scenarios/SessionCallbackDiscardScenario.swift @@ -10,7 +10,8 @@ import Foundation class SessionCallbackDiscardScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; var count = 0 @@ -19,7 +20,6 @@ class SessionCallbackDiscardScenario : Scenario { count += 1 return count == 1 } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SessionCallbackOrderScenario.swift b/features/fixtures/shared/scenarios/SessionCallbackOrderScenario.swift index 4489d400a..0cd54c971 100644 --- a/features/fixtures/shared/scenarios/SessionCallbackOrderScenario.swift +++ b/features/fixtures/shared/scenarios/SessionCallbackOrderScenario.swift @@ -10,7 +10,8 @@ import Foundation class SessionCallbackOrderScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; var count = 0 @@ -23,7 +24,6 @@ class SessionCallbackOrderScenario : Scenario { session.device.id = "Second callback: \(count)" return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SessionCallbackOverrideScenario.swift b/features/fixtures/shared/scenarios/SessionCallbackOverrideScenario.swift index 80c985ab4..3416c98e7 100644 --- a/features/fixtures/shared/scenarios/SessionCallbackOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/SessionCallbackOverrideScenario.swift @@ -10,7 +10,8 @@ import Foundation class SessionCallbackOverrideScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.addOnSession { (session) -> Bool in session.app.id = "customAppId" @@ -18,7 +19,6 @@ class SessionCallbackOverrideScenario : Scenario { session.setUser("customUserId", withEmail: nil, andName: nil) return true } - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SessionCallbackRemovalScenario.m b/features/fixtures/shared/scenarios/SessionCallbackRemovalScenario.m index 49b49e780..f8d0246cf 100644 --- a/features/fixtures/shared/scenarios/SessionCallbackRemovalScenario.m +++ b/features/fixtures/shared/scenarios/SessionCallbackRemovalScenario.m @@ -14,7 +14,8 @@ @interface SessionCallbackRemovalScenario : Scenario @implementation SessionCallbackRemovalScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; [self.config addOnSessionBlock:^BOOL(BugsnagSession * _Nonnull session) { @@ -30,7 +31,6 @@ - (void)startBugsnag { }; BugsnagOnSessionRef onSession = [self.config addOnSessionBlock:block]; [self.config removeOnSession:onSession]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/SessionOOMScenario.m b/features/fixtures/shared/scenarios/SessionOOMScenario.m index 70d071ebc..c550b9a66 100644 --- a/features/fixtures/shared/scenarios/SessionOOMScenario.m +++ b/features/fixtures/shared/scenarios/SessionOOMScenario.m @@ -8,11 +8,11 @@ @interface SessionOOMScenario : Scenario @implementation SessionOOMScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; self.config.enabledErrorTypes = errorTypes; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/StackOverflowScenario.m b/features/fixtures/shared/scenarios/StackOverflowScenario.m index 8d5b06de3..0e4fd8642 100644 --- a/features/fixtures/shared/scenarios/StackOverflowScenario.m +++ b/features/fixtures/shared/scenarios/StackOverflowScenario.m @@ -36,9 +36,9 @@ @interface StackOverflowScenario : Scenario @implementation StackOverflowScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/StopSessionOOMScenario.m b/features/fixtures/shared/scenarios/StopSessionOOMScenario.m index a23df8904..9a20c7efa 100644 --- a/features/fixtures/shared/scenarios/StopSessionOOMScenario.m +++ b/features/fixtures/shared/scenarios/StopSessionOOMScenario.m @@ -6,11 +6,11 @@ @interface StopSessionOOMScenario : Scenario @implementation StopSessionOOMScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; BugsnagErrorTypes *errorTypes = [BugsnagErrorTypes new]; self.config.enabledErrorTypes = errorTypes; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/StoppedSessionScenario.swift b/features/fixtures/shared/scenarios/StoppedSessionScenario.swift index 2e6e7f221..3ece8b3bd 100644 --- a/features/fixtures/shared/scenarios/StoppedSessionScenario.swift +++ b/features/fixtures/shared/scenarios/StoppedSessionScenario.swift @@ -9,9 +9,10 @@ import Foundation internal class StoppedSessionScenario: Scenario { - override func startBugsnag() { + + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/SwiftAssertionScenario.swift b/features/fixtures/shared/scenarios/SwiftAssertionScenario.swift index 8249ffbe8..3fc82dc17 100644 --- a/features/fixtures/shared/scenarios/SwiftAssertionScenario.swift +++ b/features/fixtures/shared/scenarios/SwiftAssertionScenario.swift @@ -1,9 +1,10 @@ import Foundation class SwiftAssertionScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/SwiftCrashScenario.swift b/features/fixtures/shared/scenarios/SwiftCrashScenario.swift index 6b2edca71..19b0ee17f 100644 --- a/features/fixtures/shared/scenarios/SwiftCrashScenario.swift +++ b/features/fixtures/shared/scenarios/SwiftCrashScenario.swift @@ -30,9 +30,10 @@ import Foundation * Trigger a crash from inside a Swift method. */ class SwiftCrashScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/TelemetryUsageDisabledScenario.swift b/features/fixtures/shared/scenarios/TelemetryUsageDisabledScenario.swift index 341eb99a8..9a20cd912 100644 --- a/features/fixtures/shared/scenarios/TelemetryUsageDisabledScenario.swift +++ b/features/fixtures/shared/scenarios/TelemetryUsageDisabledScenario.swift @@ -1,8 +1,8 @@ class TelemetryUsageDisabledScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.telemetry.remove(.usage) - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/ThermalStateBreadcrumbScenario.swift b/features/fixtures/shared/scenarios/ThermalStateBreadcrumbScenario.swift index e9f52547b..7710b6551 100644 --- a/features/fixtures/shared/scenarios/ThermalStateBreadcrumbScenario.swift +++ b/features/fixtures/shared/scenarios/ThermalStateBreadcrumbScenario.swift @@ -9,11 +9,10 @@ @available(iOS 11.0, tvOS 11.0, *) class ThermalStateBreadcrumbScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() config.autoTrackSessions = false config.enabledBreadcrumbTypes = [.state] - - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UndefinedInstructionScenario.m b/features/fixtures/shared/scenarios/UndefinedInstructionScenario.m index 8a242e95a..277a45ca0 100644 --- a/features/fixtures/shared/scenarios/UndefinedInstructionScenario.m +++ b/features/fixtures/shared/scenarios/UndefinedInstructionScenario.m @@ -37,9 +37,9 @@ @interface UndefinedInstructionScenario : Scenario @implementation UndefinedInstructionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorChangeInvalidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/UnhandledErrorChangeInvalidReleaseStageScenario.swift index 272240080..e87f14565 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorChangeInvalidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/UnhandledErrorChangeInvalidReleaseStageScenario.swift @@ -1,8 +1,9 @@ class UnhandledErrorChangeInvalidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - if (self.eventMode == "noevent") { + if (args[0] == "noevent") { // The event is evaluated whether to be sent self.config.releaseStage = "prod" } else { @@ -10,7 +11,6 @@ class UnhandledErrorChangeInvalidReleaseStageScenario : Scenario { self.config.releaseStage = "test" } self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorChangeValidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/UnhandledErrorChangeValidReleaseStageScenario.swift index 16014d5dc..86c8597d7 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorChangeValidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/UnhandledErrorChangeValidReleaseStageScenario.swift @@ -1,8 +1,9 @@ class UnhandledErrorChangeValidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - if (self.eventMode == "noevent") { + if (args[0] == "noevent") { // The event is evaluated whether to be sent self.config.releaseStage = "test" } else { @@ -10,7 +11,6 @@ class UnhandledErrorChangeValidReleaseStageScenario : Scenario { self.config.releaseStage = "prod" } self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorInvalidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/UnhandledErrorInvalidReleaseStageScenario.swift index 08aa29245..69ca98a76 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorInvalidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/UnhandledErrorInvalidReleaseStageScenario.swift @@ -1,10 +1,10 @@ class UnhandledErrorInvalidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.releaseStage = "dev" self.config.enabledReleaseStages = ["prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorThreadSendAlwaysScenario.m b/features/fixtures/shared/scenarios/UnhandledErrorThreadSendAlwaysScenario.m index b23f297bf..b96cc88ac 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorThreadSendAlwaysScenario.m +++ b/features/fixtures/shared/scenarios/UnhandledErrorThreadSendAlwaysScenario.m @@ -14,9 +14,9 @@ @interface UnhandledErrorThreadSendAlwaysScenario : Scenario @implementation UnhandledErrorThreadSendAlwaysScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorThreadSendNeverScenario.m b/features/fixtures/shared/scenarios/UnhandledErrorThreadSendNeverScenario.m index 36df09b01..89c247baf 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorThreadSendNeverScenario.m +++ b/features/fixtures/shared/scenarios/UnhandledErrorThreadSendNeverScenario.m @@ -14,10 +14,10 @@ @interface UnhandledErrorThreadSendNeverScenario : Scenario @implementation UnhandledErrorThreadSendNeverScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = false; self.config.sendThreads = BSGThreadSendPolicyNever; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UnhandledErrorValidReleaseStageScenario.swift b/features/fixtures/shared/scenarios/UnhandledErrorValidReleaseStageScenario.swift index 5c22a1dc6..391521085 100644 --- a/features/fixtures/shared/scenarios/UnhandledErrorValidReleaseStageScenario.swift +++ b/features/fixtures/shared/scenarios/UnhandledErrorValidReleaseStageScenario.swift @@ -1,10 +1,10 @@ class UnhandledErrorValidReleaseStageScenario : Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.releaseStage = "prod" self.config.enabledReleaseStages = ["dev", "prod"] - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UnhandledMachExceptionOverrideScenario.m b/features/fixtures/shared/scenarios/UnhandledMachExceptionOverrideScenario.m index ac5feb761..a35dcc24f 100644 --- a/features/fixtures/shared/scenarios/UnhandledMachExceptionOverrideScenario.m +++ b/features/fixtures/shared/scenarios/UnhandledMachExceptionOverrideScenario.m @@ -14,9 +14,9 @@ @interface UnhandledMachExceptionOverrideScenario : MarkUnhandledHandledScenario @implementation UnhandledMachExceptionOverrideScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m index b41682e0c..02b8f4138 100644 --- a/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m +++ b/features/fixtures/shared/scenarios/UnhandledMachExceptionScenario.m @@ -14,9 +14,9 @@ @interface UnhandledMachExceptionScenario : Scenario @implementation UnhandledMachExceptionScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.autoTrackSessions = NO; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UserEventOverrideScenario.swift b/features/fixtures/shared/scenarios/UserEventOverrideScenario.swift index c4506e617..c4928c7cd 100644 --- a/features/fixtures/shared/scenarios/UserEventOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/UserEventOverrideScenario.swift @@ -13,9 +13,9 @@ import Foundation */ internal class UserEventOverrideScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UserFromClientScenario.swift b/features/fixtures/shared/scenarios/UserFromClientScenario.swift index 996706b09..a2deae79e 100644 --- a/features/fixtures/shared/scenarios/UserFromClientScenario.swift +++ b/features/fixtures/shared/scenarios/UserFromClientScenario.swift @@ -13,9 +13,9 @@ import Foundation */ internal class UserFromClientScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UserFromConfigScenario.swift b/features/fixtures/shared/scenarios/UserFromConfigScenario.swift index 8d8be1d42..52087dab0 100644 --- a/features/fixtures/shared/scenarios/UserFromConfigScenario.swift +++ b/features/fixtures/shared/scenarios/UserFromConfigScenario.swift @@ -13,10 +13,10 @@ import Foundation */ internal class UserFromConfigScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; self.config.setUser("abc", withEmail: "fake@gmail.com", andName: "Fay K") - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/scenarios/UserInfoScenario.swift b/features/fixtures/shared/scenarios/UserInfoScenario.swift index 3ec79d7b6..29ad7e2c4 100644 --- a/features/fixtures/shared/scenarios/UserInfoScenario.swift +++ b/features/fixtures/shared/scenarios/UserInfoScenario.swift @@ -13,9 +13,9 @@ import Foundation */ internal class UserInfoScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/UserNilScenario.swift b/features/fixtures/shared/scenarios/UserNilScenario.swift index d66a1ba93..3cb5d227e 100644 --- a/features/fixtures/shared/scenarios/UserNilScenario.swift +++ b/features/fixtures/shared/scenarios/UserNilScenario.swift @@ -1,8 +1,8 @@ class UserNilScenario: Scenario { - override func startBugsnag() { - self.config.autoTrackSessions = false; - super.startBugsnag() + override func configure() { + super.configure() + self.config.autoTrackSessions = false; } override func run() { diff --git a/features/fixtures/shared/scenarios/UserPersistenceDontPersistUserScenario.m b/features/fixtures/shared/scenarios/UserPersistenceDontPersistUserScenario.m index 82b4c0305..35db9cdfb 100644 --- a/features/fixtures/shared/scenarios/UserPersistenceDontPersistUserScenario.m +++ b/features/fixtures/shared/scenarios/UserPersistenceDontPersistUserScenario.m @@ -17,10 +17,10 @@ @interface UserPersistenceDontPersistUserScenario : Scenario @implementation UserPersistenceDontPersistUserScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.persistUser = NO; [self.config setUser:@"john" withEmail:@"george@ringo.com" andName:@"paul"]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UserPersistenceNoUserScenario.m b/features/fixtures/shared/scenarios/UserPersistenceNoUserScenario.m index 7affb9ab6..0254117d1 100644 --- a/features/fixtures/shared/scenarios/UserPersistenceNoUserScenario.m +++ b/features/fixtures/shared/scenarios/UserPersistenceNoUserScenario.m @@ -17,10 +17,6 @@ @interface UserPersistenceNoUserScenario : Scenario @implementation UserPersistenceNoUserScenario -- (void)startBugsnag { - [super startBugsnag]; -} - - (void)run { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [Bugsnag notifyError:[NSError errorWithDomain:@"com.bugsnag" code:833 userInfo:nil]]; diff --git a/features/fixtures/shared/scenarios/UserPersistencePersistUserClientScenario.m b/features/fixtures/shared/scenarios/UserPersistencePersistUserClientScenario.m index b36aa7fda..86a7bd668 100644 --- a/features/fixtures/shared/scenarios/UserPersistencePersistUserClientScenario.m +++ b/features/fixtures/shared/scenarios/UserPersistencePersistUserClientScenario.m @@ -17,9 +17,9 @@ @interface UserPersistencePersistUserClientScenario : Scenario @implementation UserPersistencePersistUserClientScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.persistUser = YES; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UserPersistencePersistUserScenario.m b/features/fixtures/shared/scenarios/UserPersistencePersistUserScenario.m index c793f09b6..9f6be26f5 100644 --- a/features/fixtures/shared/scenarios/UserPersistencePersistUserScenario.m +++ b/features/fixtures/shared/scenarios/UserPersistencePersistUserScenario.m @@ -17,10 +17,10 @@ @interface UserPersistencePersistUserScenario : Scenario @implementation UserPersistencePersistUserScenario -- (void)startBugsnag { +- (void)configure { + [super configure]; self.config.persistUser = YES; [self.config setUser:@"foo" withEmail:@"baz@grok.com" andName:@"bar"]; - [super startBugsnag]; } - (void)run { diff --git a/features/fixtures/shared/scenarios/UserSessionOverrideScenario.swift b/features/fixtures/shared/scenarios/UserSessionOverrideScenario.swift index 0d3d9dec2..a4d3ea7b2 100644 --- a/features/fixtures/shared/scenarios/UserSessionOverrideScenario.swift +++ b/features/fixtures/shared/scenarios/UserSessionOverrideScenario.swift @@ -13,9 +13,9 @@ import Foundation */ internal class UserSessionOverrideScenario: Scenario { - override func startBugsnag() { + override func configure() { + super.configure() self.config.autoTrackSessions = false; - super.startBugsnag() } override func run() { diff --git a/features/fixtures/shared/utils/BugsnagWrapper.swift b/features/fixtures/shared/utils/BugsnagWrapper.swift index 79d0d50ba..f8841afd7 100644 --- a/features/fixtures/shared/utils/BugsnagWrapper.swift +++ b/features/fixtures/shared/utils/BugsnagWrapper.swift @@ -29,8 +29,8 @@ class BugsnagWrapper : Bugsnag { } // Overwrite the configuration endpoints - configuration.endpoints.notify = String(format: "%@/notify", Scenario.baseMazeAddress); - configuration.endpoints.sessions = String(format: "%@/sessions", Scenario.baseMazeAddress); + configuration.endpoints.notify = String(format: "%@/notify", Fixture.baseMazeRunnerAddress!.absoluteString); + configuration.endpoints.sessions = String(format: "%@/sessions", Fixture.baseMazeRunnerAddress!.absoluteString); return Bugsnag.start(with: configuration); } diff --git a/features/fixtures/ios/iOSTestApp/CommandReaderThread.swift b/features/fixtures/shared/utils/CommandReaderThread.swift similarity index 100% rename from features/fixtures/ios/iOSTestApp/CommandReaderThread.swift rename to features/fixtures/shared/utils/CommandReaderThread.swift diff --git a/features/fixtures/ios/iOSTestApp/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift similarity index 57% rename from features/fixtures/ios/iOSTestApp/Fixture.swift rename to features/fixtures/shared/utils/Fixture.swift index 21a2eca2a..a1608fd45 100644 --- a/features/fixtures/ios/iOSTestApp/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -15,16 +15,19 @@ protocol CommandReceiver { class Fixture: NSObject, CommandReceiver { static let defaultMazeRunnerURL = URL(string: "http://bs-local.com:9339")! + static let defaultApiKey = "12312312312312312312312312312312" + @objc static var baseMazeRunnerAddress: URL? var readyToReceiveCommand = false var commandReaderThread: CommandReaderThread? - var fixtureConfig: FixtureConfig = FixtureConfig(mazeRunnerBaseAddress: defaultMazeRunnerURL) - var scenario: Scenario? = nil + var fixtureConfig: FixtureConfig = FixtureConfig(apiKey: defaultApiKey, mazeRunnerBaseAddress: defaultMazeRunnerURL) + var currentScenario: Scenario? = nil func start() { DispatchQueue.global(qos: .userInitiated).async { self.loadMazeRunnerAddress { address in - self.fixtureConfig = FixtureConfig(mazeRunnerBaseAddress: address) + Fixture.baseMazeRunnerAddress = address + self.fixtureConfig = FixtureConfig(apiKey: Fixture.defaultApiKey, mazeRunnerBaseAddress: address) self.beginReceivingCommands(fixtureConfig: self.fixtureConfig) } } @@ -47,7 +50,13 @@ class Fixture: NSObject, CommandReceiver { logInfo("Executing command [\(command.action)] with args \(command.args)") switch command.action { case "run_scenario": - self.runScenario(scenarioName: command.args[0], completion: { + self.runScenario(scenarioName: command.args[0], args: Array(command.args[1...]), completion: { + self.readyToReceiveCommand = true + }) + isReady = false; + break + case "start_bugsnag": + self.startBugsnagForScenario(scenarioName: command.args[0], args: Array(command.args[1...]), completion: { self.readyToReceiveCommand = true }) isReady = false; @@ -55,6 +64,9 @@ class Fixture: NSObject, CommandReceiver { case "invoke_method": self.invokeMethod(methodName: command.args[0], args: Array(command.args[1...])) break + case "reset_data": + self.clearPersistentData() + break case "noop": break default: @@ -66,42 +78,74 @@ class Fixture: NSObject, CommandReceiver { } } - private func runScenario(scenarioName: String, completion: @escaping () -> ()) { + @objc func clearPersistentData() { + Scenario.clearPersistentData() + } + + @objc func startBugsnagForScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { + logInfo("========== Starting Bugsnag for scenario \(scenarioName) ==========") + loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) + logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + completion() + } + } + + @objc func runScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { logInfo("========== Running scenario \(scenarioName) ==========") - let scenarioClass: AnyClass = NSClassFromString("Fixture.\(scenarioName)")! - logInfo("Loaded scenario class: \(scenarioClass)") - scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig) as Scenario? - logInfo("Configuring scenario in class \(scenarioClass)") - scenario!.configure() - logInfo("Clearing persistent data") - scenario!.clearPersistentData() - logInfo("Starting bugsnag performance") - scenario!.startBugsnag() - logInfo("Starting scenario in class \(scenarioClass)") - scenario!.run() - logInfo("========== Completed scenario \(scenarioName) ==========") + loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) + logInfo("Starting scenario in class \(String(describing: currentScenario.self))") + currentScenario!.run() + logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.scenario!.reportMeasurements() - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - completion() - } + completion() } } + @objc func setApiKey(apiKey: String) { + self.fixtureConfig.apiKey = apiKey + } + + @objc func setNotifyEndpoint(endpoint: String) { + self.fixtureConfig.notifyURL = URL(string: endpoint)! + } + + @objc func setSessionEndpoint(endpoint: String) { + self.fixtureConfig.sessionsURL = URL(string: endpoint)! + } + + func didEnterBackgroundNotification() { + currentScenario?.didEnterBackgroundNotification() + } + + private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String]) { + logInfo("Loading scenario from class named: \(scenarioName)") + let scenarioClass: AnyClass = NSClassFromString("Fixture.\(scenarioName)")! + logInfo("Initializing scenario class: \(scenarioClass)") + let scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig, args:args) + currentScenario = scenario + logInfo("Configuring scenario in class \(String(describing: scenario.self))") + scenario.configure() + logInfo("Clearing persistent data") + Scenario.clearPersistentData() + logInfo("Starting bugsnag") + scenario.startBugsnag() + } + private func invokeMethod(methodName: String, args: Array) { - logInfo("Invoking method \(methodName) with args \(args) on \(String(describing: scenario!.self))") + logInfo("Invoking method \(methodName) with args \(args) on \(String(describing: currentScenario!.self))") let sel = NSSelectorFromString(methodName) - if (!scenario!.responds(to: sel)) { - fatalError("\(String(describing: scenario!.self)) does not respond to \(methodName). Did you set the @objcMembers annotation on \(String(describing: scenario!.self))?") + if (!currentScenario!.responds(to: sel)) { + fatalError("\(String(describing: currentScenario!.self)) does not respond to \(methodName). Did you set the @objcMembers annotation on \(String(describing: currentScenario!.self))?") } switch args.count { case 0: - scenario!.perform(sel) + currentScenario!.perform(sel) case 1: // Note: Parameter must accept a string - scenario!.perform(sel, with: args[0]) + currentScenario!.perform(sel, with: args[0]) default: fatalError("invoking \(methodName) with args \(args): Fixture currently only supports up to 1 argument") } diff --git a/features/fixtures/ios/iOSTestApp/FixtureConfig.swift b/features/fixtures/shared/utils/FixtureConfig.swift similarity index 79% rename from features/fixtures/ios/iOSTestApp/FixtureConfig.swift rename to features/fixtures/shared/utils/FixtureConfig.swift index f2029141d..75bfc690d 100644 --- a/features/fixtures/ios/iOSTestApp/FixtureConfig.swift +++ b/features/fixtures/shared/utils/FixtureConfig.swift @@ -8,16 +8,18 @@ import Foundation -@objc class FixtureConfig: NSObject { +@objcMembers class FixtureConfig: NSObject { + var apiKey: String let mazeRunnerURL: URL let tracesURL: URL let commandURL: URL let metricsURL: URL let reflectURL: URL - let notifyURL: URL - let sessionsURL: URL + var notifyURL: URL + var sessionsURL: URL - init(mazeRunnerBaseAddress: URL) { + init(apiKey: String, mazeRunnerBaseAddress: URL) { + self.apiKey = apiKey mazeRunnerURL = mazeRunnerBaseAddress tracesURL = mazeRunnerBaseAddress.appendingPathComponent("traces") commandURL = mazeRunnerBaseAddress.appendingPathComponent("command") diff --git a/features/fixtures/shared/scenarios/Logging.h b/features/fixtures/shared/utils/Logging.h similarity index 100% rename from features/fixtures/shared/scenarios/Logging.h rename to features/fixtures/shared/utils/Logging.h diff --git a/features/fixtures/shared/scenarios/Logging.m b/features/fixtures/shared/utils/Logging.m similarity index 100% rename from features/fixtures/shared/scenarios/Logging.m rename to features/fixtures/shared/utils/Logging.m diff --git a/features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift b/features/fixtures/shared/utils/MazeRunnerCommand.swift similarity index 100% rename from features/fixtures/ios/iOSTestApp/MazeRunnerCommand.swift rename to features/fixtures/shared/utils/MazeRunnerCommand.swift diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index d0d579c37..91046fcc1 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -1,5 +1,5 @@ When('I run {string}') do |scenario_name| - execute_command :run_scenario, scenario_name + execute_command "run_scenario", [scenario_name, $scenario_mode] end When("I run {string} and relaunch the crashed app") do |event_type| @@ -11,11 +11,11 @@ end When('I clear all persistent data') do - $reset_data = true + execute_command "reset_data", [] end When('I configure Bugsnag for {string}') do |scenario_name| - execute_command :start_bugsnag, scenario_name + execute_command "start_bugsnag", [scenario_name, $scenario_mode] end When('I kill and relaunch the app') do @@ -84,19 +84,12 @@ Maze::Server.errors.clear end -def execute_command(action, scenario_name) - platform = Maze::Helper.get_current_platform - command = { action: action, scenario_name: scenario_name, scenario_mode: $scenario_mode, reset_data: $reset_data } - Maze::Server.commands.add command - trigger_app_command - $scenario_mode = nil - $reset_data = false + +def execute_command(action, args) + Maze::Server.commands.add({ action: action, args: args }) # Ensure fixture has read the command count = 100 - # Launching the fixture via xcdebug is slow - count = 600 if Maze::Helper.get_current_platform == 'watchos' 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 def trigger_app_command From cbec510bebed709703265e53af096618206f88c2 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 15 Mar 2024 10:50:36 +0100 Subject: [PATCH 05/51] Final tweaks to get the new mazerunner working --- .../ios/iOSTestApp/ViewController.swift | 5 +++- .../shared/utils/CommandReaderThread.swift | 24 ++++++++++++++++--- features/fixtures/shared/utils/Fixture.swift | 9 ++++--- features/steps/app_steps.rb | 4 ++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 9c8f9f691..10e57473b 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -17,10 +17,13 @@ class ViewController: UIViewController { required init?(coder: NSCoder) { super.init(coder: coder) - apiKeyField.text = Fixture.defaultApiKey fixture.start() } + override func viewDidLoad() { + apiKeyField.text = Fixture.defaultApiKey + } + @IBAction func runTestScenario() { let apiKey = apiKeyField.text! let scenarioName = scenarioNameField.text! diff --git a/features/fixtures/shared/utils/CommandReaderThread.swift b/features/fixtures/shared/utils/CommandReaderThread.swift index 00be4cf45..9857d5cf1 100644 --- a/features/fixtures/shared/utils/CommandReaderThread.swift +++ b/features/fixtures/shared/utils/CommandReaderThread.swift @@ -9,6 +9,7 @@ import Foundation class CommandReaderThread: Thread { + let uuidKey = "bugsnagPerformanceCI_lastCommandUUID" var fixtureConfig: FixtureConfig var commandReceiver: CommandReceiver var lastCommandID: String = "" @@ -16,6 +17,7 @@ class CommandReaderThread: Thread { init(fixtureConfig: FixtureConfig, commandReceiver: CommandReceiver) { self.fixtureConfig = fixtureConfig self.commandReceiver = commandReceiver + self.lastCommandID = UserDefaults.standard.string(forKey: uuidKey) ?? "" } override func main() { @@ -35,6 +37,11 @@ class CommandReaderThread: Thread { return fetchTask } + func saveLastCommandID(uuid: String) { + lastCommandID = uuid + UserDefaults.standard.set(uuid, forKey: uuidKey) + } + func receiveNextCommand() { let maxWaitTime = 5.0 let pollingInterval = 1.0 @@ -49,7 +56,7 @@ class CommandReaderThread: Thread { logDebug("Command fetch: Request succeeded") let command = fetchTask.command! if (command.uuid != "") { - lastCommandID = command.uuid + saveLastCommandID(uuid: command.uuid) } commandReceiver.receiveCommand(command: command) return @@ -63,6 +70,12 @@ class CommandReaderThread: Thread { fetchTask = newStartedFetchTask() } break + case CommandFetchState.unknownCommandID: + logInfo("Command fetch: Unknown command ID. Starting from the first command...") + self.lastCommandID = "" + fetchTask = newStartedFetchTask() + break + case CommandFetchState.failed: logInfo("Command fetch: Request failed. Trying again...") fetchTask = newStartedFetchTask() @@ -79,7 +92,7 @@ extension Date { } enum CommandFetchState { - case failed, fetching, success + case failed, unknownCommandID, fetching, success } class CommandFetchTask { @@ -112,7 +125,12 @@ class CommandFetchTask { } catch { self.state = CommandFetchState.failed let dataAsString = String(data: data, encoding: .utf8) - logError("Failed to fetch command: Invalid Response from \(String(describing: self.url)): [\(String(describing: dataAsString))]: Error is: \(error)") + let isInvalidUUID = dataAsString != nil && dataAsString!.contains("there is no command with a UUID of") + if isInvalidUUID { + self.state = CommandFetchState.unknownCommandID + } else { + logError("Failed to fetch command: Invalid Response from \(String(describing: self.url)): [\(String(describing: dataAsString))]: Error is: \(error)") + } } } else if let error = error { self.state = CommandFetchState.failed diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index a1608fd45..055e22d6e 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -86,7 +86,7 @@ class Fixture: NSObject, CommandReceiver { logInfo("========== Starting Bugsnag for scenario \(scenarioName) ==========") loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } } @@ -97,7 +97,7 @@ class Fixture: NSObject, CommandReceiver { logInfo("Starting scenario in class \(String(describing: currentScenario.self))") currentScenario!.run() logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } } @@ -120,14 +120,13 @@ class Fixture: NSObject, CommandReceiver { private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String]) { logInfo("Loading scenario from class named: \(scenarioName)") - let scenarioClass: AnyClass = NSClassFromString("Fixture.\(scenarioName)")! + let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String + let scenarioClass: AnyClass = NSClassFromString("\(namespace).\(scenarioName)")! logInfo("Initializing scenario class: \(scenarioClass)") let scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig, args:args) currentScenario = scenario logInfo("Configuring scenario in class \(String(describing: scenario.self))") scenario.configure() - logInfo("Clearing persistent data") - Scenario.clearPersistentData() logInfo("Starting bugsnag") scenario.startBugsnag() } diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 91046fcc1..be7cf2b13 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -1,5 +1,5 @@ When('I run {string}') do |scenario_name| - execute_command "run_scenario", [scenario_name, $scenario_mode] + execute_command "run_scenario", [scenario_name, $scenario_mode || ''] end When("I run {string} and relaunch the crashed app") do |event_type| @@ -15,7 +15,7 @@ end When('I configure Bugsnag for {string}') do |scenario_name| - execute_command "start_bugsnag", [scenario_name, $scenario_mode] + execute_command "start_bugsnag", [scenario_name, $scenario_mode || ''] end When('I kill and relaunch the app') do From 5e74590ab53fa27f6a2da84ebc34baa41492e939 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 15 Mar 2024 13:08:49 +0100 Subject: [PATCH 06/51] The loaded scenario may be exposed using a namespace, or not --- features/fixtures/shared/utils/Fixture.swift | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index 055e22d6e..b2c4d2113 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -118,10 +118,27 @@ class Fixture: NSObject, CommandReceiver { currentScenario?.didEnterBackgroundNotification() } - private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String]) { - logInfo("Loading scenario from class named: \(scenarioName)") + private func loadScenarioClass(named: String) -> AnyClass { + let scenarioName = named let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String - let scenarioClass: AnyClass = NSClassFromString("\(namespace).\(scenarioName)")! + var scenarioClass: AnyClass? + + scenarioClass = NSClassFromString("\(namespace).\(scenarioName)") + if scenarioClass != nil { + return scenarioClass! + } + + scenarioClass = NSClassFromString(scenarioName) + if scenarioClass != nil { + return scenarioClass! + } + + fatalError("Could not find class \(scenarioName) or \(namespace).\(scenarioName). Aborting scenario load...") + } + + private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String]) { + logInfo("Loading scenario: \(scenarioName)") + let scenarioClass: AnyClass = loadScenarioClass(named: scenarioName) logInfo("Initializing scenario class: \(scenarioClass)") let scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig, args:args) currentScenario = scenario From 2db78dc7bf233582513da4ec3c466103b4e23991 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Mon, 18 Mar 2024 10:56:25 +0100 Subject: [PATCH 07/51] Fix newly exposed flakes --- features/delivery.feature | 6 ++++-- features/user_persistence.feature | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/features/delivery.feature b/features/delivery.feature index 08af636e5..477d76831 100644 --- a/features/delivery.feature +++ b/features/delivery.feature @@ -137,11 +137,13 @@ Feature: Delivery of errors And I discard the oldest session When I set the HTTP status code to 200 And I kill and relaunch the app - And I configure Bugsnag for "MaxPersistedSessionsScenario" - And I wait to receive 2 sessions + And I run "MaxPersistedSessionsScenario" + And I wait to receive 3 sessions Then the session "user.id" equals "3" And I discard the oldest session And the session "user.id" equals "2" + And I discard the oldest session + And the session "user.id" equals "4" Scenario: Breadcrumbs should be trimmed if payload is oversized When I run "OversizedBreadcrumbsScenario" diff --git a/features/user_persistence.feature b/features/user_persistence.feature index 89ae859a3..2560d491d 100644 --- a/features/user_persistence.feature +++ b/features/user_persistence.feature @@ -8,12 +8,14 @@ Feature: Persisting User Information # User is set and comes through And I wait to receive a session + And I wait to receive an error And I kill and relaunch the app Then the session is valid for the session reporting API And the session "user.id" equals "foo" And the session "user.email" equals "baz@grok.com" And the session "user.name" equals "bar" And I discard the oldest session + And I discard the oldest error # Generate session and event Then I run "UserPersistenceNoUserScenario" @@ -38,7 +40,7 @@ Scenario: User Info is persisted from client across app runs # Session is captured before the user can be set on the Client And I wait to receive a session - And I wait for 1 second + And I wait to receive an error And I kill and relaunch the app Then the session is valid for the session reporting API @@ -46,6 +48,7 @@ Scenario: User Info is persisted from client across app runs And the session "user.email" is null And the session "user.name" is null And I discard the oldest session + And I discard the oldest error # Generate session and event Then I run "UserPersistenceNoUserScenario" From e245b30e44abf6c805b3bd6ef1c7420577962415 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Mon, 18 Mar 2024 15:09:17 +0100 Subject: [PATCH 08/51] Expose Fixture.start to objective-c --- features/fixtures/macos/macOSTestApp/AppDelegate.m | 2 +- .../macos/macOSTestApp/MainWindowController.h | 2 +- .../macos/macOSTestApp/MainWindowController.m | 11 +++++++++-- features/fixtures/shared/utils/Fixture.swift | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/features/fixtures/macos/macOSTestApp/AppDelegate.m b/features/fixtures/macos/macOSTestApp/AppDelegate.m index 652778f8b..6d0430563 100644 --- a/features/fixtures/macos/macOSTestApp/AppDelegate.m +++ b/features/fixtures/macos/macOSTestApp/AppDelegate.m @@ -38,7 +38,7 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification { static BOOL once; if (!once && [self launchedByMazeRunner]) { once = YES; - [self.mainWindowController executeMazeRunnerCommand:self]; + [self.mainWindowController startFixture]; } } diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.h b/features/fixtures/macos/macOSTestApp/MainWindowController.h index c158da37c..1743b6238 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.h +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN @interface MainWindowController : NSWindowController -- (IBAction)executeMazeRunnerCommand:(id)sender; +- (void)startFixture; @end diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index bb277b973..6a5941eaf 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -39,6 +39,10 @@ - (void)windowDidLoad { self.fixture = [[Fixture alloc] init]; } +- (void)startFixture { + [self.fixture start]; +} + - (BugsnagConfiguration *)configuration { BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:self.apiKey]; if (self.notifyEndpoint) { @@ -57,8 +61,11 @@ - (IBAction)runScenario:(id)sender { [self.fixture setNotifyEndpointWithEndpoint:self.notifyEndpoint]; [self.fixture setSessionEndpointWithEndpoint:self.sessionEndpoint]; NSString *scenarioName = self.scenarioName; - NSArray *args = @[self.scenarioMetadata]; - + NSArray *args = @[]; + if (self.scenarioMetadata != nil) { + args = @[self.scenarioMetadata]; + } + // 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. diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index b2c4d2113..84c105223 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -23,7 +23,7 @@ class Fixture: NSObject, CommandReceiver { var fixtureConfig: FixtureConfig = FixtureConfig(apiKey: defaultApiKey, mazeRunnerBaseAddress: defaultMazeRunnerURL) var currentScenario: Scenario? = nil - func start() { + @objc func start() { DispatchQueue.global(qos: .userInitiated).async { self.loadMazeRunnerAddress { address in Fixture.baseMazeRunnerAddress = address From 18837b2c0d5464f9aa455e4dac1a18153b21c07d Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Tue, 19 Mar 2024 15:31:52 +0100 Subject: [PATCH 09/51] Refactor to support macos again --- .../ios/iOSTestApp/ViewController.swift | 2 +- .../macos/macOSTestApp/MainWindowController.m | 27 ++++++++--------- features/fixtures/shared/utils/Fixture.swift | 29 ++++++++++++++----- features/steps/app_steps.rb | 4 +-- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 10e57473b..2436aec5e 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -13,7 +13,7 @@ class ViewController: UIViewController { @IBOutlet var scenarioNameField : UITextField! @IBOutlet var scenarioMetaDataField : UITextField! @IBOutlet var apiKeyField: UITextField! - var fixture: Fixture = Fixture() + var fixture: Fixture = Fixture(defaultMazeRunnerURL: URL(string: "http://bs-local.com:9339")!, shouldLoadMazeRunnerURL: true) required init?(coder: NSCoder) { super.init(coder: coder) diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 6a5941eaf..7bb1edd49 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -22,38 +22,35 @@ @interface MainWindowController () @property (copy) NSString *scenarioMetadata; @property (copy) NSString *scenarioName; @property (copy) NSString *sessionEndpoint; + +@property (nonatomic,strong) FixtureConfig *fixtureConfig; @property (nonatomic,strong) Fixture *fixture; @end #pragma mark - +static NSString *defaultAPIKey = @"12312312312312312312312312312312"; +static NSString *defaultMazeRunnerURLString = @"http://localhost:9339"; + @implementation MainWindowController - (void)windowDidLoad { [super windowDidLoad]; - self.apiKey = @"12312312312312312312312312312312"; - self.notifyEndpoint = @"http://localhost:9339/notify"; - self.sessionEndpoint = @"http://localhost:9339/sessions"; - self.fixture = [[Fixture alloc] init]; + self.fixtureConfig = [[FixtureConfig alloc] initWithApiKey:defaultAPIKey + mazeRunnerBaseAddress:[NSURL URLWithString:defaultMazeRunnerURLString]]; + self.fixture = [[Fixture alloc] initWithDefaultMazeRunnerURL:self.fixtureConfig.mazeRunnerURL + shouldLoadMazeRunnerURL:NO]; + self.apiKey = self.fixtureConfig.apiKey; + self.notifyEndpoint = self.fixtureConfig.notifyURL.absoluteString; + self.sessionEndpoint = self.fixtureConfig.sessionsURL.absoluteString; } - (void)startFixture { [self.fixture start]; } -- (BugsnagConfiguration *)configuration { - BugsnagConfiguration *configuration = [[BugsnagConfiguration alloc] initWithApiKey:self.apiKey]; - if (self.notifyEndpoint) { - configuration.endpoints.notify = self.notifyEndpoint; - } - if (self.sessionEndpoint) { - configuration.endpoints.sessions = self.sessionEndpoint; - } - return configuration; -} - - (IBAction)runScenario:(id)sender { logDebug(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index 84c105223..a36014926 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -14,22 +14,35 @@ protocol CommandReceiver { } class Fixture: NSObject, CommandReceiver { - static let defaultMazeRunnerURL = URL(string: "http://bs-local.com:9339")! static let defaultApiKey = "12312312312312312312312312312312" @objc static var baseMazeRunnerAddress: URL? + let defaultMazeRunnerURL: URL + let shouldLoadMazeRunnerURL: Bool var readyToReceiveCommand = false var commandReaderThread: CommandReaderThread? - var fixtureConfig: FixtureConfig = FixtureConfig(apiKey: defaultApiKey, mazeRunnerBaseAddress: defaultMazeRunnerURL) + var fixtureConfig: FixtureConfig var currentScenario: Scenario? = nil + @objc init(defaultMazeRunnerURL: URL, shouldLoadMazeRunnerURL: Bool) { + self.defaultMazeRunnerURL = defaultMazeRunnerURL + self.shouldLoadMazeRunnerURL = shouldLoadMazeRunnerURL + fixtureConfig = FixtureConfig(apiKey: Fixture.defaultApiKey, mazeRunnerBaseAddress: defaultMazeRunnerURL) + } + @objc func start() { - DispatchQueue.global(qos: .userInitiated).async { - self.loadMazeRunnerAddress { address in - Fixture.baseMazeRunnerAddress = address - self.fixtureConfig = FixtureConfig(apiKey: Fixture.defaultApiKey, mazeRunnerBaseAddress: address) - self.beginReceivingCommands(fixtureConfig: self.fixtureConfig) + let startFunc: (URL)->() = { address in + Fixture.baseMazeRunnerAddress = address + self.fixtureConfig = FixtureConfig(apiKey: Fixture.defaultApiKey, mazeRunnerBaseAddress: address) + self.beginReceivingCommands(fixtureConfig: self.fixtureConfig) + } + + if shouldLoadMazeRunnerURL { + DispatchQueue.global(qos: .userInitiated).async { + self.loadMazeRunnerAddress(completion: startFunc) } + } else { + startFunc(fixtureConfig.mazeRunnerURL) } } @@ -168,7 +181,7 @@ class Fixture: NSObject, CommandReceiver { } func loadMazeRunnerAddress(completion: (URL)->()) { - let defaultUrl = Fixture.defaultMazeRunnerURL + let defaultUrl = defaultMazeRunnerURL // Only iOS 12 and above will run on BitBar for now if #available(iOS 12.0, *) {} else { diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index be7cf2b13..a4ece98aa 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -87,9 +87,7 @@ def execute_command(action, args) Maze::Server.commands.add({ action: action, args: args }) - # Ensure fixture has read the command - count = 100 - sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 + trigger_app_command end def trigger_app_command From e5489f26101ab2819a0245c6e9da956282e7ccb7 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 20 Mar 2024 15:34:31 +0100 Subject: [PATCH 10/51] Refactor maze runner app launch --- .buildkite/pipeline.yml | 1 + features/steps/app_steps.rb | 66 ++++++++++++++++++++++--------------- features/support/env.rb | 2 ++ 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4fb9a0df3..fe84c994c 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -232,6 +232,7 @@ steps: - echo "--- Test" - bundle exec maze-runner features/stress_test.feature + --os=macos --no-log-requests artifact_paths: - features/fixtures/macos-stress-test/*.log diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index a4ece98aa..3d862a95e 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -1,5 +1,6 @@ When('I run {string}') do |scenario_name| execute_command "run_scenario", [scenario_name, $scenario_mode || ''] + $scenario_mode = nil end When("I run {string} and relaunch the crashed app") do |event_type| @@ -16,36 +17,15 @@ When('I configure Bugsnag for {string}') do |scenario_name| execute_command "start_bugsnag", [scenario_name, $scenario_mode || ''] + $scenario_mode = nil end When('I kill and relaunch the app') do - case Maze::Helper.get_current_platform - when 'ios' - Maze.driver.close_app - Maze.driver.launch_app - when 'macos' - # noop - when 'watchos' - # noop - else - raise "Unsupported platform: #{Maze::Helper.get_current_platform}" - end + kill_and_relaunch_app end When("I relaunch the app after a crash") do - case Maze::Helper.get_current_platform - when 'ios' - # Wait for the app to stop running before relaunching - step 'the app is not running' - Maze.driver.launch_app - when 'macos' - # Wait for the app to stop running before relaunching - step 'the app is not running' - when 'watchos' - sleep 5 # We have no way to poll the app state on watchOS - else - raise "Unsupported platform: #{Maze::Helper.get_current_platform}" - end + relaunch_crashed_app end # @@ -87,10 +67,9 @@ def execute_command(action, args) Maze::Server.commands.add({ action: action, args: args }) - trigger_app_command end -def trigger_app_command +def launch_app case Maze::Helper.get_current_platform when 'ios' # Do nothing @@ -103,6 +82,41 @@ def trigger_app_command end end +def relaunch_crashed_app + # Give it time to settle down + sleep 1 + + case Maze::Helper.get_current_platform + when 'ios' + # Wait for the app to stop running before relaunching + step 'the app is not running' + Maze.driver.launch_app + when 'macos' + # Wait for the app to stop running before relaunching + step 'the app is not running' + launch_app + when 'watchos' + sleep 5 # We have no way to poll the app state on watchOS + launch_app + else + raise "Unsupported platform: #{Maze::Helper.get_current_platform}" + end +end + +def kill_and_relaunch_app + case Maze::Helper.get_current_platform + when 'ios' + Maze.driver.close_app + Maze.driver.launch_app + when 'macos' + # noop + when 'watchos' + # noop + else + raise "Unsupported platform: #{Maze::Helper.get_current_platform}" + end +end + def wait_for_true(description) max_attempts = 300 attempts = 0 diff --git a/features/support/env.rb b/features/support/env.rb index 386858a05..50461b442 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -121,6 +121,8 @@ def skip_between(os, version_lo, version_hi) # Reset to defaults in case previous scenario changed them Maze.config.captured_invalid_requests = Set[:errors, :sessions, :builds, :uploads, :sourcemaps] + launch_app + $started_at = Time.now end From f87c6dce9c001f0af4cca837cd967d25e27f5ede Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 21 Mar 2024 09:16:18 +0100 Subject: [PATCH 11/51] Shorten maze runner command fetch polling interval --- .../fixtures/shared/utils/CommandReaderThread.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/features/fixtures/shared/utils/CommandReaderThread.swift b/features/fixtures/shared/utils/CommandReaderThread.swift index 9857d5cf1..b6a2631a9 100644 --- a/features/fixtures/shared/utils/CommandReaderThread.swift +++ b/features/fixtures/shared/utils/CommandReaderThread.swift @@ -44,13 +44,13 @@ class CommandReaderThread: Thread { func receiveNextCommand() { let maxWaitTime = 5.0 - let pollingInterval = 1.0 + let pollingInterval = 0.2 var fetchTask = newStartedFetchTask() let startTime = Date() + logDebug("Command fetch: Command request sent. Waiting for response...") while true { - Thread.sleep(forTimeInterval: pollingInterval) switch fetchTask.state { case CommandFetchState.success: logDebug("Command fetch: Request succeeded") @@ -62,9 +62,7 @@ class CommandReaderThread: Thread { return case CommandFetchState.fetching: let duration = Date() - startTime - if duration < maxWaitTime { - logDebug("Command fetch: Server hasn't responded in \(duration)s (max \(maxWaitTime)). Waiting \(pollingInterval)s more...") - } else { + if duration >= maxWaitTime { fetchTask.cancel() logInfo("Command fetch: Server hasn't responded in \(duration)s (max \(maxWaitTime)). Trying again...") fetchTask = newStartedFetchTask() @@ -81,6 +79,7 @@ class CommandReaderThread: Thread { fetchTask = newStartedFetchTask() break } + Thread.sleep(forTimeInterval: pollingInterval) } } } @@ -118,6 +117,7 @@ class CommandFetchTask { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase do { + logDebug("Command response: \(String(describing: String(data: data, encoding: .utf8)))") let command = try decoder.decode(MazeRunnerCommand.self, from: data) logInfo("Command fetched and decoded") self.command = command; From 49bfb5003d214b8832057e5a106fd24eff6c1235 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 21 Mar 2024 09:44:18 +0100 Subject: [PATCH 12/51] Increase wait time on app hang scenarios --- features/app_hangs.feature | 8 ++++---- .../shared/scenarios/AppHangFatalDisabledScenario.swift | 9 ++++----- .../shared/scenarios/AppHangFatalOnlyScenario.swift | 9 ++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/features/app_hangs.feature b/features/app_hangs.feature index d39dbf474..d8555285e 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -108,7 +108,7 @@ Feature: App hangs Scenario: Fatal app hangs should be reported if appHangThresholdMillis = BugsnagAppHangThresholdFatalOnly When I run "AppHangFatalOnlyScenario" - And I wait for 6 seconds + And I wait for 10 seconds And I kill and relaunch the app And I set the HTTP status code to 500 And I configure Bugsnag for "AppHangFatalOnlyScenario" @@ -140,7 +140,7 @@ Feature: App hangs Scenario: Fatal app hangs should not be reported if enabledErrorTypes.appHangs = false When I run "AppHangFatalDisabledScenario" - And I wait for 5 seconds + And I wait for 10 seconds And I kill and relaunch the app And I configure Bugsnag for "AppHangFatalDisabledScenario" Then I should receive no errors @@ -148,7 +148,7 @@ Feature: App hangs @skip_macos Scenario: Fatal app hangs should be reported if the app hangs before going to the background When I run "AppHangFatalOnlyScenario" - And I wait for 5 seconds + And I wait for 10 seconds And I send the app to the background And I kill and relaunch the app And I configure Bugsnag for "AppHangFatalOnlyScenario" @@ -158,7 +158,7 @@ Feature: App hangs @skip_macos Scenario: Fatal app hangs should not be reported if they occur once the app is in the background When I run "AppHangDidEnterBackgroundScenario" - And I send the app to the background for 6 seconds + And I send the app to the background for 10 seconds And I kill and relaunch the app And I configure Bugsnag for "AppHangDidEnterBackgroundScenario" Then I should receive no errors diff --git a/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift b/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift index ef591412e..b3ed721c3 100644 --- a/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangFatalDisabledScenario.swift @@ -7,10 +7,9 @@ class AppHangFatalDisabledScenario: Scenario { } override func run() { - logDebug("Hanging indefinitely...") - // Use asyncAfter to allow the Appium click event to be handled - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - while true {} - } + let timeInterval: TimeInterval = 500 + logDebug("Simulating an app hang of \(timeInterval) seconds...") + Thread.sleep(forTimeInterval: timeInterval) + logError("Should not have finished sleeping") } } diff --git a/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift b/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift index 0c68629d0..6e495ebaa 100644 --- a/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift +++ b/features/fixtures/shared/scenarios/AppHangFatalOnlyScenario.swift @@ -9,10 +9,9 @@ class AppHangFatalOnlyScenario: Scenario { } override func run() { - logDebug("Hanging indefinitely...") - // Use asyncAfter to allow the Appium click event to be handled - DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { - while true {} - } + let timeInterval: TimeInterval = 500 + logDebug("Simulating an app hang of \(timeInterval) seconds...") + Thread.sleep(forTimeInterval: timeInterval) + logError("Should not have finished sleeping") } } From cbbec3d10fbb37c2eadbe6e5a2f9c6da977da01d Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 21 Mar 2024 13:18:10 +0000 Subject: [PATCH 13/51] Refresh iOS 16 and 17 strategy [full ci] --- .buildkite/pipeline.full.yml | 93 +++++++++++++++++++++++++ .buildkite/pipeline.yml | 130 ++++++++++------------------------- 2 files changed, 130 insertions(+), 93 deletions(-) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index cc1752872..ca1c81802 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -10,6 +10,68 @@ steps: # # BitBar # + - label: ':bitbar: iOS 16 E2E tests batch 1' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.9.0: + download: "features/fixtures/ios/output/ipa_url_bb.txt" + upload: "maze_output/failed/**/*" + docker-compose#v4.7.0: + pull: cocoa-maze-runner-bitbar + run: cocoa-maze-runner-bitbar + service-ports: true + command: + - "--app=@/app/build/ipa_url_bb.txt" + - "--farm=bb" + - "--device=IOS_16" + - "--no-tunnel" + - "--aws-public-ip" + - "--fail-fast" + # PLAT-11155: App hang scenarios run on BrowserStack + - "--exclude=features/app_hangs.feature" + - "--exclude=features/[e-z].*.feature$" + concurrency: 25 + concurrency_group: 'bitbar' + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + + - label: ':bitbar: iOS 16 E2E tests batch 2' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.9.0: + download: "features/fixtures/ios/output/ipa_url_bb.txt" + upload: "maze_output/failed/**/*" + docker-compose#v4.7.0: + pull: cocoa-maze-runner-bitbar + run: cocoa-maze-runner-bitbar + service-ports: true + command: + - "--app=@/app/build/ipa_url_bb.txt" + - "--farm=bb" + - "--device=IOS_16" + - "--no-tunnel" + - "--aws-public-ip" + - "--fail-fast" + - "--exclude=features/[a-d].*.feature$" + concurrency: 25 + concurrency_group: 'bitbar' + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + - label: ':bitbar: iOS 15 E2E tests batch 1' depends_on: "cocoa_fixture" timeout_in_minutes: 90 @@ -189,6 +251,37 @@ steps: # # BrowserStack # + # PLAT-11155: App hang tests run on BrowserStack (Appium 1.x) for now + - label: ':browserstack: iOS 16 app hang tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 30 + agents: + queue: opensource + plugins: + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url_bs.txt" + upload: "maze_output/failed/**/*" + docker-compose#v3.7.0: + pull: cocoa-maze-runner + run: cocoa-maze-runner + command: + - "--app=@build/ipa_url_bs.txt" + - "--farm=bs" + - "--device=IOS_16" + - "--appium-version=1.21.0" + - "--fail-fast" + - "features/app_hangs.feature" + concurrency: 5 + concurrency_group: 'browserstack-app' + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + - exit_status: 104 # App hang related error + limit: 2 + # PLAT-11155: App hang tests run on BrowserStack (Appium 1.x) for now - label: ':browserstack: iOS 15 app hang tests' depends_on: diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index fe84c994c..e0c93738b 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -240,68 +240,67 @@ steps: ############################################################################## # - # Basic build E2E tests + # Full set of E2E tests on one iOS version # # - # BitBar + # BrowserStack # - - label: ':bitbar: iOS 16 E2E tests batch 1' + - label: ':browserstack: iOS 17 E2E tests batch 1' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: queue: opensource plugins: - artifacts#v1.9.0: - download: "features/fixtures/ios/output/ipa_url_bb.txt" + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url_bs.txt" upload: "maze_output/failed/**/*" - docker-compose#v4.7.0: - pull: cocoa-maze-runner-bitbar - run: cocoa-maze-runner-bitbar + docker-compose#v3.7.0: + pull: cocoa-maze-runner + run: cocoa-maze-runner service-ports: true command: - - "--app=@/app/build/ipa_url_bb.txt" - - "--farm=bb" - - "--device=IOS_16" - - "--no-tunnel" - - "--aws-public-ip" + - "--app=@/app/build/ipa_url_bs.txt" + - "--farm=bs" + - "--device=IOS_17" + - "--appium-version=1.21.0" + - "--a11y-locator" - "--fail-fast" - # PLAT-11155: App hang scenarios run on BrowserStack - "--exclude=features/app_hangs.feature" - "--exclude=features/[e-z].*.feature$" concurrency: 25 - concurrency_group: 'bitbar' + concurrency_group: 'browserstack-app' concurrency_method: eager retry: automatic: - exit_status: -1 # Agent was lost limit: 2 - - label: ':bitbar: iOS 16 E2E tests batch 2' + - label: ':browserstack: iOS 17 E2E tests batch 2' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: queue: opensource plugins: - artifacts#v1.9.0: - download: "features/fixtures/ios/output/ipa_url_bb.txt" + artifacts#v1.5.0: + download: "features/fixtures/ios/output/ipa_url_bs.txt" upload: "maze_output/failed/**/*" - docker-compose#v4.7.0: - pull: cocoa-maze-runner-bitbar - run: cocoa-maze-runner-bitbar + docker-compose#v3.7.0: + pull: cocoa-maze-runner + run: cocoa-maze-runner service-ports: true command: - - "--app=@/app/build/ipa_url_bb.txt" - - "--farm=bb" - - "--device=IOS_16" - - "--no-tunnel" - - "--aws-public-ip" + - "--app=@/app/build/ipa_url_bs.txt" + - "--farm=bs" + - "--device=IOS_17" + - "--appium-version=1.21.0" + - "--a11y-locator" - "--fail-fast" - "--exclude=features/[a-d].*.feature$" concurrency: 25 - concurrency_group: 'bitbar' + concurrency_group: 'browserstack-app' concurrency_method: eager retry: automatic: @@ -309,7 +308,7 @@ steps: limit: 2 # PLAT-11155: App hang tests run on BrowserStack (Appium 1.x) for now - - label: ':browserstack: iOS 16 app hang tests' + - label: ':browserstack: iOS 17 app hang tests' depends_on: - cocoa_fixture timeout_in_minutes: 30 @@ -325,7 +324,7 @@ steps: command: - "--app=@build/ipa_url_bs.txt" - "--farm=bs" - - "--device=IOS_16" + - "--device=IOS_17" - "--appium-version=1.21.0" - "--fail-fast" - "features/app_hangs.feature" @@ -339,6 +338,15 @@ steps: - exit_status: 104 # App hang related error limit: 2 + ############################################################################## + # + # Basic build E2E tests + # + + # + # BitBar + # + - label: ':bitbar: iOS 15 barebone tests' depends_on: - cocoa_fixture @@ -429,70 +437,6 @@ steps: - exit_status: -1 # Agent was lost limit: 2 - # - # BrowserStack - # - - label: ':browserstack: iOS 17 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_bs.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner - run: cocoa-maze-runner - service-ports: true - command: - - "--app=@/app/build/ipa_url_bs.txt" - - "--farm=bs" - - "--device=IOS_17" - - "--appium-version=1.18.0" - - "--a11y-locator" - - "--fail-fast" - - "--exclude=features/[e-z].*.feature$" - - "--order=random" - concurrency: 25 - concurrency_group: 'browserstack-app' - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 - - - label: ':browserstack: iOS 17 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_bs.txt" - upload: "maze_output/failed/**/*" - docker-compose#v3.7.0: - pull: cocoa-maze-runner - run: cocoa-maze-runner - service-ports: true - command: - - "--app=@/app/build/ipa_url_bs.txt" - - "--farm=bs" - - "--device=IOS_17" - - "--appium-version=1.18.0" - - "--a11y-locator" - - "--fail-fast" - - "--exclude=features/[a-d].*.feature$" - - "--order=random" - concurrency: 25 - concurrency_group: 'browserstack-app' - concurrency_method: eager - retry: - automatic: - - exit_status: -1 # Agent was lost - limit: 2 ############################################################################## # From b21d13cd4e39281506b9751b3b20646e49efd2ee Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 22 Mar 2024 12:29:08 +0100 Subject: [PATCH 14/51] More flake fixes --- features/breadcrumbs.feature | 10 +++++++-- .../ios/iOSTestApp.xcodeproj/project.pbxproj | 4 ++++ .../scenarios/AutoSessionUnhandledScenario.m | 9 ++++---- .../DelayedNotifyErrorScenario.swift | 22 +++++++++++++++++++ features/fixtures/shared/utils/Fixture.swift | 4 ++-- features/steps/app_steps.rb | 15 +++++++++++++ features/support/env.rb | 2 +- 7 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 features/fixtures/shared/scenarios/DelayedNotifyErrorScenario.swift diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 7c09fab46..4bb019716 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -43,9 +43,15 @@ Feature: Attaching a series of notable events leading up to errors @skip_below_ios_13 @skip_macos Scenario: State breadcrumbs - When I configure Bugsnag for "HandledErrorScenario" + When I configure Bugsnag for "DelayedNotifyErrorScenario" + # Do this just to sync up client and server + And I invoke "notify_error" + And I wait to receive an error + And I discard the oldest error + # Now we know that the backgrounding will occur at an appropriate time And I send the app to the background for 2 seconds - And I click the element "run_scenario" + # This next error should have the notification breadcrumbs + And I invoke "notify_error" And I wait to receive an error Then the event has a "state" breadcrumb named "Bugsnag loaded" # Bugsnag has been started too late to capture some early notifications diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 447cbd258..0333bae69 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -91,6 +91,7 @@ 09F024FA2B9F3ACD007D9F73 /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024F92B9F3ACD007D9F73 /* Fixture.swift */; }; 09F024FC2B9F3B16007D9F73 /* MazeRunnerCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */; }; 09F0250B2BA1E640007D9F73 /* FixtureConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F0250A2BA1E640007D9F73 /* FixtureConfig.swift */; }; + 09F025172BAD7B04007D9F73 /* DelayedNotifyErrorScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09F025162BAD7B04007D9F73 /* DelayedNotifyErrorScenario.swift */; }; 6526A0D4248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */; }; 8A096DF627C7E56C00DB6ECC /* CxxUnexpectedScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */; }; 8A096DFC27C7E77600DB6ECC /* CxxBareThrowScenario.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */; }; @@ -294,6 +295,7 @@ 09F024F92B9F3ACD007D9F73 /* Fixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fixture.swift; sourceTree = ""; }; 09F024FB2B9F3B16007D9F73 /* MazeRunnerCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MazeRunnerCommand.swift; sourceTree = ""; }; 09F0250A2BA1E640007D9F73 /* FixtureConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixtureConfig.swift; sourceTree = ""; }; + 09F025162BAD7B04007D9F73 /* DelayedNotifyErrorScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelayedNotifyErrorScenario.swift; sourceTree = ""; }; 6526A0D3248A83350002E2C9 /* LoadConfigFromFileAutoScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadConfigFromFileAutoScenario.swift; sourceTree = ""; }; 8A096DF527C7E56C00DB6ECC /* CxxUnexpectedScenario.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxUnexpectedScenario.mm; sourceTree = ""; }; 8A096DFB27C7E77600DB6ECC /* CxxBareThrowScenario.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CxxBareThrowScenario.mm; sourceTree = ""; }; @@ -675,6 +677,7 @@ 010BAAF22833CE570003FF36 /* UserPersistencePersistUserClientScenario.m */, 010BAAF82833CE570003FF36 /* UserPersistencePersistUserScenario.m */, E700EE49247D1164008CFFB6 /* UserSessionOverrideScenario.swift */, + 09F025162BAD7B04007D9F73 /* DelayedNotifyErrorScenario.swift */, ); name = scenarios; path = ../shared/scenarios; @@ -916,6 +919,7 @@ F4295B56219D228FAA99BC14 /* ObjCExceptionScenario.m in Sources */, F4295218A62E41518DC3C057 /* AccessNonObjectScenario.m in Sources */, E7A324E0247E70F9008B0052 /* SessionCallbackDiscardScenario.swift in Sources */, + 09F025172BAD7B04007D9F73 /* DelayedNotifyErrorScenario.swift in Sources */, 01F6B75A2832756300B75C5D /* OldCrashReportScenario.swift in Sources */, 8A72A0382396574F00328051 /* CustomPluginNotifierDescriptionScenario.m in Sources */, E700EE72247D79FF008CFFB6 /* SIGBUSScenario.m in Sources */, diff --git a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m index 352a0b0a9..bc93c0db5 100644 --- a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m @@ -25,11 +25,10 @@ - (void)configure { - (void)run { if (![self.args[0] isEqualToString:@"noevent"]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - NSException *ex = [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; - - @throw ex; - }); + // Just sleep because dispatching the code never runs. + sleep(2); + NSException *ex = [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; + @throw ex; } } diff --git a/features/fixtures/shared/scenarios/DelayedNotifyErrorScenario.swift b/features/fixtures/shared/scenarios/DelayedNotifyErrorScenario.swift new file mode 100644 index 000000000..690a87220 --- /dev/null +++ b/features/fixtures/shared/scenarios/DelayedNotifyErrorScenario.swift @@ -0,0 +1,22 @@ +// +// DelayedNotifyErrorScenario.swift +// iOSTestApp +// +// Created by Karl Stenerud on 22.03.24. +// Copyright © 2024 Bugsnag. All rights reserved. +// + +import Foundation + +class DelayedNotifyErrorScenario: Scenario { + + override func configure() { + super.configure() + self.config.autoTrackSessions = false; + } + + @objc func notify_error() { + let error = NSError(domain: "DelayedNotifyErrorScenario", code: 100, userInfo: nil) + Bugsnag.notifyError(error) + } +} diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index a36014926..d3cb78bfb 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -98,7 +98,7 @@ class Fixture: NSObject, CommandReceiver { @objc func startBugsnagForScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { logInfo("========== Starting Bugsnag for scenario \(scenarioName) ==========") loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) - logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") + logInfo("========== Completed starting Bugsnag for scenario \(String(describing: currentScenario.self)) ==========") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } @@ -109,7 +109,7 @@ class Fixture: NSObject, CommandReceiver { loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) logInfo("Starting scenario in class \(String(describing: currentScenario.self))") currentScenario!.run() - logInfo("========== Completed scenario \(String(describing: currentScenario.self)) ==========") + logInfo("========== Completed running scenario \(String(describing: currentScenario.self)) ==========") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 3d862a95e..5978c82be 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -58,6 +58,21 @@ end end +When('I invoke {string}') do |method_name| + Maze::Server.commands.add({ action: "invoke_method", args: [method_name] }) + # Ensure fixture has read the command + count = 100 + sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 +end + +When('I invoke {string} with parameter {string}') do |method_name, arg1| + # Note: The method will usually be of the form "xyzWithParam:" + Maze::Server.commands.add({ action: "invoke_method", args: [method_name, arg1] }) + # Ensure fixture has read the command + count = 100 + sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 +end + # No platform relevance When('I clear the error queue') do diff --git a/features/support/env.rb b/features/support/env.rb index 50461b442..ce1c09ad9 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -89,7 +89,7 @@ def skip_between(os, version_lo, version_hi) skip_below('ios', 13) end -Before('@skip_below_ios_13') do |_scenario| +Before('@skip_below_ios_17') do |_scenario| skip_below('ios', 17) end From f2552ee48457cfb60a6e2f29389b8065f19bd93c Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 22 Mar 2024 12:31:18 +0100 Subject: [PATCH 15/51] Disable app hang tests until we can fix the flakes --- features/app_hangs.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/app_hangs.feature b/features/app_hangs.feature index d8555285e..6228118e1 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -1,4 +1,5 @@ @app_hang_test +@skip # TODO: Investigate app hang flakes Feature: App hangs Background: From 137687fd14063c7cef6d11b176882509cafcf7cc Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 26 Mar 2024 18:06:04 +0000 Subject: [PATCH 16/51] Bump stress tests to macOS 14 --- .buildkite/pipeline.yml | 4 ++-- features/support/env.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index e0c93738b..8c03a989b 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -218,10 +218,10 @@ steps: --os=macos --fail-fast - - label: 'macOS 12 stress test' + - label: 'macOS 14 stress test' timeout_in_minutes: 3 agents: - queue: macos-12-arm + queue: macos-14 env: STRESS_TEST: "true" commands: diff --git a/features/support/env.rb b/features/support/env.rb index ce1c09ad9..c918da2e1 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -118,6 +118,8 @@ def skip_between(os, version_lo, version_hi) end Maze.hooks.before do |_scenario| + next unless ENV['STRESS_TEST'].nil? + # Reset to defaults in case previous scenario changed them Maze.config.captured_invalid_requests = Set[:errors, :sessions, :builds, :uploads, :sourcemaps] From 9032cae2ed18bad003ae9e469bb7a50c9379274d Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 26 Mar 2024 18:11:19 +0000 Subject: [PATCH 17/51] Move back to macOS 12 --- .buildkite/pipeline.yml | 4 ++-- Gemfile | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 8c03a989b..e0c93738b 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -218,10 +218,10 @@ steps: --os=macos --fail-fast - - label: 'macOS 14 stress test' + - label: 'macOS 12 stress test' timeout_in_minutes: 3 agents: - queue: macos-14 + queue: macos-12-arm env: STRESS_TEST: "true" commands: diff --git a/Gemfile b/Gemfile index fdd9471c2..9034f842f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,5 @@ source 'https://rubygems.org' -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', '~> 9.0' From 108af20e4dd3a904fe4a28144e89cfa16f8afc20 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 27 Mar 2024 10:09:03 +0100 Subject: [PATCH 18/51] Clean up messaging and fix broken e2e test --- features/crashprobe.feature | 8 +------- features/fixtures/ios/iOSTestApp/AppDelegate.swift | 2 +- features/fixtures/macos/macOSTestApp/AppDelegate.m | 2 ++ .../fixtures/macos/macOSTestApp/MainWindowController.m | 5 ++++- features/fixtures/shared/utils/Fixture.swift | 8 ++++---- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/features/crashprobe.feature b/features/crashprobe.feature index 7809025a9..4f2d975eb 100644 --- a/features/crashprobe.feature +++ b/features/crashprobe.feature @@ -189,13 +189,7 @@ Feature: Reporting crash events And on x86, the "isLR" of stack frame 1 is null Scenario: Trigger a crash with libsystem_pthread's _pthread_list_lock held - When I run "AsyncSafeThreadScenario" - - # Sleep and relaunch here instead of checking the app state as this specific - # crash seems to inhibit Appium's ability to check the app state on iOS 10 - And I wait for 3 seconds - And I kill and relaunch the app - + When I run "AsyncSafeThreadScenario" and relaunch the crashed app And I configure Bugsnag for "AsyncSafeThreadScenario" And I wait to receive an error Then the error is valid for the error reporting API diff --git a/features/fixtures/ios/iOSTestApp/AppDelegate.swift b/features/fixtures/ios/iOSTestApp/AppDelegate.swift index fa9713898..8951b04d5 100644 --- a/features/fixtures/ios/iOSTestApp/AppDelegate.swift +++ b/features/fixtures/ios/iOSTestApp/AppDelegate.swift @@ -7,7 +7,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - logInfo("========== Fixture app has launched ==========") + logInfo("==== IOS FIXTURE DID FINISH LAUNCHING ===="); return true } } diff --git a/features/fixtures/macos/macOSTestApp/AppDelegate.m b/features/fixtures/macos/macOSTestApp/AppDelegate.m index 6d0430563..1adb4e28d 100644 --- a/features/fixtures/macos/macOSTestApp/AppDelegate.m +++ b/features/fixtures/macos/macOSTestApp/AppDelegate.m @@ -7,6 +7,7 @@ // #import "AppDelegate.h" +#import "Logging.h" #import "MainWindowController.h" @@ -26,6 +27,7 @@ - (BOOL)launchedByMazeRunner { } - (void)applicationDidFinishLaunching:(NSNotification *)notification { + logInfo(@"==== MACOS FIXTURE DID FINISH LAUNCHING ===="); self.mainWindowController = [[MainWindowController alloc] initWithWindowNibName:@"MainWindowController"]; [self.mainWindowController showWindow:self]; diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index 7bb1edd49..d7f1d4349 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -76,7 +76,10 @@ - (IBAction)startBugsnag:(id)sender { logDebug(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); NSString *scenarioName = self.scenarioName; - NSArray *args = @[self.scenarioMetadata]; + NSArray *args = @[]; + if (self.scenarioMetadata != nil) { + args = @[self.scenarioMetadata]; + } [self.fixture startBugsnagForScenarioWithScenarioName:scenarioName args:args completion:^{}]; } diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index d3cb78bfb..17dc51ad8 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -96,20 +96,20 @@ class Fixture: NSObject, CommandReceiver { } @objc func startBugsnagForScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { - logInfo("========== Starting Bugsnag for scenario \(scenarioName) ==========") + logInfo("---- Starting Bugsnag for scenario \(scenarioName) ----") loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) - logInfo("========== Completed starting Bugsnag for scenario \(String(describing: currentScenario.self)) ==========") + logInfo("---- Completed starting Bugsnag for scenario \(String(describing: currentScenario.self)) ----") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } } @objc func runScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { - logInfo("========== Running scenario \(scenarioName) ==========") + logInfo("---- Running scenario \(scenarioName) ----") loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) logInfo("Starting scenario in class \(String(describing: currentScenario.self))") currentScenario!.run() - logInfo("========== Completed running scenario \(String(describing: currentScenario.self)) ==========") + logInfo("---- Completed running scenario \(String(describing: currentScenario.self)) ----") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } From 820c7a0c3225124e4231b959962c6eb08ba07245 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Wed, 27 Mar 2024 15:04:58 +0000 Subject: [PATCH 19/51] (Kill and) relaunch the app on macOS --- features/steps/app_steps.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 5978c82be..0f9155982 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -124,7 +124,7 @@ def kill_and_relaunch_app Maze.driver.close_app Maze.driver.launch_app when 'macos' - # noop + run_macos_app # This will kill the app if it's running when 'watchos' # noop else From fa9f80bae6eef49623aa051eec2519b1bedd47cc Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 28 Mar 2024 11:57:06 +0100 Subject: [PATCH 20/51] Defensive copy exception type --- .../Sentry/BSG_KSCrashSentry_CPPException.mm | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm index e8ee68726..521cbc240 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm @@ -104,6 +104,20 @@ void __cxa_throw(void *thrown_exception, std::type_info *tinfo, } } +static const char *getExceptionTypeName(std::type_info *tinfo) { + static char buff[sizeof(*tinfo)]; + // Runtime bug workaround: In some situations, __cxa_current_exception_type returns an invalid address. + // Check to make sure it's in valid memory before we try to call tinfo->name(). + if (tinfo != NULL && bsg_ksmachcopyMem(tinfo, buff, sizeof(buff)) == KERN_SUCCESS) { + const char *name = tinfo->name(); + // Also make sure the name pointer is valid. + if (name != NULL && bsg_ksmachcopyMem(name, buff, 1) == KERN_SUCCESS) { + return name; + } + } + return NULL; +} + static void CPPExceptionTerminate(void) { BSG_KSLOG_DEBUG("Trapped c++ exception"); @@ -113,9 +127,7 @@ static void CPPExceptionTerminate(void) { BSG_KSLOG_DEBUG("Get exception type name."); std::type_info *tinfo = __cxxabiv1::__cxa_current_exception_type(); - if (tinfo != NULL) { - name = tinfo->name(); - } else { + if (tinfo == NULL) { name = "std::terminate"; crashReason = "throw may have been called without an exception"; if (!bsg_g_stackTraceCount) { @@ -127,6 +139,13 @@ static void CPPExceptionTerminate(void) { goto after_rethrow; // Using goto to avoid indenting code below } + name = getExceptionTypeName(tinfo); + if (name == NULL) { + name = "unknown"; + crashReason = "unable to determine C++ exception type"; + goto after_rethrow; + } + BSG_KSLOG_DEBUG("Discovering what kind of exception was thrown."); bsg_g_captureNextStackTrace = false; try { From d5cf638cbec2836954aaae24a9f2d4e7a91edf9e Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 12 Mar 2024 11:51:04 +0000 Subject: [PATCH 21/51] Use Xcode 15 in CI jobs [full ci] --- .buildkite/pipeline.full.yml | 10 +++++----- .buildkite/pipeline.yml | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index ca1c81802..d8595d808 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -443,7 +443,7 @@ steps: - label: 'examples/objective-c-ios' timeout_in_minutes: 30 agents: - queue: macos-12-arm + queue: macos-13-arm commands: - bundle install - cd examples/objective-c-ios @@ -459,7 +459,7 @@ steps: - label: 'examples/objective-c-osx' timeout_in_minutes: 30 agents: - queue: macos-12-arm + queue: macos-13-arm commands: - bundle install - cd examples/objective-c-osx @@ -473,7 +473,7 @@ steps: - label: 'examples/swift-ios' timeout_in_minutes: 30 agents: - queue: macos-12-arm + queue: macos-13-arm commands: - bundle install - cd examples/swift-ios @@ -487,7 +487,7 @@ steps: - label: 'examples/swift-package-manager' timeout_in_minutes: 30 agents: - queue: macos-12-arm + queue: macos-13-arm commands: - bundle install - cd examples/swift-package-manager @@ -502,7 +502,7 @@ steps: - label: 'examples/swiftui' timeout_in_minutes: 30 agents: - queue: macos-12-arm + queue: macos-13-arm commands: - bundle install - cd examples/swiftui diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index e0c93738b..25a12e6c7 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -13,7 +13,7 @@ steps: agents: queue: macos-13-arm env: - DEVELOPER_DIR: /Applications/Xcode14.3.app + DEVELOPER_DIR: /Applications/Xcode15.app artifact_paths: - features/fixtures/ios/output/iOSTestApp.ipa - features/fixtures/macos/output/macOSTestApp.zip @@ -30,7 +30,7 @@ steps: agents: queue: macos-13-arm env: - DEVELOPER_DIR: /Applications/Xcode14.3.app + DEVELOPER_DIR: /Applications/Xcode15.app commands: - make build_swift - make build_ios_static @@ -145,9 +145,9 @@ steps: - label: watchOS 8 unit tests timeout_in_minutes: 60 agents: - queue: opensource-arm-mac-cocoa-12 + queue: macos-13-arm env: - DEVELOPER_DIR: /Applications/Xcode14.0.app + DEVELOPER_DIR: /Applications/Xcode15.app commands: - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.3 SDK=watchsimulator9.0 artifact_paths: @@ -156,9 +156,9 @@ steps: - label: watchOS 7 unit tests timeout_in_minutes: 60 agents: - queue: opensource-arm-mac-cocoa-12 + queue: macos-13-arm env: - DEVELOPER_DIR: /Applications/Xcode14.0.app + DEVELOPER_DIR: /Applications/Xcode14.app commands: - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator9.0 artifact_paths: From fd3c5b59c3284244c13d8996cef1f167ac03e971 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 12 Mar 2024 13:34:00 +0000 Subject: [PATCH 22/51] Move back to macOS 12 for now --- .buildkite/pipeline.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 25a12e6c7..531fcf175 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -145,9 +145,9 @@ steps: - label: watchOS 8 unit tests timeout_in_minutes: 60 agents: - queue: macos-13-arm + queue: macos-12-arm env: - DEVELOPER_DIR: /Applications/Xcode15.app + DEVELOPER_DIR: /Applications/Xcode14.app commands: - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.3 SDK=watchsimulator9.0 artifact_paths: @@ -156,7 +156,7 @@ steps: - label: watchOS 7 unit tests timeout_in_minutes: 60 agents: - queue: macos-13-arm + queue: macos-12-arm env: DEVELOPER_DIR: /Applications/Xcode14.app commands: From 86982c796e34a596336ae8a01dd50307cb57d04b Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 28 Mar 2024 16:42:28 +0000 Subject: [PATCH 23/51] Move to macOS 14 --- .buildkite/pipeline.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 531fcf175..7e7b2673c 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -11,9 +11,9 @@ steps: key: cocoa_fixture timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode15.app + DEVELOPER_DIR: /Applications/Xcode-15.app artifact_paths: - features/fixtures/ios/output/iOSTestApp.ipa - features/fixtures/macos/output/macOSTestApp.zip @@ -28,9 +28,9 @@ steps: - label: Static framework and Swift Package Manager builds timeout_in_minutes: 10 agents: - queue: macos-13-arm + queue: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode15.app + DEVELOPER_DIR: /Applications/Xcode-15.app commands: - make build_swift - make build_ios_static @@ -197,7 +197,7 @@ steps: - bundle install - bundle exec maze-runner --os=macos - --fail-fast +# --fail-fast - label: 'ARM macOS 12 barebones E2E tests' depends_on: From ba4b310c498f9e913a2bd90c06860056339d7240 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 2 Apr 2024 15:41:38 +0100 Subject: [PATCH 24/51] Move watchOS tests to macOS 15 --- .buildkite/pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 7e7b2673c..2b4d4acfe 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -145,18 +145,18 @@ steps: - label: watchOS 8 unit tests timeout_in_minutes: 60 agents: - queue: macos-12-arm + queue: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode14.app + DEVELOPER_DIR: /Applications/Xcode-15.app commands: - - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.3 SDK=watchsimulator9.0 + - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.5 SDK=watchsimulator9.0 artifact_paths: - logs/* - label: watchOS 7 unit tests timeout_in_minutes: 60 agents: - queue: macos-12-arm + queue: macos-14 env: DEVELOPER_DIR: /Applications/Xcode14.app commands: From c4b2bb24387c7120b33a6c4ba3db9b08b5eed27b Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 2 Apr 2024 15:46:22 +0100 Subject: [PATCH 25/51] Bump to macOS 14 --- .buildkite/pipeline.full.yml | 10 +++++----- .buildkite/pipeline.yml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index d8595d808..4243f306d 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -443,7 +443,7 @@ steps: - label: 'examples/objective-c-ios' timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 commands: - bundle install - cd examples/objective-c-ios @@ -459,7 +459,7 @@ steps: - label: 'examples/objective-c-osx' timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 commands: - bundle install - cd examples/objective-c-osx @@ -473,7 +473,7 @@ steps: - label: 'examples/swift-ios' timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 commands: - bundle install - cd examples/swift-ios @@ -487,7 +487,7 @@ steps: - label: 'examples/swift-package-manager' timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 commands: - bundle install - cd examples/swift-package-manager @@ -502,7 +502,7 @@ steps: - label: 'examples/swiftui' timeout_in_minutes: 30 agents: - queue: macos-13-arm + queue: macos-14 commands: - bundle install - cd examples/swiftui diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 2b4d4acfe..aaddf758c 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -38,7 +38,7 @@ steps: - label: Carthage timeout_in_minutes: 15 agents: - queue: macos-13-arm + queue: macos-14 commands: - ./scripts/build-carthage.sh plugins: @@ -98,7 +98,7 @@ steps: - label: iOS 17 unit tests timeout_in_minutes: 10 agents: - queue: macos-13-arm + queue: macos-14 commands: - ./scripts/run-unit-tests.sh PLATFORM=iOS OS=17.0.1 DEVICE="iPhone 15" env: From 358b6816de9fbbe24a15ee426159f6f9eb9a60d3 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 2 Apr 2024 16:24:23 +0100 Subject: [PATCH 26/51] Bump versions --- .buildkite/pipeline.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index aaddf758c..51d7571db 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -102,7 +102,7 @@ steps: commands: - ./scripts/run-unit-tests.sh PLATFORM=iOS OS=17.0.1 DEVICE="iPhone 15" env: - XCODE_VERSION: 15.0.1 + XCODE_VERSION: 15.3.0 artifact_paths: - logs/* @@ -147,9 +147,9 @@ steps: agents: queue: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode-15.app + XCODE_VERSION: 15.3.0 commands: - - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.5 SDK=watchsimulator9.0 + - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=8.5 SDK=watchsimulator10.4 artifact_paths: - logs/* @@ -158,9 +158,9 @@ steps: agents: queue: macos-14 env: - DEVELOPER_DIR: /Applications/Xcode14.app + XCODE_VERSION: 15.3.0 commands: - - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator9.0 + - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator10.4 artifact_paths: - logs/* From 723a3eb2c92c13f6aae5b5b1605677a3b7c76841 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 2 Apr 2024 19:05:04 +0100 Subject: [PATCH 27/51] Move back to macOS 13 [full ci] --- .buildkite/pipeline.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 51d7571db..17c47e010 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -156,11 +156,11 @@ steps: - label: watchOS 7 unit tests timeout_in_minutes: 60 agents: - queue: macos-14 + queue: macos-13-arm env: - XCODE_VERSION: 15.3.0 + XCODE_VERSION: 15.2.0 commands: - - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator10.4 + - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator10.2 artifact_paths: - logs/* From fab87716d88eec184f2824103352d4ce1232cdbf Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 2 Apr 2024 19:24:45 +0100 Subject: [PATCH 28/51] Back to macOS 12 --- .buildkite/pipeline.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 17c47e010..c891fb4a2 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -156,9 +156,9 @@ steps: - label: watchOS 7 unit tests timeout_in_minutes: 60 agents: - queue: macos-13-arm + queue: macos-12-arm env: - XCODE_VERSION: 15.2.0 + DEVELOPER_DIR: /Applications/Xcode14.0.app commands: - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator10.2 artifact_paths: From ed962ef085fa09bd373d773ad4ae59e73c90ad83 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Mon, 8 Apr 2024 18:00:41 +0100 Subject: [PATCH 29/51] Correct watchOS simulator --- .buildkite/pipeline.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index c891fb4a2..667deabc5 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -160,7 +160,7 @@ steps: env: DEVELOPER_DIR: /Applications/Xcode14.0.app commands: - - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator10.2 + - ./scripts/run-unit-tests.sh PLATFORM=watchOS OS=7.4 SDK=watchsimulator9.0 artifact_paths: - logs/* From d541d0182321f68a3def9492fdc032b3e40225be Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 9 Apr 2024 00:02:16 +0100 Subject: [PATCH 30/51] Skip test failing on 10.14 [full ci] --- features/session_tracking.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/features/session_tracking.feature b/features/session_tracking.feature index 3db5ac2aa..b2813ccb4 100644 --- a/features/session_tracking.feature +++ b/features/session_tracking.feature @@ -104,6 +104,7 @@ Feature: Session Tracking And the error payload field "events.0.session.events.handled" equals 2 And the error payload field "events.0.session.id" equals the stored value "session_id" + @skip_macos Scenario: Encountering an unhandled event during a session When I run "AutoSessionUnhandledScenario" And I wait for 4 seconds From 6b4884dc3b2a8a504df6a30918879e9ebd2c4e7a Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 11 Apr 2024 13:25:13 +0100 Subject: [PATCH 31/51] Refresh e2e tests to include macOS 13 and 14 --- .buildkite/pipeline.full.yml | 58 ++++++++++++---- .buildkite/pipeline.yml | 126 ++++++++++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 14 deletions(-) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index 4243f306d..83a86bace 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -369,12 +369,12 @@ steps: - exit_status: -1 # Agent was lost limit: 2 - - label: 'macOS 11 E2E tests' + - label: 'macOS 10.13 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-11 + queue: opensource-mac-cocoa-10.13 plugins: artifacts#v1.5.0: download: ["features/fixtures/macos/output/macOSTestApp.zip"] @@ -385,30 +385,28 @@ steps: --os=macos --fail-fast - - label: 'ARM macOS 12 E2E tests' + - label: 'macOS 10.14 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-12-arm + queue: opensource-mac-cocoa-10.14 plugins: artifacts#v1.5.0: - download: "features/fixtures/macos/output/macOSTestApp.zip" - upload: - - "macOSTestApp.log" - - "maze_output/failed/**/*" + download: ["features/fixtures/macos/output/macOSTestApp.zip"] + upload: ["macOSTestApp.log", "maze_output/failed/**/*"] commands: - bundle install - bundle exec maze-runner --os=macos --fail-fast - - label: 'macOS 10.13 E2E tests' + - label: 'macOS 10.15 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: opensource-mac-cocoa-10.13 + queue: macos-10.15 plugins: artifacts#v1.5.0: download: ["features/fixtures/macos/output/macOSTestApp.zip"] @@ -419,12 +417,12 @@ steps: --os=macos --fail-fast - - label: 'macOS 10.14 E2E tests' + - label: 'macOS 11 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: opensource-mac-cocoa-10.14 + queue: macos-11 plugins: artifacts#v1.5.0: download: ["features/fixtures/macos/output/macOSTestApp.zip"] @@ -435,6 +433,42 @@ steps: --os=macos --fail-fast + - label: 'ARM macOS 12 E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: macos-12-arm + plugins: + artifacts#v1.5.0: + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" + commands: + - bundle install + - bundle exec maze-runner + --os=macos + --fail-fast + + - label: 'ARM macOS 13 E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: macos-13-arm + plugins: + artifacts#v1.5.0: + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" + commands: + - bundle install + - bundle exec maze-runner + --os=macos + --fail-fast + ############################################################################## # # Build example apps diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 667deabc5..7b8e22932 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -50,6 +50,24 @@ steps: # Unit tests # + - label: ARM macOS 14 unit tests + timeout_in_minutes: 10 + agents: + queue: macos-14 + commands: + - ./scripts/run-unit-tests.sh PLATFORM=macOS + artifact_paths: + - logs/* + + - label: ARM macOS 13 unit tests + timeout_in_minutes: 10 + agents: + queue: macos-13-arm + commands: + - ./scripts/run-unit-tests.sh PLATFORM=macOS + artifact_paths: + - logs/* + - label: ARM macOS 12 unit tests timeout_in_minutes: 10 agents: @@ -183,7 +201,26 @@ steps: --os=macos --fail-fast - - label: 'macOS 10.15 E2E tests' + - label: 'macOS 10.14 barebones E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 10 + agents: + queue: opensource-mac-cocoa-10.14 + plugins: + artifacts#v1.5.0: + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" + commands: + - bundle install + - bundle exec maze-runner + features/barebone_tests.feature + --os=macos + --fail-fast + + - label: 'macOS 10.15 barebones E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 @@ -196,8 +233,26 @@ steps: commands: - bundle install - bundle exec maze-runner + features/barebone_tests.feature + --os=macos + --fail-fast + + - label: 'macOS 11 barebones E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: macos-11 + plugins: + artifacts#v1.5.0: + download: ["features/fixtures/macos/output/macOSTestApp.zip"] + upload: ["macOSTestApp.log", "maze_output/failed/**/*"] + commands: + - bundle install + - bundle exec maze-runner + features/barebone_tests.feature --os=macos -# --fail-fast + --fail-fast - label: 'ARM macOS 12 barebones E2E tests' depends_on: @@ -238,6 +293,43 @@ steps: - features/fixtures/macos-stress-test/*.log - features/fixtures/macos-stress-test/*.crash + - label: 'ARM macOS 13 barebones E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 10 + agents: + queue: macos-13-arm + plugins: + artifacts#v1.5.0: + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" + commands: + - bundle install + - bundle exec maze-runner + features/barebone_tests.feature + --os=macos + --fail-fast + + - label: 'ARM macOS 14 E2E tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: macos-14 + plugins: + artifacts#v1.5.0: + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" + commands: + - bundle install + - bundle exec maze-runner + --os=macos + --fail-fast + ############################################################################## # # Full set of E2E tests on one iOS version @@ -347,6 +439,36 @@ steps: # BitBar # + - label: ':bitbar: iOS 16 barebone tests' + depends_on: + - cocoa_fixture + timeout_in_minutes: 60 + agents: + queue: opensource + plugins: + artifacts#v1.9.0: + download: "features/fixtures/ios/output/ipa_url_bb.txt" + upload: "maze_output/failed/**/*" + docker-compose#v4.7.0: + pull: cocoa-maze-runner-bitbar + run: cocoa-maze-runner-bitbar + service-ports: true + command: + - "--app=@/app/build/ipa_url_bb.txt" + - "--farm=bb" + - "--device=IOS_16" + - "--no-tunnel" + - "--aws-public-ip" + - "--fail-fast" + - "features/barebone_tests.feature" + concurrency: 25 + concurrency_group: 'bitbar' + concurrency_method: eager + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + - label: ':bitbar: iOS 15 barebone tests' depends_on: - cocoa_fixture From eff39f170170e475b18ca7feeb91d79c506d917f Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 11 Apr 2024 13:52:37 +0100 Subject: [PATCH 32/51] Reorder buildkite jobs --- .buildkite/pipeline.full.yml | 48 ++++++++++---------- .buildkite/pipeline.yml | 86 ++++++++++++++++++------------------ 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index 83a86bace..8578cfc58 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -369,44 +369,48 @@ steps: - exit_status: -1 # Agent was lost limit: 2 - - label: 'macOS 10.13 E2E tests' + - label: 'ARM macOS 13 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: opensource-mac-cocoa-10.13 + queue: macos-13-arm plugins: artifacts#v1.5.0: - download: ["features/fixtures/macos/output/macOSTestApp.zip"] - upload: ["macOSTestApp.log", "maze_output/failed/**/*"] + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" commands: - bundle install - bundle exec maze-runner --os=macos --fail-fast - - label: 'macOS 10.14 E2E tests' + - label: 'ARM macOS 12 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: opensource-mac-cocoa-10.14 + queue: macos-12-arm plugins: artifacts#v1.5.0: - download: ["features/fixtures/macos/output/macOSTestApp.zip"] - upload: ["macOSTestApp.log", "maze_output/failed/**/*"] + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" commands: - bundle install - bundle exec maze-runner --os=macos --fail-fast - - label: 'macOS 10.15 E2E tests' + - label: 'macOS 11 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-10.15 + queue: macos-11 plugins: artifacts#v1.5.0: download: ["features/fixtures/macos/output/macOSTestApp.zip"] @@ -417,12 +421,12 @@ steps: --os=macos --fail-fast - - label: 'macOS 11 E2E tests' + - label: 'macOS 10.15 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-11 + queue: macos-10.15 plugins: artifacts#v1.5.0: download: ["features/fixtures/macos/output/macOSTestApp.zip"] @@ -433,36 +437,32 @@ steps: --os=macos --fail-fast - - label: 'ARM macOS 12 E2E tests' + - label: 'macOS 10.14 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-12-arm + queue: opensource-mac-cocoa-10.14 plugins: artifacts#v1.5.0: - download: "features/fixtures/macos/output/macOSTestApp.zip" - upload: - - "macOSTestApp.log" - - "maze_output/failed/**/*" + download: ["features/fixtures/macos/output/macOSTestApp.zip"] + upload: ["macOSTestApp.log", "maze_output/failed/**/*"] commands: - bundle install - bundle exec maze-runner --os=macos --fail-fast - - label: 'ARM macOS 13 E2E tests' + - label: 'macOS 10.13 E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 60 agents: - queue: macos-13-arm + queue: opensource-mac-cocoa-10.13 plugins: artifacts#v1.5.0: - download: "features/fixtures/macos/output/macOSTestApp.zip" - upload: - - "macOSTestApp.log" - - "maze_output/failed/**/*" + download: ["features/fixtures/macos/output/macOSTestApp.zip"] + upload: ["macOSTestApp.log", "maze_output/failed/**/*"] commands: - bundle install - bundle exec maze-runner diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 7b8e22932..51e18c257 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -182,12 +182,12 @@ steps: artifact_paths: - logs/* - - label: 'macOS 10.13 barebones E2E tests' + - label: 'ARM macOS 14 E2E tests' depends_on: - cocoa_fixture - timeout_in_minutes: 10 + timeout_in_minutes: 60 agents: - queue: opensource-mac-cocoa-10.13 + queue: macos-14 plugins: artifacts#v1.5.0: download: "features/fixtures/macos/output/macOSTestApp.zip" @@ -197,16 +197,15 @@ steps: commands: - bundle install - bundle exec maze-runner - features/barebone_tests.feature --os=macos --fail-fast - - label: 'macOS 10.14 barebones E2E tests' + - label: 'ARM macOS 13 barebones E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 10 agents: - queue: opensource-mac-cocoa-10.14 + queue: macos-13-arm plugins: artifacts#v1.5.0: download: "features/fixtures/macos/output/macOSTestApp.zip" @@ -220,16 +219,18 @@ steps: --os=macos --fail-fast - - label: 'macOS 10.15 barebones E2E tests' + - label: 'ARM macOS 12 barebones E2E tests' depends_on: - cocoa_fixture - timeout_in_minutes: 60 + timeout_in_minutes: 10 agents: - queue: macos-10.15 + queue: macos-12-arm plugins: artifacts#v1.5.0: - download: ["features/fixtures/macos/output/macOSTestApp.zip"] - upload: ["macOSTestApp.log", "maze_output/failed/**/*"] + download: "features/fixtures/macos/output/macOSTestApp.zip" + upload: + - "macOSTestApp.log" + - "maze_output/failed/**/*" commands: - bundle install - bundle exec maze-runner @@ -237,6 +238,26 @@ steps: --os=macos --fail-fast + - label: 'macOS 12 stress test' + timeout_in_minutes: 3 + agents: + queue: macos-12-arm + env: + STRESS_TEST: "true" + commands: + - echo "--- Bundle install" + - bundle install + - echo "--- Build" + - make -C features/fixtures/macos-stress-test + - echo "--- Test" + - bundle exec maze-runner + features/stress_test.feature + --os=macos + --no-log-requests + artifact_paths: + - features/fixtures/macos-stress-test/*.log + - features/fixtures/macos-stress-test/*.crash + - label: 'macOS 11 barebones E2E tests' depends_on: - cocoa_fixture @@ -254,18 +275,16 @@ steps: --os=macos --fail-fast - - label: 'ARM macOS 12 barebones E2E tests' + - label: 'macOS 10.15 barebones E2E tests' depends_on: - cocoa_fixture - timeout_in_minutes: 10 + timeout_in_minutes: 60 agents: - queue: macos-12-arm + queue: macos-10.15 plugins: artifacts#v1.5.0: - download: "features/fixtures/macos/output/macOSTestApp.zip" - upload: - - "macOSTestApp.log" - - "maze_output/failed/**/*" + download: ["features/fixtures/macos/output/macOSTestApp.zip"] + upload: ["macOSTestApp.log", "maze_output/failed/**/*"] commands: - bundle install - bundle exec maze-runner @@ -273,32 +292,12 @@ steps: --os=macos --fail-fast - - label: 'macOS 12 stress test' - timeout_in_minutes: 3 - agents: - queue: macos-12-arm - env: - STRESS_TEST: "true" - commands: - - echo "--- Bundle install" - - bundle install - - echo "--- Build" - - make -C features/fixtures/macos-stress-test - - echo "--- Test" - - bundle exec maze-runner - features/stress_test.feature - --os=macos - --no-log-requests - artifact_paths: - - features/fixtures/macos-stress-test/*.log - - features/fixtures/macos-stress-test/*.crash - - - label: 'ARM macOS 13 barebones E2E tests' + - label: 'macOS 10.14 barebones E2E tests' depends_on: - cocoa_fixture timeout_in_minutes: 10 agents: - queue: macos-13-arm + queue: opensource-mac-cocoa-10.14 plugins: artifacts#v1.5.0: download: "features/fixtures/macos/output/macOSTestApp.zip" @@ -312,12 +311,12 @@ steps: --os=macos --fail-fast - - label: 'ARM macOS 14 E2E tests' + - label: 'macOS 10.13 barebones E2E tests' depends_on: - cocoa_fixture - timeout_in_minutes: 60 + timeout_in_minutes: 10 agents: - queue: macos-14 + queue: opensource-mac-cocoa-10.13 plugins: artifacts#v1.5.0: download: "features/fixtures/macos/output/macOSTestApp.zip" @@ -327,6 +326,7 @@ steps: commands: - bundle install - bundle exec maze-runner + features/barebone_tests.feature --os=macos --fail-fast From 9aaad806ad0d9c4f0c875c343da4b4a35cf9569d Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Fri, 19 Apr 2024 14:33:12 +0200 Subject: [PATCH 33/51] Add launch count to reduce timing dependence of some e2e tests --- .../ios/iOSTestApp/ViewController.swift | 4 ++-- .../macos/macOSTestApp/MainWindowController.m | 4 ++-- .../scenarios/AutoSessionUnhandledScenario.m | 17 ++++++----------- .../scenarios/ManyConcurrentNotifyScenario.m | 4 ++-- features/fixtures/shared/scenarios/Scenario.h | 3 ++- features/fixtures/shared/scenarios/Scenario.m | 3 ++- features/fixtures/shared/utils/Fixture.swift | 16 ++++++++-------- .../shared/utils/MazeRunnerCommand.swift | 5 ++++- features/session_tracking.feature | 10 ++-------- features/steps/app_steps.rb | 8 +++++--- features/support/env.rb | 1 + 11 files changed, 36 insertions(+), 39 deletions(-) diff --git a/features/fixtures/ios/iOSTestApp/ViewController.swift b/features/fixtures/ios/iOSTestApp/ViewController.swift index 2436aec5e..e9d1f8ea6 100644 --- a/features/fixtures/ios/iOSTestApp/ViewController.swift +++ b/features/fixtures/ios/iOSTestApp/ViewController.swift @@ -30,7 +30,7 @@ class ViewController: UIViewController { let args = scenarioMetaDataField.text!.count > 0 ? [scenarioMetaDataField.text!] : [] fixture.setApiKey(apiKey: apiKey) - fixture.runScenario(scenarioName: scenarioName, args: args) {} + fixture.runScenario(scenarioName: scenarioName, args: args, launchCount: 1) {} } @IBAction func startBugsnag() { @@ -39,7 +39,7 @@ class ViewController: UIViewController { let args = scenarioMetaDataField.text!.count > 0 ? [scenarioMetaDataField.text!] : [] fixture.setApiKey(apiKey: apiKey) - fixture.startBugsnagForScenario(scenarioName: scenarioName, args: args) {} + fixture.startBugsnagForScenario(scenarioName: scenarioName, args: args, launchCount: 1) {} } @IBAction func clearPersistentData(_ sender: Any) { diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index d7f1d4349..95dad5e48 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -68,7 +68,7 @@ - (IBAction)runScenario:(id)sender { // 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(), ^{ logInfo(@"Running scenario: %@", scenarioName); - [self.fixture runScenarioWithScenarioName:scenarioName args:args completion:^{}]; + [self.fixture runScenarioWithScenarioName:scenarioName args:args launchCount:1 completion:^{}]; }); } @@ -81,7 +81,7 @@ - (IBAction)startBugsnag:(id)sender { args = @[self.scenarioMetadata]; } - [self.fixture startBugsnagForScenarioWithScenarioName:scenarioName args:args completion:^{}]; + [self.fixture startBugsnagForScenarioWithScenarioName:scenarioName args:args launchCount:1 completion:^{}]; } - (IBAction)clearPersistentData:(id)sender { diff --git a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m index bc93c0db5..adadc4f53 100644 --- a/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m +++ b/features/fixtures/shared/scenarios/AutoSessionUnhandledScenario.m @@ -16,20 +16,15 @@ @implementation AutoSessionUnhandledScenario - (void)configure { [super configure]; - if ([self.args[0] isEqualToString:@"noevent"]) { - self.config.autoTrackSessions = NO; - } else { - self.config.autoTrackSessions = YES; - } + // Only track sessions on the first launch + self.config.autoTrackSessions = self.launchCount == 1; } - (void)run { - if (![self.args[0] isEqualToString:@"noevent"]) { - // Just sleep because dispatching the code never runs. - sleep(2); - NSException *ex = [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; - @throw ex; - } + // Wait for the session to be sent + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + @throw [NSException exceptionWithName:@"Kaboom" reason:@"The connection exploded" userInfo:nil]; + }); } @end diff --git a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m index 37520a539..d61921e7b 100644 --- a/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m +++ b/features/fixtures/shared/scenarios/ManyConcurrentNotifyScenario.m @@ -13,8 +13,8 @@ @implementation FooError @implementation ManyConcurrentNotifyScenario -- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args { - if (self = [super initWithFixtureConfig:config args:args]) { +- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args launchCount:(NSInteger)launchCount { + if (self = [super initWithFixtureConfig:config args:args launchCount:launchCount]) { _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 bc586b4e7..8f7abc42a 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -20,8 +20,9 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @property (strong, nonatomic, nonnull) FixtureConfig *fixtureConfig; @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; @property (strong, nonatomic, nonnull) NSArray *args; +@property (nonatomic) NSInteger launchCount; -- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args; +- (instancetype)initWithFixtureConfig:(FixtureConfig *)config args:( NSArray * _Nonnull )args launchCount:(NSInteger)launchCount; - (void)configure; diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 38cb34ac7..3cddfb7b1 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -66,11 +66,12 @@ + (void)load { }]; } -- (instancetype)initWithFixtureConfig:(FixtureConfig *)fixtureConfig args:(NSArray *)args { +- (instancetype)initWithFixtureConfig:(FixtureConfig *)fixtureConfig args:(NSArray *)args launchCount:(NSInteger)launchCount { if (self = [super init]) { _fixtureConfig = fixtureConfig; _args = args; currentScenario = self; + _launchCount = launchCount; } return self; } diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index 17dc51ad8..d7712a437 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -63,13 +63,13 @@ class Fixture: NSObject, CommandReceiver { logInfo("Executing command [\(command.action)] with args \(command.args)") switch command.action { case "run_scenario": - self.runScenario(scenarioName: command.args[0], args: Array(command.args[1...]), completion: { + self.runScenario(scenarioName: command.args[0], args: Array(command.args[1...]), launchCount: command.launchCount, completion: { self.readyToReceiveCommand = true }) isReady = false; break case "start_bugsnag": - self.startBugsnagForScenario(scenarioName: command.args[0], args: Array(command.args[1...]), completion: { + self.startBugsnagForScenario(scenarioName: command.args[0], args: Array(command.args[1...]), launchCount: command.launchCount, completion: { self.readyToReceiveCommand = true }) isReady = false; @@ -95,18 +95,18 @@ class Fixture: NSObject, CommandReceiver { Scenario.clearPersistentData() } - @objc func startBugsnagForScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { + @objc func startBugsnagForScenario(scenarioName: String, args: [String], launchCount: Int, completion: @escaping () -> ()) { logInfo("---- Starting Bugsnag for scenario \(scenarioName) ----") - loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) + loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args, launchCount: launchCount) logInfo("---- Completed starting Bugsnag for scenario \(String(describing: currentScenario.self)) ----") DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completion() } } - @objc func runScenario(scenarioName: String, args: [String], completion: @escaping () -> ()) { + @objc func runScenario(scenarioName: String, args: [String], launchCount: Int, completion: @escaping () -> ()) { logInfo("---- Running scenario \(scenarioName) ----") - loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args) + loadScenarioAndStartBugsnag(scenarioName: scenarioName, args: args, launchCount: launchCount) logInfo("Starting scenario in class \(String(describing: currentScenario.self))") currentScenario!.run() logInfo("---- Completed running scenario \(String(describing: currentScenario.self)) ----") @@ -149,11 +149,11 @@ class Fixture: NSObject, CommandReceiver { fatalError("Could not find class \(scenarioName) or \(namespace).\(scenarioName). Aborting scenario load...") } - private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String]) { + private func loadScenarioAndStartBugsnag(scenarioName: String, args: [String], launchCount: Int) { logInfo("Loading scenario: \(scenarioName)") let scenarioClass: AnyClass = loadScenarioClass(named: scenarioName) logInfo("Initializing scenario class: \(scenarioClass)") - let scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig, args:args) + let scenario = (scenarioClass as! Scenario.Type).init(fixtureConfig: fixtureConfig, args:args, launchCount: launchCount) currentScenario = scenario logInfo("Configuring scenario in class \(String(describing: scenario.self))") scenario.configure() diff --git a/features/fixtures/shared/utils/MazeRunnerCommand.swift b/features/fixtures/shared/utils/MazeRunnerCommand.swift index 608a7b29b..57e63b346 100644 --- a/features/fixtures/shared/utils/MazeRunnerCommand.swift +++ b/features/fixtures/shared/utils/MazeRunnerCommand.swift @@ -13,8 +13,10 @@ class MazeRunnerCommand: Codable { let action: String let uuid: String let args: Array + let launchCount: Int - init(uuid: String, action: String, args: Array, message: String) { + init(launchCount: Int, uuid: String, action: String, args: Array, message: String) { + self.launchCount = launchCount self.uuid = uuid self.message = message self.action = action @@ -23,6 +25,7 @@ class MazeRunnerCommand: Codable { required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) + self.launchCount = try container.decodeIfPresent(Int.self, forKey: .launchCount) ?? 0 self.uuid = try container.decodeIfPresent(String.self, forKey: .uuid) ?? "" self.message = try container.decodeIfPresent(String.self, forKey: .message) ?? "" self.action = try container.decodeIfPresent(String.self, forKey: .action) ?? "" diff --git a/features/session_tracking.feature b/features/session_tracking.feature index b2813ccb4..ee1a17892 100644 --- a/features/session_tracking.feature +++ b/features/session_tracking.feature @@ -104,12 +104,8 @@ Feature: Session Tracking And the error payload field "events.0.session.events.handled" equals 2 And the error payload field "events.0.session.id" equals the stored value "session_id" - @skip_macos Scenario: Encountering an unhandled event during a session - When I run "AutoSessionUnhandledScenario" - And I wait for 4 seconds - And I kill and relaunch the app - And I set the app to "noevent" mode + When I run "AutoSessionUnhandledScenario" and relaunch the crashed app And I configure Bugsnag for "AutoSessionUnhandledScenario" And I wait to receive a session And I wait to receive an error @@ -126,9 +122,7 @@ Feature: Session Tracking And the error payload field "events.0.session.id" equals the stored value "session_id" Scenario: Encountering handled and unhandled events during a session - When I run "AutoSessionMixedEventsScenario" - And I wait for 5 seconds - And I kill and relaunch the app + When I run "AutoSessionMixedEventsScenario" and relaunch the crashed app And I configure Bugsnag for "AutoSessionMixedEventsScenario" And I wait to receive 2 sessions Then the session is valid for the session reporting API diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 0f9155982..72247dc46 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -59,7 +59,7 @@ end When('I invoke {string}') do |method_name| - Maze::Server.commands.add({ action: "invoke_method", args: [method_name] }) + execute_command "invoke_method", [method_name] # Ensure fixture has read the command count = 100 sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 @@ -67,7 +67,7 @@ When('I invoke {string} with parameter {string}') do |method_name, arg1| # Note: The method will usually be of the form "xyzWithParam:" - Maze::Server.commands.add({ action: "invoke_method", args: [method_name, arg1] }) + execute_command "invoke_method", [method_name, arg1] # Ensure fixture has read the command count = 100 sleep 0.1 until Maze::Server.commands.remaining.empty? || (count -= 1) < 1 @@ -81,7 +81,7 @@ def execute_command(action, args) - Maze::Server.commands.add({ action: action, args: args }) + Maze::Server.commands.add({ action: action, args: args, launch_count: $launch_count }) end def launch_app @@ -116,6 +116,7 @@ def relaunch_crashed_app else raise "Unsupported platform: #{Maze::Helper.get_current_platform}" end + $launch_count += 1 end def kill_and_relaunch_app @@ -130,6 +131,7 @@ def kill_and_relaunch_app else raise "Unsupported platform: #{Maze::Helper.get_current_platform}" end + $launch_count += 1 end def wait_for_true(description) diff --git a/features/support/env.rb b/features/support/env.rb index c918da2e1..d075b1b81 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -123,6 +123,7 @@ def skip_between(os, version_lo, version_hi) # Reset to defaults in case previous scenario changed them Maze.config.captured_invalid_requests = Set[:errors, :sessions, :builds, :uploads, :sourcemaps] + $launch_count = 1 launch_app $started_at = Time.now From c13fed2231dbcda78d9e6cc5017eaacf254f9a21 Mon Sep 17 00:00:00 2001 From: Tom Longridge Date: Tue, 14 May 2024 17:34:40 +0100 Subject: [PATCH 34/51] chore: bump gem dependencies --- Gemfile.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3d4f6bc1e..97974183f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,10 +29,10 @@ GEM selenium-webdriver (~> 4.2, < 4.6) atomos (0.1.3) base64 (0.2.0) - bigdecimal (3.1.7) - bugsnag (6.26.3) + bigdecimal (3.1.8) + bugsnag (6.26.4) concurrent-ruby (~> 1.0) - bugsnag-maze-runner (9.5.0) + bugsnag-maze-runner (9.9.0) appium_lib (~> 12.0.0) appium_lib_core (~> 5.4.0) bugsnag (~> 6.24) @@ -142,9 +142,9 @@ GEM gh_inspector (1.1.3) hana (1.3.7) httpclient (2.8.3) - i18n (1.14.4) + i18n (1.14.5) concurrent-ruby (~> 1.0) - json (2.7.1) + json (2.7.2) json_schemer (0.2.25) ecma-re-validator (~> 0.3) hana (~> 1.3) @@ -153,7 +153,7 @@ GEM uri_template (~> 0.7) mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2024.0305) + mime-types-data (3.2024.0507) minitest (5.22.3) molinillo (0.8.0) multi_test (0.1.2) @@ -162,18 +162,18 @@ GEM nap (1.1.0) netrc (0.11.0) nkf (0.2.0) - nokogiri (1.16.2-arm64-darwin) + nokogiri (1.15.6-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.2-x86_64-darwin) + nokogiri (1.15.6-x86_64-darwin) racc (~> 1.4) optimist (3.0.1) os (1.0.1) power_assert (2.0.3) public_suffix (4.0.7) racc (1.7.3) - rack (2.2.8.1) + rack (2.2.9) rake (12.3.3) - regexp_parser (2.9.0) + regexp_parser (2.9.1) rexml (3.2.6) rouge (2.0.7) ruby-macho (2.5.1) @@ -183,7 +183,7 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - simpleidn (0.2.1) + simpleidn (0.2.2) unf (~> 0.1.4) sys-uname (1.2.3) ffi (~> 1.1) @@ -223,4 +223,4 @@ DEPENDENCIES xcpretty BUNDLED WITH - 2.4.8 + 2.4.18 From 2b377ef7e3ddb9d33a6d95f45b79932bbee2fa3e Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 15 May 2024 10:17:12 +0200 Subject: [PATCH 35/51] Add trace correlation to handled errors --- Bugsnag.xcodeproj/project.pbxproj | 58 +++++++++++++++++ Bugsnag/Client/BugsnagClient.m | 19 +++++- Bugsnag/Helpers/BSGKeys.h | 1 + .../BugsnagCocoaPerformanceFromBugsnagCocoa.h | 40 ++++++++++++ .../BugsnagCocoaPerformanceFromBugsnagCocoa.m | 62 +++++++++++++++++++ Bugsnag/Payload/BugsnagCorrelation+Private.h | 28 +++++++++ Bugsnag/Payload/BugsnagCorrelation.m | 49 +++++++++++++++ Bugsnag/Payload/BugsnagEvent.m | 4 ++ Bugsnag/include/Bugsnag/BugsnagCorrelation.h | 26 ++++++++ Bugsnag/include/Bugsnag/BugsnagEvent.h | 2 + Tests/BugsnagTests/BugsnagClientMirrorTest.m | 1 + .../BugsnagPerformanceBridgeTests.m | 25 ++++++++ 12 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.h create mode 100644 Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.m create mode 100644 Bugsnag/Payload/BugsnagCorrelation+Private.h create mode 100644 Bugsnag/Payload/BugsnagCorrelation.m create mode 100644 Bugsnag/include/Bugsnag/BugsnagCorrelation.h create mode 100644 Tests/BugsnagTests/BugsnagPerformanceBridgeTests.m diff --git a/Bugsnag.xcodeproj/project.pbxproj b/Bugsnag.xcodeproj/project.pbxproj index 29cbe8021..69f20bfa6 100644 --- a/Bugsnag.xcodeproj/project.pbxproj +++ b/Bugsnag.xcodeproj/project.pbxproj @@ -729,6 +729,29 @@ 093EB6672AFE4580006EB7E3 /* BSGTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 093EB6652AFE4580006EB7E3 /* BSGTestCase.mm */; }; 093EB6682AFE4580006EB7E3 /* BSGTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 093EB6652AFE4580006EB7E3 /* BSGTestCase.mm */; }; 093EB6692AFE4580006EB7E3 /* BSGTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 093EB6652AFE4580006EB7E3 /* BSGTestCase.mm */; }; + 09E312EF2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */; }; + 09E312F02BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */; }; + 09E312F12BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */; }; + 09E312F22BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */; }; + 09E312F32BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */; }; + 09E312F42BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */; }; + 09E312F52BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */; }; + 09E312F62BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */; }; + 09E312F82BF248E70081F219 /* BugsnagCorrelation.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09E312F92BF248E80081F219 /* BugsnagCorrelation.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09E312FA2BF248E90081F219 /* BugsnagCorrelation.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09E312FB2BF248E90081F219 /* BugsnagCorrelation.h in Headers */ = {isa = PBXBuildFile; fileRef = 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 09E312FC2BF2492C0081F219 /* BugsnagCorrelation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */; }; + 09E312FE2BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */; }; + 09E312FF2BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */; }; + 09E313002BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */; }; + 09E313012BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */; }; + 09E3132F2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */; }; + 09E313302BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */; }; + 09E313312BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */; }; + 09E313322BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */; }; + 09E313362BF4A7BD0081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */; }; + 09E313372BF4A7CB0081F219 /* BugsnagCorrelation.m in Sources */ = {isa = PBXBuildFile; fileRef = 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */; }; 3A700A9424A63ABC0068CD1B /* BugsnagThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A700A8024A63A8E0068CD1B /* BugsnagThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A700A9524A63AC50068CD1B /* BugsnagSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A700A8124A63A8E0068CD1B /* BugsnagSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A700A9624A63AC60068CD1B /* BugsnagStackframe.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A700A8224A63A8E0068CD1B /* BugsnagStackframe.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1244,6 +1267,7 @@ dstPath = include/Bugsnag; dstSubfolderSpec = 16; files = ( + 09E312FC2BF2492C0081F219 /* BugsnagCorrelation.h in CopyFiles */, 3A700AED24A6492F0068CD1B /* BSG_KSCrashReportWriter.h in CopyFiles */, 3A700AF324A6492F0068CD1B /* Bugsnag.h in CopyFiles */, 3A700AF724A6492F0068CD1B /* BugsnagApp.h in CopyFiles */, @@ -1551,6 +1575,12 @@ 093EB65F2AFE447E006EB7E3 /* Swizzle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Swizzle.mm; sourceTree = ""; }; 093EB6642AFE4580006EB7E3 /* BSGTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGTestCase.h; sourceTree = ""; }; 093EB6652AFE4580006EB7E3 /* BSGTestCase.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BSGTestCase.mm; sourceTree = ""; }; + 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagCocoaPerformanceFromBugsnagCocoa.h; sourceTree = ""; }; + 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagCocoaPerformanceFromBugsnagCocoa.m; sourceTree = ""; }; + 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagCorrelation.h; sourceTree = ""; }; + 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagCorrelation.m; sourceTree = ""; }; + 09E313022BF34E5E0081F219 /* BugsnagCorrelation+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagCorrelation+Private.h"; sourceTree = ""; }; + 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BugsnagPerformanceBridgeTests.m; sourceTree = ""; }; 3A700A8024A63A8E0068CD1B /* BugsnagThread.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagThread.h; sourceTree = ""; }; 3A700A8124A63A8E0068CD1B /* BugsnagSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagSession.h; sourceTree = ""; }; 3A700A8224A63A8E0068CD1B /* BugsnagStackframe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BugsnagStackframe.h; sourceTree = ""; }; @@ -1989,6 +2019,7 @@ 008966CF2486D43600DC48C2 /* BugsnagNotifierTest.m */, 008966AB2486D43500DC48C2 /* BugsnagOnBreadcrumbTest.m */, 008966C92486D43600DC48C2 /* BugsnagOnCrashTest.m */, + 09E3132E2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m */, 008966C72486D43600DC48C2 /* BugsnagPluginTest.m */, 008966B62486D43500DC48C2 /* BugsnagSessionTest.m */, 008966C32486D43600DC48C2 /* BugsnagSessionTrackerStopTest.m */, @@ -2123,6 +2154,8 @@ 008968142486DA5600DC48C2 /* BugsnagLogger.h */, 015F528325C15BB7000D1915 /* MRCCanary.m */, CB37449B284756C100A3955E /* stb_sprintf.h */, + 09E312ED2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h */, + 09E312EE2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m */, ); path = Helpers; sourceTree = ""; @@ -2145,6 +2178,8 @@ 0126DED7257A87F40031A70C /* BugsnagAppWithState+Private.h */, 0089684B2486DA9400DC48C2 /* BugsnagBreadcrumb.m */, 0126DEDF257A89490031A70C /* BugsnagBreadcrumb+Private.h */, + 09E312FD2BF34D6D0081F219 /* BugsnagCorrelation.m */, + 09E313022BF34E5E0081F219 /* BugsnagCorrelation+Private.h */, 008968482486DA9400DC48C2 /* BugsnagDevice.m */, 0126DEE7257A8B0F0031A70C /* BugsnagDevice+Private.h */, 0089685A2486DA9500DC48C2 /* BugsnagDeviceWithState.m */, @@ -2226,6 +2261,7 @@ 3A700A8524A63A8E0068CD1B /* BugsnagBreadcrumb.h */, 3A700A8924A63A8E0068CD1B /* BugsnagClient.h */, 3A700A8D24A63A8E0068CD1B /* BugsnagConfiguration.h */, + 09E312F72BF248DD0081F219 /* BugsnagCorrelation.h */, 01C41A27288FD3EA00BAE31A /* BugsnagDefines.h */, 3A700A8F24A63A8E0068CD1B /* BugsnagDevice.h */, 3A700A9224A63A8E0068CD1B /* BugsnagDeviceWithState.h */, @@ -2287,6 +2323,7 @@ 3A700A9924A63AC60068CD1B /* BugsnagBreadcrumb.h in Headers */, 0126F7AB25DD5118008483C2 /* BSGEventUploadFileOperation.h in Headers */, 3A700A9A24A63AC60068CD1B /* BSG_KSCrashReportWriter.h in Headers */, + 09E312EF2BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */, 3A700A9B24A63AC60068CD1B /* BugsnagErrorTypes.h in Headers */, 01847D962644140F00ADA4C7 /* BSGInternalErrorReporter.h in Headers */, 017DCF8C2874212F000ECB22 /* BSGTelemetry.h in Headers */, @@ -2360,6 +2397,7 @@ 0089698A2486DAD100DC48C2 /* BSG_KSString.h in Headers */, 008969962486DAD100DC48C2 /* BSG_KSBacktrace_Private.h in Headers */, 00896A112486DAD100DC48C2 /* BSG_KSCrashSentry_CPPException.h in Headers */, + 09E312F82BF248E70081F219 /* BugsnagCorrelation.h in Headers */, 008969ED2486DAD100DC48C2 /* BSG_KSCrashDoctor.h in Headers */, 01CCAEEA25D414D60057268D /* BugsnagLastRunInfo.h in Headers */, 010993B1273D2F6200128BBE /* BugsnagFeatureFlagStore.h in Headers */, @@ -2424,6 +2462,7 @@ 00896A302486DAD100DC48C2 /* BSG_KSCrashIdentifier.h in Headers */, 01B79DAA267CC4A000C8CC5E /* BSGUtils.h in Headers */, 008967B92486D9D800DC48C2 /* BugsnagBreadcrumbs.h in Headers */, + 09E312F92BF248E80081F219 /* BugsnagCorrelation.h in Headers */, 008969DF2486DAD100DC48C2 /* BSG_KSCrashC.h in Headers */, 0089697F2486DAD100DC48C2 /* BSG_KSArchSpecific.h in Headers */, 01C41A29288FD3EB00BAE31A /* BugsnagDefines.h in Headers */, @@ -2452,6 +2491,7 @@ 008967F52486DA4500DC48C2 /* BSGSessionUploader.h in Headers */, 010FF28525ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E22486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, + 09E312F02BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */, 01CB95C0278F0C830077744A /* BSG_KSFile.h in Headers */, 00896A0F2486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, 008969D32486DAD100DC48C2 /* BSG_KSCrashContext.h in Headers */, @@ -2528,6 +2568,7 @@ 00896A312486DAD100DC48C2 /* BSG_KSCrashIdentifier.h in Headers */, 01B79DAB267CC4A000C8CC5E /* BSGUtils.h in Headers */, 008967BA2486D9D800DC48C2 /* BugsnagBreadcrumbs.h in Headers */, + 09E312FA2BF248E90081F219 /* BugsnagCorrelation.h in Headers */, 008969E02486DAD100DC48C2 /* BSG_KSCrashC.h in Headers */, 008969802486DAD100DC48C2 /* BSG_KSArchSpecific.h in Headers */, 01C41A2A288FD3EB00BAE31A /* BugsnagDefines.h in Headers */, @@ -2556,6 +2597,7 @@ 008967F62486DA4500DC48C2 /* BSGSessionUploader.h in Headers */, 010FF28625ED2A8D00E4F2B0 /* BSGAppHangDetector.h in Headers */, 008969E32486DAD100DC48C2 /* BSG_KSSystemInfo.h in Headers */, + 09E312F12BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */, 01CB95C1278F0C830077744A /* BSG_KSFile.h in Headers */, 00896A102486DAD100DC48C2 /* BSG_KSCrashSentry.h in Headers */, 008969D42486DAD100DC48C2 /* BSG_KSCrashContext.h in Headers */, @@ -2624,6 +2666,7 @@ CBBDE929280068AD0070DCD3 /* BSGEventUploadObjectOperation.h in Headers */, CBBDE92A280068AD0070DCD3 /* BSGEventUploader.h in Headers */, CBBDE950280068FD0070DCD3 /* BugsnagClient.h in Headers */, + 09E312FB2BF248E90081F219 /* BugsnagCorrelation.h in Headers */, CBBDE955280068FD0070DCD3 /* BugsnagPlugin.h in Headers */, CBBDE9802800698F0070DCD3 /* BSG_KSCrashState.h in Headers */, CBBDE9902800698F0070DCD3 /* BSG_KSCrashType.h in Headers */, @@ -2680,6 +2723,7 @@ CBBDE959280068FD0070DCD3 /* BugsnagSession.h in Headers */, CBBDE930280068AD0070DCD3 /* BSGSessionUploader.h in Headers */, CBBDE93C280068D40070DCD3 /* BSGInternalErrorReporter.h in Headers */, + 09E312F22BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.h in Headers */, CBBDE942280068E60070DCD3 /* BugsnagCollections.h in Headers */, CBBDE9B0280069B20070DCD3 /* BSG_KSLogger.h in Headers */, CBBDE936280068C40070DCD3 /* BSG_RFC3339DateTool.h in Headers */, @@ -3074,9 +3118,11 @@ 008968802486DA9600DC48C2 /* BugsnagAppWithState.m in Sources */, 008969572486DAD000DC48C2 /* BSG_KSCrashDoctor.m in Sources */, 008968B92486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, + 09E312FE2BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */, 00896A142486DAD100DC48C2 /* BSG_KSCrashSentry_Signal.c in Sources */, 01468F5525876DC1002B0519 /* BSGNotificationBreadcrumbs.m in Sources */, 008967BE2486DA1900DC48C2 /* BugsnagClient.m in Sources */, + 09E312F32BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */, 008968952486DA9600DC48C2 /* BugsnagHandledState.m in Sources */, 008967FE2486DA4500DC48C2 /* BSGSessionUploader.m in Sources */, 0089686B2486DA9500DC48C2 /* BugsnagEvent.m in Sources */, @@ -3172,6 +3218,7 @@ 010F80C228645B4200D6569E /* BSGDefinesTests.m in Sources */, 01BDB1F525DEBFB200A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */, 008967782486D43700DC48C2 /* BSG_KSMachHeadersTests.m in Sources */, + 09E3132F2BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */, 0089673F2486D43700DC48C2 /* BugsnagAppTest.m in Sources */, 0089675A2486D43700DC48C2 /* BugsnagEnabledBreadcrumbTest.m in Sources */, 01935AE2275E68E9007498B3 /* UIApplicationStub.m in Sources */, @@ -3252,9 +3299,11 @@ 0089699D2486DAD100DC48C2 /* BSG_KSFileUtils.c in Sources */, 008969762486DAD100DC48C2 /* BSG_KSJSONCodec.c in Sources */, 00896A2D2486DAD100DC48C2 /* BSG_KSCrashReport.c in Sources */, + 09E312FF2BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */, 008968812486DA9600DC48C2 /* BugsnagAppWithState.m in Sources */, 008969582486DAD000DC48C2 /* BSG_KSCrashDoctor.m in Sources */, 008968BA2486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, + 09E312F42BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */, 00896A152486DAD100DC48C2 /* BSG_KSCrashSentry_Signal.c in Sources */, 01468F5625876DC1002B0519 /* BSGNotificationBreadcrumbs.m in Sources */, 01CB95C3278F0C830077744A /* BSG_KSFile.c in Sources */, @@ -3335,6 +3384,7 @@ 008967822486D43700DC48C2 /* KSSystemInfo_Tests.m in Sources */, 008967612486D43700DC48C2 /* BugsnagBreadcrumbsTest.m in Sources */, 01DE903D26CEAF9E00455213 /* BSGUtilsTests.m in Sources */, + 09E313302BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */, 093EB6672AFE4580006EB7E3 /* BSGTestCase.mm in Sources */, CB156242270707740097334C /* KSCrashNames_Test.m in Sources */, 008967012486D43700DC48C2 /* BugsnagEventPersistLoadTest.m in Sources */, @@ -3427,9 +3477,11 @@ 008969772486DAD100DC48C2 /* BSG_KSJSONCodec.c in Sources */, 00896A2E2486DAD100DC48C2 /* BSG_KSCrashReport.c in Sources */, 008968822486DA9600DC48C2 /* BugsnagAppWithState.m in Sources */, + 09E313002BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */, 008969592486DAD000DC48C2 /* BSG_KSCrashDoctor.m in Sources */, 008968BB2486DA9600DC48C2 /* BugsnagStacktrace.m in Sources */, 00896A162486DAD100DC48C2 /* BSG_KSCrashSentry_Signal.c in Sources */, + 09E312F52BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */, 01468F5725876DC1002B0519 /* BSGNotificationBreadcrumbs.m in Sources */, 008967C02486DA1900DC48C2 /* BugsnagClient.m in Sources */, 008968972486DA9600DC48C2 /* BugsnagHandledState.m in Sources */, @@ -3487,6 +3539,7 @@ 010F80C428645B4200D6569E /* BSGDefinesTests.m in Sources */, CBDD6D0F25AC3EFF00A2E12B /* BSGStorageMigratorV0V1Tests.m in Sources */, 008967502486D43700DC48C2 /* BugsnagPluginTest.m in Sources */, + 09E313312BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */, 008967142486D43700DC48C2 /* BugsnagEventTests.m in Sources */, 0089675C2486D43700DC48C2 /* BugsnagEnabledBreadcrumbTest.m in Sources */, 008966ED2486D43700DC48C2 /* BugsnagDeviceTest.m in Sources */, @@ -3582,6 +3635,7 @@ E7462912248907E500F92D67 /* BSG_KSMach_x86_64.c in Sources */, CBCF77A925010648004AF22A /* BSGJSONSerialization.m in Sources */, E7462913248907E500F92D67 /* BSG_KSSignalInfo.c in Sources */, + 09E313362BF4A7BD0081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */, E7462914248907E500F92D67 /* BSG_KSString.c in Sources */, 01CCAEF025D414D60057268D /* BugsnagLastRunInfo.m in Sources */, 01A2958428B665F5005FCC8C /* BSGNetworkBreadcrumb.m in Sources */, @@ -3601,6 +3655,7 @@ E7462905248907C100F92D67 /* BSG_KSCrash.m in Sources */, E7462906248907C100F92D67 /* BSG_KSCrashSentry_NSException.m in Sources */, E7462907248907C100F92D67 /* BSG_KSCrashSentry_CPPException.mm in Sources */, + 09E313372BF4A7CB0081F219 /* BugsnagCorrelation.m in Sources */, E7462908248907C100F92D67 /* BSG_KSSystemInfo.m in Sources */, 008968CA2486DA9600DC48C2 /* BugsnagApp.m in Sources */, 008967C12486DA1900DC48C2 /* BugsnagClient.m in Sources */, @@ -3701,9 +3756,11 @@ CBBDE92C280068AD0070DCD3 /* BSGEventUploadObjectOperation.m in Sources */, CBBDE910280068560070DCD3 /* BugsnagSessionTracker.m in Sources */, CBBDE98C2800698F0070DCD3 /* BSG_KSCrashDoctor.m in Sources */, + 09E313012BF34D6D0081F219 /* BugsnagCorrelation.m in Sources */, CBBDE948280068E60070DCD3 /* MRCCanary.m in Sources */, CBBDE93A280068D40070DCD3 /* BSGInternalErrorReporter.m in Sources */, CBBDE923280068970070DCD3 /* BugsnagEndpointConfiguration.m in Sources */, + 09E312F62BF230660081F219 /* BugsnagCocoaPerformanceFromBugsnagCocoa.m in Sources */, CBBDE92E280068AD0070DCD3 /* BSGConnectivity.m in Sources */, CBBDE912280068560070DCD3 /* BSGCrashSentry.m in Sources */, CBBDE9BC280069B20070DCD3 /* BSG_Symbolicate.c in Sources */, @@ -3783,6 +3840,7 @@ CB28F0D8282A4BA6003AB200 /* BugsnagMetadataRedactionTest.m in Sources */, CB28F121282A7D49003AB200 /* BugsnagSwiftConfigurationTests.swift in Sources */, CB28F0DF282A4BEE003AB200 /* BugsnagSessionTest.m in Sources */, + 09E313322BF3867C0081F219 /* BugsnagPerformanceBridgeTests.m in Sources */, CB28F0C2282A4857003AB200 /* BSGUtilsTests.m in Sources */, CB28F0CA282A4A2E003AB200 /* BugsnagClientMirrorTest.m in Sources */, CB28F120282A7D32003AB200 /* BugsnagSwiftTests.swift in Sources */, diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 6a187b3fe..d4a86acc1 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -53,6 +53,7 @@ #import "BugsnagBreadcrumbs.h" #import "BugsnagCollections.h" #import "BugsnagConfiguration+Private.h" +#import "BugsnagCorrelation+Private.h" #import "BugsnagDeviceWithState+Private.h" #import "BugsnagError+Private.h" #import "BugsnagErrorTypes.h" @@ -71,6 +72,7 @@ #import "BugsnagThread+Private.h" #import "BugsnagUser+Private.h" #import "BSGPersistentDeviceID.h" +#import "BugsnagCocoaPerformanceFromBugsnagCocoa.h" static struct { // Contains the user-specified metadata, including the user tab from config. @@ -237,6 +239,9 @@ - (void)start { // MUST be called before any code that accesses bsg_runContext BSGRunContextInit(BSGFileLocations.current.runContext); + // Map our bridged API early on. + [BugsnagCocoaPerformanceFromBugsnagCocoa sharedInstance]; + BSGCrashSentryInstall(self.configuration, BSSerializeDataCrashHandler); self.systemState = [[BugsnagSystemState alloc] initWithConfiguration:self.configuration]; @@ -649,7 +654,18 @@ - (void)notify:(NSException *)exception block:(BugsnagOnErrorBlock)block { // MARK: - Notify (Internal) +- (BugsnagCorrelation *)getCurrentCorrelation { + NSArray *correlation = [BugsnagCocoaPerformanceFromBugsnagCocoa.sharedInstance getCurrentTraceAndSpanId]; + if (correlation.count != 2) { + return nil; + } + NSString *traceId = correlation[0]; + NSString *spanId = correlation[1]; + return [[BugsnagCorrelation alloc] initWithTraceId:traceId spanId:spanId]; +} + - (void)notifyErrorOrException:(id)errorOrException block:(BugsnagOnErrorBlock)block { + BugsnagCorrelation *correlation = [self getCurrentCorrelation]; NSDictionary *systemInfo = [BSG_KSSystemInfo systemInfo]; BugsnagMetadata *metadata = [self.metadata copy]; @@ -691,7 +707,7 @@ - (void)notifyErrorOrException:(id)errorOrException block:(BugsnagOnErrorBlock)b bsg_log_warn(@"Unsupported error type passed to notify: %@", NSStringFromClass([errorOrException class])); return; } - + /** * Stack frames starting from this one are removed by setting the depth. * This helps remove bugsnag frames from showing in NSErrors as their @@ -738,6 +754,7 @@ - (void)notifyErrorOrException:(id)errorOrException block:(BugsnagOnErrorBlock)b event.apiKey = self.configuration.apiKey; event.context = context; event.originalError = errorOrException; + event.correlation = correlation; [self notifyInternal:event block:block]; } diff --git a/Bugsnag/Helpers/BSGKeys.h b/Bugsnag/Helpers/BSGKeys.h index e083ee2bf..4aa5e7587 100644 --- a/Bugsnag/Helpers/BSGKeys.h +++ b/Bugsnag/Helpers/BSGKeys.h @@ -30,6 +30,7 @@ static BSGKey const BSGKeyClient = @"client"; static BSGKey const BSGKeyCodeBundleId = @"codeBundleId"; static BSGKey const BSGKeyConfig = @"config"; static BSGKey const BSGKeyContext = @"context"; +static BSGKey const BSGKeyCorrelation = @"correlation"; static BSGKey const BSGKeyCppException = @"cpp_exception"; static BSGKey const BSGKeyDevelopment = @"development"; static BSGKey const BSGKeyDevice = @"device"; diff --git a/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.h b/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.h new file mode 100644 index 000000000..c645802d1 --- /dev/null +++ b/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.h @@ -0,0 +1,40 @@ +// +// BugsnagCocoaPerformanceFromBugsnagCocoa.h +// Bugsnag +// +// Created by Karl Stenerud on 13.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * Bridge from Bugsnag Coca to Bugsnag Cocoa Performance. + * + * IMPORTANT: This class name MUST be globally unique across ALL Bugsnag libraries that contain native code! + * When cloning this code as a template for your own bridge, always use the naming style "YourLibraryFromMyLibrary" + * For example: + * * "BugsnagCocoaPerformanceFromBugsnagUnity" (bridge from Bugsnag Unity to Bugsnag Cocoa Performance) + * * "BugsnagCocoaFromBugsnagReactNativePerformance" (bridge from Bugsnag Performance React Native to Bugsnag Cocoa) + */ +@interface BugsnagCocoaPerformanceFromBugsnagCocoa: NSObject + +#pragma mark Methods that will be bridged to BugsnagPerformance + +/** + * Return the current trace and span IDs as strings in a 2-entry array, or return nil if no current span exists. + * + * array[0] is an NSString containing the trace ID + * array[1] is an NSString containing the span ID + */ +- (NSArray * _Nullable)getCurrentTraceAndSpanId; + +#pragma mark Shared Instance + ++ (instancetype _Nullable) sharedInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.m b/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.m new file mode 100644 index 000000000..b83a616b3 --- /dev/null +++ b/Bugsnag/Helpers/BugsnagCocoaPerformanceFromBugsnagCocoa.m @@ -0,0 +1,62 @@ +// +// BugsnagCocoaPerformanceFromBugsnagCocoa.m +// Bugsnag +// +// Created by Karl Stenerud on 13.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import "BugsnagCocoaPerformanceFromBugsnagCocoa.h" +#import "BugsnagLogger.h" + +// Bridged API methods won't have implementations until we connect them at runtime. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wincomplete-implementation" +@implementation BugsnagCocoaPerformanceFromBugsnagCocoa + +static NSString *BSGUserInfoKeyMapped = @"mapped"; +static NSString *BSGUserInfoValueMappedYes = @"YES"; + +static id bugsnagPerformanceCrossTalkAPI = nil; + ++ (void)initialize { + // Look for a cross-talk API + Class cls = NSClassFromString(@"BugsnagPerformanceCrossTalkAPI"); + if (cls != nil) { + NSError *err = nil; + // Map the methods we want to use, with the API versions we expect + if ((err = [cls mapAPINamed:@"getCurrentTraceAndSpanIdV1" + toSelector:@selector(getCurrentTraceAndSpanId)]) != nil) { + bsg_log_err("Failed to map Bugsnag Performance API getCurrentTraceAndSpanIdV1: %@", err); + NSString *mapped = err.userInfo[BSGUserInfoKeyMapped]; + if (![mapped isEqualToString:BSGUserInfoValueMappedYes]) { + // Must abort because this method is not mapped, so we'd crash if we tried to call it. + return; + } + } + + // Our "sharedInstance" will actually be the cross-talk API whose class we loaded. + bugsnagPerformanceCrossTalkAPI = [cls sharedInstance]; + } +} + ++ (instancetype _Nullable) sharedInstance { + // We're either going to return the API object we found in +initialize, or nil. + return bugsnagPerformanceCrossTalkAPI; +} + +/** + * Map a named API to a method with the specified selector. + * If an error occurs, the user info dictionary of the error will contain a field "mapped". + * If "mapped" is "YES", then the selector has been mapped to a null implementation (does nothing, returns nil). + * If "mapped" is "NO", then no mapping has occurred, and the method doesn't exist (alling it will result in no such selector). + */ ++ (NSError *)mapAPINamed:(NSString * _Nonnull __unused)apiName toSelector:(SEL __unused)toSelector { + // This exists only to make the mapAPINamed selector available on our side + // so that we can call it on the API class we found (see +initialize). + // This implementation is never actually called. + return nil; +} + +@end +#pragma clang diagnostic pop diff --git a/Bugsnag/Payload/BugsnagCorrelation+Private.h b/Bugsnag/Payload/BugsnagCorrelation+Private.h new file mode 100644 index 000000000..fafd4d181 --- /dev/null +++ b/Bugsnag/Payload/BugsnagCorrelation+Private.h @@ -0,0 +1,28 @@ +// +// BugsnagCorrelation+Private.h +// Bugsnag +// +// Created by Karl Stenerud on 14.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import "BugsnagCorrelation.h" + +#ifndef BugsnagCorrelation_Private_h +#define BugsnagCorrelation_Private_h + +NS_ASSUME_NONNULL_BEGIN + +@interface BugsnagCorrelation () + +- (instancetype) initWithTraceId:(NSString * _Nullable) traceId spanId:(NSString * _Nullable)spanId; + +- (instancetype) initWithJsonDictionary:(NSDictionary * _Nullable) dict; + +- (NSDictionary *) toJsonDictionary; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* BugsnagCorrelation_Private_h */ diff --git a/Bugsnag/Payload/BugsnagCorrelation.m b/Bugsnag/Payload/BugsnagCorrelation.m new file mode 100644 index 000000000..1b6383ecf --- /dev/null +++ b/Bugsnag/Payload/BugsnagCorrelation.m @@ -0,0 +1,49 @@ +// +// BugsnagCorrelation.m +// Bugsnag +// +// Created by Karl Stenerud on 14.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import "BugsnagCorrelation+Private.h" + +@implementation BugsnagCorrelation + +- (instancetype) initWithTraceId:(NSString *) traceId spanId:(NSString *)spanId { + if ((self = [super init])) { + _traceId = traceId; + _spanId = spanId; + } + return self; +} + +- (instancetype) initWithJsonDictionary:(NSDictionary *) dict { + if (dict.count == 0) { + return nil; + } + + if ((self = [super init])) { + id nsnull = NSNull.null; + _traceId = (NSString *)dict[@"traceid"]; + if (_traceId == nsnull) { + _traceId = nil; + } + + _spanId = (NSString *)dict[@"spanid"]; + if (_spanId == nsnull) { + _spanId = nil; + } + + } + return self; +} + +- (NSDictionary *) toJsonDictionary { + NSMutableDictionary *dict = [NSMutableDictionary new]; + dict[@"traceid"] = self.traceId; + dict[@"spanid"] = self.spanId; + return dict; +} + +@end diff --git a/Bugsnag/Payload/BugsnagEvent.m b/Bugsnag/Payload/BugsnagEvent.m index 7af3bcffb..4d809618b 100644 --- a/Bugsnag/Payload/BugsnagEvent.m +++ b/Bugsnag/Payload/BugsnagEvent.m @@ -23,6 +23,7 @@ #import "BugsnagBreadcrumbs.h" #import "BugsnagCollections.h" #import "BugsnagConfiguration+Private.h" +#import "BugsnagCorrelation+Private.h" #import "BugsnagDeviceWithState+Private.h" #import "BugsnagError+Private.h" #import "BugsnagHandledState.h" @@ -188,6 +189,8 @@ - (instancetype)initWithJson:(NSDictionary *)json { _context = BSGDeserializeString(json[BSGKeyContext]); + _correlation = [[BugsnagCorrelation alloc] initWithJsonDictionary:json[BSGKeyCorrelation]]; + _device = BSGDeserializeObject(json[BSGKeyDevice], ^id _Nullable(NSDictionary * _Nonnull dict) { return [BugsnagDeviceWithState deviceFromJson:dict]; }) ?: [[BugsnagDeviceWithState alloc] init]; @@ -592,6 +595,7 @@ - (NSDictionary *)toJsonWithRedactedKeys:(NSSet *)redactedKeys { event[BSGKeyApp] = [self.app toDict]; event[BSGKeyContext] = [self context]; + event[BSGKeyCorrelation] = [self.correlation toJsonDictionary]; event[BSGKeyFeatureFlags] = BSGFeatureFlagStoreToJSON(self.featureFlagStore); event[BSGKeyGroupingHash] = self.groupingHash; diff --git a/Bugsnag/include/Bugsnag/BugsnagCorrelation.h b/Bugsnag/include/Bugsnag/BugsnagCorrelation.h new file mode 100644 index 000000000..c817a9821 --- /dev/null +++ b/Bugsnag/include/Bugsnag/BugsnagCorrelation.h @@ -0,0 +1,26 @@ +// +// BugsnagCorrelation.h +// Bugsnag +// +// Created by Karl Stenerud on 13.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import + +#ifndef BugsnagCorrelation_h +#define BugsnagCorrelation_h + +NS_ASSUME_NONNULL_BEGIN + +@interface BugsnagCorrelation: NSObject + +@property (readwrite, nonatomic, strong, nullable) NSString *traceId; + +@property (readwrite, nonatomic, strong, nullable) NSString *spanId; + +@end + +NS_ASSUME_NONNULL_END + +#endif /* BugsnagCorrelation_h */ diff --git a/Bugsnag/include/Bugsnag/BugsnagEvent.h b/Bugsnag/include/Bugsnag/BugsnagEvent.h index 0016271c3..e0720e00a 100644 --- a/Bugsnag/include/Bugsnag/BugsnagEvent.h +++ b/Bugsnag/include/Bugsnag/BugsnagEvent.h @@ -11,6 +11,7 @@ #import #import #import +#import @class BugsnagConfiguration; @class BugsnagHandledState; @@ -112,6 +113,7 @@ BUGSNAG_EXTERN */ @property (strong, nullable, nonatomic) id originalError; +@property (readwrite, nonatomic, strong, nullable) BugsnagCorrelation *correlation; // ============================================================================= // MARK: - User diff --git a/Tests/BugsnagTests/BugsnagClientMirrorTest.m b/Tests/BugsnagTests/BugsnagClientMirrorTest.m index 3a5d9acb9..3f9355c0f 100644 --- a/Tests/BugsnagTests/BugsnagClientMirrorTest.m +++ b/Tests/BugsnagTests/BugsnagClientMirrorTest.m @@ -64,6 +64,7 @@ - (void)setUp { @"generateOutOfMemoryEvent @16@0:8", @"generateThermalKillEvent @16@0:8", @"generateThreads @16@0:8", + @"getCurrentCorrelation @16@0:8", @"initWithConfiguration: @24@0:8@16", @"initializeNotificationNameMap v16@0:8", @"leaveBreadcrumbForEvent: v24@0:8@16", diff --git a/Tests/BugsnagTests/BugsnagPerformanceBridgeTests.m b/Tests/BugsnagTests/BugsnagPerformanceBridgeTests.m new file mode 100644 index 000000000..0c31afa6c --- /dev/null +++ b/Tests/BugsnagTests/BugsnagPerformanceBridgeTests.m @@ -0,0 +1,25 @@ +// +// BugsnagPerformanceBridgeTests.m +// Bugsnag +// +// Created by Karl Stenerud on 14.05.24. +// Copyright © 2024 Bugsnag Inc. All rights reserved. +// + +#import +#import "BugsnagCocoaPerformanceFromBugsnagCocoa.h" + +@interface BugsnagPerformanceBridgeTests : XCTestCase + +@end + +@implementation BugsnagPerformanceBridgeTests + +- (void)testBridgeStability { + BugsnagCocoaPerformanceFromBugsnagCocoa *api = BugsnagCocoaPerformanceFromBugsnagCocoa.sharedInstance; + // With BugsnagPerformance not present, we should get nil calling this. + // And most importantly it should not crash! + XCTAssertNil([api getCurrentTraceAndSpanId]); +} + +@end From aaa6807b0d887ea381c64da5ff6d71d38e41f639 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 16 May 2024 15:55:46 +0200 Subject: [PATCH 36/51] Attempt to capture trace id and span id on uncaught NSException --- Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h | 3 ++- Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m | 3 ++- Bugsnag/Client/BugsnagClient.m | 19 ++++++++++++++++--- .../KSCrash/Recording/BSG_KSCrashReport.c | 2 +- .../Recording/Sentry/BSG_KSCrashSentry.h | 3 +++ .../Sentry/BSG_KSCrashSentry_CPPException.mm | 1 + .../Sentry/BSG_KSCrashSentry_MachException.c | 1 + .../Sentry/BSG_KSCrashSentry_NSException.m | 1 + .../Sentry/BSG_KSCrashSentry_Signal.c | 1 + Bugsnag/Payload/BugsnagEvent.m | 9 +++++++++ Bugsnag/Payload/BugsnagSession+Private.h | 2 +- Bugsnag/Payload/BugsnagSession.m | 2 +- .../include/Bugsnag/BSG_KSCrashReportWriter.h | 2 +- Tests/BugsnagTests/BugsnagBreadcrumbsTest.m | 4 ++-- 14 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h b/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h index 43ba0bb0d..7158a6e69 100644 --- a/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h +++ b/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.h @@ -60,4 +60,5 @@ NS_ASSUME_NONNULL_END * This function is async-signal-safe, but requires that any threads that could be adding * breadcrumbs are suspended. */ -void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter * _Nonnull writer); +void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter * _Nonnull writer, + bool requiresAsyncSafety); diff --git a/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m b/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m index be3d02b6c..9b8b2dc78 100644 --- a/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m +++ b/Bugsnag/Breadcrumbs/BugsnagBreadcrumbs.m @@ -289,7 +289,8 @@ - (NSString *)pathForFileNumber:(unsigned int)fileNumber { #pragma mark - -void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter *writer) { +void BugsnagBreadcrumbsWriteCrashReport(const BSG_KSCrashReportWriter *writer, + bool __unused requiresAsyncSafety) { atomic_store(&g_writing_crash_report, true); writer->beginArray(writer, "breadcrumbs"); diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index d4a86acc1..386b1867e 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -96,9 +96,9 @@ * * @param writer report writer which will receive updated metadata */ -static void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer) { +static void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer, bool requiresAsyncSafety) { BOOL isCrash = YES; - BSGSessionWriteCrashReport(writer); + BSGSessionWriteCrashReport(writer, requiresAsyncSafety); if (isCrash) { writer->addJSONElement(writer, "config", bsg_g_bugsnag_data.configJSON); @@ -116,6 +116,19 @@ static void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer) { } writer->endContainer(writer); + if (!requiresAsyncSafety) { + NSArray *correlation = [BugsnagCocoaPerformanceFromBugsnagCocoa.sharedInstance getCurrentTraceAndSpanId]; + if (correlation.count == 2) { + NSString *traceId = correlation[0]; + NSString *spanId = correlation[1]; + writer->beginObject(writer, "correlation"); { + writer->addStringElement(writer, "traceid", traceId.UTF8String); + writer->addStringElement(writer, "spanid", spanId.UTF8String); + } + writer->endContainer(writer); + } + } + #if BSG_HAVE_BATTERY if (BSGIsBatteryStateKnown(bsg_runContext->batteryState)) { writer->addFloatingPointElement(writer, "batteryLevel", bsg_runContext->batteryLevel); @@ -128,7 +141,7 @@ static void BSSerializeDataCrashHandler(const BSG_KSCrashReportWriter *writer) { writer->addBooleanElement(writer, "isLaunching", bsg_runContext->isLaunching); writer->addIntegerElement(writer, "thermalState", bsg_runContext->thermalState); - BugsnagBreadcrumbsWriteCrashReport(writer); + BugsnagBreadcrumbsWriteCrashReport(writer, requiresAsyncSafety); // Create a file to indicate that the crash has been handled by // the library. This exists in case the subsequent `onCrash` handler diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index 49ce9438e..c4c333a11 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -1271,7 +1271,7 @@ void bsg_kscrashreport_writeStandardReport( // Write handled exception report info writer->beginObject(writer, BSG_KSCrashField_UserAtCrash); - crashContext->config.onCrashNotify(writer); + crashContext->config.onCrashNotify(writer, crashContext->crash.requiresAsyncSafety); writer->endContainer(writer); } } diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h index f89d0a2c8..d64d95f01 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry.h @@ -77,6 +77,9 @@ typedef struct BSG_KSCrash_SentryContext { */ bool handlingCrash; + /** If true, any code called via the crash handler MUST be async-safe. */ + bool requiresAsyncSafety; + /** If true, a second crash occurred while handling a crash. */ bool crashedDuringCrashHandling; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm index 521cbc240..a1925fd2a 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm @@ -221,6 +221,7 @@ static void CPPExceptionTerminate(void) { #endif bsg_g_context->crashType = BSG_KSCrashTypeCPPException; + bsg_g_context->requiresAsyncSafety = true; bsg_g_context->registersAreValid = false; bsg_g_context->stackTrace = bsg_g_stackTrace + 1; // Don't record __cxa_throw stack entry diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c index 60aed6db2..643204f4b 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_MachException.c @@ -277,6 +277,7 @@ void *ksmachexc_i_handleExceptions(void *const userData) { BSG_KSLOG_DEBUG("Filling out context."); bsg_g_context->crashType = BSG_KSCrashTypeMachException; + bsg_g_context->requiresAsyncSafety = true; bsg_g_context->registersAreValid = true; bsg_g_context->mach.type = exceptionMessage.exception; bsg_g_context->mach.code = exceptionMessage.code[0] & (int64_t)MACH_ERROR_CODE_MASK; diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m index 1656fc8d0..4cc1bba29 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_NSException.m @@ -136,6 +136,7 @@ void bsg_recordException(NSException *exception) { } bsg_g_context->crashType = BSG_KSCrashTypeNSException; + bsg_g_context->requiresAsyncSafety = false; bsg_g_context->offendingThread = bsg_ksmachthread_self(); bsg_g_context->registersAreValid = false; bsg_g_context->NSException.name = CopyUTF8String([exception name]); diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c index db1bd846c..e43a724e5 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_Signal.c @@ -111,6 +111,7 @@ void bsg_kssighndl_i_handleSignal(int sigNum, siginfo_t *signalInfo, BSG_KSLOG_DEBUG("Filling out context."); bsg_g_context->crashType = BSG_KSCrashTypeSignal; + bsg_g_context->requiresAsyncSafety = true; bsg_g_context->registersAreValid = true; bsg_g_context->faultAddress = (uintptr_t)signalInfo->si_addr; bsg_g_context->signal.userContext = userContext; diff --git a/Bugsnag/Payload/BugsnagEvent.m b/Bugsnag/Payload/BugsnagEvent.m index 4d809618b..e505f3cc7 100644 --- a/Bugsnag/Payload/BugsnagEvent.m +++ b/Bugsnag/Payload/BugsnagEvent.m @@ -379,6 +379,10 @@ - (instancetype)initWithKSCrashReport:(NSDictionary *)event { BugsnagConfiguration *config = [[BugsnagConfiguration alloc] initWithDictionaryRepresentation: [configDict isKindOfClass:[NSDictionary class]] ? configDict : @{}]; + NSDictionary *correlationDict = [event valueForKeyPath:@"user.correlation"]; + NSString *traceId = correlationDict[@"traceid"]; + NSString *spanId = correlationDict[@"spanid"]; + BugsnagAppWithState *app = [BugsnagAppWithState appWithDictionary:event config:config codeBundleId:self.codeBundleId]; BugsnagSession *session = BSGSessionFromCrashReport(event, app, device, user); @@ -402,6 +406,11 @@ - (instancetype)initWithKSCrashReport:(NSDictionary *)event { obj.customException = BSGParseCustomException(event, [errors[0].errorClass copy], [errors[0].errorMessage copy]); obj.depth = depth; obj.usage = [event valueForKeyPath:@"user._usage"]; + + if (traceId.length > 0 || spanId.length > 0) { + obj.correlation = [[BugsnagCorrelation alloc] initWithTraceId:traceId spanId:spanId]; + } + return obj; } diff --git a/Bugsnag/Payload/BugsnagSession+Private.h b/Bugsnag/Payload/BugsnagSession+Private.h index 4f5df709d..127d5b5c3 100644 --- a/Bugsnag/Payload/BugsnagSession+Private.h +++ b/Bugsnag/Payload/BugsnagSession+Private.h @@ -54,7 +54,7 @@ void BSGSessionUpdateRunContext(BugsnagSession *_Nullable session); BugsnagSession *_Nullable BSGSessionFromLastRunContext(BugsnagApp *app, BugsnagDevice *device, BugsnagUser *user); /// Saves current session information (from bsg_runContext) into a crash report. -void BSGSessionWriteCrashReport(const BSG_KSCrashReportWriter *writer); +void BSGSessionWriteCrashReport(const BSG_KSCrashReportWriter *writer, bool requiresAsyncSafety); /// Returns session information from a crash report previously written to by BSGSessionWriteCrashReport or BSSerializeDataCrashHandler. BugsnagSession *_Nullable BSGSessionFromCrashReport(NSDictionary *report, BugsnagApp *app, BugsnagDevice *device, BugsnagUser *user); diff --git a/Bugsnag/Payload/BugsnagSession.m b/Bugsnag/Payload/BugsnagSession.m index fbe329f06..0af76cdbe 100644 --- a/Bugsnag/Payload/BugsnagSession.m +++ b/Bugsnag/Payload/BugsnagSession.m @@ -132,7 +132,7 @@ void BSGSessionUpdateRunContext(BugsnagSession *_Nullable session) { } } -void BSGSessionWriteCrashReport(const BSG_KSCrashReportWriter *writer) { +void BSGSessionWriteCrashReport(const BSG_KSCrashReportWriter *writer, bool __unused requiresAsyncSafety) { if (bsg_runContext->sessionId[0] && bsg_runContext->sessionStartTime > 0) { writer->addStringElement(writer, "id", bsg_runContext->sessionId); writer->addFloatingPointElement(writer, "startedAt", bsg_runContext->sessionStartTime); diff --git a/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h b/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h index f3a9cbd59..a4ba115e4 100644 --- a/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h +++ b/Bugsnag/include/Bugsnag/BSG_KSCrashReportWriter.h @@ -229,7 +229,7 @@ typedef struct BSG_KSCrashReportWriter { } BSG_KSCrashReportWriter; typedef void (*BSG_KSReportWriteCallback)( - const BSG_KSCrashReportWriter *writer); + const BSG_KSCrashReportWriter *writer, bool requiresAsyncSafety); #ifdef __cplusplus } diff --git a/Tests/BugsnagTests/BugsnagBreadcrumbsTest.m b/Tests/BugsnagTests/BugsnagBreadcrumbsTest.m index 26edd15d2..951faa426 100644 --- a/Tests/BugsnagTests/BugsnagBreadcrumbsTest.m +++ b/Tests/BugsnagTests/BugsnagBreadcrumbsTest.m @@ -452,7 +452,7 @@ - (void)testCallbackFreeConstructors3 { - (void)testCrashReportWriter { NSDictionary *object = JSONObject(^(BSG_KSCrashReportWriter *writer) { writer->beginObject(writer, ""); - BugsnagBreadcrumbsWriteCrashReport(writer); + BugsnagBreadcrumbsWriteCrashReport(writer, true); writer->endContainer(writer); }); @@ -527,7 +527,7 @@ - (void)testCrashReportWriterConcurrency { bsg_kscrw_i_prepareReportWriter(&writer, &context); bsg_ksjsonbeginEncode(&context, false, (BSG_KSJSONAddDataFunc)json_buffer_append, &buffer); writer.beginObject(&writer, ""); - BugsnagBreadcrumbsWriteCrashReport(&writer); + BugsnagBreadcrumbsWriteCrashReport(&writer, true); writer.endContainer(&writer); NSError *error = nil; From ffdcc60fbb73b5409bc300ac243de0cddf4ffa83 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 16 May 2024 16:06:52 +0200 Subject: [PATCH 37/51] Use 'process' to copy the privacy manifest since that's what the example does --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 94231ab60..09875b1c3 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,7 @@ let package = Package( dependencies: [], path: "Bugsnag", resources: [ - .copy("resources/PrivacyInfo.xcprivacy") + .process("resources/PrivacyInfo.xcprivacy") ], publicHeadersPath: "include", cSettings: [ From ec636af8754bc518e04c50cb423dde08f405718a Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 23 May 2024 15:22:13 +0200 Subject: [PATCH 38/51] New method to background apps in e2e tests --- features/app_hangs.feature | 8 ++++---- features/breadcrumbs.feature | 5 +++-- .../fixtures/docs/background_for_0_sec.html | 9 +++++++++ .../fixtures/docs/background_for_1_sec.html | 9 +++++++++ .../fixtures/docs/background_for_2_sec.html | 9 +++++++++ .../fixtures/docs/background_for_3_sec.html | 9 +++++++++ .../fixtures/docs/background_for_4_sec.html | 9 +++++++++ .../fixtures/docs/background_for_5_sec.html | 9 +++++++++ .../fixtures/docs/background_forever.html | 8 ++++++++ .../fixtures/ios/iOSTestApp/AppDelegate.swift | 7 +++++++ features/fixtures/ios/iOSTestApp/Info.plist | 13 ++++++++++++ features/fixtures/shared/scenarios/Scenario.h | 6 ++++++ features/fixtures/shared/scenarios/Scenario.m | 20 +++++++++++++++++++ features/fixtures/shared/utils/Fixture.swift | 3 +++ .../fixtures/shared/utils/FixtureConfig.swift | 2 ++ features/steps/app_steps.rb | 8 ++++++++ features/support/env.rb | 1 + features/thermal_state.feature | 4 ++-- 18 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 features/fixtures/docs/background_for_0_sec.html create mode 100644 features/fixtures/docs/background_for_1_sec.html create mode 100644 features/fixtures/docs/background_for_2_sec.html create mode 100644 features/fixtures/docs/background_for_3_sec.html create mode 100644 features/fixtures/docs/background_for_4_sec.html create mode 100644 features/fixtures/docs/background_for_5_sec.html create mode 100644 features/fixtures/docs/background_forever.html diff --git a/features/app_hangs.feature b/features/app_hangs.feature index 6228118e1..aeb076b43 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -150,7 +150,7 @@ Feature: App hangs Scenario: Fatal app hangs should be reported if the app hangs before going to the background When I run "AppHangFatalOnlyScenario" And I wait for 10 seconds - And I send the app to the background + And I switch to the web browser And I kill and relaunch the app And I configure Bugsnag for "AppHangFatalOnlyScenario" And I wait to receive an error @@ -159,7 +159,7 @@ Feature: App hangs @skip_macos Scenario: Fatal app hangs should not be reported if they occur once the app is in the background When I run "AppHangDidEnterBackgroundScenario" - And I send the app to the background for 10 seconds + And I switch to the web browser for 10 seconds And I kill and relaunch the app And I configure Bugsnag for "AppHangDidEnterBackgroundScenario" Then I should receive no errors @@ -167,7 +167,7 @@ Feature: App hangs @skip_macos Scenario: App hangs should be reported if the app hangs after resuming from the background When I run "AppHangDidBecomeActiveScenario" - And I send the app to the background for 3 seconds + And I switch to the web browser for 3 seconds And I wait to receive an error And the exception "message" equals "The app's main thread failed to respond to an event within 2000 milliseconds" @@ -185,7 +185,7 @@ Feature: App hangs @skip_macos Scenario: Background app hangs should be reported if reportBackgroundAppHangs = true When I run "ReportBackgroundAppHangScenario" - And I send the app to the background + And I switch to the web browser And I wait to receive an error Then the exception "errorClass" equals "App Hang" And the exception "message" equals "The app's main thread failed to respond to an event within 1000 milliseconds" diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 4bb019716..ae483e0b8 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -49,7 +49,7 @@ Feature: Attaching a series of notable events leading up to errors And I wait to receive an error And I discard the oldest error # Now we know that the backgrounding will occur at an appropriate time - And I send the app to the background for 2 seconds + And I switch to the web browser for 2 seconds # This next error should have the notification breadcrumbs And I invoke "notify_error" And I wait to receive an error @@ -59,7 +59,8 @@ Feature: Attaching a series of notable events leading up to errors And the event has a "state" breadcrumb named "App Will Enter Foreground" And the event has a "state" breadcrumb named "Scene Entered Background" And the event has a "state" breadcrumb named "Scene Will Enter Foreground" - And the event has a "state" breadcrumb named "Scene Activated" + # UISceneDidActivateNotification doesn't seem to be sent on app foregrounding anymore + # And the event has a "state" breadcrumb named "Scene Activated" @watchos Scenario: Network breadcrumbs diff --git a/features/fixtures/docs/background_for_0_sec.html b/features/fixtures/docs/background_for_0_sec.html new file mode 100644 index 000000000..4e6dadc4f --- /dev/null +++ b/features/fixtures/docs/background_for_0_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app immediately...

+ + diff --git a/features/fixtures/docs/background_for_1_sec.html b/features/fixtures/docs/background_for_1_sec.html new file mode 100644 index 000000000..e3a028ec7 --- /dev/null +++ b/features/fixtures/docs/background_for_1_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app in 1 second...

+ + diff --git a/features/fixtures/docs/background_for_2_sec.html b/features/fixtures/docs/background_for_2_sec.html new file mode 100644 index 000000000..dfa8e1b85 --- /dev/null +++ b/features/fixtures/docs/background_for_2_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app in 2 seconds...

+ + diff --git a/features/fixtures/docs/background_for_3_sec.html b/features/fixtures/docs/background_for_3_sec.html new file mode 100644 index 000000000..fc1e568e3 --- /dev/null +++ b/features/fixtures/docs/background_for_3_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app in 3 seconds...

+ + diff --git a/features/fixtures/docs/background_for_4_sec.html b/features/fixtures/docs/background_for_4_sec.html new file mode 100644 index 000000000..0e9a55490 --- /dev/null +++ b/features/fixtures/docs/background_for_4_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app in 4 seconds...

+ + diff --git a/features/fixtures/docs/background_for_5_sec.html b/features/fixtures/docs/background_for_5_sec.html new file mode 100644 index 000000000..f9e9531f3 --- /dev/null +++ b/features/fixtures/docs/background_for_5_sec.html @@ -0,0 +1,9 @@ + + + + Mazerunner + + +

Going back to app in 5 seconds...

+ + diff --git a/features/fixtures/docs/background_forever.html b/features/fixtures/docs/background_forever.html new file mode 100644 index 000000000..ec31fd03a --- /dev/null +++ b/features/fixtures/docs/background_forever.html @@ -0,0 +1,8 @@ + + + Mazerunner + + +

Never going back to the app!

+ + diff --git a/features/fixtures/ios/iOSTestApp/AppDelegate.swift b/features/fixtures/ios/iOSTestApp/AppDelegate.swift index 8951b04d5..9095d46bc 100644 --- a/features/fixtures/ios/iOSTestApp/AppDelegate.swift +++ b/features/fixtures/ios/iOSTestApp/AppDelegate.swift @@ -10,4 +10,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { logInfo("==== IOS FIXTURE DID FINISH LAUNCHING ===="); return true } + + func application(_ application: UIApplication, + open url: URL, + options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { + logInfo("App has been opened via its registered URL"); + return true + } } diff --git a/features/fixtures/ios/iOSTestApp/Info.plist b/features/fixtures/ios/iOSTestApp/Info.plist index b24cc7ebe..bbc5cd4c8 100644 --- a/features/fixtures/ios/iOSTestApp/Info.plist +++ b/features/fixtures/ios/iOSTestApp/Info.plist @@ -16,6 +16,19 @@ APPL CFBundleShortVersionString 1.0.3 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.bugsnag.fixture + CFBundleURLSchemes + + fixture + + + CFBundleVersion 5 LSRequiresIPhoneOS diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index 8f7abc42a..5045fd21e 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -35,6 +35,12 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); - (void)didEnterBackgroundNotification; +/** + * Background the app for the specified number of seconds. + * If the value is < 0, background forever. + */ +- (void)enterBackgroundForSeconds:(NSInteger)seconds; + // Wait for the next event to be delivered, and then run a block on the main thread. - (void)waitForEventDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock; diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 3cddfb7b1..1be307083 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -97,6 +97,26 @@ - (void)startBugsnag { - (void)didEnterBackgroundNotification { } +- (void)enterBackgroundForSeconds:(NSInteger)seconds { +#if __has_include() + if (@available(iOS 10.0, *)) { + NSString *documentName = @"background_forever.html"; + if (seconds >= 0) { + documentName = [NSString stringWithFormat:@"background_for_%ld_sec.html", (long)seconds]; + } + NSURL *url = [self.fixtureConfig.docsURL URLByAppendingPathComponent:documentName]; + + logInfo(@"Backgrounding the app using %@", documentName); + [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { + NSLog(@"Opened %@ %@", url, success ? @"successfully" : @"unsuccessfully"); + }]; + } +#else + [NSException raise:@"Mazerunner fixture error" + format:@"This e2e test requires UIApplication, which is not available on this platform."]; +#endif +} + -(void)waitForEventDelivery:(dispatch_block_t)deliveryBlock andThen:(dispatch_block_t)thenBlock { _onEventDelivery = ^{ dispatch_async(dispatch_get_main_queue(), thenBlock); diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index d7712a437..a08621913 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -80,6 +80,9 @@ class Fixture: NSObject, CommandReceiver { case "reset_data": self.clearPersistentData() break + case "background": + self.currentScenario?.enterBackground(forSeconds: Int(command.args[0])!) + break case "noop": break default: diff --git a/features/fixtures/shared/utils/FixtureConfig.swift b/features/fixtures/shared/utils/FixtureConfig.swift index 75bfc690d..825c5d082 100644 --- a/features/fixtures/shared/utils/FixtureConfig.swift +++ b/features/fixtures/shared/utils/FixtureConfig.swift @@ -11,6 +11,7 @@ import Foundation @objcMembers class FixtureConfig: NSObject { var apiKey: String let mazeRunnerURL: URL + let docsURL: URL let tracesURL: URL let commandURL: URL let metricsURL: URL @@ -21,6 +22,7 @@ import Foundation init(apiKey: String, mazeRunnerBaseAddress: URL) { self.apiKey = apiKey mazeRunnerURL = mazeRunnerBaseAddress + docsURL = mazeRunnerBaseAddress.appendingPathComponent("docs") tracesURL = mazeRunnerBaseAddress.appendingPathComponent("traces") commandURL = mazeRunnerBaseAddress.appendingPathComponent("command") metricsURL = mazeRunnerBaseAddress.appendingPathComponent("metrics") diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 72247dc46..5c52295f8 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -28,6 +28,14 @@ relaunch_crashed_app end +When('I switch to the web browser for {int} second(s)') do |duration| + execute_command "background", [duration.to_s] +end + +When('I switch to the web browser') do + execute_command "background", ["-1"] +end + # # Setting scenario mode # diff --git a/features/support/env.rb b/features/support/env.rb index d075b1b81..a1a77655d 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -9,6 +9,7 @@ } Maze.config.receive_no_requests_wait = 15 + Maze.config.document_server_root = 'features/fixtures/docs' # Setup a 3 minute timeout for receiving requests is STRESS_TEST env var is set Maze.config.receive_requests_wait = 180 unless ENV['STRESS_TEST'].nil? diff --git a/features/thermal_state.feature b/features/thermal_state.feature index f215a901d..02f9ed422 100644 --- a/features/thermal_state.feature +++ b/features/thermal_state.feature @@ -36,13 +36,13 @@ Feature: Thermal State And the event "severityReason.unhandledOverridden" is null And the event "unhandled" is true - # The "I send the app to the background" step is not available on macOS + # The "I switch to the web browser" step is not available on macOS @skip_macos Scenario: Thermal Kills should not be reported if app was in the background When I run "CriticalThermalStateScenario" And I wait to receive a session And I discard the oldest session - And I send the app to the background + And I switch to the web browser And I wait for 1 seconds And I kill and relaunch the app And I configure Bugsnag for "CriticalThermalStateScenario" From 69c7a43f0f74dce9ac5e5d2f9e738480e7cfd742 Mon Sep 17 00:00:00 2001 From: robert-smartbear <126675445+robert-smartbear@users.noreply.github.com> Date: Wed, 29 May 2024 14:24:05 +0200 Subject: [PATCH 39/51] Added null checks before adding JSON elements to crash reports (#1655) Co-authored-by: Robert --- .../KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c index c4c333a11..55dc9491f 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSCrashReport.c @@ -221,6 +221,10 @@ void bsg_kscrw_i_addUUIDElement(const BSG_KSCrashReportWriter *const writer, void bsg_kscrw_i_addJSONElement(const BSG_KSCrashReportWriter *const writer, const char *const key, const char *const jsonElement) { + if (jsonElement == NULL) { + BSG_KSLOG_ERROR("JSON element for key %s is null", key); + return; + } int jsonResult = bsg_ksjsonaddJSONElement(bsg_getJsonContext(writer), key, jsonElement, strlen(jsonElement)); if (jsonResult != BSG_KSJSON_OK) { @@ -242,6 +246,10 @@ void bsg_kscrw_i_addJSONElement(const BSG_KSCrashReportWriter *const writer, void bsg_kscrw_i_addJSONElementFromFile( const BSG_KSCrashReportWriter *const writer, const char *const key, const char *const filePath) { + if (filePath == NULL) { + BSG_KSLOG_ERROR("JSON file path for key %s is null", key); + return; + } const int fd = open(filePath, O_RDONLY); if (fd < 0) { BSG_KSLOG_ERROR("Could not open file %s: %s", filePath, From d66b676cc32585ccffefaf8fee770ee34471edc8 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 30 May 2024 16:12:45 +0100 Subject: [PATCH 40/51] Fix error in e2e test run --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index a6eede362..cf8779be2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,7 @@ services: BITBAR_ACCESS_KEY: ports: - "9000-9499:9339" + - "9000-9499:9340" volumes: - ./features/fixtures/ios/output:/app/build - ./features/:/app/features/ From 4c0b3db5684e69b09cc999543f3225bbb81e6f21 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 4 Jun 2024 10:18:51 +0100 Subject: [PATCH 41/51] Temporarily skip iOS 17 testing due to device farm issues --- .buildkite/pipeline.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 51e18c257..28fb4b7d4 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -339,6 +339,8 @@ steps: # BrowserStack # - label: ':browserstack: iOS 17 E2E tests batch 1' + # TODO Skipped pending PLAT-12209 + skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 60 @@ -370,6 +372,8 @@ steps: limit: 2 - label: ':browserstack: iOS 17 E2E tests batch 2' + # TODO Skipped pending PLAT-12209 + skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 60 @@ -401,6 +405,8 @@ steps: # PLAT-11155: App hang tests run on BrowserStack (Appium 1.x) for now - label: ':browserstack: iOS 17 app hang tests' + # TODO Skipped pending PLAT-12209 + skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 30 From 2ad3f6d284b460c073161abbdfb213dea7f41db3 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Wed, 5 Jun 2024 10:50:58 +0100 Subject: [PATCH 42/51] Make test fixture bundle id all lower case --- .buildkite/pipeline.yml | 6 ------ features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 28fb4b7d4..51e18c257 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -339,8 +339,6 @@ steps: # BrowserStack # - label: ':browserstack: iOS 17 E2E tests batch 1' - # TODO Skipped pending PLAT-12209 - skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 60 @@ -372,8 +370,6 @@ steps: limit: 2 - label: ':browserstack: iOS 17 E2E tests batch 2' - # TODO Skipped pending PLAT-12209 - skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 60 @@ -405,8 +401,6 @@ steps: # PLAT-11155: App hang tests run on BrowserStack (Appium 1.x) for now - label: ':browserstack: iOS 17 app hang tests' - # TODO Skipped pending PLAT-12209 - skip: Skipped pending PLAT-12209 depends_on: - cocoa_fixture timeout_in_minutes: 30 diff --git a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj index 0333bae69..ff74fb394 100644 --- a/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj +++ b/features/fixtures/ios/iOSTestApp.xcodeproj/project.pbxproj @@ -1131,7 +1131,7 @@ LINKER_DISPLAYS_MANGLED_NAMES = NO; OTHER_SWIFT_FLAGS = "-Onone"; PRESERVE_DEAD_CODE_INITS_AND_TERMS = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.fixtures.iOSTestApp; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.fixtures.cocoa; PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = all; @@ -1174,7 +1174,7 @@ MTL_ENABLE_DEBUG_INFO = YES; OTHER_SWIFT_FLAGS = "-Onone"; PRESERVE_DEAD_CODE_INITS_AND_TERMS = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.fixtures.iOSTestApp; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.fixtures.cocoa; PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; STRIP_STYLE = all; From acab3a095e88c23c4e8545165b34c2c141d9cd0a Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Wed, 5 Jun 2024 13:03:27 +0100 Subject: [PATCH 43/51] Update expectations [full ci] --- features/app_and_device_attributes.feature | 2 +- features/app_hangs.feature | 2 +- features/barebone_tests.feature | 4 ++-- features/out_of_memory.feature | 2 +- features/scripts/foreground_ios_app.sh | 2 +- features/steps/app_steps.rb | 2 +- features/support/env.rb | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/features/app_and_device_attributes.feature b/features/app_and_device_attributes.feature index b317b352c..f732922e6 100644 --- a/features/app_and_device_attributes.feature +++ b/features/app_and_device_attributes.feature @@ -42,7 +42,7 @@ Feature: App and Device attributes present And the error payload field "events.0.app.bundleVersion" is not null #And the error payload field "events.0.app.dsymUUIDs" is a non-empty array # Fails, == nil And the error payload field "events.0.app.id" equals the platform-dependent string: - | ios | com.bugsnag.fixtures.iOSTestApp | + | ios | com.bugsnag.fixtures.cocoa | | macos | com.bugsnag.fixtures.macOSTestApp | And the error payload field "events.0.app.isLaunching" is true And the error payload field "events.0.app.releaseStage" equals "development" diff --git a/features/app_hangs.feature b/features/app_hangs.feature index aeb076b43..a335068e7 100644 --- a/features/app_hangs.feature +++ b/features/app_hangs.feature @@ -69,7 +69,7 @@ Feature: App hangs And the error payload field "events.0.app.bundleVersion" is not null #And the error payload field "events.0.app.dsymUUIDs" is a non-empty array # Fails, == nil And the error payload field "events.0.app.id" equals the platform-dependent string: - | ios | com.bugsnag.fixtures.iOSTestApp | + | ios | com.bugsnag.fixtures.cocoa | | macos | com.bugsnag.fixtures.macOSTestApp | And the error payload field "events.0.app.isLaunching" is true And the error payload field "events.0.app.releaseStage" equals "development" diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index b8547805b..d6664c37c 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -19,7 +19,7 @@ Feature: Barebone tests And the event "app.binaryArch" matches "(arm|x86)" And the event "app.bundleVersion" equals "12301" And the event "app.id" equals the platform-dependent string: - | ios | com.bugsnag.fixtures.iOSTestApp | + | ios | com.bugsnag.fixtures.cocoa | | macos | com.bugsnag.fixtures.macOSTestApp | | watchos | com.bugsnag.fixtures.watchOSTestApp.watchkitapp.watchkitextension | And the event "app.inForeground" is true @@ -271,7 +271,7 @@ Feature: Barebone tests And the event "app.bundleVersion" equals "321.123" And the event "app.dsymUUIDs" is not null And the event "app.id" equals the platform-dependent string: - | ios | com.bugsnag.fixtures.iOSTestApp | + | ios | com.bugsnag.fixtures.cocoa | | macos | com.bugsnag.fixtures.macOSTestApp | And the event "app.inForeground" is true And the event "app.isLaunching" is true diff --git a/features/out_of_memory.feature b/features/out_of_memory.feature index 206284891..f0ce2667c 100644 --- a/features/out_of_memory.feature +++ b/features/out_of_memory.feature @@ -38,7 +38,7 @@ Feature: Out of memory errors And the event "metaData.device.timezone" is not null And the event "metaData.device.simulator" is false And the event "metaData.device.wordSize" is not null - And the event "app.id" equals "com.bugsnag.fixtures.iOSTestApp" + And the event "app.id" equals "com.bugsnag.fixtures.cocoa" And the event "metaData.app.name" equals "iOSTestApp" And the event "app.inForeground" is true And the event "app.type" equals "iOS" diff --git a/features/scripts/foreground_ios_app.sh b/features/scripts/foreground_ios_app.sh index f817d1c2c..8daaa9ff5 100755 --- a/features/scripts/foreground_ios_app.sh +++ b/features/scripts/foreground_ios_app.sh @@ -2,7 +2,7 @@ # Bring a previously running app to the foreground -xcrun simctl launch "$iOS_Simulator" com.bugsnag.fixtures.iOSTestApp \ +xcrun simctl launch "$iOS_Simulator" com.bugsnag.fixtures.cocoa \ "EVENT_TYPE=AutoCaptureRunScenario" \ "BUGSNAG_API_KEY=$BUGSNAG_API_KEY" \ "MOCK_API_PATH=http://localhost:$MOCK_API_PORT" diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 5c52295f8..44ea21cba 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -57,7 +57,7 @@ wait_for_true('the app is not running') do case Maze::Helper.get_current_platform when 'ios' - Maze.driver.app_state('com.bugsnag.fixtures.iOSTestApp') == :not_running + Maze.driver.app_state('com.bugsnag.fixtures.cocoa') == :not_running when 'macos' `lsappinfo info -only pid -app com.bugsnag.fixtures.macOSTestApp`.empty? else diff --git a/features/support/env.rb b/features/support/env.rb index a1a77655d..1fe0f7282 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -16,7 +16,7 @@ if Maze.config.os == 'ios' && Maze.config.farm == :local # Recent Appium versions don't always uninstall the old version of the app ¯\_(ツ)_/¯ - system('ideviceinstaller --uninstall com.bugsnag.fixtures.iOSTestApp') + system('ideviceinstaller --uninstall com.bugsnag.fixtures.cocoa') end if Maze.config.os == 'ios' @@ -159,7 +159,7 @@ def skip_between(os, version_lo, version_hi) end when 'ios' begin - data = Maze.driver.pull_file '@com.bugsnag.fixtures.iOSTestApp/Documents/kscrash.log' + data = Maze.driver.pull_file '@com.bugsnag.fixtures.cocoa/Documents/kscrash.log' File.open(File.join(path, 'kscrash.log'), 'wb') { |file| file << data } rescue StandardError puts "Maze.driver.pull_file failed: #{$ERROR_INFO}" From aa89547aceb1c4ecc9bc8d1ce90b74918df5b69f Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Tue, 11 Jun 2024 16:21:57 +0100 Subject: [PATCH 44/51] Fix test flake for foregrounding breadcrumb --- features/breadcrumbs.feature | 2 ++ features/fixtures/shared/utils/Fixture.swift | 14 ++++++++++++++ features/steps/app_steps.rb | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index ae483e0b8..079fe8995 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -50,6 +50,8 @@ Feature: Attaching a series of notable events leading up to errors And I discard the oldest error # Now we know that the backgrounding will occur at an appropriate time And I switch to the web browser for 2 seconds + # Give iOS sufficient time to raise the notification for foregrounding the app + And I make the test fixture wait for 1 second # This next error should have the notification breadcrumbs And I invoke "notify_error" And I wait to receive an error diff --git a/features/fixtures/shared/utils/Fixture.swift b/features/fixtures/shared/utils/Fixture.swift index a08621913..c694479a8 100644 --- a/features/fixtures/shared/utils/Fixture.swift +++ b/features/fixtures/shared/utils/Fixture.swift @@ -83,6 +83,10 @@ class Fixture: NSObject, CommandReceiver { case "background": self.currentScenario?.enterBackground(forSeconds: Int(command.args[0])!) break + case "wait": + self.pauseCommandReader(forSeconds: Int(command.args[0])!) + isReady = false; + break case "noop": break default: @@ -118,6 +122,16 @@ class Fixture: NSObject, CommandReceiver { } } + func pauseCommandReader(forSeconds: Int) { + logInfo("Pausing command reader for \(forSeconds) seconds") + + self.readyToReceiveCommand = false + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(forSeconds)) { + self.readyToReceiveCommand = true + logInfo("Resuming command reader") + } + } + @objc func setApiKey(apiKey: String) { self.fixtureConfig.apiKey = apiKey } diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 44ea21cba..d51925d53 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -32,6 +32,10 @@ execute_command "background", [duration.to_s] end +When('I make the test fixture wait for {int} second(s)') do |duration| + execute_command "wait", [duration.to_s] +end + When('I switch to the web browser') do execute_command "background", ["-1"] end From ce15de648de3eaf87375f29382dfb90cd75c2d38 Mon Sep 17 00:00:00 2001 From: Ben Cherry Date: Fri, 2 Feb 2024 15:41:26 -0800 Subject: [PATCH 45/51] Add support for visionOS --- Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m | 2 +- Bugsnag/Helpers/BSGDefines.h | 8 ++++---- Bugsnag/Helpers/BSGRunContext.m | 8 ++++---- .../KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m | 2 ++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m b/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m index 962af8772..8ff03b81b 100644 --- a/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m +++ b/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m @@ -318,7 +318,7 @@ - (BOOL)tryAddSceneNotification:(NSNotification *)notification { #endif - (BOOL)tryAddWindowNotification:(NSNotification *)notification { -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION if ([notification.name hasPrefix:@"UIWindow"] && [notification.object isKindOfClass:UIWINDOW]) { UIWindow *window = notification.object; NSMutableDictionary *metadata = [NSMutableDictionary dictionary]; diff --git a/Bugsnag/Helpers/BSGDefines.h b/Bugsnag/Helpers/BSGDefines.h index 25ac28bf2..4b8a29c95 100644 --- a/Bugsnag/Helpers/BSGDefines.h +++ b/Bugsnag/Helpers/BSGDefines.h @@ -13,15 +13,15 @@ // Capabilities dependent upon system defines and files #define BSG_HAVE_BATTERY ( TARGET_OS_IOS || TARGET_OS_WATCH) #define BSG_HAVE_MACH_EXCEPTIONS (TARGET_OS_OSX || TARGET_OS_IOS ) -#define BSG_HAVE_MACH_THREADS (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV ) +#define BSG_HAVE_MACH_THREADS (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_OOM_DETECTION ( TARGET_OS_IOS || TARGET_OS_TV ) && !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST -#define BSG_HAVE_REACHABILITY (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV ) +#define BSG_HAVE_REACHABILITY (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_REACHABILITY_WWAN ( TARGET_OS_IOS || TARGET_OS_TV ) -#define BSG_HAVE_SIGNAL (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV ) +#define BSG_HAVE_SIGNAL (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_SIGALTSTACK (TARGET_OS_OSX || TARGET_OS_IOS ) #define BSG_HAVE_SYSCALL (TARGET_OS_IOS || TARGET_OS_TV ) #define BSG_HAVE_UIDEVICE __has_include() -#define BSG_HAVE_WINDOW (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV ) +#define BSG_HAVE_WINDOW (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) // Capabilities dependent upon previously defined capabilities #define BSG_HAVE_APP_HANG_DETECTION (BSG_HAVE_MACH_THREADS) diff --git a/Bugsnag/Helpers/BSGRunContext.m b/Bugsnag/Helpers/BSGRunContext.m index 974958b19..f0c045b5c 100644 --- a/Bugsnag/Helpers/BSGRunContext.m +++ b/Bugsnag/Helpers/BSGRunContext.m @@ -40,7 +40,7 @@ static uint64_t GetBootTime(void); static bool GetIsActive(void); static bool GetIsForeground(void); -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION static UIApplication * GetUIApplication(void); #endif static void InstallTimer(void); @@ -112,7 +112,7 @@ static bool GetIsActive(void) { return GetIsForeground(); #endif -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION UIApplication *app = GetUIApplication(); return app && app.applicationState == UIApplicationStateActive; #endif @@ -160,7 +160,7 @@ static bool GetIsForeground(void) { } #endif -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION UIApplication *application = GetUIApplication(); // There will be no UIApplication if UIApplicationMain() has not yet been @@ -195,7 +195,7 @@ static bool GetIsForeground(void) { #endif } -#if TARGET_OS_IOS || TARGET_OS_TV +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION static UIApplication * GetUIApplication(void) { // +sharedApplication is unavailable to app extensions diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index b4cb8f570..913435240 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -319,6 +319,8 @@ + (NSDictionary *)buildSystemInfoStatic { NSString *systemName = @"tvOS"; #elif TARGET_OS_WATCH NSString *systemName = @"watchOS"; +#elif TARGET_OS_VISION + NSString *systemName = @"visionOS"; #endif sysInfo[@BSG_KSSystemField_SystemName] = systemName; From b25b3e5e06332a85965e730dc4709d9826810d36 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 12 Jun 2024 10:20:00 +0200 Subject: [PATCH 46/51] Source code level visionOS support --- Bugsnag/Helpers/BSGDefines.h | 10 +++++----- Bugsnag/Helpers/BSGHardware.h | 6 +++--- Bugsnag/Helpers/BSGRunContext.m | 8 ++++++++ .../Source/KSCrash/Recording/BSG_KSSystemInfo.m | 2 ++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Bugsnag/Helpers/BSGDefines.h b/Bugsnag/Helpers/BSGDefines.h index 4b8a29c95..a8a0fb1be 100644 --- a/Bugsnag/Helpers/BSGDefines.h +++ b/Bugsnag/Helpers/BSGDefines.h @@ -11,17 +11,17 @@ #include // Capabilities dependent upon system defines and files -#define BSG_HAVE_BATTERY ( TARGET_OS_IOS || TARGET_OS_WATCH) +#define BSG_HAVE_BATTERY ( TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_VISION) #define BSG_HAVE_MACH_EXCEPTIONS (TARGET_OS_OSX || TARGET_OS_IOS ) -#define BSG_HAVE_MACH_THREADS (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) +#define BSG_HAVE_MACH_THREADS (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_OOM_DETECTION ( TARGET_OS_IOS || TARGET_OS_TV ) && !TARGET_OS_SIMULATOR && !TARGET_OS_MACCATALYST -#define BSG_HAVE_REACHABILITY (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) +#define BSG_HAVE_REACHABILITY (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_REACHABILITY_WWAN ( TARGET_OS_IOS || TARGET_OS_TV ) -#define BSG_HAVE_SIGNAL (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) +#define BSG_HAVE_SIGNAL (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) #define BSG_HAVE_SIGALTSTACK (TARGET_OS_OSX || TARGET_OS_IOS ) #define BSG_HAVE_SYSCALL (TARGET_OS_IOS || TARGET_OS_TV ) #define BSG_HAVE_UIDEVICE __has_include() -#define BSG_HAVE_WINDOW (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) +#define BSG_HAVE_WINDOW (TARGET_OS_OSX || TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION) // Capabilities dependent upon previously defined capabilities #define BSG_HAVE_APP_HANG_DETECTION (BSG_HAVE_MACH_THREADS) diff --git a/Bugsnag/Helpers/BSGHardware.h b/Bugsnag/Helpers/BSGHardware.h index ce3c5b95a..6424e6b84 100644 --- a/Bugsnag/Helpers/BSGHardware.h +++ b/Bugsnag/Helpers/BSGHardware.h @@ -17,7 +17,7 @@ #pragma mark Device -#if TARGET_OS_IOS +#if TARGET_OS_IOS || TARGET_OS_VISION static inline UIDevice *BSGGetDevice(void) { return [UIDEVICE currentDevice]; } @@ -32,7 +32,7 @@ static inline WKInterfaceDevice *BSGGetDevice(void) { #if BSG_HAVE_BATTERY static inline BOOL BSGIsBatteryStateKnown(long battery_state) { -#if TARGET_OS_IOS +#if TARGET_OS_IOS || TARGET_OS_VISION const long state_unknown = UIDeviceBatteryStateUnknown; #elif TARGET_OS_WATCH const long state_unknown = WKInterfaceDeviceBatteryStateUnknown; @@ -41,7 +41,7 @@ static inline BOOL BSGIsBatteryStateKnown(long battery_state) { } static inline BOOL BSGIsBatteryCharging(long battery_state) { -#if TARGET_OS_IOS +#if TARGET_OS_IOS || TARGET_OS_VISION const long state_charging = UIDeviceBatteryStateCharging; #elif TARGET_OS_WATCH const long state_charging = WKInterfaceDeviceBatteryStateCharging; diff --git a/Bugsnag/Helpers/BSGRunContext.m b/Bugsnag/Helpers/BSGRunContext.m index f0c045b5c..58015b685 100644 --- a/Bugsnag/Helpers/BSGRunContext.m +++ b/Bugsnag/Helpers/BSGRunContext.m @@ -128,6 +128,10 @@ static bool GetIsActive(void) { return true; } #endif + +#if TARGET_OS_VISION + return true; +#endif } static bool GetIsForeground(void) { @@ -193,6 +197,10 @@ static bool GetIsForeground(void) { return true; } #endif + +#if TARGET_OS_VISION + return true; +#endif } #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_VISION diff --git a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m index 913435240..2d9ba25ab 100644 --- a/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m +++ b/Bugsnag/KSCrash/Source/KSCrash/Recording/BSG_KSSystemInfo.m @@ -289,6 +289,8 @@ + (NSDictionary *)buildSystemInfoStatic { sysInfo[@BSG_KSSystemField_SystemName] = @"tvOS"; #elif TARGET_OS_WATCH sysInfo[@BSG_KSSystemField_SystemName] = @"watchOS"; +#elif TARGET_OS_VISION + sysInfo[@BSG_KSSystemField_SystemName] = @"visionOS"; #endif // TARGET_OS_IOS NSDictionary *env = NSProcessInfo.processInfo.environment; From a516f3d1717e2c868e895fcfd04734d1d792d4f4 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Wed, 12 Jun 2024 12:07:01 +0200 Subject: [PATCH 47/51] Add default define for older xcode versions --- Bugsnag/Helpers/BSGDefines.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Bugsnag/Helpers/BSGDefines.h b/Bugsnag/Helpers/BSGDefines.h index a8a0fb1be..729d21bb3 100644 --- a/Bugsnag/Helpers/BSGDefines.h +++ b/Bugsnag/Helpers/BSGDefines.h @@ -10,6 +10,11 @@ #include +#ifndef TARGET_OS_VISION + // For older Xcode that doesn't have VisionOS support... + #define TARGET_OS_VISION 0 +#endif + // Capabilities dependent upon system defines and files #define BSG_HAVE_BATTERY ( TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_VISION) #define BSG_HAVE_MACH_EXCEPTIONS (TARGET_OS_OSX || TARGET_OS_IOS ) From c1a4f5f418ee04d8a2b35909f2d9d0af1b1e050b Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Thu, 13 Jun 2024 15:15:11 +0200 Subject: [PATCH 48/51] Do everything to dissuade the optimizer from inlining our stack trace gathering paths so that we can prune entries with confidence. --- Bugsnag/Client/BugsnagClient.m | 22 ++++++++++++++++------ CHANGELOG.md | 3 +++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Bugsnag/Client/BugsnagClient.m b/Bugsnag/Client/BugsnagClient.m index 386b1867e..73215e52c 100644 --- a/Bugsnag/Client/BugsnagClient.m +++ b/Bugsnag/Client/BugsnagClient.m @@ -641,28 +641,37 @@ - (NSString *)context { // MARK: - Notify +// Prevent the compiler from inlining or optimizing, which would reduce +// the number of bugsnag-only stack entries and mess up our pruning. +// We have to do it this way because you can't mark Objective-C methods noinline or optnone. +// We leave it externable to further dissuade the optimizer. +__attribute__((optnone)) +void bsg_notifyErrorOrException(BugsnagClient *self, id errorOrException, BugsnagOnErrorBlock block) { + [self notifyErrorOrException:errorOrException block:block]; +} + // note - some duplication between notifyError calls is required to ensure // the same number of stackframes are used for each call. // see notify:handledState:block for further info - (void)notifyError:(NSError *)error { bsg_log_debug(@"%s %@", __PRETTY_FUNCTION__, error); - [self notifyErrorOrException:error block:nil]; + bsg_notifyErrorOrException(self, error, nil); } - (void)notifyError:(NSError *)error block:(BugsnagOnErrorBlock)block { bsg_log_debug(@"%s %@", __PRETTY_FUNCTION__, error); - [self notifyErrorOrException:error block:block]; + bsg_notifyErrorOrException(self, error, block); } - (void)notify:(NSException *)exception { bsg_log_debug(@"%s %@", __PRETTY_FUNCTION__, exception); - [self notifyErrorOrException:exception block:nil]; + bsg_notifyErrorOrException(self, exception, nil); } - (void)notify:(NSException *)exception block:(BugsnagOnErrorBlock)block { bsg_log_debug(@"%s %@", __PRETTY_FUNCTION__, exception); - [self notifyErrorOrException:exception block:block]; + bsg_notifyErrorOrException(self, exception, block); } // MARK: - Notify (Internal) @@ -731,9 +740,10 @@ - (void)notifyErrorOrException:(id)errorOrException block:(BugsnagOnErrorBlock)b * * 1. +[Bugsnag notifyError:block:] * 2. -[BugsnagClient notifyError:block:] - * 3. -[BugsnagClient notify:handledState:block:] + * 3. bsg_notifyErrorOrException() + * 4. -[BugsnagClient notifyErrorOrException:block:] */ - NSUInteger depth = 3; + NSUInteger depth = 4; if (!callStack.count) { // If the NSException was not raised by the Objective-C runtime, it will be missing a call stack. diff --git a/CHANGELOG.md b/CHANGELOG.md index cb65bc1d6..04c6a8950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Changelog ### Bug fixes +* Prevent inlining of Bugsnag stack trace entries that are marked to be pruned away (to promote a consistent number of those frames). + [1661](https://github.com/bugsnag/bugsnag-cocoa/pull/1661) + * Fix off-by-1 error when fetching register values on arm64 that could potentially run off the array. [1635](https://github.com/bugsnag/bugsnag-cocoa/pull/1635) From 39070f7e52647b6de263ceecade0d1c912cb9b26 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 13 Jun 2024 15:32:27 +0100 Subject: [PATCH 49/51] Wait longer for the macOS test fixture to stop running --- features/steps/app_steps.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index d51925d53..9c7a6d26c 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -62,8 +62,6 @@ case Maze::Helper.get_current_platform when 'ios' Maze.driver.app_state('com.bugsnag.fixtures.cocoa') == :not_running - when 'macos' - `lsappinfo info -only pid -app com.bugsnag.fixtures.macOSTestApp`.empty? else raise "Don't know how to query app state on this platform" end @@ -119,8 +117,7 @@ def relaunch_crashed_app step 'the app is not running' Maze.driver.launch_app when 'macos' - # Wait for the app to stop running before relaunching - step 'the app is not running' + sleep 4 launch_app when 'watchos' sleep 5 # We have no way to poll the app state on watchOS From 608fe4737bb3e68b9a905991cf6b9eacd3deda65 Mon Sep 17 00:00:00 2001 From: Karl Stenerud Date: Tue, 18 Jun 2024 14:45:55 +0200 Subject: [PATCH 50/51] Support building an all-architecture xcframework from the makefile --- Makefile | 3 + scripts/build-xcframework.sh | 121 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100755 scripts/build-xcframework.sh diff --git a/Makefile b/Makefile index 9616fa7ad..1413b0e7e 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,9 @@ build_ios_static: ## Build the static library target build_carthage: ## Build the latest pushed commit with Carthage @./scripts/build-carthage.sh +build_xcframework: ## Build as a multiplatform xcframework + @./scripts/build-xcframework.sh + build_swift: ## Build with Swift Package Manager @swift build diff --git a/scripts/build-xcframework.sh b/scripts/build-xcframework.sh new file mode 100755 index 000000000..06e233567 --- /dev/null +++ b/scripts/build-xcframework.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash +# +# Written by: Karl Stenerud +# +# Script to build all platforms, architectures, and simulators into a single xcframework per target. +# +# See https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle +# + +set -e -u -o pipefail + + + +# ------------------------------------------------------------------- +# Configuration +# ------------------------------------------------------------------- + +# Base name of the .xcodeproj directory +PROJECT_NAME=Bugsnag + +# Targets that are suffixed with the platform (e.g. XYZ-iOS, XYZ-macOS etc) +NAMESPACED_TARGETS=( "Bugsnag" ) + +# Targets that are not namespaced +GENERIC_TARGETS=( ) + +# Platforms we are building for: iOS, macOS, tvOS, watchOS (in future: visionOS) +PLATFORMS=( "iOS" "macOS" "tvOS" "watchOS" ) + + + +# ------------------------------------------------------------------- +# Script +# ------------------------------------------------------------------- + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PROJECT_DIR=$( cd -- "$( dirname -- "${SCRIPT_DIR}" )" &> /dev/null && pwd ) +XCFW_DIR="${PROJECT_DIR}/build/xcframeworks" +INTERMEDIATES_DIR="${XCFW_DIR}/intermediates" +PRODUCTS_DIR="${XCFW_DIR}/products" + +rm -rf "${XCFW_DIR}" +mkdir -p "${INTERMEDIATES_DIR}" +mkdir -p "${PRODUCTS_DIR}" + +## +# Build a target. This function tries to sort out whether the target is namespaced or not +# (i.e. if it's named XYZ or XYZ-iOS) so that it produces a consistently named output file +# (such as XYZ-iOS.framework, XYZ-iOS-simulator.framework). +# +# Args: +# 1: Name of compilation target +# 2: Name of platform to build for (iOS, macOS, etc) +# +# Return: A set of commandline argumets for the final framework build. +## +build_target() { + target=$1 + platform=$2 + + framework_name=${target} + if [[ ${framework_name} == *${platform} ]]; then + # If our target is named "XYZ-iOS", remove the "-iOS" part from the framework name + framework_name="${framework_name%-${platform}}" + fi + + destination="generic/platform=${platform}" + archive_path="${INTERMEDIATES_DIR}/${target}" + if [[ ${archive_path} != *${platform} ]]; then + # Make sure our archive path is namespaced with the platform + archive_path="${archive_path}-${platform}" + fi + # Note: Redirecting xcodebuild's stdout to stderr because we need stdout for ourselves. + xcodebuild archive -project ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj \ + -scheme ${target} \ + -configuration Release \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + -destination "${destination}" \ + -archivePath "${archive_path}" 1>&2 + echo "-archive ${archive_path}.xcarchive -framework ${framework_name}.framework" + + if [ "${platform}" != "macOS" ]; then + # For non-macos targrets, build for the simulator as well. + destination="${destination} Simulator" + archive_path="${archive_path}-simulator" + xcodebuild archive -project ${PROJECT_DIR}/${PROJECT_NAME}.xcodeproj \ + -scheme ${target} \ + -configuration Release \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + -destination "${destination}" \ + -archivePath "${archive_path}" 1>&2 + echo "-archive ${archive_path}.xcarchive -framework ${framework_name}.framework" + fi +} + +if [[ ${#NAMESPACED_TARGETS[@]} -gt 0 ]]; then + for target in "${NAMESPACED_TARGETS[@]}"; do + xcframework_args="" + for platform in "${PLATFORMS[@]}"; do + new_args=$( build_target ${target}-${platform} ${platform} ) + xcframework_args="${xcframework_args} ${new_args}" + done + xcodebuild -create-xcframework ${xcframework_args} -output "${PRODUCTS_DIR}/${target}.xcframework" + done +fi + +if [[ ${#GENERIC_TARGETS[@]} -gt 0 ]]; then + for target in "${GENERIC_TARGETS[@]}"; do + xcframework_args="" + for platform in "${PLATFORMS[@]}"; do + new_args=$( build_target ${target} ${platform} ) + xcframework_args="${xcframework_args} ${new_args}" + done + xcodebuild -create-xcframework ${xcframework_args} -output "${PRODUCTS_DIR}/${target}.xcframework" + done +fi + +echo +echo "** build-frameworks.sh: script completed successfully **" From 565fc31c47259aff973b3e25127467b9981cbd04 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 19 Jun 2024 15:05:10 +0200 Subject: [PATCH 51/51] Release v6.29.0 --- .jazzy.yaml | 4 ++-- Bugsnag.podspec.json | 4 ++-- Bugsnag/Payload/BugsnagNotifier.m | 2 +- BugsnagNetworkRequestPlugin.podspec.json | 6 +++--- CHANGELOG.md | 7 ++++++- Framework/Info.plist | 2 +- Tests/BugsnagTests/Info.plist | 2 +- Tests/TestHost-iOS/Info.plist | 2 +- VERSION | 2 +- 9 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.jazzy.yaml b/.jazzy.yaml index 8eb2be6d3..c1cab8809 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.28.1/Bugsnag" +github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.29.0/Bugsnag" github_url: "https://github.com/bugsnag/bugsnag-cocoa" hide_documentation_coverage: true module: "Bugsnag" -module_version: "6.28.1" +module_version: "6.29.0" objc: true output: "docs" readme: "README.md" diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index 17392ef0f..2e5354142 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.28.1", + "version": "6.29.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.28.1" + "tag": "v6.29.0" }, "ios": { "frameworks": [ diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index 1ba1edb0e..6e84d27d0 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.28.1"; + _version = @"6.29.0"; _url = @"https://github.com/bugsnag/bugsnag-cocoa"; _dependencies = @[]; } diff --git a/BugsnagNetworkRequestPlugin.podspec.json b/BugsnagNetworkRequestPlugin.podspec.json index 53d3b74b6..c8038afd7 100644 --- a/BugsnagNetworkRequestPlugin.podspec.json +++ b/BugsnagNetworkRequestPlugin.podspec.json @@ -1,16 +1,16 @@ { "name": "BugsnagNetworkRequestPlugin", - "version": "6.28.1", + "version": "6.29.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.28.1/BugsnagNetworkRequestPlugin/README.md", + "readme": "https://raw.githubusercontent.com/bugsnag/bugsnag-cocoa/v6.29.0/BugsnagNetworkRequestPlugin/README.md", "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.28.1" + "tag": "v6.29.0" }, "dependencies": { "Bugsnag": "~> 6.13" diff --git a/CHANGELOG.md b/CHANGELOG.md index 04c6a8950..ea381eaab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ Changelog ========= -## TBD +## 6.29.0 (2024-06-19) + +### Enhancements + +* Adds basic support for visionOS with SPM projects + [1660](https://github.com/bugsnag/bugsnag-cocoa/pull/1660) ### Bug fixes diff --git a/Framework/Info.plist b/Framework/Info.plist index 7809220f0..2073f3c90 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.28.1 + 6.29.0 CFBundleVersion 1 diff --git a/Tests/BugsnagTests/Info.plist b/Tests/BugsnagTests/Info.plist index 762469f87..8f7f2256b 100644 --- a/Tests/BugsnagTests/Info.plist +++ b/Tests/BugsnagTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.28.1 + 6.29.0 CFBundleVersion 1 diff --git a/Tests/TestHost-iOS/Info.plist b/Tests/TestHost-iOS/Info.plist index bc6b3f582..9c8ac151c 100644 --- a/Tests/TestHost-iOS/Info.plist +++ b/Tests/TestHost-iOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.28.1 + 6.29.0 CFBundleVersion 1 LSRequiresIPhoneOS diff --git a/VERSION b/VERSION index 8993da977..94ae9e992 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.28.1 +6.29.0