-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
/
Copy pathDefaultSlotMatcher.java
181 lines (165 loc) · 7.41 KB
/
DefaultSlotMatcher.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// 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.grid.data;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.openqa.selenium.Capabilities;
/**
* Default matching implementation for slots, loosely based on the requirements for capability
* matching from the WebDriver spec. A match is made if the following are all true:
*
* <ul>
* <li>All non-extension capabilities from the {@code stereotype} match those in the {@link
* Capabilities} being considered.
* <li>If the {@link Capabilities} being considered contain any of:
* <ul>
* <li>browserName
* <li>browserVersion
* <li>platformName
* </ul>
* 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.
*/
public class DefaultSlotMatcher implements SlotMatcher, Serializable {
/*
List of prefixed extension capabilities we never should try to match, they should be
matched in the Node or in the browser driver.
*/
private static final List<String> EXTENSION_CAPABILITIES_PREFIXES =
Arrays.asList("goog:", "moz:", "ms:", "safari:", "se:");
@Override
public boolean matches(Capabilities stereotype, Capabilities capabilities) {
if (capabilities.asMap().isEmpty()) {
return false;
}
if (!initialMatch(stereotype, capabilities)) {
return false;
}
if (!managedDownloadsEnabled(stereotype, capabilities)) {
return false;
}
if (!platformVersionMatch(stereotype, capabilities)) {
return false;
}
if (!extensionCapabilitiesMatch(stereotype, capabilities)) {
return false;
}
// At the end, a simple browser, browserVersion and platformName match
boolean browserNameMatch =
(capabilities.getBrowserName() == null || capabilities.getBrowserName().isEmpty())
|| Objects.equals(stereotype.getBrowserName(), capabilities.getBrowserName());
boolean browserVersionMatch =
(capabilities.getBrowserVersion() == null
|| capabilities.getBrowserVersion().isEmpty()
|| Objects.equals(capabilities.getBrowserVersion(), "stable"))
|| browserVersionMatch(
stereotype.getBrowserVersion(), capabilities.getBrowserVersion());
boolean platformNameMatch =
capabilities.getPlatformName() == null
|| Objects.equals(stereotype.getPlatformName(), capabilities.getPlatformName())
|| (stereotype.getPlatformName() != null
&& stereotype.getPlatformName().is(capabilities.getPlatformName()));
return browserNameMatch && browserVersionMatch && platformNameMatch;
}
private boolean browserVersionMatch(String stereotype, String capabilities) {
return new SemanticVersionComparator().compare(stereotype, capabilities) == 0;
}
private Boolean initialMatch(Capabilities stereotype, Capabilities capabilities) {
return stereotype.getCapabilityNames().stream()
// Matching of extension capabilities is implementation independent. Skip them
.filter(name -> !name.contains(":"))
// Platform matching is special, we do it later
.filter(name -> !"platformName".equalsIgnoreCase(name))
.map(
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);
}
private Boolean managedDownloadsEnabled(Capabilities stereotype, Capabilities capabilities) {
// First lets check if user wanted a Node with managed downloads enabled
Object raw = capabilities.getCapability("se:downloadsEnabled");
if (raw == null || !Boolean.parseBoolean(raw.toString())) {
// User didn't ask. So lets move on to the next matching criteria
return true;
}
// User wants managed downloads enabled to be done on this Node, let's check the stereotype
raw = stereotype.getCapability("se:downloadsEnabled");
// Try to match what the user requested
return raw != null && Boolean.parseBoolean(raw.toString());
}
private Boolean platformVersionMatch(Capabilities stereotype, Capabilities capabilities) {
/*
This platform version match is not W3C compliant but users can add Appium servers as
Nodes, so we avoid delaying the match until the Slot, which makes the whole matching
process faster.
*/
return capabilities.getCapabilityNames().stream()
.filter(name -> name.contains("platformVersion"))
.map(
platformVersionCapName ->
Objects.equals(
stereotype.getCapability(platformVersionCapName),
capabilities.getCapability(platformVersionCapName)))
.reduce(Boolean::logicalAnd)
.orElse(true);
}
private Boolean extensionCapabilitiesMatch(Capabilities stereotype, Capabilities capabilities) {
/*
We match extension capabilities when they are not prefixed with any of the
EXTENSION_CAPABILITIES_PREFIXES items. Also, we match them only when the capabilities
of the new session request contains that specific extension capability.
We are also filtering "options" extension capabilities, as they should be handled
by the remote endpoint.
*/
return stereotype.getCapabilityNames().stream()
.filter(name -> name.contains(":"))
.filter(name -> !name.toLowerCase().contains("options"))
.filter(name -> capabilities.asMap().containsKey(name))
.filter(name -> EXTENSION_CAPABILITIES_PREFIXES.stream().noneMatch(name::contains))
.map(
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);
}
}