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
100 changes: 60 additions & 40 deletions java/src/org/openqa/selenium/grid/data/DefaultSlotMatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,25 @@
* Then the {@code stereotype} must contain the same values.
* </ul>
*
* <p>One thing to note is that extension capabilities are not considered when matching slots, since
* the matching of these is implementation-specific to each driver.
* <p>Note that extension capabilities are considered for slot matching, with the following exceptions:
*
* <ul>
* <li>Extension capabilities with these prefixes:
* <ul>
* <li>"goog:" - Google
* <li>"moz:" - Mozilla
* <li>"ms:" - Microsoft
* <li>"safari:" - Apple
* <li>"se:" - Selenium
* </ul>
* <li>Extension capabilities with these suffixes:
* <ul>
* <li>"Options"
* <li>"options"
* <li>"loggingPrefs"
* <li>"debuggerAddress"
* </ul>
* </ul>
*/
public class DefaultSlotMatcher implements SlotMatcher, Serializable {

Expand All @@ -55,6 +72,13 @@ public class DefaultSlotMatcher implements SlotMatcher, Serializable {
public static final List<String> MANDATORY_CAPABILITIES =
Arrays.asList("platformName", "browserName", "browserVersion");

/*
List of extension capability suffixes we never should try to match, they should be
matched in the Node or in the browser driver.
*/
private static final List<String> EXTENSION_CAPABILITY_SUFFIXES =
Arrays.asList("Options", "options", "loggingPrefs", "debuggerAddress");

@Override
public boolean matches(Capabilities stereotype, Capabilities capabilities) {

Expand All @@ -80,13 +104,14 @@ public boolean matches(Capabilities stereotype, Capabilities capabilities) {

// At the end, a simple browser, browserVersion and platformName match
boolean browserNameMatch =
(capabilities.getBrowserName() == null || capabilities.getBrowserName().isEmpty())
capabilities.getBrowserName() == null
|| capabilities.getBrowserName().isEmpty()
|| Objects.equals(stereotype.getBrowserName(), capabilities.getBrowserName())
|| matchConditionToRemoveCapability(capabilities);
boolean browserVersionMatch =
(capabilities.getBrowserVersion() == null
|| capabilities.getBrowserVersion().isEmpty()
|| Objects.equals(capabilities.getBrowserVersion(), "stable"))
capabilities.getBrowserVersion() == null
|| capabilities.getBrowserVersion().isEmpty()
|| Objects.equals(capabilities.getBrowserVersion(), "stable")
|| browserVersionMatch(stereotype.getBrowserVersion(), capabilities.getBrowserVersion())
|| matchConditionToRemoveCapability(capabilities);
boolean platformNameMatch =
Expand All @@ -110,21 +135,17 @@ private Boolean initialMatch(Capabilities stereotype, Capabilities capabilities)
name ->
MANDATORY_CAPABILITIES.stream()
.noneMatch(mandatory -> mandatory.equalsIgnoreCase(name)))
.map(
.filter(name -> capabilities.getCapability(name) != null)
.allMatch(
name -> {
if (capabilities.getCapability(name) instanceof String) {
return stereotype
.getCapability(name)
.toString()
.equalsIgnoreCase(capabilities.getCapability(name).toString());
} else {
return capabilities.getCapability(name) == null
|| Objects.equals(
stereotype.getCapability(name), capabilities.getCapability(name));
}
})
.reduce(Boolean::logicalAnd)
.orElse(true);
if (stereotype.getCapability(name) instanceof String
&& capabilities.getCapability(name) instanceof String) {
return ((String) stereotype.getCapability(name))
.equalsIgnoreCase((String) capabilities.getCapability(name));
}
return Objects.equals(
stereotype.getCapability(name), capabilities.getCapability(name));
});
}

private Boolean managedDownloadsEnabled(Capabilities stereotype, Capabilities capabilities) {
Expand All @@ -148,13 +169,11 @@ private Boolean platformVersionMatch(Capabilities stereotype, Capabilities capab
*/
return capabilities.getCapabilityNames().stream()
.filter(name -> name.contains("platformVersion"))
.map(
.allMatch(
platformVersionCapName ->
Objects.equals(
stereotype.getCapability(platformVersionCapName),
capabilities.getCapability(platformVersionCapName)))
.reduce(Boolean::logicalAnd)
.orElse(true);
capabilities.getCapability(platformVersionCapName)));
}

private Boolean extensionCapabilitiesMatch(Capabilities stereotype, Capabilities capabilities) {
Expand All @@ -166,25 +185,26 @@ private Boolean extensionCapabilitiesMatch(Capabilities stereotype, Capabilities
by the remote endpoint.
*/
return stereotype.getCapabilityNames().stream()
// examine only extension capabilities
.filter(name -> name.contains(":"))
.filter(name -> !name.toLowerCase().contains("options"))
.filter(name -> capabilities.asMap().containsKey(name))
// ignore special extension capability prefixes
.filter(name -> EXTENSION_CAPABILITIES_PREFIXES.stream().noneMatch(name::contains))
.map(
// ignore special extension capability suffixes
.filter(name -> EXTENSION_CAPABILITY_SUFFIXES.stream().noneMatch(name::endsWith))
// ignore capabilities not specified in the request
.filter(name -> capabilities.getCapability(name) != null)
.allMatch(
name -> {
if (capabilities.getCapability(name) instanceof String) {
return stereotype
.getCapability(name)
.toString()
.equalsIgnoreCase(capabilities.getCapability(name).toString());
} else {
return capabilities.getCapability(name) == null
|| Objects.equals(
stereotype.getCapability(name), capabilities.getCapability(name));
}
})
.reduce(Boolean::logicalAnd)
.orElse(true);
// evaluate capabilities with String values
if (stereotype.getCapability(name) instanceof String
&& capabilities.getCapability(name) instanceof String) {
return ((String) stereotype.getCapability(name))
.equalsIgnoreCase((String) capabilities.getCapability(name));
}
// evaluate capabilities with non-String values
return Objects.equals(
stereotype.getCapability(name), capabilities.getCapability(name));
});
}

public static Boolean matchConditionToRemoveCapability(Capabilities capabilities) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Map;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.ImmutableCapabilities;
Expand Down Expand Up @@ -659,7 +660,7 @@ void multipleExtensionPrefixedCapabilitiesDoNotMatchWhenOneIsDifferent() {
}

@Test
void vendorExtensionPrefixedCapabilitiesAreIgnoredForMatching() {
void seleniumExtensionCapabilitiesAreIgnoredForMatching() {
Capabilities stereotype =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
Expand All @@ -668,23 +669,83 @@ void vendorExtensionPrefixedCapabilitiesAreIgnoredForMatching() {
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"goog:cheese",
"amsterdam",
"ms:fruit",
"mango");
"se:cdpVersion",
1,
"se:downloadsEnabled",
true);

Capabilities capabilities =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
"chrome",
CapabilityType.BROWSER_VERSION,
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"se:cdpVersion",
2,
"se:downloadsEnabled",
false);
assertThat(slotMatcher.matches(stereotype, capabilities)).isTrue();
}

