diff --git a/Makefile b/Makefile index 0c4720bf2..047587294 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,23 @@ e2e_macos: ./features/scripts/export_mac_app.sh bundle exec maze-runner --app=macOSTestApp --os=macOS --os-version=11 $(FEATURES) +.PHONY: e2e_watchos +e2e_watchos: features/fixtures/watchos/Podfile.lock features/fixtures/shared/scenarios/watchos_maze_host.h + open --background features/fixtures/watchos/watchOSTestApp.xcworkspace +ifneq ($(FEATURES),) + bundle exec maze-runner --os=watchos $(FEATURES) +else + bundle exec maze-runner --os=watchos --tags @watchos +endif + +features/fixtures/watchos/Podfile.lock: features/fixtures/watchos/Podfile + cd features/fixtures/watchos && pod install + +.PHONY: features/fixtures/shared/scenarios/watchos_maze_host.h +features/fixtures/shared/scenarios/watchos_maze_host.h: + printf '#define WATCHOS_MAZE_HOST ' > $@ + ruby -r socket -e 'p Socket.ip_address_list.select{ |a| a.ipv4_private? }[0].ip_address' >> $@ + #-------------------------------------------------------------------------- # Release # diff --git a/TESTING.md b/TESTING.md index f49f34428..43b3b5b2c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -92,9 +92,45 @@ Build the test iOS fixture: ``` `` is the device Identifier found under Devices and Simulators in Xcode. +### Running tests on Apple Watch + +#### Prerequisites + +1. The `xcdebug` command line tool - included in Xcode 13.4 and later. + +2. An Apple Watch paired to an iPhone that is connected via USB and visible in Xcode's devices window. + +3. The Apple Watch must be on the same WiFi network as the computer executing Maze runner. + +#### Running tests + +Due to device and tooling constraints, not all tests are suitable for running on Apple Watch. + +Tests that support Apple Watch are [tagged](https://cucumber.io/docs/cucumber/api/#tags) with `@watchos`. + +1. To run all supported tests: + ```shell script + make e2e_watchos + ``` + +2. To run an individual test: + ```shell script + make e2e_watchos FEATURES=features/breadcrumbs.feature:59 + ``` + +#### Troubleshooting + +E2E testing for Apple Watch relies on the `xcdebug` to build and run the test fixture. + +`xcdebug` triggers actions in the Xcode IDE, but does not provide feedback about the success of those actions, so Maze runner will not be able to detect failures. + +Pay attention to Xcode's UI to see if there are any build or run failures. + +Ensure Xcode's run destination is set to the Apple Watch. + ### Notes 1. Maze Runner supports various other options, as well as all those that Cucumber does. For full details run: ```shell script - `bundle exec maze-runner --help` + bundle exec maze-runner --help ``` diff --git a/features/barebone_tests.feature b/features/barebone_tests.feature index 57ebfaa01..016f43fda 100644 --- a/features/barebone_tests.feature +++ b/features/barebone_tests.feature @@ -3,6 +3,7 @@ Feature: Barebone tests Background: Given I clear all persistent data + @watchos Scenario: Barebone test: handled errors When I run "BareboneTestHandledScenario" And I wait to receive a session @@ -18,14 +19,16 @@ 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.iOSTestApp | - | macos | com.bugsnag.macOSTestApp | + | ios | com.bugsnag.iOSTestApp | + | macos | com.bugsnag.macOSTestApp | + | watchos | com.bugsnag.watchOSTestApp.watchkitapp.watchkitextension | And the event "app.inForeground" is true And the event "app.isLaunching" is true And the event "app.releaseStage" equals "development" And the event "app.type" equals the platform-dependent string: - | ios | iOS | - | macos | macOS | + | ios | iOS | + | macos | macOS | + | watchos | watchOS | And the event "app.version" equals "12.3" And the event "breadcrumbs.0.name" equals "Running BareboneTestHandledScenario" And the event "breadcrumbs.1.name" equals "This is super " @@ -35,18 +38,20 @@ Feature: Barebone tests And the event "device.locale" is not null And the event "device.manufacturer" equals "Apple" And the event "device.modelNumber" equals the platform-dependent string: - | ios | @not_null | - | macos | @null | + | ios | @not_null | + | macos | @null | + | watchos | @not_null | And on iOS, the event "device.orientation" matches "(face(down|up)|landscape(left|right)|portrait(upsidedown)?)" And the event "device.osName" equals the platform-dependent string: - | ios | iOS | - | macos | Mac OS | + | ios | iOS | + | macos | Mac OS | + | watchos | watchOS | And the event "device.osVersion" matches "\d+\.\d+" And the event "device.runtimeVersions.clangVersion" is not null And the event "device.runtimeVersions.osBuild" is not null And the event "device.time" is a timestamp - And on iOS, the event "metaData.device.batteryLevel" is a number - And on iOS, the event "metaData.device.charging" is a boolean + And on !macOS, the event "metaData.device.batteryLevel" is a number + And on !macOS, the event "metaData.device.charging" is a boolean And the event "metaData.device.simulator" is false And the event "metaData.device.timezone" is not null And the event "metaData.device.wordSize" is not null @@ -114,6 +119,7 @@ Feature: Barebone tests And the "method" of stack frame 0 matches "BareboneTestHandledScenario" And the stacktrace is valid for the event + @watchos Scenario: Barebone test: unhandled error When I run "BareboneTestUnhandledErrorScenario" and relaunch the crashed app And I set the app to "report" mode @@ -127,8 +133,9 @@ Feature: Barebone tests And the event "app.isLaunching" is true And the event "app.releaseStage" equals "development" And the event "app.type" equals the platform-dependent string: - | ios | iOS | - | macos | macOS | + | ios | iOS | + | macos | macOS | + | watchos | watchOS | And the event "app.version" equals "12.3" And the event "breadcrumbs.0.name" equals "Bugsnag loaded" And the event "breadcrumbs.1.name" is null @@ -140,18 +147,16 @@ Feature: Barebone tests And the event "device.manufacturer" equals "Apple" And on iOS, the event "device.orientation" matches "(face(down|up)|landscape(left|right)|portrait(upsidedown)?)" And the event "device.osName" equals the platform-dependent string: - | ios | iOS | - | macos | Mac OS | + | ios | iOS | + | macos | Mac OS | + | watchos | watchOS | And the event "device.osVersion" matches "\d+\.\d+" And the event "device.runtimeVersions.clangVersion" is not null And the event "device.runtimeVersions.osBuild" is not null And the event "device.time" is a timestamp - And on iOS, the event "metaData.device.batteryLevel" is a number - And on iOS, the event "metaData.device.charging" is a boolean + And on !macOS, the event "metaData.device.batteryLevel" is a number + And on !macOS, the event "metaData.device.charging" is a boolean And the event "metaData.device.simulator" is false - And the event "metaData.error.mach.code_name" equals "KERN_INVALID_ADDRESS" - And the event "metaData.error.mach.code" equals "0x1" - And the event "metaData.error.mach.exception_name" is not null And the event "metaData.lastRunInfo.consecutiveLaunchCrashes" equals 1 And the event "metaData.lastRunInfo.crashed" is true And the event "metaData.lastRunInfo.crashedDuringLaunch" is true @@ -166,10 +171,10 @@ Feature: Barebone tests And the event "severity" equals "error" And the event "severityReason.type" equals "unhandledException" And the event "severityReason.unhandledOverridden" is null - And the event "threads.0.errorReportingThread" is true - And the event "threads.0.id" equals "0" - And the event "threads.0.state" is not null - And the event "threads.0.stacktrace.0.method" matches "(assertionFailure|fatalErrorMessage|)" + And on !watchOS, the event "threads.0.errorReportingThread" is true + And on !watchOS, the event "threads.0.id" equals "0" + And on !watchOS, the event "threads.0.state" is not null + And on watchOS, the event "threads" is an array with 0 elements And the event "unhandled" is true And the event "user.email" equals "barfoo@example.com" And the event "user.id" equals "barfoo" @@ -177,8 +182,8 @@ Feature: Barebone tests And the event contains the following feature flags: | featureFlag | variant | | Testing | | - And the exception "errorClass" equals "Fatal error" - And the exception "message" equals "Unexpectedly found nil while implicitly unwrapping an Optional value" + And the exception "errorClass" equals "NSRangeException" + And the exception "message" equals "*** -[__NSArray0 objectAtIndex:]: index 42 beyond bounds for empty NSArray" And the exception "type" equals "cocoa" And the error payload field "events.0.app.dsymUUIDs" is a non-empty array And the error payload field "events.0.app.duration" is a number @@ -187,9 +192,8 @@ Feature: Barebone tests And the error payload field "events.0.device.freeMemory" is an integer And the error payload field "events.0.device.model" matches the test device model And the error payload field "events.0.device.totalMemory" is an integer - And the error payload field "events.0.threads" is a non-empty array - And the error payload field "events.0.threads.1" is not null - And the "method" of stack frame 0 matches "(assertionFailure|fatalErrorMessage|)" + And on !watchOS, the error payload field "events.0.threads" is a non-empty array + And on !watchOS, the error payload field "events.0.threads.1" is not null And the stacktrace is valid for the event @skip_macos diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index d900937ee..2ecf471a5 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -55,6 +55,7 @@ Feature: Attaching a series of notable events leading up to errors And the event has a "state" breadcrumb named "Scene Will Enter Foreground" And the event has a "state" breadcrumb named "Scene Activated" + @watchos Scenario: Network breadcrumbs When I start the document server And I run "NetworkBreadcrumbsScenario" @@ -63,7 +64,7 @@ Feature: Attaching a series of notable events leading up to errors And the event "breadcrumbs.0.name" equals "NSURLSession request failed" And the event "breadcrumbs.0.type" equals "request" And the event "breadcrumbs.0.metaData.method" equals "GET" - And the event "breadcrumbs.0.metaData.url" equals "http://bs-local.com:9340/reflect/" + And the event "breadcrumbs.0.metaData.url" matches "http://.*:9340/reflect/" And the event "breadcrumbs.0.metaData.urlParams.status" equals "444" And the event "breadcrumbs.0.metaData.urlParams.password" equals "[REDACTED]" And the event "breadcrumbs.0.metaData.status" equals 444 @@ -74,7 +75,7 @@ Feature: Attaching a series of notable events leading up to errors And the event "breadcrumbs.1.name" equals "NSURLSession request succeeded" And the event "breadcrumbs.1.type" equals "request" And the event "breadcrumbs.1.metaData.method" equals "GET" - And the event "breadcrumbs.1.metaData.url" equals "http://bs-local.com:9340/reflect/" + And the event "breadcrumbs.1.metaData.url" matches "http://.*:9340/reflect/" And the event "breadcrumbs.1.metaData.urlParams.delay_ms" equals "3000" And the event "breadcrumbs.1.metaData.status" equals 200 And the event "breadcrumbs.1.metaData.duration" is greater than 0 diff --git a/features/delivery.feature b/features/delivery.feature index 741f0a335..89f53ec80 100644 --- a/features/delivery.feature +++ b/features/delivery.feature @@ -3,6 +3,7 @@ Feature: Delivery of errors Background: Given I clear all persistent data + @watchos Scenario: Delivery is retried after an HTTP 500 error When I set the HTTP status code for the next request to 500 And I run "HandledExceptionScenario" diff --git a/features/fixtures/shared/scenarios/.gitignore b/features/fixtures/shared/scenarios/.gitignore new file mode 100644 index 000000000..f6e41a622 --- /dev/null +++ b/features/fixtures/shared/scenarios/.gitignore @@ -0,0 +1 @@ +watchos_maze_host.h diff --git a/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift b/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift index c4fc35aba..e8240476a 100644 --- a/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift +++ b/features/fixtures/shared/scenarios/BareboneTestHandledScenario.swift @@ -42,7 +42,9 @@ class BareboneTestHandledScenario: Scenario { config.addMetadata(["Testing": true], section: "Flags") config.addMetadata(["password": "123456"], section: "Other") config.launchDurationMillis = 0 +#if !os(watchOS) config.sendThreads = .unhandledOnly +#endif config.setUser("foobar", withEmail: "foobar@example.com", andName: "Foo Bar") config.addMetadata(["group": "users"], section: "user") config.addFeatureFlag(name: "Testing") diff --git a/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift b/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift index a8c9ed6f7..8212cf703 100644 --- a/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift +++ b/features/fixtures/shared/scenarios/BareboneTestUnhandledErrorScenario.swift @@ -30,8 +30,7 @@ class BareboneTestUnhandledErrorScenario: Scenario { } override func run() { - // Triggers "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: ..." - print(payload.name) + NSArray().object(at: 42) } } diff --git a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift index af88ca489..101e370e2 100644 --- a/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift +++ b/features/fixtures/shared/scenarios/NetworkBreadcrumbsScenario.swift @@ -10,13 +10,18 @@ import Foundation @available(iOS 10.0, macOS 10.12, *) class NetworkBreadcrumbsScenario : Scenario { - + + lazy var baseURL: URL = { + var components = URLComponents(string: Scenario.mazeRunnerURL.absoluteString)! + components.port = 9340 // `/reflect` listens on a different port :-(( + return components.url! + }() + override func startBugsnag() { config.autoTrackSessions = false; config.add(BugsnagNetworkRequestPlugin()) config.addOnBreadcrumb { - return $0.type == .request && - ($0.metadata["url"] as? String)?.hasPrefix("http://bs-local.com:9340") == true + ($0.metadata["url"] as? String ?? "").hasPrefix(self.baseURL.absoluteString) } super.startBugsnag() @@ -24,16 +29,16 @@ class NetworkBreadcrumbsScenario : Scenario { override func run() { // Make some network requests so that automatic network breadcrumbs are left - query(address: "http://bs-local.com:9340/reflect/?status=444&password=T0p5ecr3t") - query(address: "http://bs-local.com:9340/reflect/?delay_ms=3000") + query(string: "/reflect/?status=444&password=T0p5ecr3t") + query(string: "/reflect/?delay_ms=3000") // Send a handled error let error = NSError(domain: "NetworkBreadcrumbsScenario", code: 100, userInfo: nil) Bugsnag.notifyError(error) } - func query(address: String) { - let url = URL(string: address)! + func query(string: String) { + let url = URL(string: string, relativeTo: baseURL)! let semaphore = DispatchSemaphore(value: 0) let task = URLSession.shared.dataTask(with: url) {(data, response, error) in diff --git a/features/fixtures/shared/scenarios/Scenario.h b/features/fixtures/shared/scenarios/Scenario.h index f6c88c785..ab5802e92 100644 --- a/features/fixtures/shared/scenarios/Scenario.h +++ b/features/fixtures/shared/scenarios/Scenario.h @@ -17,6 +17,8 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); @interface Scenario : NSObject +@property (class, readonly) NSURL *mazeRunnerURL; + @property (strong, nonatomic, nonnull) BugsnagConfiguration *config; + (Scenario *)createScenarioNamed:(NSString *)className withConfig:(nullable BugsnagConfiguration *)config; @@ -42,7 +44,7 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer); + (void)clearPersistentData; -+ (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; ++ (void)executeMazeRunnerCommand:(nullable void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler; @end diff --git a/features/fixtures/shared/scenarios/Scenario.m b/features/fixtures/shared/scenarios/Scenario.m index 33da15bf2..6db35ec47 100644 --- a/features/fixtures/shared/scenarios/Scenario.m +++ b/features/fixtures/shared/scenarios/Scenario.m @@ -6,6 +6,20 @@ #import +#if TARGET_OS_IOS +#define MAZE_RUNNER_URL "http://bs-local.com:9339" +#define SWIFT_MODULE "iOSTestApp" +#elif TARGET_OS_OSX +#define MAZE_RUNNER_URL "http://localhost:9339" +#define SWIFT_MODULE "macOSTestApp" +#elif TARGET_OS_WATCH +#import "watchos_maze_host.h" +#define MAZE_RUNNER_URL "http://" WATCHOS_MAZE_HOST ":9339" +#define SWIFT_MODULE "watchOSTestApp_WatchKit_Extension" +#else +#error Unsupported TARGET_OS +#endif + extern bool bsg_kslog_setLogFilename(const char *filename, bool overwrite); extern void bsg_i_kslog_logCBasic(const char *fmt, ...) __printflike(1, 2); @@ -22,7 +36,9 @@ void markErrorHandledCallback(const BSG_KSCrashReportWriter *writer) { static Scenario *theScenario; +#if !TARGET_OS_WATCH static char ksLogPath[PATH_MAX]; +#endif @implementation Scenario { dispatch_block_t _onEventDelivery; @@ -55,10 +71,13 @@ + (void)load { }]; } ++ (NSURL *)mazeRunnerURL { + return [NSURL URLWithString:@MAZE_RUNNER_URL]; +} + + (Scenario *)createScenarioNamed:(NSString *)className withConfig:(BugsnagConfiguration *)config { Class class = NSClassFromString(className) ?: - NSClassFromString([@"iOSTestApp." stringByAppendingString:className]) ?: - NSClassFromString([@"macOSTestApp." stringByAppendingString:className]); + NSClassFromString([@SWIFT_MODULE "." stringByAppendingString:className]); if (!class) { [NSException raise:NSInvalidArgumentException format:@"Failed to find scenario class named %@", className]; @@ -77,10 +96,12 @@ - (instancetype)initWithConfig:(BugsnagConfiguration *)config { _config = config; } else { _config = [[BugsnagConfiguration alloc] initWithApiKey:@"12312312312312312312312312312312"]; - _config.endpoints.notify = @"http://bs-local.com:9339/notify"; - _config.endpoints.sessions = @"http://bs-local.com:9339/sessions"; + _config.endpoints.notify = @MAZE_RUNNER_URL "/notify"; + _config.endpoints.sessions = @MAZE_RUNNER_URL "/sessions"; } +#if !TARGET_OS_WATCH _config.enabledErrorTypes.ooms = NO; +#endif } return self; } @@ -144,6 +165,7 @@ - (void)requestDidComplete:(NSURLRequest *)request { + (void)initialize { if (self == [Scenario self]) { +#if !TARGET_OS_WATCH #if TARGET_OS_IPHONE NSString *logPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"kscrash.log"]; @@ -152,7 +174,7 @@ + (void)initialize { #endif [logPath getFileSystemRepresentation:ksLogPath maxLength:sizeof(ksLogPath)]; bsg_kslog_setLogFilename(ksLogPath, false); - +#endif Method method = class_getInstanceMethod([NSURLSession class], @selector(uploadTaskWithRequest:fromData:completionHandler:)); NSURLSession_uploadTaskWithRequest_fromData_completionHandler = (void *)method_setImplementation(method, (void *)uploadTaskWithRequest_fromData_completionHandler); @@ -188,14 +210,16 @@ + (void)clearPersistentData { NSLog(@"%@", error); } } +#if !TARGET_OS_WATCH bsg_kslog_setLogFilename(ksLogPath, true); +#endif } + (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioName, NSString *scenarioMode))preHandler { NSLog(@"%s", __PRETTY_FUNCTION__); NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://bs-local.com:9339/command"]]; + NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@MAZE_RUNNER_URL "/command"]]; [[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (![response isKindOfClass:[NSHTTPURLResponse class]] || [(NSHTTPURLResponse *)response statusCode] != 200) { NSLog(@"%s request failed with %@", __PRETTY_FUNCTION__, response ?: error); @@ -219,9 +243,11 @@ + (void)executeMazeRunnerCommand:(void (^)(NSString *action, NSString *scenarioN [self clearPersistentData]; } - dispatch_async(dispatch_get_main_queue(), ^{ - preHandler(action, scenarioName, eventMode); - }); + if (preHandler) { + dispatch_async(dispatch_get_main_queue(), ^{ + preHandler(action, scenarioName, eventMode); + }); + } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if ([action isEqualToString:@"run_scenario"]) { diff --git a/features/fixtures/watchos/Podfile b/features/fixtures/watchos/Podfile new file mode 100644 index 000000000..64a0465ce --- /dev/null +++ b/features/fixtures/watchos/Podfile @@ -0,0 +1,13 @@ +platform :watchos, '6.3' + +# target 'watchOSTestApp' do +# end + +# target 'watchOSTestApp WatchKit App' do +# end + +target 'watchOSTestApp WatchKit Extension' do + use_frameworks! + pod 'Bugsnag', path: '../../..' + pod 'BugsnagNetworkRequestPlugin', path: '../../..' +end diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AccentColor.colorset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AppIcon.appiconset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..193150c7a --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,109 @@ +{ + "images" : [ + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "24x24", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "27.5x27.5", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "companionSettings", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "watch", + "role" : "notificationCenter", + "scale" : "2x", + "size" : "33x33", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "40x40", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "44x44", + "subtype" : "40mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "46x46", + "subtype" : "41mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "50x50", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "appLauncher", + "scale" : "2x", + "size" : "51x51", + "subtype" : "45mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "86x86", + "subtype" : "38mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "98x98", + "subtype" : "42mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "108x108", + "subtype" : "44mm" + }, + { + "idiom" : "watch", + "role" : "quickLook", + "scale" : "2x", + "size" : "117x117", + "subtype" : "45mm" + }, + { + "idiom" : "watch-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit App/Interface.storyboard b/features/fixtures/watchos/watchOSTestApp WatchKit App/Interface.storyboard new file mode 100644 index 000000000..fbbf23dc5 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit App/Interface.storyboard @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json new file mode 100644 index 000000000..26454cac8 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Contents.json new file mode 100644 index 000000000..e8b3252e3 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Contents.json @@ -0,0 +1,53 @@ +{ + "assets" : [ + { + "filename" : "Circular.imageset", + "idiom" : "watch", + "role" : "circular" + }, + { + "filename" : "Extra Large.imageset", + "idiom" : "watch", + "role" : "extra-large" + }, + { + "filename" : "Graphic Bezel.imageset", + "idiom" : "watch", + "role" : "graphic-bezel" + }, + { + "filename" : "Graphic Circular.imageset", + "idiom" : "watch", + "role" : "graphic-circular" + }, + { + "filename" : "Graphic Corner.imageset", + "idiom" : "watch", + "role" : "graphic-corner" + }, + { + "filename" : "Graphic Extra Large.imageset", + "idiom" : "watch", + "role" : "graphic-extra-large" + }, + { + "filename" : "Graphic Large Rectangular.imageset", + "idiom" : "watch", + "role" : "graphic-large-rectangular" + }, + { + "filename" : "Modular.imageset", + "idiom" : "watch", + "role" : "modular" + }, + { + "filename" : "Utilitarian.imageset", + "idiom" : "watch", + "role" : "utilitarian" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json new file mode 100644 index 000000000..26454cac8 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json new file mode 100644 index 000000000..6e184db8f --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json new file mode 100644 index 000000000..6e184db8f --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json new file mode 100644 index 000000000..6e184db8f --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json new file mode 100644 index 000000000..26454cac8 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Extra Large.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json new file mode 100644 index 000000000..6e184db8f --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json new file mode 100644 index 000000000..26454cac8 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json new file mode 100644 index 000000000..26454cac8 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json @@ -0,0 +1,25 @@ +{ + "images" : [ + { + "idiom" : "watch", + "scale" : "2x" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : "<=145" + }, + { + "idiom" : "watch", + "scale" : "2x", + "screen-width" : ">183" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "auto-scaling" : "auto" + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ComplicationController.swift b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ComplicationController.swift new file mode 100644 index 000000000..042869342 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ComplicationController.swift @@ -0,0 +1,60 @@ +// +// ComplicationController.swift +// watchOSTestApp WatchKit Extension +// +// Created by Nick Dowell on 01/06/2022. +// + +import ClockKit + + +@available(watchOSApplicationExtension 7.0, *) +class ComplicationController: NSObject, CLKComplicationDataSource { + + // MARK: - Complication Configuration + + func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) { + let descriptors = [ + CLKComplicationDescriptor(identifier: "complication", displayName: "watchOSTestApp", supportedFamilies: CLKComplicationFamily.allCases) + // Multiple complication support can be added here with more descriptors + ] + + // Call the handler with the currently supported complication descriptors + handler(descriptors) + } + + func handleSharedComplicationDescriptors(_ complicationDescriptors: [CLKComplicationDescriptor]) { + // Do any necessary work to support these newly shared complication descriptors + } + + // MARK: - Timeline Configuration + + func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) { + // Call the handler with the last entry date you can currently provide or nil if you can't support future timelines + handler(nil) + } + + func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) { + // Call the handler with your desired behavior when the device is locked + handler(.showOnLockScreen) + } + + // MARK: - Timeline Population + + func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) { + // Call the handler with the current timeline entry + handler(nil) + } + + func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) { + // Call the handler with the timeline entries after the given date + handler(nil) + } + + // MARK: - Sample Templates + + func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { + // This method will be called once per supported complication, and the results will be cached + handler(nil) + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ExtensionDelegate.swift b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ExtensionDelegate.swift new file mode 100644 index 000000000..2b7355b41 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/ExtensionDelegate.swift @@ -0,0 +1,56 @@ +// +// ExtensionDelegate.swift +// watchOSTestApp WatchKit Extension +// +// Created by Nick Dowell on 08/06/2022. +// + +import WatchKit + +class ExtensionDelegate: NSObject, WKExtensionDelegate { + + func applicationDidFinishLaunching() { + // Perform any final initialization of your application. + Scenario.executeMazeRunnerCommand() + } + + func applicationDidBecomeActive() { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillResignActive() { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, etc. + } + + func handle(_ backgroundTasks: Set) { + // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. + for task in backgroundTasks { + // Use a switch statement to check the task type + switch task { + case let backgroundTask as WKApplicationRefreshBackgroundTask: + // Be sure to complete the background task once you’re done. + backgroundTask.setTaskCompletedWithSnapshot(false) + case let snapshotTask as WKSnapshotRefreshBackgroundTask: + // Snapshot tasks have a unique completion call, make sure to set your expiration date + snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) + case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: + // Be sure to complete the connectivity task once you’re done. + connectivityTask.setTaskCompletedWithSnapshot(false) + case let urlSessionTask as WKURLSessionRefreshBackgroundTask: + // Be sure to complete the URL session task once you’re done. + urlSessionTask.setTaskCompletedWithSnapshot(false) + case let relevantShortcutTask as WKRelevantShortcutRefreshBackgroundTask: + // Be sure to complete the relevant-shortcut task once you're done. + relevantShortcutTask.setTaskCompletedWithSnapshot(false) + case let intentDidRunTask as WKIntentDidRunRefreshBackgroundTask: + // Be sure to complete the intent-did-run task once you're done. + intentDidRunTask.setTaskCompletedWithSnapshot(false) + default: + // make sure to complete unhandled task types + task.setTaskCompletedWithSnapshot(false) + } + } + } + +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Info.plist b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Info.plist new file mode 100644 index 000000000..88c4e4733 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Info.plist @@ -0,0 +1,28 @@ + + + + + bugsnag + + apiKey + 0192837465afbecd0192837465afbecd + releaseStage + beta2 + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSExtension + + NSExtensionAttributes + + WKAppBundleIdentifier + com.bugsnag.watchOSTestApp.watchkitapp + + NSExtensionPointIdentifier + com.apple.watchkit + + + diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/InterfaceController.swift b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/InterfaceController.swift new file mode 100644 index 000000000..c50d04456 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/InterfaceController.swift @@ -0,0 +1,26 @@ +// +// InterfaceController.swift +// watchOSTestApp WatchKit Extension +// +// Created by Nick Dowell on 08/06/2022. +// + +import WatchKit +import Foundation + + +class InterfaceController: WKInterfaceController { + + override func awake(withContext context: Any?) { + // Configure interface objects here. + } + + override func willActivate() { + // This method is called when watch view controller is about to be visible to user + } + + override func didDeactivate() { + // This method is called when watch view controller is no longer visible + } + +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Preview Content/Preview Assets.xcassets/Contents.json b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Swift-Objc-Bridging-Header.h b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Swift-Objc-Bridging-Header.h new file mode 100644 index 000000000..9bafdca4f --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp WatchKit Extension/Swift-Objc-Bridging-Header.h @@ -0,0 +1,7 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "Scenario.h" +#import +#import diff --git a/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.pbxproj b/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..98d264b60 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.pbxproj @@ -0,0 +1,686 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + 01479A1B2847775500E53BB9 /* watchOSTestApp WatchKit App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 01479A1A2847775500E53BB9 /* watchOSTestApp WatchKit App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 01479A202847775600E53BB9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01479A1F2847775600E53BB9 /* Assets.xcassets */; }; + 01479A262847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 01479A252847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 01479A2F2847775600E53BB9 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01479A2E2847775600E53BB9 /* ComplicationController.swift */; }; + 01479A312847775800E53BB9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01479A302847775800E53BB9 /* Assets.xcassets */; }; + 01479A342847775800E53BB9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01479A332847775800E53BB9 /* Preview Assets.xcassets */; }; + 01479B55284779A500E53BB9 /* Scenario.m in Sources */ = {isa = PBXBuildFile; fileRef = 01479AA6284779A400E53BB9 /* Scenario.m */; }; + 01479BA82847923A00E53BB9 /* BareboneTestHandledScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01479BA72847923A00E53BB9 /* BareboneTestHandledScenario.swift */; }; + 01479BAA284792B100E53BB9 /* BareboneTestUnhandledErrorScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01479BA9284792B100E53BB9 /* BareboneTestUnhandledErrorScenario.swift */; }; + 01515B1928508CD800E498EC /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01515B1728508BA600E498EC /* Interface.storyboard */; }; + 01515B1C28508DBB00E498EC /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01515B1A28508DBB00E498EC /* InterfaceController.swift */; }; + 01515B1D28508DBB00E498EC /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01515B1B28508DBB00E498EC /* ExtensionDelegate.swift */; }; + 018F050D284F314D004EA50D /* HandledExceptionScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F050C284F314D004EA50D /* HandledExceptionScenario.swift */; }; + 018F050F284F3155004EA50D /* NetworkBreadcrumbsScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F050E284F3155004EA50D /* NetworkBreadcrumbsScenario.swift */; }; + C1AD3C358BC309F832CCC972 /* Pods_watchOSTestApp_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B2DE4764F62DB21DAE7435C /* Pods_watchOSTestApp_WatchKit_Extension.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 01479A1C2847775500E53BB9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 01479A102847775500E53BB9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 01479A192847775500E53BB9; + remoteInfo = "watchOSTestApp WatchKit App"; + }; + 01479A272847775600E53BB9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 01479A102847775500E53BB9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 01479A242847775600E53BB9; + remoteInfo = "watchOSTestApp WatchKit Extension"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 01479A3B2847775800E53BB9 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 01479A262847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 01479A3F2847775800E53BB9 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 01479A1B2847775500E53BB9 /* watchOSTestApp WatchKit App.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 01479A162847775500E53BB9 /* watchOSTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = watchOSTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 01479A1A2847775500E53BB9 /* watchOSTestApp WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchOSTestApp WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 01479A1F2847775600E53BB9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 01479A252847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "watchOSTestApp WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 01479A2E2847775600E53BB9 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = ""; }; + 01479A302847775800E53BB9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 01479A332847775800E53BB9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 01479A352847775800E53BB9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; usesTabs = 1; }; + 01479A46284779A400E53BB9 /* Scenario.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scenario.h; sourceTree = ""; }; + 01479AA6284779A400E53BB9 /* Scenario.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Scenario.m; sourceTree = ""; }; + 01479BA628477A0000E53BB9 /* Swift-Objc-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Swift-Objc-Bridging-Header.h"; sourceTree = ""; }; + 01479BA72847923A00E53BB9 /* BareboneTestHandledScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BareboneTestHandledScenario.swift; sourceTree = ""; }; + 01479BA9284792B100E53BB9 /* BareboneTestUnhandledErrorScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BareboneTestUnhandledErrorScenario.swift; sourceTree = ""; }; + 01515B1728508BA600E498EC /* Interface.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Interface.storyboard; sourceTree = ""; }; + 01515B1A28508DBB00E498EC /* InterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; }; + 01515B1B28508DBB00E498EC /* ExtensionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; + 018F050C284F314D004EA50D /* HandledExceptionScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HandledExceptionScenario.swift; sourceTree = ""; }; + 018F050E284F3155004EA50D /* NetworkBreadcrumbsScenario.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkBreadcrumbsScenario.swift; sourceTree = ""; }; + 5B2DE4764F62DB21DAE7435C /* Pods_watchOSTestApp_WatchKit_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_watchOSTestApp_WatchKit_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C7F2423F2C2AA215837EC98D /* Pods-watchOSTestApp WatchKit Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOSTestApp WatchKit Extension.release.xcconfig"; path = "Target Support Files/Pods-watchOSTestApp WatchKit Extension/Pods-watchOSTestApp WatchKit Extension.release.xcconfig"; sourceTree = ""; }; + F7B7E9CC24320877BE6D3B95 /* Pods-watchOSTestApp WatchKit Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-watchOSTestApp WatchKit Extension.debug.xcconfig"; path = "Target Support Files/Pods-watchOSTestApp WatchKit Extension/Pods-watchOSTestApp WatchKit Extension.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 01479A222847775600E53BB9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1AD3C358BC309F832CCC972 /* Pods_watchOSTestApp_WatchKit_Extension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 01479A0F2847775500E53BB9 = { + isa = PBXGroup; + children = ( + 01479A1E2847775500E53BB9 /* watchOSTestApp WatchKit App */, + 01479A292847775600E53BB9 /* watchOSTestApp WatchKit Extension */, + 01479A172847775500E53BB9 /* Products */, + 20F2DE790618D69398A7119E /* Pods */, + 2163A3B659B7D39D4DA64CAB /* Frameworks */, + ); + sourceTree = ""; + }; + 01479A172847775500E53BB9 /* Products */ = { + isa = PBXGroup; + children = ( + 01479A162847775500E53BB9 /* watchOSTestApp.app */, + 01479A1A2847775500E53BB9 /* watchOSTestApp WatchKit App.app */, + 01479A252847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex */, + ); + name = Products; + sourceTree = ""; + }; + 01479A1E2847775500E53BB9 /* watchOSTestApp WatchKit App */ = { + isa = PBXGroup; + children = ( + 01515B1728508BA600E498EC /* Interface.storyboard */, + 01479A1F2847775600E53BB9 /* Assets.xcassets */, + ); + path = "watchOSTestApp WatchKit App"; + sourceTree = ""; + }; + 01479A292847775600E53BB9 /* watchOSTestApp WatchKit Extension */ = { + isa = PBXGroup; + children = ( + 01479A2E2847775600E53BB9 /* ComplicationController.swift */, + 01515B1B28508DBB00E498EC /* ExtensionDelegate.swift */, + 01515B1A28508DBB00E498EC /* InterfaceController.swift */, + 01479BA628477A0000E53BB9 /* Swift-Objc-Bridging-Header.h */, + 01479A302847775800E53BB9 /* Assets.xcassets */, + 01479A352847775800E53BB9 /* Info.plist */, + 01479A43284779A400E53BB9 /* scenarios */, + 01479A322847775800E53BB9 /* Preview Content */, + ); + path = "watchOSTestApp WatchKit Extension"; + sourceTree = ""; + }; + 01479A322847775800E53BB9 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 01479A332847775800E53BB9 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 01479A43284779A400E53BB9 /* scenarios */ = { + isa = PBXGroup; + children = ( + 01479BA72847923A00E53BB9 /* BareboneTestHandledScenario.swift */, + 01479BA9284792B100E53BB9 /* BareboneTestUnhandledErrorScenario.swift */, + 018F050C284F314D004EA50D /* HandledExceptionScenario.swift */, + 018F050E284F3155004EA50D /* NetworkBreadcrumbsScenario.swift */, + 01479A46284779A400E53BB9 /* Scenario.h */, + 01479AA6284779A400E53BB9 /* Scenario.m */, + ); + name = scenarios; + path = ../../shared/scenarios; + sourceTree = ""; + }; + 20F2DE790618D69398A7119E /* Pods */ = { + isa = PBXGroup; + children = ( + F7B7E9CC24320877BE6D3B95 /* Pods-watchOSTestApp WatchKit Extension.debug.xcconfig */, + C7F2423F2C2AA215837EC98D /* Pods-watchOSTestApp WatchKit Extension.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 2163A3B659B7D39D4DA64CAB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5B2DE4764F62DB21DAE7435C /* Pods_watchOSTestApp_WatchKit_Extension.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 01479A152847775500E53BB9 /* watchOSTestApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01479A402847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp" */; + buildPhases = ( + 01479A142847775500E53BB9 /* Resources */, + 01479A3F2847775800E53BB9 /* Embed Watch Content */, + ); + buildRules = ( + ); + dependencies = ( + 01479A1D2847775500E53BB9 /* PBXTargetDependency */, + ); + name = watchOSTestApp; + productName = watchOSTestApp; + productReference = 01479A162847775500E53BB9 /* watchOSTestApp.app */; + productType = "com.apple.product-type.application.watchapp2-container"; + }; + 01479A192847775500E53BB9 /* watchOSTestApp WatchKit App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01479A3C2847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp WatchKit App" */; + buildPhases = ( + 01479A182847775500E53BB9 /* Resources */, + 01479A3B2847775800E53BB9 /* Embed App Extensions */, + ); + buildRules = ( + ); + dependencies = ( + 01479A282847775600E53BB9 /* PBXTargetDependency */, + ); + name = "watchOSTestApp WatchKit App"; + productName = "watchOSTestApp WatchKit App"; + productReference = 01479A1A2847775500E53BB9 /* watchOSTestApp WatchKit App.app */; + productType = "com.apple.product-type.application.watchapp2"; + }; + 01479A242847775600E53BB9 /* watchOSTestApp WatchKit Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01479A382847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp WatchKit Extension" */; + buildPhases = ( + 6599F75C5BDFB59740A518A4 /* [CP] Check Pods Manifest.lock */, + 01479A212847775600E53BB9 /* Sources */, + 01479A222847775600E53BB9 /* Frameworks */, + 01479A232847775600E53BB9 /* Resources */, + B5174B0E030D7B147D29F1EA /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "watchOSTestApp WatchKit Extension"; + productName = "watchOSTestApp WatchKit Extension"; + productReference = 01479A252847775600E53BB9 /* watchOSTestApp WatchKit Extension.appex */; + productType = "com.apple.product-type.watchkit2-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 01479A102847775500E53BB9 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1340; + LastUpgradeCheck = 1340; + TargetAttributes = { + 01479A152847775500E53BB9 = { + CreatedOnToolsVersion = 13.4; + }; + 01479A192847775500E53BB9 = { + CreatedOnToolsVersion = 13.4; + }; + 01479A242847775600E53BB9 = { + CreatedOnToolsVersion = 13.4; + }; + }; + }; + buildConfigurationList = 01479A132847775500E53BB9 /* Build configuration list for PBXProject "watchOSTestApp" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 01479A0F2847775500E53BB9; + productRefGroup = 01479A172847775500E53BB9 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 01479A152847775500E53BB9 /* watchOSTestApp */, + 01479A192847775500E53BB9 /* watchOSTestApp WatchKit App */, + 01479A242847775600E53BB9 /* watchOSTestApp WatchKit Extension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 01479A142847775500E53BB9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01479A182847775500E53BB9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01515B1928508CD800E498EC /* Interface.storyboard in Resources */, + 01479A202847775600E53BB9 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01479A232847775600E53BB9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01479A342847775800E53BB9 /* Preview Assets.xcassets in Resources */, + 01479A312847775800E53BB9 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 6599F75C5BDFB59740A518A4 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-watchOSTestApp WatchKit Extension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + B5174B0E030D7B147D29F1EA /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-watchOSTestApp WatchKit Extension/Pods-watchOSTestApp WatchKit Extension-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-watchOSTestApp WatchKit Extension/Pods-watchOSTestApp WatchKit Extension-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-watchOSTestApp WatchKit Extension/Pods-watchOSTestApp WatchKit Extension-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 01479A212847775600E53BB9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01479BAA284792B100E53BB9 /* BareboneTestUnhandledErrorScenario.swift in Sources */, + 018F050D284F314D004EA50D /* HandledExceptionScenario.swift in Sources */, + 01515B1D28508DBB00E498EC /* ExtensionDelegate.swift in Sources */, + 01479A2F2847775600E53BB9 /* ComplicationController.swift in Sources */, + 01515B1C28508DBB00E498EC /* InterfaceController.swift in Sources */, + 01479BA82847923A00E53BB9 /* BareboneTestHandledScenario.swift in Sources */, + 018F050F284F3155004EA50D /* NetworkBreadcrumbsScenario.swift in Sources */, + 01479B55284779A500E53BB9 /* Scenario.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 01479A1D2847775500E53BB9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 01479A192847775500E53BB9 /* watchOSTestApp WatchKit App */; + targetProxy = 01479A1C2847775500E53BB9 /* PBXContainerItemProxy */; + }; + 01479A282847775600E53BB9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 01479A242847775600E53BB9 /* watchOSTestApp WatchKit Extension */; + targetProxy = 01479A272847775600E53BB9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 01479A362847775800E53BB9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WATCHOS_DEPLOYMENT_TARGET = 6.3; + }; + name = Debug; + }; + 01479A372847775800E53BB9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 6.3; + }; + name = Release; + }; + 01479A392847775800E53BB9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F7B7E9CC24320877BE6D3B95 /* Pods-watchOSTestApp WatchKit Extension.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "watchOSTestApp WatchKit Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "watchOSTestApp WatchKit Extension"; + INFOPLIST_KEY_CLKComplicationPrincipalClass = "$(PRODUCT_MODULE_NAME).ComplicationController"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_WKExtensionDelegateClassName = "$(PRODUCT_MODULE_NAME).ExtensionDelegate"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "watchOSTestApp WatchKit Extension/Swift-Objc-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Debug; + }; + 01479A3A2847775800E53BB9 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C7F2423F2C2AA215837EC98D /* Pods-watchOSTestApp WatchKit Extension.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "watchOSTestApp WatchKit Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "watchOSTestApp WatchKit Extension"; + INFOPLIST_KEY_CLKComplicationPrincipalClass = "$(PRODUCT_MODULE_NAME).ComplicationController"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INFOPLIST_KEY_WKExtensionDelegateClassName = "$(PRODUCT_MODULE_NAME).ExtensionDelegate"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "watchOSTestApp WatchKit Extension/Swift-Objc-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Release; + }; + 01479A3D2847775800E53BB9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + GENERATE_INFOPLIST_FILE = YES; + IBSC_MODULE = watchOSTestApp_WatchKit_Extension; + INFOPLIST_KEY_CFBundleDisplayName = "watchOSTestApp WatchKit App"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Debug; + }; + 01479A3E2847775800E53BB9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + GENERATE_INFOPLIST_FILE = YES; + IBSC_MODULE = watchOSTestApp_WatchKit_Extension; + INFOPLIST_KEY_CFBundleDisplayName = "watchOSTestApp WatchKit App"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + }; + name = Release; + }; + 01479A412847775800E53BB9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 01479A422847775800E53BB9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.bugsnag.watchOSTestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 01479A132847775500E53BB9 /* Build configuration list for PBXProject "watchOSTestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01479A362847775800E53BB9 /* Debug */, + 01479A372847775800E53BB9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01479A382847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp WatchKit Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01479A392847775800E53BB9 /* Debug */, + 01479A3A2847775800E53BB9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01479A3C2847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp WatchKit App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01479A3D2847775800E53BB9 /* Debug */, + 01479A3E2847775800E53BB9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01479A402847775800E53BB9 /* Build configuration list for PBXNativeTarget "watchOSTestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01479A412847775800E53BB9 /* Debug */, + 01479A422847775800E53BB9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 01479A102847775500E53BB9 /* Project object */; +} diff --git a/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App (Complication).xcscheme b/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App (Complication).xcscheme new file mode 100644 index 000000000..f2f9d0738 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App (Complication).xcscheme @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App.xcscheme b/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App.xcscheme new file mode 100644 index 000000000..dbe5b2b06 --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp.xcodeproj/xcshareddata/xcschemes/watchOSTestApp WatchKit App.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/features/fixtures/watchos/watchOSTestApp.xcworkspace/contents.xcworkspacedata b/features/fixtures/watchos/watchOSTestApp.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..bbf28ce4c --- /dev/null +++ b/features/fixtures/watchos/watchOSTestApp.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/features/steps/app_steps.rb b/features/steps/app_steps.rb index 34271630f..cc09cf0fb 100644 --- a/features/steps/app_steps.rb +++ b/features/steps/app_steps.rb @@ -10,15 +10,17 @@ end When("I run the configured scenario and relaunch the crashed app") do - platform = Maze::Helper.get_current_platform - case platform + case Maze::Helper.get_current_platform when 'ios' run_and_relaunch when 'macos' $scenario_mode = $last_scenario[:scenario_mode] execute_command($last_scenario[:action], $last_scenario[:scenario_name]) + when 'watchos' + run_watchos_app + sleep 10 # we don't have a way to check if the app is still running else - raise "Unknown platform: #{platform}" + raise "Unsupported platform: #{Maze::Helper.get_current_platform}" end end @@ -40,20 +42,31 @@ def run_and_relaunch When('I kill and relaunch the app') do case Maze::Helper.get_current_platform - when 'macos' - # Pass - else + 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 When("I relaunch the app after a crash") do - # Wait for the app to stop running before relaunching - step 'the app is not running' 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 end @@ -114,19 +127,22 @@ def execute_command(action, scenario_name) $reset_data = false # 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 - platform = Maze::Helper.get_current_platform - case platform + case Maze::Helper.get_current_platform when 'ios' Maze.driver.click_element :execute_command when 'macos' run_macos_app + when 'watchos' + run_watchos_app else - raise "Unknown platform: #{platform}" + raise "Unsupported platform: #{Maze::Helper.get_current_platform}" end end @@ -153,3 +169,9 @@ def run_macos_app %i[err out] => :close ) end + +def run_watchos_app + system( + "xcdebug --workspace watchOSTestApp.xcworkspace --scheme 'watchOSTestApp WatchKit App' --build --background --destination platform=watchOS", exception: true + ) +end diff --git a/features/steps/cocoa_steps.rb b/features/steps/cocoa_steps.rb index 8564906d3..e61b2b66e 100644 --- a/features/steps/cocoa_steps.rb +++ b/features/steps/cocoa_steps.rb @@ -94,6 +94,11 @@ def check_device_model(field, list) Then the error is valid for the error reporting API version "4.0" for the "OSX Bugsnag Notifier" notifier Then the breadcrumb timestamps are valid for the event ) + when 'watchos' + steps %( + Then the error is valid for the error reporting API version "4.0" for the "watchOS Bugsnag Notifier" notifier + Then the breadcrumb timestamps are valid for the event + ) else raise "Unknown platform: #{platform}" end @@ -110,6 +115,10 @@ def check_device_model(field, list) steps %( Then the error is valid for the error reporting API version "4.0" for the "OSX Bugsnag Notifier" notifier ) + when 'watchos' + steps %( + Then the error is valid for the error reporting API version "4.0" for the "watchOS Bugsnag Notifier" notifier + ) else raise "Unknown platform: #{platform}" end @@ -133,6 +142,10 @@ def check_device_model(field, list) steps %( Then the session is valid for the session reporting API version "1.0" for the "OSX Bugsnag Notifier" notifier ) + when 'watchos' + steps %( + Then the session is valid for the session reporting API version "1.0" for the "watchOS Bugsnag Notifier" notifier + ) else raise "Unknown platform: #{platform}" end @@ -150,8 +163,7 @@ def check_device_model(field, list) unless device['time'].nil? breadcrumbs = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.breadcrumbs') breadcrumbs.each do |breadcrumb| - assert(breadcrumb['timestamp'] <= device['time'], - "Expected breadcrumb timestamp (#{breadcrumb['timestamp']}) <= event timestamp (#{device['time']})") + Maze.check.operator(breadcrumb['timestamp'], :<=, device['time']) end end end diff --git a/features/steps/reusable_steps.rb b/features/steps/reusable_steps.rb index f6ccc664b..da8399504 100644 --- a/features/steps/reusable_steps.rb +++ b/features/steps/reusable_steps.rb @@ -6,7 +6,7 @@ Maze.config.captured_invalid_requests.delete(type.to_sym) end -Then(/^on (iOS|macOS), (.+)/) do |platform, step_text| +Then(/^on (iOS|macOS|watchOS), (.+)/) do |platform, step_text| step(step_text) if platform.downcase == Maze::Helper.get_current_platform end @@ -14,6 +14,10 @@ step(step_text) if platform.downcase == Maze::Helper.get_current_platform && Maze.config.os_version >= version end +Then(/^on !(iOS|macOS|watchOS), (.+)/) do |platform, step_text| + step(step_text) unless platform.downcase == Maze::Helper.get_current_platform +end + Then('the event {string} equals one of:') do |field, possible_values| value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.0.#{field}") Maze.check.includes(possible_values.raw.flatten, value) @@ -35,6 +39,12 @@ Maze.check.kind_of Numeric, value end +Then('the event {string} is an array with {int} elements') do |field, count| + value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.0.#{field}") + Maze.check.kind_of Array, value + Maze.check.equal(count, value.length) +end + Then('the event {string} is an integer') do |field| value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.0.#{field}") Maze.check.kind_of Integer, value diff --git a/features/support/env.rb b/features/support/env.rb index 056ca4342..655e41e98 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -78,7 +78,8 @@ def skip_below(os, version) FileUtils.makedirs(path) - if Maze.config.os == 'macos' + case Maze.config.os + when 'macos' if $fixture_pid # will be nil if scenario was skipped Process.kill 'KILL', $fixture_pid Process.waitpid $fixture_pid @@ -93,7 +94,7 @@ def skip_below(os, version) Process.wait log FileUtils.mv '/tmp/kscrash.log', path end - else + when 'ios' if $logger_pid Process.kill 'TERM', $logger_pid Process.waitpid $logger_pid