Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
profile=selenium4
version=28.0.0-SNAPSHOT
version=34.0.1-SNAPSHOT
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.http.client.utils.URIBuilder;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.net.PortProber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -44,6 +45,7 @@
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameC_Servlet;
import com.nordstrom.automation.selenium.servlet.ExamplePageServlet.FrameD_Servlet;
import com.nordstrom.automation.selenium.support.SearchContextWait;
import com.nordstrom.automation.selenium.utility.GridHubPortAllocator;
import com.nordstrom.automation.selenium.utility.HostUtils;
import com.nordstrom.automation.settings.SettingsCore;
import com.nordstrom.common.base.UncheckedThrow;
Expand Down Expand Up @@ -159,8 +161,8 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
* name: <b>selenium.grid.launcher</b><br>
* default: (populated by {@link SeleniumConfig#getDefaults() getDefaults()})
* <ul>
* <li>Selenium 3: <b>org.openqa.grid.selenium.GridLauncherV3</b></li>
* <li>Selenium 4: <b>org.openqa.selenium.grid.Bootstrap</b></li>
* <li>Selenium 3: <b>org.openqa.grid.selenium.GridLauncherV3</b></li>
* </ul>
*/
GRID_LAUNCHER("selenium.grid.launcher", null),
Expand All @@ -178,29 +180,45 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
* This setting specifies the configuration file name/path for the local <b>Selenium Grid</b> hub server.
* <p>
* name: <b>selenium.hub.config</b><br>
* Selenium 3: <b>hubConfig-s3.json</b><br>
* Selenium 4: <b>hubConfig-s4.json</b>
* Selenium 4: <b>hubConfig-s4.json</b><br>
* Selenium 3: <b>hubConfig-s3.json</b>
*/
HUB_CONFIG("selenium.hub.config", null),

/**
* This is the URL for the <b>Selenium Grid</b> endpoint: [scheme:][//authority]/wd/hub
* <p>
* name: <b>selenium.hub.host</b><br>
* Selenium 3: <b>http://&lt;{@code localhost}&gt;:4445/wd/hub</b><br>
* Selenium 4: <b>http://&lt;{@code localhost}&gt;:4446/wd/hub</b>
* Selenium 4: <b>http://&lt;{@code localhost}&gt;:4444/wd/hub</b><br>
* Selenium 3: <b>http://&lt;{@code localhost}&gt;:4445/wd/hub</b>
*/
HUB_HOST("selenium.hub.host", null),

/**
* This is the port assigned to the local <b>Selenium Grid</b> hub server.
* <p>
* name: <b>selenium.hub.port</b><br>
* Selenium 3: <b>4445</b><br>
* Selenium 4: <b>4446</b>
* Selenium 4: <b>4444</b><br>
* Selenium 3: <b>4445</b>
*/
HUB_PORT("selenium.hub.port", null),

/**
* This is the port used by local <b>Selenium Grid</b> components for publishing events.
* <p>
* name: <b>selenium.publish.port</b><br>
* default: <b>4442</b>
*/
PUBLISH_PORT("selenium.publish.port", "4442"),

/**
* This is the port used by local <b>Selenium Grid</b> components for subscribing to events.
* <p>
* name: <b>selenium.subscribe.port</b><br>
* default: <b>4443</b>
*/
SUBSCRIBE_PORT("selenium.subscribe.port", "4443"),

/**
* This setting specifies the slot matcher used by the local <b>Selenium Grid</b> hub server.
*
Expand All @@ -221,8 +239,8 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
* This setting specifies the configuration template name/path for local <b>Selenium Grid</b> node servers.
* <p>
* name: <b>selenium.node.config</b><br>
* Selenium 3: <b>nodeConfig-s3.json</b><br>
* Selenium 4: <b>nodeConfig-s4.json</b>
* Selenium 4: <b>nodeConfig-s4.json</b><br>
* Selenium 3: <b>nodeConfig-s3.json</b>
*/
NODE_CONFIG("selenium.node.config", null),

Expand Down Expand Up @@ -354,11 +372,20 @@ public enum SeleniumSettings implements SettingsCore.SettingsAPI {
*/
CONTEXT_PLATFORM("selenium.context.platform", "support"),

/**
* This setting specifies the port that the {@code Appium} server listens on.
* <p>
* name: <b>appium.server.port</b><br>
* default: <b>4723</b>
*/
APPIUM_SERVER_PORT("appium.server.port", "4723"),

/**
* This setting specifies the path to an {@code Appium} configuration file provided to the server
* when it's launched as a local <b>Selenium Grid</b> node server.
* <p>
* <b>NOTE</b>: If specified, this setting
* <b>NOTE</b>: If specified, the config file indicated by this setting provides base values for the options
* it declares. These values can be supplemented or overridden via the {@link #APPIUM_CLI_ARGS} setting.
* <p>
* name: <b>appium.config.path</b><br>
* default: {@code null}
Expand Down Expand Up @@ -693,6 +720,22 @@ public synchronized URL getHubUrl() {
return hubUrl;
}

/**
* Get the <b>Selenium Grid</b> event bus 'publish' URL.
*
* @return URL for publishing <b>Grid</b> events
* @see SeleniumSettings#PUBLISH_PORT
*/
public abstract String getPublishUrl();

/**
* Get the <b>Selenium Grid</b> event bus 'subscribe' URL.
*
* @return URL for subscribing to <b>Grid</b> events
* @see SeleniumSettings#SUBSCRIBE_PORT
*/
public abstract String getSubscribeUrl();

/**
* Get object that represents the active Selenium Grid.
*
Expand Down Expand Up @@ -822,6 +865,16 @@ public Path getHubConfigPath() {
return hubConfigPath;
}

/**
* Get the port that the {@code Appium} server listens on.
*
* @return {@link SeleniumSettings#APPIUM_SERVER_PORT APPIUM_SERVER_PORT} if defined and available;
* otherwise random available port
*/
public int getAppiumServerPort() {
return getAvailablePort(SeleniumSettings.APPIUM_SERVER_PORT);
}

/**
* Get the path to the Appium configuration.
*
Expand Down Expand Up @@ -1117,4 +1170,25 @@ public boolean appiumWithPM2() {
public String getSettingsPath() {
return SETTINGS_FILE;
}

/**
* Get available port for the specified setting.
*
* @param setting setting to check for port availability
* @return value of setting if defined and available; otherwise random available port
*/
protected int getAvailablePort(final SeleniumSettings setting) {
int port = getInt(setting.key(), -1);
if (port != -1) {
if (!GridHubPortAllocator.isFree(port)) {
LOGGER.warn("{} port '{}' is unavailable; finding free port", setting.name(), port);
port = -1;
}
}
if (port == -1) {
port = PortProber.findFreePort();
System.setProperty(setting.key(), Integer.toString(port));
}
return port;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ default LocalGridServer create(SeleniumConfig config, String launcherClassName,
* @throws IOException if an I/O error occurs
*/
LocalGridServer create(SeleniumConfig config, String launcherClassName, String[] dependencyContexts,
URL hubUrl, final Path workingPath, final Path outputPath) throws IOException;
URL hubUrl, Path workingPath, Path outputPath) throws IOException;

/**
* Get constructor for this driver's {@link RemoteWebDriver} implementation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,25 @@
public class ServerPidFinder {

private enum PidFinder {
WINDOWS("cmd.exe", "/c", "for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d ^| findstr LISTENING') do @echo %%a"),
MAC_UNIX("sh", "-c", "lsof -iTCP:%d -sTCP:LISTEN -t");
WINDOWS("cmd.exe",
"/c",
"for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d ^| findstr LISTENING') do @echo %%a",
"for /f \"tokens=5\" %%a in ('netstat -ano ^| findstr :%d') do @echo %%a"),
MAC_UNIX("sh",
"-c",
"lsof -nP -iTCP:%d -sTCP:LISTEN -t",
"lsof -nP -iTCP:%d -t");

private String executable;
private String commandOption;
private String commandFormat;
private String listenMode;
private String anyMode;

PidFinder(String execuable, String commandOption, String commandFormat) {
PidFinder(String execuable, String commandOption, String listenMode, String anyMode) {
this.executable = execuable;
this.commandOption = commandOption;
this.commandFormat = commandFormat;
this.listenMode = listenMode;
this.anyMode = anyMode;
}

String getExecutable() {
Expand All @@ -34,8 +42,8 @@ String getOption() {
return commandOption;
}

String getCommand(int port) {
return String.format(commandFormat, port);
String getCommand(int port, boolean listen) {
return String.format(listen ? listenMode : anyMode, port);
}
}

Expand All @@ -50,16 +58,17 @@ private ServerPidFinder() {
* Get the process ID of the server listening to the specified port.
*
* @param port {@code localhost} port to check
* @param listen {@code true} to require LISTEN mode; {@code false} to accept any mode
* @return if found, ID of listening process; otherwise {@code null}
*/
public static String getPidOfServerAt(int port) {
public static String getPidOfServerAt(int port, boolean listen) {
String pid = null;

try {
PidFinder finder =
OSInfo.getDefault().getType() == OSInfo.OSType.WINDOWS ? PidFinder.WINDOWS : PidFinder.MAC_UNIX;

ProcessBuilder pb = new ProcessBuilder(finder.getExecutable(), finder.getOption(), finder.getCommand(port));
ProcessBuilder pb =
new ProcessBuilder(finder.getExecutable(), finder.getOption(), finder.getCommand(port, listen));
Process process = pb.start();

try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.nordstrom.automation.selenium.grid;

public final class BackendCapabilities {

private final boolean dynamicPorts;
private final boolean managedNodes;
private final boolean gracefulShutdown;
private final boolean healthCheck;
private final boolean externalDiscovery;

public BackendCapabilities(boolean dynamicPorts,
boolean managedNodes,
boolean gracefulShutdown,
boolean healthCheck,
boolean externalDiscovery) {
this.dynamicPorts = dynamicPorts;
this.managedNodes = managedNodes;
this.gracefulShutdown = gracefulShutdown;
this.healthCheck = healthCheck;
this.externalDiscovery = externalDiscovery;
}

public boolean supportsDynamicPorts() { return dynamicPorts; }

public boolean supportsManagedNodes() { return managedNodes; }

public boolean supportsGracefulShutdown() { return gracefulShutdown; }

public boolean supportsHealthCheck() { return healthCheck; }

public boolean supportsExternalDiscovery() { return externalDiscovery; }

public static BackendCapabilities selenium3() {
return new BackendCapabilities(
true,
true,
false,
true,
true
);
}

public static BackendCapabilities selenium4() {
return new BackendCapabilities(
true,
true,
true,
true,
true
);
}

BackendCapabilities dockerCaps = new BackendCapabilities(
false,
false,
true,
true,
true
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.nordstrom.automation.selenium.grid;

import com.nordstrom.automation.selenium.interfaces.BackendCapabilities;
import com.nordstrom.automation.selenium.interfaces.GridBackend;

public class GridControlPlane implements GridBackend {

private final GridBackend backend;

public GridControlPlane(GridBackend backend) {
this.backend = backend;
}

public HubInstance startHub(HubSpec spec) {
return backend.startHub(spec);
}

public NodeInstance startNode(NodeSpec spec) {
return backend.startNode(spec);
}

public void requestHubShutdown(HubInstance instance) {
backend.requestHubShutdown(instance);
}

public void requestNodeShutdown(NodeInstance node) {
backend.requestNodeShutdown(node);
}

public BackendCapabilities capabilities() {
return backend.capabilities();
}

}
Loading