From 2b5150e2b63f36f5a867d4cd5f71fc5d9f558ab8 Mon Sep 17 00:00:00 2001 From: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:11:34 +0300 Subject: [PATCH 1/4] wda: add USE_IP env var to override what IP to bind to for multi-simulator scenarios Signed-off-by: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> --- .../xcschemes/WebDriverAgentRunner.xcscheme | 5 +++++ .../xcschemes/WebDriverAgentRunner_tvOS.xcscheme | 5 +++++ WebDriverAgentLib/Routing/FBWebServer.m | 6 ++++++ WebDriverAgentLib/Utilities/FBConfiguration.h | 6 ++++++ WebDriverAgentLib/Utilities/FBConfiguration.m | 11 +++++++++++ WebDriverAgentTests/UnitTests/FBConfigurationTests.m | 12 ++++++++++++ 6 files changed, 45 insertions(+) diff --git a/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme b/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme index 8857b934c..4c2919f78 100644 --- a/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme +++ b/WebDriverAgent.xcodeproj/xcshareddata/xcschemes/WebDriverAgentRunner.xcscheme @@ -66,6 +66,11 @@ value = "$(USE_PORT)" isEnabled = "YES"> + + + + 0) { + return NSProcessInfo.processInfo.environment[@"USE_IP"]; + } + + return nil; +} + + (NSInteger)mjpegServerPort { if (self.mjpegServerPortFromArguments != NSNotFound) { diff --git a/WebDriverAgentTests/UnitTests/FBConfigurationTests.m b/WebDriverAgentTests/UnitTests/FBConfigurationTests.m index 05e53d65f..6d25ea6b0 100644 --- a/WebDriverAgentTests/UnitTests/FBConfigurationTests.m +++ b/WebDriverAgentTests/UnitTests/FBConfigurationTests.m @@ -20,6 +20,7 @@ - (void)setUp { [super setUp]; unsetenv("USE_PORT"); + unsetenv("USE_IP"); unsetenv("VERBOSE_LOGGING"); } @@ -45,4 +46,15 @@ - (void)testVerboseLoggingEnvironmentOverwrite XCTAssertTrue([FBConfiguration verboseLoggingEnabled]); } +- (void)testBindingIPDefault +{ + XCTAssertNil([FBConfiguration bindingIPAddress]); +} + +- (void)testBindingIPEnvironmentOverwrite +{ + setenv("USE_IP", "192.168.1.100", 1); + XCTAssertEqualObjects([FBConfiguration bindingIPAddress], @"192.168.1.100"); +} + @end From f88b345042a501eff2eae9bd8049ed6639fd7e1e Mon Sep 17 00:00:00 2001 From: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:18:50 +0300 Subject: [PATCH 2/4] wda: make sure we log the true server host when USE_IP is used too Signed-off-by: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> --- WebDriverAgentLib/Routing/FBWebServer.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WebDriverAgentLib/Routing/FBWebServer.m b/WebDriverAgentLib/Routing/FBWebServer.m index 17673615b..0b82eaff5 100644 --- a/WebDriverAgentLib/Routing/FBWebServer.m +++ b/WebDriverAgentLib/Routing/FBWebServer.m @@ -117,7 +117,9 @@ - (void)startHTTPServer [FBLogger logFmt:@"Last attempt to start web server failed with error %@", [error description]]; abort(); } - [FBLogger logFmt:@"%@http://%@:%d%@", FBServerURLBeginMarker, [XCUIDevice sharedDevice].fb_wifiIPAddress ?: @"localhost", [self.server port], FBServerURLEndMarker]; + + NSString *serverHost = bindingIP ?: ([XCUIDevice sharedDevice].fb_wifiIPAddress ?: @"127.0.0.1"); + [FBLogger logFmt:@"%@http://%@:%d%@", FBServerURLBeginMarker, serverHost, [self.server port], FBServerURLEndMarker]; } - (void)initScreenshotsBroadcaster From 2d5d803106674eead2375013895eb14040d969bc Mon Sep 17 00:00:00 2001 From: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:21:59 +0300 Subject: [PATCH 3/4] lib: update to incorporate the new USE_IP env var Signed-off-by: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> --- lib/types.ts | 2 ++ lib/utils.js | 31 ++++++++++++++++++++----------- lib/webdriveragent.js | 6 +++++- lib/xcodebuild.js | 14 ++++++++------ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/lib/types.ts b/lib/types.ts index ee5cdf996..32a32493b 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -64,6 +64,7 @@ export interface WebDriverAgentArgs { wdaLocalPort?: number; wdaRemotePort?: number; wdaBaseUrl?: string; + wdaBindingIP?: string; prebuildWDA?: boolean; webDriverAgentUrl?: string; wdaConnectionTimeout?: number; @@ -116,6 +117,7 @@ export interface XcodeBuildArgs { useXctestrunFile?: boolean; launchTimeout?: number; wdaRemotePort?: number; + wdaBindingIP?: string; updatedWDABundleId?: string; derivedDataPath?: string; mjpegServerPort?: number; diff --git a/lib/utils.js b/lib/utils.js index 4ff845abd..4385ccf55 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -166,6 +166,16 @@ async function setRealDeviceSecurity (keychainPath, keychainPassword) { * @property {string} platformVersion - The platform version of OS. * @property {string} platformName - The platform name of iOS, tvOS */ + +/** + * Arguments for setting xctestrun file + * @typedef {Object} XctestrunFileArgs + * @property {DeviceInfo} deviceInfo - Information of the device under test + * @property {string} sdkVersion - The Xcode SDK version of OS. + * @property {string} bootstrapPath - The folder path containing xctestrun file. + * @property {number|string} wdaRemotePort - The remote port WDA is listening on. + * @property {string|undefined} wdaBindingIP - The IP address to bind to. If not given, it binds to all interfaces. + */ /** * Creates xctestrun file per device & platform version. * We expects to have WebDriverAgentRunner_iphoneos${sdkVersion|platformVersion}-arm64.xctestrun for real device @@ -174,19 +184,17 @@ async function setRealDeviceSecurity (keychainPath, keychainPassword) { * e.g. Xcode which has iOS SDK Version 12.2 on an intel Mac host machine generates WebDriverAgentRunner_iphonesimulator.2-x86_64.xctestrun * even if the cap has platform version 11.4 * - * @param {DeviceInfo} deviceInfo - * @param {string} sdkVersion - The Xcode SDK version of OS. - * @param {string} bootstrapPath - The folder path containing xctestrun file. - * @param {number|string} wdaRemotePort - The remote port WDA is listening on. + * @param {XctestrunFileArgs} args * @return {Promise} returns xctestrunFilePath for given device * @throws if WebDriverAgentRunner_iphoneos${sdkVersion|platformVersion}-arm64.xctestrun for real device * or WebDriverAgentRunner_iphonesimulator${sdkVersion|platformVersion}-x86_64.xctestrun for simulator is not found @bootstrapPath, - * then it will throw file not found exception + * then it will throw a file not found exception */ -async function setXctestrunFile (deviceInfo, sdkVersion, bootstrapPath, wdaRemotePort) { +async function setXctestrunFile (args) { + const {deviceInfo, sdkVersion, bootstrapPath, wdaRemotePort, wdaBindingIP} = args; const xctestrunFilePath = await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath); const xctestRunContent = await plist.parsePlistFile(xctestrunFilePath); - const updateWDAPort = getAdditionalRunContent(deviceInfo.platformName, wdaRemotePort); + const updateWDAPort = getAdditionalRunContent(deviceInfo.platformName, wdaRemotePort, wdaBindingIP); const newXctestRunContent = _.merge(xctestRunContent, updateWDAPort); await plist.updatePlistFile(xctestrunFilePath, newXctestRunContent, true); @@ -197,16 +205,17 @@ async function setXctestrunFile (deviceInfo, sdkVersion, bootstrapPath, wdaRemot * Return the WDA object which appends existing xctest runner content * @param {string} platformName - The name of the platform * @param {number|string} wdaRemotePort - The remote port number - * @return {object} returns a runner object which has USE_PORT + * @param {string|undefined} wdaBindingIP - The IP address to bind to. If not given, it binds to all interfaces. + * @return {object} returns a runner object which has USE_PORT and optionally USE_IP */ -function getAdditionalRunContent (platformName, wdaRemotePort) { +function getAdditionalRunContent (platformName, wdaRemotePort, wdaBindingIP) { const runner = `WebDriverAgentRunner${isTvOS(platformName) ? '_tvOS' : ''}`; - return { [runner]: { EnvironmentVariables: { // USE_PORT must be 'string' - USE_PORT: `${wdaRemotePort}` + USE_PORT: `${wdaRemotePort}`, + ...(wdaBindingIP ? { USE_IP: wdaBindingIP } : {}), } } }; diff --git a/lib/webdriveragent.js b/lib/webdriveragent.js index 7e3f88f07..a48f79626 100644 --- a/lib/webdriveragent.js +++ b/lib/webdriveragent.js @@ -60,7 +60,7 @@ export class WebDriverAgent { this.wdaRemotePort = ((this.isRealDevice ? args.wdaRemotePort : null) ?? args.wdaLocalPort) || WDA_AGENT_PORT; this.wdaBaseUrl = args.wdaBaseUrl || WDA_BASE_URL; - + this.wdaBindingIP = args.wdaBindingIP; this.prebuildWDA = args.prebuildWDA; // this.args.webDriverAgentUrl guiarantees the capabilities acually @@ -104,6 +104,7 @@ export class WebDriverAgent { updatedWDABundleId: this.updatedWDABundleId, launchTimeout: this.wdaLaunchTimeout, wdaRemotePort: this.wdaRemotePort, + wdaBindingIP: this.wdaBindingIP, useXctestrunFile: this.useXctestrunFile, derivedDataPath: args.derivedDataPath, mjpegServerPort: this.mjpegServerPort, @@ -390,6 +391,9 @@ export class WebDriverAgent { if (this.mjpegServerPort) { xctestEnv.MJPEG_SERVER_PORT = this.mjpegServerPort; } + if (this.wdaBindingIP) { + xctestEnv.USE_IP = this.wdaBindingIP; + } this.log.info('Launching WebDriverAgent on the device without xcodebuild'); if (this.isRealDevice) { // Current method to launch WDA process can be done via 'xcrun devicectl', diff --git a/lib/xcodebuild.js b/lib/xcodebuild.js index 8a25361d0..927987f8d 100644 --- a/lib/xcodebuild.js +++ b/lib/xcodebuild.js @@ -83,6 +83,7 @@ export class XcodeBuild { this.launchTimeout = args.launchTimeout; this.wdaRemotePort = args.wdaRemotePort; + this.wdaBindingIP = args.wdaBindingIP; this.updatedWDABundleId = args.updatedWDABundleId; this.derivedDataPath = args.derivedDataPath; @@ -116,12 +117,13 @@ export class XcodeBuild { platformVersion: this.platformVersion || '', platformName: this.platformName || '' }; - this.xctestrunFilePath = await setXctestrunFile( - deviceInfo, - this.iosSdkVersion || '', - this.bootstrapPath, - this.wdaRemotePort || 8100 - ); + this.xctestrunFilePath = await setXctestrunFile({ + deviceInfo, + sdkVersion: this.iosSdkVersion || '', + bootstrapPath: this.bootstrapPath, + wdaRemotePort: this.wdaRemotePort || 8100, + wdaBindingIP: this.wdaBindingIP + }); return; } From 1946c9723e07550dff151ea337913cf5b7c067da Mon Sep 17 00:00:00 2001 From: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:09:54 +0300 Subject: [PATCH 4/4] lib: fix the jsdoc for the optional wdaBindingIp argument Signed-off-by: Karl Baumgartner <178656887+karlbaumg@users.noreply.github.com> --- lib/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 4385ccf55..3d0bb3ec0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -174,7 +174,7 @@ async function setRealDeviceSecurity (keychainPath, keychainPassword) { * @property {string} sdkVersion - The Xcode SDK version of OS. * @property {string} bootstrapPath - The folder path containing xctestrun file. * @property {number|string} wdaRemotePort - The remote port WDA is listening on. - * @property {string|undefined} wdaBindingIP - The IP address to bind to. If not given, it binds to all interfaces. + * @property {string} [wdaBindingIP] - The IP address to bind to. If not given, it binds to all interfaces. */ /** * Creates xctestrun file per device & platform version. @@ -205,7 +205,7 @@ async function setXctestrunFile (args) { * Return the WDA object which appends existing xctest runner content * @param {string} platformName - The name of the platform * @param {number|string} wdaRemotePort - The remote port number - * @param {string|undefined} wdaBindingIP - The IP address to bind to. If not given, it binds to all interfaces. + * @param {string} [wdaBindingIP] - The IP address to bind to. If not given, it binds to all interfaces. * @return {object} returns a runner object which has USE_PORT and optionally USE_IP */ function getAdditionalRunContent (platformName, wdaRemotePort, wdaBindingIP) {