@Test
void vendorOptionsCapabilitiesAreIgnoredForMatching() {
Capabilities stereotype =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
"chrome",
CapabilityType.BROWSER_VERSION,
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"goog:cheese",
"gouda",
"ms:fruit",
"orange");
"food:fruitOptions",
"mango",
"dairy:options",
Map.of("cheese", "amsterdam"));

Capabilities capabilities =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
"chrome",
CapabilityType.BROWSER_VERSION,
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"food:fruitOptions",
"orange",
"dairy:options",
Map.of("cheese", "gouda"));
assertThat(slotMatcher.matches(stereotype, capabilities)).isTrue();
}

@Test
void specialExtensionCapabilitiesAreIgnoredForMatching() {
Capabilities stereotype =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
"chrome",
CapabilityType.BROWSER_VERSION,
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"food:loggingPrefs",
"mango",
"food:debuggerAddress",
Map.of("cheese", "amsterdam"));

Capabilities capabilities =
new ImmutableCapabilities(
CapabilityType.BROWSER_NAME,
"chrome",
CapabilityType.BROWSER_VERSION,
"84",
CapabilityType.PLATFORM_NAME,
Platform.WINDOWS,
"food:loggingPrefs",
"orange",
"food:debuggerAddress",
Map.of("cheese", "gouda"));
assertThat(slotMatcher.matches(stereotype, capabilities)).isTrue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import org.openqa.selenium.internal.Either;
import org.openqa.selenium.json.Json;
import org.openqa.selenium.net.NetworkUtils;
import org.openqa.selenium.remote.Browser;
import org.openqa.selenium.safari.SafariDriverInfo;

@SuppressWarnings("DuplicatedCode")
Expand Down Expand Up @@ -148,7 +149,10 @@ boolean isDownloadEnabled(WebDriverInfo driver, String customMsg) {
reported.add(caps);
return Collections.singleton(HelperFactory.create(config, caps));
});
String expected = driver.getDisplayName();
String expected =
"Edge".equals(driver.getDisplayName())
? Browser.EDGE.browserName()
: driver.getDisplayName();

Capabilities found =
reported.stream()
Expand Down