Skip to content

Commit

Permalink
Introduce WebDriverInfo
Browse files Browse the repository at this point in the history
Similar to how JavaBeans allow introspection of what particular
beans can do, the WebDriverInfo allows introspection into which
webdriver implementations are on the current system and available
for use.

The information provided includes:

  * A display name
  * How many simultaneous instances can be run
  * A mechanism for getting a basic set of `Capabilities`

A basic implementation for all drivers has been included.
  • Loading branch information
shs96c committed Sep 10, 2018
1 parent a4d8dd8 commit 8ebb691
Show file tree
Hide file tree
Showing 17 changed files with 618 additions and 13 deletions.
63 changes: 63 additions & 0 deletions java/client/src/org/openqa/selenium/WebDriverInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.openqa.selenium;

import java.util.Optional;

/**
* Describes, in general terms, a webdriver instance. This allows services to query the system at
* run time and offer instances of particular {@link WebDriver} subclasses should they be availble.
*/
@Beta
public interface WebDriverInfo {

/**
* @return A human-readable name that describes the browser.
*/
String getDisplayName();

/**
* Describes the smallest set of {@link Capabilities} that could be used to create an instance of
* this {@link WebDriver} implementation.
* <p>
* Note, this set does not need to be exhaustive: the only requirement is that if
* {@link #isAvailable()} returns {@code true}, the returned {@link Capabilities} can be passed to
* {@link #createDriver(Capabilities)} and a session will be created.
*
* @return The smallest set of {@link Capabilities} required to create an instance of this
* {@link WebDriver} implementation.
*/
Capabilities getCanonicalCapabilities();

/**
* @return Whether a call to {@link #createDriver(Capabilities)} would succeed if given
* {@code capabilities}.
*/
boolean isSupporting(Capabilities capabilities);

/**
* Often, a {@link WebDriver} instance needs one or more supporting files or executables to be
* present (such as a vendor-provided executable which speaks the WebDriver Protocol). This means
* that even though the driver classes might be present in Java, it would make no sense to attempt
* to instantiate the driver itself.
*
* @return Whether or not the prerequisites required for this {@link WebDriver} are present.
*/
boolean isAvailable();

/**
* Some browsers require all the resources of the current system in order to run (for example,
* Safari on iOS) and so do not support multiple simultaneous sessions on the same system. Other
* browsers can create isolated state for each new {@link WebDriver} instance.
* <p>
* The count of simultaneous sessions is typically 1, some multiple of the available number of
* cores, or {@link Integer#MAX_VALUE} if the number is unbounded or no-one cares.
*/
int getMaximumSimultaneousSessions();

/**
* Creates a new instance of the {@link WebDriver} implementation. The instance must be killed by
* sending the "quit" command. If the instance cannot be created because {@link #isAvailable()} is
* {@code false}, then {@link Optional#empty()} is returned. Otherwise, an attempt to start the
* session is made and the result returned.
*/
Optional<WebDriver> createDriver(Capabilities capabilities) throws SessionNotCreatedException;
}
62 changes: 62 additions & 0 deletions java/client/src/org/openqa/selenium/chrome/ChromeDriverInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.openqa.selenium.chrome;

import com.google.auto.service.AutoService;

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.BrowserType;
import org.openqa.selenium.remote.CapabilityType;

import java.util.Optional;

