Skip to content

Commit 5d3f2b7

Browse files
committed
Merge remote-tracking branch 'origin/trunk' into router-cache
2 parents 05f2d38 + 4ecdae3 commit 5d3f2b7

File tree

15 files changed

+2221
-146
lines changed

15 files changed

+2221
-146
lines changed

java/src/org/openqa/selenium/firefox/GeckoDriverService.java

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ public static class Builder
147147
private @Nullable FirefoxDriverLogLevel logLevel;
148148
private @Nullable Boolean logTruncate;
149149
private @Nullable File profileRoot;
150+
private @Nullable Integer marionettePort;
151+
private @Nullable Integer websocketPort;
150152

151153
@Override
152154
public int score(Capabilities capabilities) {
@@ -204,6 +206,31 @@ public GeckoDriverService.Builder withProfileRoot(@Nullable File root) {
204206
return this;
205207
}
206208

209+
/**
210+
* Configures geckodriver to connect to an existing Firefox instance via the specified
211+
* Marionette port.
212+
*
213+
* @param marionettePort The port where Marionette is listening on the existing Firefox
214+
* instance.
215+
* @return A self reference.
216+
*/
217+
public GeckoDriverService.Builder connectToExisting(int marionettePort) {
218+
this.marionettePort = marionettePort;
219+
return this;
220+
}
221+
222+
/**
223+
* Configures the WebSocket port for BiDi. A value of 0 will automatically allocate a free port.
224+
*
225+
* @param websocketPort The port to use for WebSocket communication, or 0 for automatic
226+
* allocation.
227+
* @return A self reference.
228+
*/
229+
public GeckoDriverService.Builder withWebSocketPort(@Nullable Integer websocketPort) {
230+
this.websocketPort = websocketPort;
231+
return this;
232+
}
233+
207234
@Override
208235
protected void loadSystemProperties() {
209236
parseLogOutput(GECKO_DRIVER_LOG_PROPERTY);
@@ -229,13 +256,27 @@ protected List<String> createArgs() {
229256
List<String> args = new ArrayList<>();
230257
args.add(String.format(Locale.ROOT, "--port=%d", getPort()));
231258

232-
int wsPort = PortProber.findFreePort();
233-
args.add(String.format("--websocket-port=%d", wsPort));
234-
235-
args.add("--allow-origins");
236-
args.add(String.format("http://127.0.0.1:%d", wsPort));
237-
args.add(String.format("http://localhost:%d", wsPort));
238-
args.add(String.format("http://[::1]:%d", wsPort));
259+
// Check if marionette port is specified via connectToExisting method
260+
if (marionettePort != null) {
261+
args.add("--connect-existing");
262+
args.add("--marionette-port");
263+
args.add(String.valueOf(marionettePort));
264+
} else {
265+
// Configure websocket port for BiDi communication
266+
if (websocketPort != null) {
267+
args.add("--websocket-port");
268+
args.add(String.valueOf(websocketPort));
269+
270+
args.add("--allow-origins");
271+
args.add(String.format("http://127.0.0.1:%d", websocketPort));
272+
args.add(String.format("http://localhost:%d", websocketPort));
273+
args.add(String.format("http://[::1]:%d", websocketPort));
274+
} else {
275+
// Use 0 to auto-allocate a free port
276+
args.add("--websocket-port");
277+
args.add("0");
278+
}
279+
}
239280

240281
if (logLevel != null) {
241282
args.add("--log");

java/src/org/openqa/selenium/grid/distributor/local/LocalDistributor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ public void run() {
539539
// up starving a session request.
540540
Map<Capabilities, Long> stereotypes =
541541
getAvailableNodes().stream()
542-
.filter(node -> node.hasCapacity())
542+
.filter(NodeStatus::hasCapacity)
543543
.flatMap(node -> node.getSlots().stream().map(Slot::getStereotype))
544544
.collect(
545545
Collectors.groupingBy(ImmutableCapabilities::copyOf, Collectors.counting()));

java/src/org/openqa/selenium/grid/distributor/local/LocalNodeRegistry.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,8 @@ public Set<NodeStatus> getAvailableNodes() {
361361
readLock.lock();
362362
try {
363363
return model.getSnapshot().stream()
364-
.filter(
365-
node ->
366-
!DOWN.equals(node.getAvailability()) && !DRAINING.equals(node.getAvailability()))
364+
// Filter nodes are UP and have capacity (available slots)
365+
.filter(node -> UP.equals(node.getAvailability()) && node.hasCapacity())
367366
.collect(ImmutableSet.toImmutableSet());
368367
} finally {
369368
readLock.unlock();

java/src/org/openqa/selenium/grid/sessionqueue/local/LocalNewSessionQueue.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.openqa.selenium.grid.data.TraceSessionRequest;
5656
import org.openqa.selenium.grid.distributor.config.DistributorOptions;
5757
import org.openqa.selenium.grid.jmx.JMXHelper;
58+
import org.openqa.selenium.grid.jmx.MBean;
5859
import org.openqa.selenium.grid.jmx.ManagedAttribute;
5960
import org.openqa.selenium.grid.jmx.ManagedService;
6061
import org.openqa.selenium.grid.log.LoggingOptions;
@@ -110,6 +111,7 @@ public class LocalNewSessionQueue extends NewSessionQueue implements Closeable {
110111
thread.setName(NAME);
111112
return thread;
112113
});
114+
private final MBean jmxBean;
113115

114116
public LocalNewSessionQueue(
115117
Tracer tracer,
@@ -139,7 +141,8 @@ public LocalNewSessionQueue(
139141
requestTimeoutCheck.toMillis(),
140142
MILLISECONDS);
141143

142-
new JMXHelper().register(this);
144+
// Manage JMX and unregister on close()
145+
this.jmxBean = new JMXHelper().register(this);
143146
}
144147

145148
public static NewSessionQueue create(Config config) {
@@ -502,6 +505,10 @@ public boolean isReady() {
502505
@Override
503506
public void close() {
504507
shutdownGracefully(NAME, service);
508+
509+
if (jmxBean != null) {
510+
new JMXHelper().unregister(jmxBean.getObjectName());
511+
}
505512
}
506513

507514
private void failDueToTimeout(RequestId reqId) {

java/test/org/openqa/selenium/firefox/FirefoxDriverConcurrentTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.openqa.selenium.ParallelTestRunner.Worker;
2929
import org.openqa.selenium.WebDriver;
3030
import org.openqa.selenium.WebElement;
31+
import org.openqa.selenium.bidi.BiDi;
3132
import org.openqa.selenium.testing.JupiterTestBase;
3233
import org.openqa.selenium.testing.drivers.WebDriverBuilder;
3334

@@ -164,4 +165,69 @@ void shouldBeAbleToUseTheSameProfileMoreThanOnce() {
164165
if (two != null) two.quit();
165166
}
166167
}
168+
169+
@Test
170+
void multipleFirefoxInstancesWithBiDiEnabledCanRunSimultaneously() {
171+
// Create two Firefox instances with BiDi enabled, should use different ports
172+
FirefoxOptions options1 = new FirefoxOptions().enableBiDi();
173+
FirefoxOptions options2 = new FirefoxOptions().enableBiDi();
174+
175+
WebDriver driver1 = null;
176+
WebDriver driver2 = null;
177+
178+
try {
179+
driver1 = new WebDriverBuilder().get(options1);
180+
BiDi biDi1 = ((FirefoxDriver) driver1).getBiDi();
181+
assertThat(biDi1).isNotNull();
182+
183+
// Extract the BiDi websocket URL and port for the first instance
184+
String webSocketUrl1 =
185+
(String) ((FirefoxDriver) driver1).getCapabilities().getCapability("webSocketUrl");
186+
String port1 = webSocketUrl1.replaceAll("^ws://[^:]+:(\\d+)/.*$", "$1");
187+
188+
driver2 = new WebDriverBuilder().get(options2);
189+
BiDi biDi2 = ((FirefoxDriver) driver2).getBiDi();
190+
assertThat(biDi2).isNotNull();
191+
192+
// Extract the BiDi websocket URL and port for the second instance
193+
String webSocketUrl2 =
194+
(String) ((FirefoxDriver) driver2).getCapabilities().getCapability("webSocketUrl");
195+
String port2 = webSocketUrl2.replaceAll("^ws://[^:]+:(\\d+)/.*$", "$1");
196+
197+
// Verify that the ports are different
198+
assertThat(port1).isNotEqualTo(port2);
199+
} finally {
200+
// Clean up
201+
if (driver1 != null) {
202+
driver1.quit();
203+
}
204+
if (driver2 != null) {
205+
driver2.quit();
206+
}
207+
}
208+
}
209+
210+
@Test
211+
void geckoDriverServiceConnectToExistingFirefox() {
212+
GeckoDriverService.Builder builder = new GeckoDriverService.Builder();
213+
214+
// Test connectToExisting method
215+
builder.connectToExisting(2829);
216+
GeckoDriverService service = builder.build();
217+
218+
assertThat(service).isNotNull();
219+
service.stop();
220+
}
221+
222+
@Test
223+
void geckoDriverServiceCustomWebSocketPort() {
224+
GeckoDriverService.Builder builder = new GeckoDriverService.Builder();
225+
226+
// Test withWebSocketPort method
227+
builder.withWebSocketPort(9225);
228+
GeckoDriverService service = builder.build();
229+
230+
assertThat(service).isNotNull();
231+
service.stop();
232+
}
167233
}

0 commit comments

Comments
 (0)