diff --git a/java/src/org/openqa/selenium/PersistentCapabilities.java b/java/src/org/openqa/selenium/PersistentCapabilities.java index d42e08229b6a5..fc0dd14205ddc 100644 --- a/java/src/org/openqa/selenium/PersistentCapabilities.java +++ b/java/src/org/openqa/selenium/PersistentCapabilities.java @@ -50,7 +50,6 @@ private PersistentCapabilities(Capabilities previousValues, Capabilities newValu public PersistentCapabilities setCapability(String name, Object value) { Require.nonNull("Name", name); - Require.nonNull("Value", value); return new PersistentCapabilities(this, new ImmutableCapabilities(name, value)); } diff --git a/java/src/org/openqa/selenium/chrome/ChromeDriver.java b/java/src/org/openqa/selenium/chrome/ChromeDriver.java index 507d703eebc2a..ead048107f072 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeDriver.java +++ b/java/src/org/openqa/selenium/chrome/ChromeDriver.java @@ -26,7 +26,6 @@ import org.openqa.selenium.chromium.ChromiumDriver; import org.openqa.selenium.chromium.ChromiumDriverCommandExecutor; import org.openqa.selenium.internal.Require; -import org.openqa.selenium.manager.SeleniumManagerOutput.Result; import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.RemoteWebDriverBuilder; @@ -96,10 +95,11 @@ private static ChromeDriverCommandExecutor generateExecutor( Require.nonNull("Driver service", service); Require.nonNull("Driver options", options); Require.nonNull("Driver clientConfig", clientConfig); - Result result = DriverFinder.getPath(service, options); - service.setExecutable(result.getDriverPath()); - if (result.getBrowserPath() != null && !result.getBrowserPath().isEmpty()) { - options.setBinary(result.getBrowserPath()); + DriverFinder finder = new DriverFinder(service, options); + service.setExecutable(finder.getDriverPath()); + if (finder.hasBrowserPath()) { + options.setBinary(finder.getBrowserPath()); + options.setCapability("browserVersion", (Object) null); } return new ChromeDriverCommandExecutor(service, clientConfig); } diff --git a/java/src/org/openqa/selenium/chrome/ChromeDriverInfo.java b/java/src/org/openqa/selenium/chrome/ChromeDriverInfo.java index ed69ab80517cb..8f364520f9cbd 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeDriverInfo.java +++ b/java/src/org/openqa/selenium/chrome/ChromeDriverInfo.java @@ -21,17 +21,14 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; import org.openqa.selenium.chromium.ChromiumDriverInfo; import org.openqa.selenium.remote.CapabilityType; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @AutoService(WebDriverInfo.class) @@ -65,29 +62,14 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - DriverFinder.getPath(ChromeDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(ChromeDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - DriverFinder.getPath( - ChromeDriverService.createDefaultService(), getCanonicalCapabilities(), true); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(ChromeDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java index 7aba516bf86b0..09d4857b911c8 100644 --- a/java/src/org/openqa/selenium/chrome/ChromeDriverService.java +++ b/java/src/org/openqa/selenium/chrome/ChromeDriverService.java @@ -128,7 +128,7 @@ public Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link ChromeDriverService} using the default configuration. In * this configuration, the service will use the ChromeDriver executable identified by {@link - * org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, Capabilities)}. Each + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each * service created by this method will be configured to use a free port on the current system. * * @return A new ChromeDriverService using the default configuration. diff --git a/java/src/org/openqa/selenium/edge/EdgeDriver.java b/java/src/org/openqa/selenium/edge/EdgeDriver.java index 3c47bc68dcc88..c36fde7132d73 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriver.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriver.java @@ -24,7 +24,6 @@ import org.openqa.selenium.chromium.ChromiumDriver; import org.openqa.selenium.chromium.ChromiumDriverCommandExecutor; import org.openqa.selenium.internal.Require; -import org.openqa.selenium.manager.SeleniumManagerOutput.Result; import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.RemoteWebDriverBuilder; @@ -68,10 +67,11 @@ private static EdgeDriverCommandExecutor generateExecutor( Require.nonNull("Driver service", service); Require.nonNull("Driver options", options); Require.nonNull("Driver clientConfig", clientConfig); - Result result = DriverFinder.getPath(service, options); - service.setExecutable(result.getDriverPath()); - if (result.getBrowserPath() != null && !result.getBrowserPath().isEmpty()) { - options.setBinary(result.getBrowserPath()); + DriverFinder finder = new DriverFinder(service, options); + service.setExecutable(finder.getDriverPath()); + if (finder.hasBrowserPath()) { + options.setBinary(finder.getBrowserPath()); + options.setCapability("browserVersion", (Object) null); } return new EdgeDriverCommandExecutor(service, clientConfig); } diff --git a/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java b/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java index 3fb25ba752413..728859c989b6c 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriverInfo.java @@ -21,17 +21,14 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; import org.openqa.selenium.chromium.ChromiumDriverInfo; import org.openqa.selenium.remote.CapabilityType; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @AutoService(WebDriverInfo.class) @@ -68,29 +65,14 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - DriverFinder.getPath(EdgeDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(EdgeDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - DriverFinder.getPath( - EdgeDriverService.createDefaultService(), getCanonicalCapabilities(), true); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(EdgeDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/edge/EdgeDriverService.java b/java/src/org/openqa/selenium/edge/EdgeDriverService.java index 087d8215bcee6..2aca47ff37a27 100644 --- a/java/src/org/openqa/selenium/edge/EdgeDriverService.java +++ b/java/src/org/openqa/selenium/edge/EdgeDriverService.java @@ -117,7 +117,7 @@ public Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link EdgeDriverService} using the default configuration. In this * configuration, the service will use the MSEdgeDriver executable identified by the {@link - * org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, Capabilities)}. Each + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each * service created by this method will be configured to use a free port on the current system. * * @return A new EdgeDriverService using the default configuration. diff --git a/java/src/org/openqa/selenium/firefox/FirefoxDriver.java b/java/src/org/openqa/selenium/firefox/FirefoxDriver.java index 89607a94eb60d..36111d411c9a0 100644 --- a/java/src/org/openqa/selenium/firefox/FirefoxDriver.java +++ b/java/src/org/openqa/selenium/firefox/FirefoxDriver.java @@ -51,7 +51,6 @@ import org.openqa.selenium.html5.SessionStorage; import org.openqa.selenium.html5.WebStorage; import org.openqa.selenium.internal.Require; -import org.openqa.selenium.manager.SeleniumManagerOutput.Result; import org.openqa.selenium.remote.CommandInfo; import org.openqa.selenium.remote.FileDetector; import org.openqa.selenium.remote.RemoteWebDriver; @@ -138,10 +137,11 @@ private static FirefoxDriverCommandExecutor generateExecutor( Require.nonNull("Driver service", service); Require.nonNull("Driver options", options); Require.nonNull("Driver clientConfig", clientConfig); - Result result = DriverFinder.getPath(service, options); - service.setExecutable(result.getDriverPath()); - if (result.getBrowserPath() != null && !result.getBrowserPath().isEmpty()) { - options.setBinary(result.getBrowserPath()); + DriverFinder finder = new DriverFinder(service, options); + service.setExecutable(finder.getDriverPath()); + if (finder.hasBrowserPath()) { + options.setBinary(finder.getBrowserPath()); + options.setCapability("browserVersion", (Object) null); } return new FirefoxDriverCommandExecutor(service, clientConfig); } diff --git a/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java b/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java index 2f4f5001e8058..e643f6d502c89 100644 --- a/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java +++ b/java/src/org/openqa/selenium/firefox/GeckoDriverInfo.java @@ -22,15 +22,12 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @AutoService(WebDriverInfo.class) @@ -68,29 +65,14 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - DriverFinder.getPath(GeckoDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(GeckoDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - DriverFinder.getPath( - GeckoDriverService.createDefaultService(), getCanonicalCapabilities(), true); - return true; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return new DriverFinder(GeckoDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/firefox/GeckoDriverService.java b/java/src/org/openqa/selenium/firefox/GeckoDriverService.java index d9d4479a90eac..c2a73827046b8 100644 --- a/java/src/org/openqa/selenium/firefox/GeckoDriverService.java +++ b/java/src/org/openqa/selenium/firefox/GeckoDriverService.java @@ -110,7 +110,7 @@ public Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link GeckoDriverService} using the default configuration. In * this configuration, the service will use the GeckoDriver executable identified by the {@link - * org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, Capabilities)}. Each + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each * service created by this method will be configured to use a free port on the current system. * * @return A new GeckoDriverService using the default configuration. diff --git a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java index 8648247f315c9..b1ff2cec5b07f 100644 --- a/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java +++ b/java/src/org/openqa/selenium/grid/node/config/DriverServiceSessionFactory.java @@ -49,7 +49,6 @@ import org.openqa.selenium.grid.node.SessionFactory; import org.openqa.selenium.internal.Either; import org.openqa.selenium.internal.Require; -import org.openqa.selenium.manager.SeleniumManagerOutput.Result; import org.openqa.selenium.net.HostIdentifier; import org.openqa.selenium.net.NetworkUtils; import org.openqa.selenium.remote.Command; @@ -130,10 +129,10 @@ public Either apply(CreateSessionRequest sess attributeMap.put(AttributeKey.LOGGER_CLASS.getKey(), this.getClass().getName()); DriverService service = builder.build(); - Result driverResult = DriverFinder.getPath(service, capabilities); - service.setExecutable(driverResult.getDriverPath()); - if (driverResult.getBrowserPath() != null && !driverResult.getBrowserPath().isEmpty()) { - capabilities = setBrowserBinary(capabilities, driverResult.getBrowserPath()); + DriverFinder finder = new DriverFinder(service, capabilities); + service.setExecutable(finder.getDriverPath()); + if (finder.hasBrowserPath()) { + capabilities = setBrowserBinary(capabilities, finder.getBrowserPath()); } Optional platformName = Optional.ofNullable(capabilities.getPlatformName()); @@ -327,7 +326,8 @@ private Capabilities setBrowserBinary(Capabilities options, String browserPath) (Map) options.getCapability(vendorOptionsCapability); vendorOptions.put("binary", browserPath); return new PersistentCapabilities(options) - .setCapability(vendorOptionsCapability, vendorOptions); + .setCapability(vendorOptionsCapability, vendorOptions) + .setCapability("browserVersion", null); } catch (Exception e) { LOG.warning( String.format( diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriver.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriver.java index 40e414b7e577e..daa8765bdb000 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriver.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriver.java @@ -21,7 +21,6 @@ import org.openqa.selenium.Capabilities; import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriverException; -import org.openqa.selenium.manager.SeleniumManagerOutput.Result; import org.openqa.selenium.remote.FileDetector; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.RemoteWebDriverBuilder; @@ -125,8 +124,7 @@ public InternetExplorerDriver( if (service == null) { service = InternetExplorerDriverService.createDefaultService(); } - Result result = DriverFinder.getPath(service, options); - service.setExecutable(result.getDriverPath()); + service.setExecutable(new DriverFinder(service, options).getDriverPath()); if (clientConfig == null) { clientConfig = ClientConfig.defaultConfig(); } diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java index 0ba7ae264785f..916bf7b0efce4 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriverInfo.java @@ -22,16 +22,13 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Platform; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @AutoService(WebDriverInfo.class) @@ -65,36 +62,18 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - if (Platform.getCurrent().is(Platform.WINDOWS)) { - DriverFinder.getPath( - InternetExplorerDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.WINDOWS) + && new DriverFinder( + InternetExplorerDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - if (Platform.getCurrent().is(Platform.WINDOWS)) { - DriverFinder.getPath( - InternetExplorerDriverService.createDefaultService(), getCanonicalCapabilities(), true); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.WINDOWS) + && new DriverFinder( + InternetExplorerDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java index b69a0f4977111..7e07f15f5423a 100644 --- a/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java +++ b/java/src/org/openqa/selenium/ie/InternetExplorerDriverService.java @@ -104,9 +104,9 @@ public Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link InternetExplorerDriverService} using the default * configuration. In this configuration, the service will use the IEDriverServer executable - * identified by the {@link org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, - * Capabilities)}. Each service created by this method will be configured to use a free port on - * the current system. + * identified by the {@link + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each + * service created by this method will be configured to use a free port on the current system. * * @return A new InternetExplorerDriverService using the default configuration. */ diff --git a/java/src/org/openqa/selenium/internal/Require.java b/java/src/org/openqa/selenium/internal/Require.java index 51517dd6b1ce2..24fcd9311334b 100644 --- a/java/src/org/openqa/selenium/internal/Require.java +++ b/java/src/org/openqa/selenium/internal/Require.java @@ -42,6 +42,7 @@ public final class Require { private static final String MUST_BE_DIR = "%s must be a directory: %s"; private static final String MUST_BE_FILE = "%s must be a regular file: %s"; private static final String MUST_BE_EQUAL = "%s must be equal to `%s`"; + private static final String MUST_BE_EXECUTABLE = "%s must be executable: %s"; private static final String MUST_BE_NON_NEGATIVE = "%s must be 0 or greater"; private static final String MUST_BE_POSITIVE = "%s must be greater than 0"; @@ -362,6 +363,15 @@ public File isDirectory() { } return file; } + + public File isExecutable() { + isFile(); + if (!file.canExecute()) { + throw new IllegalStateException( + String.format(MUST_BE_EXECUTABLE, name, file.getAbsolutePath())); + } + return file; + } } public static class PathStateChecker { diff --git a/java/src/org/openqa/selenium/manager/SeleniumManager.java b/java/src/org/openqa/selenium/manager/SeleniumManager.java index 8b2cde2a5f6a2..75af5188e20ae 100644 --- a/java/src/org/openqa/selenium/manager/SeleniumManager.java +++ b/java/src/org/openqa/selenium/manager/SeleniumManager.java @@ -27,18 +27,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Beta; import org.openqa.selenium.BuildInfo; -import org.openqa.selenium.Capabilities; -import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.Platform; -import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.json.Json; import org.openqa.selenium.json.JsonException; @@ -72,7 +66,7 @@ public class SeleniumManager { private static volatile SeleniumManager manager; private final String managerPath = System.getenv("SE_MANAGER_PATH"); private Path binary = managerPath == null ? null : Paths.get(managerPath); - private String seleniumManagerVersion; + private final String seleniumManagerVersion; private boolean binaryInTemporalFolder = false; /** Wrapper for the Selenium Manager binary. */ @@ -201,9 +195,7 @@ private synchronized Path getBinary() { binary.getParent().toFile().mkdirs(); Files.copy(inputStream, binary); } - binary.toFile().setExecutable(true); } - } catch (Exception e) { throw new WebDriverException("Unable to obtain Selenium Manager Binary", e); } @@ -214,98 +206,26 @@ private synchronized Path getBinary() { binary.toFile().setExecutable(true); LOG.fine(String.format("Selenium Manager binary found at: %s", binary)); - return binary; } /** - * Returns the browser binary path when present in the vendor options - * - * @param options browser options used to start the session - * @return the browser binary path when present, only Chrome/Firefox/Edge - */ - private String getBrowserBinary(Capabilities options) { - List vendorOptionsCapabilities = - Arrays.asList("moz:firefoxOptions", "goog:chromeOptions", "ms:edgeOptions"); - for (String vendorOptionsCapability : vendorOptionsCapabilities) { - if (options.asMap().containsKey(vendorOptionsCapability)) { - try { - @SuppressWarnings("unchecked") - Map vendorOptions = - (Map) options.getCapability(vendorOptionsCapability); - return (String) vendorOptions.get("binary"); - } catch (Exception e) { - LOG.warning( - String.format( - "Exception while retrieving the browser binary path. %s: %s", - options, e.getMessage())); - } - } - } - return null; - } - - /** - * Determines the location of the correct driver. + * Executes Selenium Manager to get the locations of the requested assets * - * @param options Browser Options instance. - * @return the location of the driver. + * @param arguments List of command line arguments to send to Selenium Manager binary + * @return the locations of the assets from Selenium Manager execution */ - public Result getDriverPath(Capabilities options, boolean offline) { - Path binaryFile = getBinary(); - if (binaryFile == null) { - return null; - } - - List arguments = new ArrayList<>(); - arguments.add("--browser"); - arguments.add(options.getBrowserName()); + public Result getBinaryPaths(List arguments) { arguments.add("--language-binding"); arguments.add("java"); arguments.add("--output"); arguments.add("json"); - if (!options.getBrowserVersion().isEmpty()) { - arguments.add("--browser-version"); - arguments.add(options.getBrowserVersion()); - // We know the browser binary path, we don't need the browserVersion. - // Useful when "beta" is specified as browserVersion, but the browser driver cannot match it. - if (options instanceof MutableCapabilities) { - ((MutableCapabilities) options).setCapability("browserVersion", (String) null); - } - } - - String browserBinary = getBrowserBinary(options); - if (browserBinary != null && !browserBinary.isEmpty()) { - arguments.add("--browser-path"); - arguments.add(browserBinary); - } - if (getLogLevel().intValue() <= Level.FINE.intValue()) { arguments.add("--debug"); } - if (offline) { - arguments.add("--offline"); - } - - Proxy proxy = Proxy.extractFrom(options); - if (proxy != null) { - if (proxy.getSslProxy() != null) { - arguments.add("--proxy"); - arguments.add(proxy.getSslProxy()); - } else if (proxy.getHttpProxy() != null) { - arguments.add("--proxy"); - arguments.add(proxy.getHttpProxy()); - } - } - - Result result = runCommand(binaryFile, arguments); - LOG.fine( - String.format( - "Using driver at location: %s, browser at location %s", - result.getDriverPath(), result.getBrowserPath())); - return result; + return runCommand(getBinary(), arguments); } private Level getLogLevel() { diff --git a/java/src/org/openqa/selenium/remote/NoSuchDriverException.java b/java/src/org/openqa/selenium/remote/NoSuchDriverException.java index 8075ae8190213..664396e9f3ac6 100644 --- a/java/src/org/openqa/selenium/remote/NoSuchDriverException.java +++ b/java/src/org/openqa/selenium/remote/NoSuchDriverException.java @@ -22,7 +22,7 @@ import org.openqa.selenium.remote.service.DriverService; /** - * Thrown by {@link org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, + * Thrown by {@link org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, * Capabilities)}. */ public class NoSuchDriverException extends WebDriverException { diff --git a/java/src/org/openqa/selenium/remote/service/DriverFinder.java b/java/src/org/openqa/selenium/remote/service/DriverFinder.java index f87d7d8dd5e47..35d20505a07a9 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverFinder.java +++ b/java/src/org/openqa/selenium/remote/service/DriverFinder.java @@ -18,8 +18,15 @@ package org.openqa.selenium.remote.service; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; +import org.openqa.selenium.Proxy; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.internal.Require; import org.openqa.selenium.manager.SeleniumManager; import org.openqa.selenium.manager.SeleniumManagerOutput.Result; @@ -28,51 +35,154 @@ public class DriverFinder { private static final Logger LOG = Logger.getLogger(DriverFinder.class.getName()); + private final DriverService service; + private final Capabilities options; + private final SeleniumManager seleniumManager; + private boolean offline; + private Result result; - public static Result getPath(DriverService service, Capabilities options) { - return getPath(service, options, false); + public DriverFinder(DriverService service, Capabilities options) { + this(service, options, SeleniumManager.getInstance()); } - public static Result getPath(DriverService service, Capabilities options, boolean offline) { - Require.nonNull("Browser options", options); - String driverName = service.getDriverName(); + DriverFinder(DriverService service, Capabilities options, SeleniumManager seleniumManager) { + this.service = service; + this.options = options; + this.seleniumManager = seleniumManager; + } - Result result = new Result(service.getExecutable()); - if (result.getDriverPath() == null) { - result = new Result(System.getProperty(service.getDriverProperty())); - if (result.getDriverPath() == null) { - try { - result = SeleniumManager.getInstance().getDriverPath(options, offline); - } catch (RuntimeException e) { - throw new NoSuchDriverException( - String.format("Unable to obtain: %s, error %s", options, e.getMessage()), e); + public String getDriverPath() { + return getBinaryPaths().getDriverPath(); + } + + public String getBrowserPath() { + return getBinaryPaths().getBrowserPath(); + } + + public boolean isAvailable() { + try { + offline = false; + getBinaryPaths(); + return true; + } catch (NoSuchDriverException e) { + return false; + } catch (IllegalStateException | WebDriverException e) { + LOG.log(Level.WARNING, "failed to discover driver path", e); + return false; + } + } + + public boolean isPresent() { + try { + offline = true; + getBinaryPaths(); + return true; + } catch (NoSuchDriverException e) { + return false; + } catch (IllegalStateException | WebDriverException e) { + LOG.log(Level.WARNING, "failed to discover driver path", e); + return false; + } + } + + public boolean hasBrowserPath() { + String browserPath = result.getBrowserPath(); + return browserPath != null && !browserPath.isEmpty(); + } + + private Result getBinaryPaths() { + if (result == null) { + try { + String driverName = service.getDriverName(); + result = new Result(service.getExecutable()); + if (result.getDriverPath() == null) { + result = new Result(System.getProperty(service.getDriverProperty())); + if (result.getDriverPath() == null) { + List arguments = toArguments(); + result = seleniumManager.getBinaryPaths(arguments); + Require.state(options.getBrowserName(), new File(result.getBrowserPath())) + .isExecutable(); + } else { + LOG.fine( + String.format( + "Skipping Selenium Manager, path to %s found in system property: %s", + driverName, result.getDriverPath())); + } + } else { + LOG.fine( + String.format( + "Skipping Selenium Manager, path to %s specified in Service class: %s", + driverName, result.getDriverPath())); } - } else { - LOG.fine( + + Require.state(driverName, new File(result.getDriverPath())).isExecutable(); + } catch (RuntimeException e) { + throw new NoSuchDriverException( String.format( - "Skipping Selenium Manager, path to %s found in system property: %s", - driverName, result.getDriverPath())); + "Unable to obtain: %s, error %s", service.getDriverName(), e.getMessage()), + e); } - } else { - LOG.fine( - String.format( - "Skipping Selenium Manager, path to %s specified in Service class: %s", - driverName, result.getDriverPath())); } - String message; - if (result.getDriverPath() == null) { - message = String.format("Unable to locate or obtain %s", driverName); - } else if (!new File(result.getDriverPath()).exists()) { - message = - String.format("%s at location %s, does not exist", driverName, result.getDriverPath()); - } else if (!new File(result.getDriverPath()).canExecute()) { - message = - String.format("%s located at %s, cannot be executed", driverName, result.getDriverPath()); - } else { - return result; + return result; + } + + private List toArguments() { + List arguments = new ArrayList<>(); + arguments.add("--browser"); + arguments.add(options.getBrowserName()); + + if (!options.getBrowserVersion().isEmpty()) { + arguments.add("--browser-version"); + arguments.add(options.getBrowserVersion()); + } + + String browserBinary = getBrowserBinary(options); + if (browserBinary != null && !browserBinary.isEmpty()) { + arguments.add("--browser-path"); + arguments.add(browserBinary); + } + + if (offline) { + arguments.add("--offline"); } - throw new NoSuchDriverException(message); + Proxy proxy = Proxy.extractFrom(options); + if (proxy != null) { + arguments.add("--proxy"); + if (proxy.getSslProxy() != null) { + arguments.add(proxy.getSslProxy()); + } else if (proxy.getHttpProxy() != null) { + arguments.add(proxy.getHttpProxy()); + } + } + return arguments; + } + + /** + * Returns the browser binary path when present in the vendor options + * + * @param options browser options used to start the session + * @return the browser binary path when present, only Chrome/Firefox/Edge + */ + private static String getBrowserBinary(Capabilities options) { + List vendorOptionsCapabilities = + Arrays.asList("moz:firefoxOptions", "goog:chromeOptions", "ms:edgeOptions"); + for (String vendorOptionsCapability : vendorOptionsCapabilities) { + if (options.asMap().containsKey(vendorOptionsCapability)) { + try { + @SuppressWarnings("unchecked") + Map vendorOptions = + (Map) options.getCapability(vendorOptionsCapability); + return (String) vendorOptions.get("binary"); + } catch (Exception e) { + LOG.warning( + String.format( + "Exception while retrieving the browser binary path. %s: %s", + options, e.getMessage())); + } + } + } + return null; } } diff --git a/java/src/org/openqa/selenium/remote/service/DriverService.java b/java/src/org/openqa/selenium/remote/service/DriverService.java index a2b4d321d2b85..90fb1f2ca127d 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverService.java +++ b/java/src/org/openqa/selenium/remote/service/DriverService.java @@ -196,7 +196,7 @@ public void start() throws IOException { if (getDefaultDriverOptions().getBrowserName().isEmpty()) { throw new WebDriverException("Driver executable is null and browser name is not set."); } - this.executable = DriverFinder.getPath(this, getDefaultDriverOptions()).getDriverPath(); + this.executable = new DriverFinder(this, getDefaultDriverOptions()).getDriverPath(); } LOG.fine(String.format("Starting driver at %s with %s", this.executable, this.args)); diff --git a/java/src/org/openqa/selenium/safari/SafariDriver.java b/java/src/org/openqa/selenium/safari/SafariDriver.java index 2500e60b48dca..3dc2198a2a7ae 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriver.java +++ b/java/src/org/openqa/selenium/safari/SafariDriver.java @@ -91,10 +91,8 @@ private static SafariDriverCommandExecutor generateExecutor( Require.nonNull("Driver service", service); Require.nonNull("Driver options", options); Require.nonNull("Driver clientConfig", clientConfig); - if (service.getExecutable() == null) { - String path = DriverFinder.getPath(service, options).getDriverPath(); - service.setExecutable(path); - } + DriverFinder finder = new DriverFinder(service, options); + service.setExecutable(finder.getDriverPath()); return new SafariDriverCommandExecutor(service, clientConfig); } diff --git a/java/src/org/openqa/selenium/safari/SafariDriverInfo.java b/java/src/org/openqa/selenium/safari/SafariDriverInfo.java index 83736f8947c12..a9bc55d54f681 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriverInfo.java +++ b/java/src/org/openqa/selenium/safari/SafariDriverInfo.java @@ -22,16 +22,13 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Platform; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @AutoService(WebDriverInfo.class) @@ -70,36 +67,16 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - if (Platform.getCurrent().is(Platform.MAC)) { - DriverFinder.getPath( - SafariDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.MAC) + && new DriverFinder(SafariDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - if (Platform.getCurrent().is(Platform.MAC)) { - DriverFinder.getPath( - SafariDriverService.createDefaultService(), getCanonicalCapabilities(), true); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.MAC) + && new DriverFinder(SafariDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/safari/SafariDriverService.java b/java/src/org/openqa/selenium/safari/SafariDriverService.java index 32979c6ed5aba..4527c0d46c15a 100644 --- a/java/src/org/openqa/selenium/safari/SafariDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariDriverService.java @@ -95,7 +95,7 @@ protected Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link SafariDriverService} using the default configuration. In * this configuration, the service will use the SafariDriver executable identified by the {@link - * org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, Capabilities)}. Each + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each * service created by this method will be configured to use a free port on the current system. * * @return A new SafariDriverService using the default configuration. diff --git a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverInfo.java b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverInfo.java index 12bbafabf87f3..d8d50d99e09c6 100644 --- a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverInfo.java +++ b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverInfo.java @@ -22,16 +22,13 @@ import com.google.auto.service.AutoService; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; import org.openqa.selenium.Capabilities; import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.Platform; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebDriverInfo; -import org.openqa.selenium.remote.NoSuchDriverException; import org.openqa.selenium.remote.service.DriverFinder; @SuppressWarnings("unused") @@ -70,38 +67,18 @@ public boolean isSupportingBiDi() { @Override public boolean isAvailable() { - try { - if (Platform.getCurrent().is(Platform.MAC)) { - DriverFinder.getPath( - SafariTechPreviewDriverService.createDefaultService(), getCanonicalCapabilities()); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.MAC) + && new DriverFinder( + SafariTechPreviewDriverService.createDefaultService(), getCanonicalCapabilities()) + .isAvailable(); } @Override public boolean isPresent() { - try { - if (Platform.getCurrent().is(Platform.MAC)) { - DriverFinder.getPath( - SafariTechPreviewDriverService.createDefaultService(), - getCanonicalCapabilities(), - true); - return true; - } - return false; - } catch (NoSuchDriverException e) { - return false; - } catch (IllegalStateException | WebDriverException e) { - LOG.log(Level.WARNING, "failed to discover driver path", e); - return false; - } + return Platform.getCurrent().is(Platform.MAC) + && new DriverFinder( + SafariTechPreviewDriverService.createDefaultService(), getCanonicalCapabilities()) + .isPresent(); } @Override diff --git a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java index d8a26e134fc28..92a185810b4d4 100644 --- a/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java +++ b/java/src/org/openqa/selenium/safari/SafariTechPreviewDriverService.java @@ -96,9 +96,9 @@ public Capabilities getDefaultDriverOptions() { /** * Configures and returns a new {@link SafariTechPreviewDriverService} using the default * configuration. In this configuration, the service will use the SafariDriver executable - * identified by the {@link org.openqa.selenium.remote.service.DriverFinder#getPath(DriverService, - * Capabilities)}. Each service created by this method will be configured to use a free port on - * the current system. + * identified by the {@link + * org.openqa.selenium.remote.service.DriverFinder#getResult(DriverService, Capabilities)}. Each + * service created by this method will be configured to use a free port on the current system. * * @return A new SafariTechPreviewDriverService using the default configuration. */ diff --git a/java/test/org/openqa/selenium/remote/service/BUILD.bazel b/java/test/org/openqa/selenium/remote/service/BUILD.bazel index eb8cff87246b5..876a43067a485 100644 --- a/java/test/org/openqa/selenium/remote/service/BUILD.bazel +++ b/java/test/org/openqa/selenium/remote/service/BUILD.bazel @@ -6,6 +6,7 @@ java_test_suite( size = "small", srcs = glob(["*.java"]), deps = [ + "//java/src/org/openqa/selenium/manager", "//java/src/org/openqa/selenium/remote", "//java/test/org/openqa/selenium/testing:annotations", artifact("org.junit.jupiter:junit-jupiter-api"), diff --git a/java/test/org/openqa/selenium/remote/service/DriverFinderTest.java b/java/test/org/openqa/selenium/remote/service/DriverFinderTest.java new file mode 100644 index 0000000000000..53644da24c115 --- /dev/null +++ b/java/test/org/openqa/selenium/remote/service/DriverFinderTest.java @@ -0,0 +1,138 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.Capabilities; +import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.Proxy; +import org.openqa.selenium.manager.SeleniumManager; +import org.openqa.selenium.manager.SeleniumManagerOutput.Result; + +@Tag("UnitTests") +class DriverFinderTest { + private final DriverService service = mock(DriverService.class); + private final SeleniumManager seleniumManager = mock(SeleniumManager.class); + Path driverFile; + Path browserFile; + + @BeforeEach + void createMocks() { + driverFile = createExecutableFile("testDriver"); + browserFile = createExecutableFile("testBrowser"); + when(service.getDriverName()).thenReturn("driverName"); + } + + @Test + void serviceValueIgnoresSeleniumManager() { + when(service.getExecutable()).thenReturn(driverFile.toString()); + + Capabilities capabilities = new ImmutableCapabilities("browserName", "chrome"); + DriverFinder finder = new DriverFinder(service, capabilities); + + assertThat(finder.getDriverPath()).isEqualTo(driverFile.toString()); + assertThat(finder.getBrowserPath()).isNull(); + verify(service, times(1)).getDriverName(); + verify(service, times(1)).getExecutable(); + verify(service, never()).getDriverProperty(); + } + + @Test + void systemPropertyIgnoresSeleniumManager() throws IOException { + when(service.getExecutable()).thenReturn(null); + when(service.getDriverProperty()).thenReturn("property.ignores.selenium.manager"); + System.setProperty("property.ignores.selenium.manager", driverFile.toString()); + + Capabilities capabilities = new ImmutableCapabilities("browserName", "chrome"); + DriverFinder finder = new DriverFinder(service, capabilities); + + assertThat(finder.getDriverPath()).isEqualTo(driverFile.toString()); + assertThat(finder.getBrowserPath()).isNull(); + verify(service, times(1)).getExecutable(); + verify(service, times(1)).getDriverName(); + verify(service, times(1)).getDriverProperty(); + } + + @Test + void createsArgumentsForSeleniumManager() throws IOException { + when(service.getExecutable()).thenReturn(null); + when(service.getDriverProperty()).thenReturn("property.selenium.manager.empty"); + + Proxy proxy = new Proxy().setHttpProxy("https://localhost:1234"); + Capabilities capabilities = + new ImmutableCapabilities( + "browserName", + "chrome", + "browserVersion", + "beta", + "proxy", + proxy, + "goog:chromeOptions", + Map.of("binary", browserFile.toString())); + DriverFinder finder = new DriverFinder(service, capabilities, seleniumManager); + + List arguments = new ArrayList<>(); + arguments.add("--browser"); + arguments.add("chrome"); + arguments.add("--browser-version"); + arguments.add("beta"); + arguments.add("--browser-path"); + arguments.add(browserFile.toString()); + arguments.add("--proxy"); + arguments.add("https://localhost:1234"); + Result result = new Result(0, "", driverFile.toString(), browserFile.toString()); + doReturn(result).when(seleniumManager).getBinaryPaths(arguments); + + assertThat(finder.getDriverPath()).isEqualTo(driverFile.toString()); + assertThat(finder.getBrowserPath()).isEqualTo(browserFile.toString()); + verify(service, times(1)).getExecutable(); + verify(service, times(1)).getDriverName(); + verify(service, times(1)).getDriverProperty(); + verifyNoMoreInteractions(service); + verify(seleniumManager, times(1)).getBinaryPaths(arguments); + verifyNoMoreInteractions(seleniumManager); + } + + private Path createExecutableFile(String prefix) { + Path driverFile = null; + try { + driverFile = Files.createTempFile(prefix, ".tmp"); + } catch (IOException e) { + throw new RuntimeException(e); + } + driverFile.toFile().setExecutable(true); + return driverFile; + } +}