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) {