Marathon integration - #3173
Conversation
🚀 Preview Deployment Ready!Preview URL: https://pr-3173-patrol-docs.vercel.app Latest DeploymentThis preview URL is stable and will be updated with each new commit to this PR. |
There was a problem hiding this comment.
Code Review
This pull request introduces Marathon integration for running Patrol tests in parallel on Android and iOS simulators, including necessary configuration files, helper scripts, and documentation. To support parallel execution, the native iOS and macOS test runners have been updated to dynamically bind to available ports and communicate them to the Dart side via a new method channel. The review feedback highlights a potential runtime crash in Swift when casting the port to UInt16 without validation, and recommends using %ld format specifiers with explicit (long) casts in Objective-C runner macros to prevent compiler warnings on 64-bit architectures.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…ng working if flutter removes dev dependency plugins from release builds
2da5f7c to
8deef0a
Compare
# Conflicts: # packages/patrol/CHANGELOG.md
There was a problem hiding this comment.
Pull request overview
Adds Marathon-based parallel Patrol testing, including dynamic Darwin port allocation and improved app-start synchronization.
Changes:
- Adds Marathon documentation, configurations, scripts, and smoke tests.
- Introduces runtime port injection for parallel iOS/macOS execution.
- Replaces fixed startup delays with readiness checks and timeouts.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
packages/patrol/lib/src/platform/platform_automator.dart |
Passes host and port configuration. |
packages/patrol/lib/src/platform/mobile/patrol_runtime_ports.dart |
Reads injected Darwin ports. |
packages/patrol/lib/src/platform/mobile/patrol_app_service_io.dart |
Resolves the app-service port dynamically. |
packages/patrol/lib/src/platform/mobile/mobile_automator_native.dart |
Lazily creates the mobile client. |
packages/patrol/lib/src/platform/mobile/mobile_automator_config.dart |
Adds dynamic test-server port resolution. |
packages/patrol/lib/src/platform/ios/ios_automator_native.dart |
Lazily creates the iOS client. |
packages/patrol/lib/src/platform/ios/ios_automator_config.dart |
Updates iOS config construction. |
packages/patrol/lib/src/platform/android/android_automator_config.dart |
Updates Android config construction. |
packages/patrol/darwin/patrol/Sources/PatrolImpl/SwiftPatrolPlugin.swift |
Exposes runtime ports through a method channel. |
packages/patrol/darwin/patrol/Sources/PatrolImpl/ObjCPatrolAppServiceClient.swift |
Supports explicit ports and removes startup delay. |
packages/patrol/darwin/patrol/Sources/PatrolImpl/AutomatorServer/PatrolServer.swift |
Scans and binds available port pairs. |
packages/patrol/darwin/patrol/Sources/patrol/include/PatrolIntegrationTestMacosRunner.h |
Injects ports and adds readiness checks. |
packages/patrol/darwin/patrol/Sources/patrol/include/PatrolIntegrationTestIosRunner.h |
Adds Marathon selection, ports, and synchronization. |
packages/patrol/CHANGELOG.md |
Announces Marathon integration. |
packages/patrol_cli/lib/src/test_bundler.dart |
Loads runtime ports in generated bundles. |
docs/index.mdx |
Links the Marathon guide. |
docs/documentation/integrations/meta.json |
Adds Marathon to navigation. |
docs/documentation/integrations/marathon.mdx |
Documents Marathon setup and usage. |
dev/e2e_app/run_marathon_ios |
Adds an iOS Marathon runner script. |
dev/e2e_app/run_marathon_android |
Adds an Android Marathon runner script. |
dev/e2e_app/patrol_test/marathon_smoke_test.dart |
Adds iOS sharding smoke tests. |
dev/e2e_app/Marathonfile.ios |
Configures iOS Marathon runs. |
dev/e2e_app/Marathonfile.android |
Configures Android Marathon runs. |
dev/e2e_app/Marathondevices |
Defines the iOS simulator pool. |
dev/e2e_app/ios/Podfile.lock |
Updates resolved iOS dependencies. |
Comments suppressed due to low confidence (2)
packages/patrol/darwin/patrol/Sources/patrol/include/PatrolIntegrationTestIosRunner.h:253
- On a
listDartTestsfailure,listedTestsremainsnil, so the loop below never exits—even after the client's two-hour HTTP timeout fires. Record the error and stop discovery instead of turning an RPC failure into an infinite hang.
listDartTestsWithCompletion:^(NSArray<NSDictionary *> *_Nullable tests, NSError *_Nullable err) { \
if (err != NULL) { \
NSLog(@"listDartTests(): failed, err: %@", err); \
} \
listedTests = tests; \
packages/patrol/darwin/patrol/Sources/patrol/include/PatrolIntegrationTestMacosRunner.h:73
- On a
listDartTestsfailure,dartTestsremainsnil, so the loop below never exits—even after the client's two-hour HTTP timeout fires. Record the error and stop discovery instead of turning an RPC failure into an infinite hang.
listDartTestsWithCompletion:^(NSArray<NSDictionary *> *_Nullable tests, NSError *_Nullable err) { \
if (err != NULL) { \
NSLog(@"listDartTests(): failed, err: %@", err); \
} \
\
dartTests = tests; \
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| MobileAutomatorConfig({ | ||
| String? host, | ||
| String? port, | ||
| Duration? connectionTimeout, | ||
| Duration? findTimeout, | ||
| void Function(String)? logger, | ||
| }) : host = | ||
| host ?? | ||
| const String.fromEnvironment( | ||
| 'PATROL_HOST', | ||
| defaultValue: 'localhost', | ||
| ), | ||
| port = | ||
| port ?? | ||
| const String.fromEnvironment( | ||
| 'PATROL_TEST_SERVER_PORT', | ||
| defaultValue: '8081', | ||
| ), | ||
| }) : host = host ?? _defaultHost(), | ||
| _portOverride = port, |
| @@ -0,0 +1,66 @@ | |||
| import 'dart:io' show Platform; | |||
| +(NSString *)patrolSelectorForDartTestName : (NSString *)dartTestName { \ | ||
| return [dartTestName stringByReplacingOccurrencesOfString:@" " withString:@"+"]; \ |
| NSError *err = nil; \ | ||
| [server startAndReturnError:&err]; \ | ||
| if (err != nil) { \ | ||
| NSLog(@"patrolServer.start(): failed, err: %@", err); \ | ||
| } \ | ||
| \ | ||
| /* Create a client for PatrolAppService, which lets us list and run Dart tests */ \ | ||
| __block ObjCPatrolAppServiceClient *appServiceClient = [[ObjCPatrolAppServiceClient alloc] init]; \ | ||
| appServiceClientSingleton = [[ObjCPatrolAppServiceClient alloc] initWithPort:server.boundAppPort]; \ |
| NSError *_Nullable __autoreleasing *_Nullable err = NULL; \ | ||
| [server startAndReturnError:err]; \ | ||
| if (err != NULL) { \ | ||
| NSLog(@"patrolServer.start(): failed, err: %@", err); \ | ||
| } \ |
Summary
XCUIApplication.launchEnvironment, then reading them in Dart over a method channel (getRuntimePorts).appReadyper test beforerunDartTest, add a 60s timeout, and remove the obsolete 1s delay workaround.Why iOS needed the port work
xcodebuildon one Mac starts multiple host-sidePatrolServers, so they can no longer all use8081/8082.8081/8082,8083/8084, …) until a free one is found.launchEnvironmentto DartPlatform.environment, so ports are read from nativeProcessInfoover a method channel.