@AutoService(WebDriverInfo.class)
public class ChromeDriverInfo implements WebDriverInfo {

@Override
public String getDisplayName() {
return "Chrome";
}

@Override
public Capabilities getCanonicalCapabilities() {
return new ImmutableCapabilities(CapabilityType.BROWSER_NAME, BrowserType.CHROME);
}

@Override
public boolean isSupporting(Capabilities capabilities) {
return BrowserType.CHROME.equals(capabilities.getBrowserName()) ||
capabilities.getCapability("chromeOptions") != null ||
capabilities.getCapability("goog:chromeOptions") != null;
}

@Override
public boolean isAvailable() {
try {
ChromeDriverService.createDefaultService();
return true;
} catch (IllegalStateException | WebDriverException e) {
return false;
}
}

@Override
public int getMaximumSimultaneousSessions() {
return Runtime.getRuntime().availableProcessors() + 1;
}

@Override
public Optional<WebDriver> createDriver(Capabilities capabilities)
throws SessionNotCreatedException {
if (!isAvailable() || !isSupporting(capabilities)) {
return Optional.empty();
}

WebDriver driver = new ChromeDriver(capabilities);

return Optional.of(driver);
}
}
30 changes: 18 additions & 12 deletions java/client/src/org/openqa/selenium/chrome/ChromeDriverService.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,20 @@ public class ChromeDriverService extends DriverService {
* System property that defines comma-separated list of remote IPv4 addresses which are
* allowed to connect to ChromeDriver.
*/
public final static String CHROME_DRIVER_WHITELISTED_IPS_PROPERTY = "webdriver.chrome.whitelistedIps";
public final static String CHROME_DRIVER_WHITELISTED_IPS_PROPERTY =
"webdriver.chrome.whitelistedIps";

/**
*
* @param executable The chromedriver executable.
* @param port Which port to start the ChromeDriver on.
* @param args The arguments to the launched server.
* @param executable The chromedriver executable.
* @param port Which port to start the ChromeDriver on.
* @param args The arguments to the launched server.
* @param environment The environment for the launched server.
* @throws IOException If an I/O error occurs.
*/
public ChromeDriverService(File executable, int port, ImmutableList<String> args,
public ChromeDriverService(
File executable,
int port,
ImmutableList<String> args,
ImmutableMap<String, String> environment) throws IOException {
super(executable, port, args, environment);
}
Expand Down Expand Up @@ -122,7 +125,7 @@ public int score(Capabilities capabilites) {
*
* @param verbose True for verbose output, false otherwise.
* @return A self reference.
*/
*/
public Builder withVerbose(boolean verbose) {
this.verbose = verbose;
return this;
Expand All @@ -133,7 +136,7 @@ public Builder withVerbose(boolean verbose) {
*
* @param silent True for silent output, false otherwise.
* @return A self reference.
*/
*/
public Builder withSilent(boolean silent) {
this.silent = silent;
return this;
Expand All @@ -153,7 +156,8 @@ public Builder withWhitelistedIps(String whitelistedIps) {

@Override
protected File findDefaultExecutable() {
return findExecutable("chromedriver", CHROME_DRIVER_EXE_PROPERTY,
return findExecutable(
"chromedriver", CHROME_DRIVER_EXE_PROPERTY,
"https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver",
"http://chromedriver.storage.googleapis.com/index.html");
}
Expand Down Expand Up @@ -186,9 +190,11 @@ protected ImmutableList<String> createArgs() {
}

@Override
protected ChromeDriverService createDriverService(File exe, int port,
ImmutableList<String> args,
ImmutableMap<String, String> environment) {
protected ChromeDriverService createDriverService(
File exe,
int port,
ImmutableList<String> args,
ImmutableMap<String, String> environment) {
try {
return new ChromeDriverService(exe, port, args, environment);
} catch (IOException e) {
Expand Down
3 changes: 3 additions & 0 deletions java/client/src/org/openqa/selenium/chrome/module-info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ module org.openqa.selenium.chrome {
provides org.openqa.selenium.remote.service.DriverService$Builder with
org.openqa.selenium.chrome.ChromeDriverService$Builder;

provides org.openqa.selenium.WebDriverInfo with
org.openqa.selenium.chrome.ChromeDriverInfo;

}
60 changes: 60 additions & 0 deletions java/client/src/org/openqa/selenium/edge/EdgeDriverInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.openqa.selenium.edge;

import static org.openqa.selenium.remote.CapabilityType.BROWSER_NAME;

import com.google.auto.service.AutoService;

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.BrowserType;

import java.util.Optional;

@AutoService(WebDriverInfo.class)
public class EdgeDriverInfo implements WebDriverInfo {

@Override
public String getDisplayName() {
return "Edge";
}

@Override
public Capabilities getCanonicalCapabilities() {
return new ImmutableCapabilities(BROWSER_NAME, BrowserType.EDGE);
}

@Override
public boolean isSupporting(Capabilities capabilities) {
return BrowserType.EDGE.equals(capabilities.getBrowserName()) ||
capabilities.getCapability("edgeOptions") != null;
}

@Override
public boolean isAvailable() {
try {
EdgeDriverService.createDefaultService();
return true;
} catch (IllegalStateException | WebDriverException e) {
return false;
}
}

@Override
public int getMaximumSimultaneousSessions() {
return 1;
}

@Override
public Optional<WebDriver> createDriver(Capabilities capabilities)
throws SessionNotCreatedException {
if (!isAvailable() || !isSupporting(capabilities)) {
return Optional.empty();
}

return Optional.of(new EdgeDriver(capabilities));
}
}
3 changes: 3 additions & 0 deletions java/client/src/org/openqa/selenium/edge/module-info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ module org.openqa.selenium.edge {

exports org.openqa.selenium.edge;

provides org.openqa.selenium.WebDriverInfo with
org.openqa.selenium.edge.EdgeDriverInfo;

provides org.openqa.selenium.remote.service.DriverService$Builder with
org.openqa.selenium.edge.EdgeDriverService$Builder;

Expand Down
75 changes: 75 additions & 0 deletions java/client/src/org/openqa/selenium/firefox/GeckoDriverInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.openqa.selenium.firefox;

import static org.openqa.selenium.firefox.FirefoxDriver.MARIONETTE;
import static org.openqa.selenium.remote.CapabilityType.BROWSER_NAME;

import com.google.auto.service.AutoService;

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.BrowserType;

import java.util.Optional;

@AutoService(WebDriverInfo.class)
public class GeckoDriverInfo implements WebDriverInfo {

@Override
public String getDisplayName() {
return "Firefox";
}

@Override
public Capabilities getCanonicalCapabilities() {
return new ImmutableCapabilities(BROWSER_NAME, BrowserType.FIREFOX);
}

@Override
public boolean isSupporting(Capabilities capabilities) {
if (capabilities.is(MARIONETTE)) {
return false;
}

if (BrowserType.FIREFOX.equals(capabilities.getBrowserName())) {
return true;
}

return capabilities.asMap().keySet().stream()
.map(key -> key.startsWith("moz:"))
.reduce(Boolean::logicalOr)
.orElse(false);
}

@Override
public boolean isAvailable() {
try {
GeckoDriverService.createDefaultService();
return true;
} catch (IllegalStateException | WebDriverException e) {
return false;
}
}

@Override
public int getMaximumSimultaneousSessions() {
return Runtime.getRuntime().availableProcessors() + 1;
}

@Override
public Optional<WebDriver> createDriver(Capabilities capabilities)
throws SessionNotCreatedException {
if (!isAvailable()) {
return Optional.empty();
}

if (capabilities.is(MARIONETTE)) {
return Optional.empty();
}

return Optional.of(new FirefoxDriver(capabilities));
}
}
Loading

0 comments on commit 8ebb691

Please sign in to comment.