Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic selenium version matching #97

Merged
merged 3 commits into from Mar 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/usage/webdriver_containers.md
Expand Up @@ -10,6 +10,8 @@ from SeleniumHQ's [docker-selenium](https://github.com/SeleniumHQ/docker-seleniu
is a working Docker installation and your Java JUnit test suite.
* Browsers are always launched from a fixed, clean image. This means no configuration drift from user changes or
automatic browser upgrades.
* Compatibility between browser version and the Selenium API is assured: a compatible version of the browser docker
images will be automatically selected to match the version of `selenium-api-*.jar` on the classpath
* Additionally the use of a clean browser prevents leakage of cookies, cached data or other state between tests.
* **VNC screen recording**: Test Containers can automatically record video of test runs (optionally capturing just
failing tests)
Expand Down
2 changes: 1 addition & 1 deletion modules/selenium/pom.xml
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>2.45.0</version>
<version>2.52.0</version>
<scope>provided</scope>
</dependency>

Expand Down
Expand Up @@ -16,9 +16,7 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -28,14 +26,17 @@
*/
public class BrowserWebDriverContainer extends GenericContainer implements VncService, LinkableContainer {

private static final String CHROME_IMAGE = "selenium/standalone-chrome-debug:2.45.0";
private static final String FIREFOX_IMAGE = "selenium/standalone-firefox-debug:2.45.0";
private static final String CHROME_IMAGE = "selenium/standalone-chrome-debug:%s";
private static final String FIREFOX_IMAGE = "selenium/standalone-firefox-debug:%s";

private static final String DEFAULT_PASSWORD = "secret";
private static final int SELENIUM_PORT = 4444;
private static final int VNC_PORT = 5900;

@Nullable private DesiredCapabilities desiredCapabilities;
@Nullable private RemoteWebDriver driver;
@Nullable
private DesiredCapabilities desiredCapabilities;
@Nullable
private RemoteWebDriver driver;

private VncRecordingMode recordingMode = VncRecordingMode.RECORD_FAILING;
private File vncRecordingDirectory = new File("/tmp");
Expand All @@ -51,6 +52,7 @@ public BrowserWebDriverContainer() {

}


public BrowserWebDriverContainer withDesiredCapabilities(DesiredCapabilities desiredCapabilities) {
super.setDockerImageName(getImageForCapabilities(desiredCapabilities));
this.desiredCapabilities = desiredCapabilities;
Expand All @@ -77,12 +79,14 @@ protected void configure() {

public static String getImageForCapabilities(DesiredCapabilities desiredCapabilities) {

String seleniumVersion = SeleniumUtils.determineClasspathSeleniumVersion();

String browserName = desiredCapabilities.getBrowserName();
switch (browserName) {
case BrowserType.CHROME:
return CHROME_IMAGE;
return String.format(CHROME_IMAGE, seleniumVersion);
case BrowserType.FIREFOX:
return FIREFOX_IMAGE;
return String.format(FIREFOX_IMAGE, seleniumVersion);
default:
throw new UnsupportedOperationException("Browser name must be 'chrome' or 'firefox'; provided '" + browserName + "' is not supported");
}
Expand Down Expand Up @@ -205,7 +209,10 @@ public BrowserWebDriverContainer withRecordingMode(VncRecordingMode recordingMod
return this;
}


public enum VncRecordingMode {
SKIP, RECORD_ALL, RECORD_FAILING
SKIP, RECORD_ALL, RECORD_FAILING;
}


}
@@ -0,0 +1,70 @@
package org.testcontainers.containers;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

/**
* Utility methods for Selenium.
*/
public final class SeleniumUtils {

public static final String DEFAULT_SELENIUM_VERSION = "2.45.0";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't it be 2.52.0 now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in that 2.52.0 is the latest available version :D. However, a major feature of Testcontainers' selenium support is that versions of browsers are stable and users should not be afflicted by sudden breakage when a browser version silently changes in the background.

The automatic detection of classpath selenium version should make this a moot point, as finally there's only one single source of the truth for the version the user wants to use.

If for some reason this automated detection fails, though, I wouldn't want to trip up a user who had been happily using v2.45.0, so unfortunately the default needs to be the lowest common denominator rather than the latest version.

The best case is the user gets exactly the version of the container images they need (matching their API JAR). The worst case is they get the older version they would have had to deal with anyway, hopefully :)


private static final Logger LOGGER = LoggerFactory.getLogger(SeleniumUtils.class);

private SeleniumUtils() {}

/**
* Based on the JARs detected on the classpath, determine which version of selenium-api is available.
* @return the detected version of Selenium API, or DEFAULT_SELENIUM_VERSION if it could not be determined
*/
public static String determineClasspathSeleniumVersion() {
Set<String> seleniumVersions = new HashSet<>();
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> manifests = classLoader.getResources("META-INF/MANIFEST.MF");

while (manifests.hasMoreElements()) {
URL manifestURL = manifests.nextElement();
try (InputStream is = manifestURL.openStream()) {
Manifest manifest = new Manifest();
manifest.read(is);

Attributes buildInfo = manifest.getAttributes("Build-Info");
if (buildInfo != null) {
String seleniumVersion = buildInfo.getValue("Selenium-Version");

if (seleniumVersion != null) {
seleniumVersions.add(seleniumVersion);
LOGGER.info("Selenium API version {} detected on classpath", seleniumVersion);
}
}
}

}

} catch (Exception e) {
LOGGER.debug("Failed to determine Selenium-Version from selenium-api JAR Manifest", e);
}

if (seleniumVersions.size() == 0) {
LOGGER.warn("Failed to determine Selenium version from classpath - will use default version of {}", DEFAULT_SELENIUM_VERSION);
return DEFAULT_SELENIUM_VERSION;
}

String foundVersion = seleniumVersions.iterator().next();
if (seleniumVersions.size() > 1) {
LOGGER.warn("Multiple versions of Selenium API found on classpath - will select {}, but this may not be reliable", foundVersion);
}

return foundVersion;
}
}