1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit;
16
17 import static java.nio.charset.StandardCharsets.ISO_8859_1;
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertTrue;
21
22 import java.io.File;
23 import java.net.UnknownHostException;
24 import java.text.DateFormat;
25 import java.text.SimpleDateFormat;
26 import java.util.HashMap;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.concurrent.TimeUnit;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang3.StringUtils;
37 import org.htmlunit.html.DomNode;
38 import org.htmlunit.html.DomNodeList;
39 import org.htmlunit.html.HtmlAnchor;
40 import org.htmlunit.html.HtmlPage;
41 import org.htmlunit.xml.XmlPage;
42 import org.junit.Assert;
43 import org.junit.Ignore;
44 import org.junit.Test;
45
46
47
48
49
50
51
52 public class ExternalTest {
53
54 static String SONATYPE_SNAPSHOT_REPO_URL_ = "https://s01.oss.sonatype.org/content/repositories/snapshots/";
55 static String MAVEN_REPO_URL_ = "https://repo1.maven.org/maven2/";
56
57
58 static String CHROME_DRIVER_ = "136.0.7103";
59 static String CHROME_DRIVER_URL_ =
60 "https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json";
61
62 static String EDGE_DRIVER_ = "136.0.3240";
63 static String EDGE_DRIVER_URL_ = "https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/";
64
65
66 static String GECKO_DRIVER_ = "0.36.0";
67 static String GECKO_DRIVER_URL_ = "https://github.com/mozilla/geckodriver/releases/latest";
68
69
70
71
72
73 @Test
74 public void testEnvironment() throws Exception {
75 assertEquals("en_US", Locale.getDefault().toString());
76 }
77
78
79
80
81
82
83
84
85 @Test
86 public void pom() throws Exception {
87 final Map<String, String> properties = new HashMap<>();
88 final List<String> lines = FileUtils.readLines(new File("pom.xml"), ISO_8859_1);
89
90 final List<String> wrongVersions = new LinkedList<>();
91 for (int i = 0; i < lines.size(); i++) {
92 final String line = lines.get(i);
93 if (line.trim().equals("<properties>")) {
94 processProperties(lines, i + 1, properties);
95 }
96 if (line.contains("artifactId")
97 && !line.contains(">htmlunit<")
98 && !line.contains(">selenium-devtools-v")) {
99 final String artifactId = getValue(line);
100 final String groupId = getValue(lines.get(i - 1));
101 if (!lines.get(i + 1).contains("</exclusion>")) {
102 String version = getValue(lines.get(i + 1));
103 if (version.startsWith("${")) {
104 version = properties.get(version.substring(2, version.length() - 1));
105 }
106 try {
107 assertVersion(groupId, artifactId, version);
108 }
109 catch (final AssertionError e) {
110 wrongVersions.add(e.getMessage());
111 }
112 }
113 }
114 }
115
116 if (wrongVersions.size() > 0) {
117 Assert.fail(String.join("\n ", wrongVersions));
118 }
119
120 assertVersion("org.sonatype.oss", "oss-parent", "9");
121 }
122
123 private static void processProperties(final List<String> lines, int i, final Map<String, String> map) {
124 for ( ; i < lines.size(); i++) {
125 final String line = lines.get(i).trim();
126 if (StringUtils.isNotBlank(line) && !line.startsWith("<!--")) {
127 if ("</properties>".equals(line)) {
128 break;
129 }
130 final String name = line.substring(line.indexOf('<') + 1, line.indexOf('>'));
131 map.put(name, getValue(line));
132 }
133 }
134 }
135
136
137
138
139
140 @Test
141 public void assertChromeDriver() throws Exception {
142 try (WebClient webClient = buildWebClient()) {
143 final Page page = webClient.getPage(CHROME_DRIVER_URL_);
144 final String content = page.getWebResponse().getContentAsString();
145
146 String version = "0.0.0.0";
147 final Pattern regex =
148 Pattern.compile("\"channels\":\\{\"Stable\":\\{.*?\"version\":\"(\\d*\\.\\d*\\.\\d*)\\.\\d*\"");
149 final Matcher matcher = regex.matcher(content);
150 while (matcher.find()) {
151 if (version.compareTo(matcher.group(1)) < 0) {
152 version = matcher.group(1);
153 break;
154 }
155 }
156 assertEquals("Chrome Driver", version, CHROME_DRIVER_);
157 }
158 }
159
160
161
162
163
164 @Test
165 @Ignore("javascript errors")
166 public void assertEdgeDriver() throws Exception {
167 try (WebClient webClient = buildWebClient()) {
168 final HtmlPage page = webClient.getPage(EDGE_DRIVER_URL_);
169 String content = page.asNormalizedText();
170 content = content.substring(content.indexOf("Current general public release channel."));
171 content = content.replace("\r\n", "");
172
173 String version = "0.0.0.0";
174 final Pattern regex =
175 Pattern.compile("Version ("
176 + BrowserVersion.EDGE.getBrowserVersionNumeric()
177 + "\\.\\d*\\.\\d*)\\.\\d*\\s");
178 final Matcher matcher = regex.matcher(content);
179 while (matcher.find()) {
180 if (version.compareTo(matcher.group(1)) < 0) {
181 version = matcher.group(1);
182 break;
183 }
184 }
185 assertEquals("Edge Driver", version, EDGE_DRIVER_);
186 }
187 }
188
189
190
191
192
193 @Test
194 public void assertGeckoDriver() throws Exception {
195 try (WebClient webClient = buildWebClient()) {
196 try {
197 final HtmlPage page = webClient.getPage(GECKO_DRIVER_URL_);
198 final DomNodeList<DomNode> divs = page.querySelectorAll("li.breadcrumb-item-selected");
199 assertEquals("Gecko Driver", divs.get(0).asNormalizedText(), "v" + GECKO_DRIVER_);
200 }
201 catch (final FailingHttpStatusCodeException e) {
202
203 }
204 }
205 }
206
207
208
209
210
211
212
213
214 @Test
215 public void snapshot() throws Exception {
216 final List<String> lines = FileUtils.readLines(new File("pom.xml"), ISO_8859_1);
217 String version = null;
218 for (int i = 0; i < lines.size(); i++) {
219 if ("<artifactId>htmlunit</artifactId>".equals(lines.get(i).trim())) {
220 version = getValue(lines.get(i + 1));
221 break;
222 }
223 }
224 assertNotNull(version);
225 if (version.contains("SNAPSHOT")) {
226 try (WebClient webClient = buildWebClient()) {
227 final XmlPage page = webClient.getPage(SONATYPE_SNAPSHOT_REPO_URL_
228 + "org/htmlunit/htmlunit/" + version + "/maven-metadata.xml");
229 final String timestamp = page.getElementsByTagName("timestamp").get(0).getTextContent();
230 final DateFormat format = new SimpleDateFormat("yyyyMMdd.HHmmss", Locale.ROOT);
231 final long snapshotMillis = format.parse(timestamp).getTime();
232 final long nowMillis = System.currentTimeMillis();
233 final long days = TimeUnit.MILLISECONDS.toDays(nowMillis - snapshotMillis);
234 assertTrue("Snapshot not deployed for " + days + " days", days < 14);
235 }
236 }
237 }
238
239 private static void assertVersion(final String groupId, final String artifactId, final String pomVersion)
240 throws Exception {
241 String latestMavenCentralVersion = null;
242 String url = MAVEN_REPO_URL_
243 + groupId.replace('.', '/') + '/'
244 + artifactId.replace('.', '/');
245 if (!url.endsWith("/")) {
246 url += "/";
247 }
248 try (WebClient webClient = buildWebClient()) {
249 webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
250
251 try {
252 final HtmlPage page = webClient.getPage(url);
253 for (final HtmlAnchor anchor : page.getAnchors()) {
254 String mavenCentralVersion = anchor.getTextContent();
255 mavenCentralVersion = mavenCentralVersion.substring(0, mavenCentralVersion.length() - 1);
256 if (!isIgnored(groupId, artifactId, mavenCentralVersion)) {
257 if (isVersionAfter(mavenCentralVersion, latestMavenCentralVersion)) {
258 latestMavenCentralVersion = mavenCentralVersion;
259 }
260 }
261 }
262 }
263 catch (final UnknownHostException e) {
264
265 }
266 }
267 if (!pomVersion.endsWith("-SNAPSHOT")
268 || !isVersionAfter(
269 pomVersion.substring(0, pomVersion.length() - "-SNAPSHOT".length()),
270 latestMavenCentralVersion)) {
271
272
273 if (!isVersionAfter(pomVersion, latestMavenCentralVersion)) {
274 assertEquals(groupId + ":" + artifactId, latestMavenCentralVersion, pomVersion);
275 }
276 }
277 }
278
279 private static boolean isVersionAfter(final String pomVersion, final String centralVersion) {
280 if (centralVersion == null) {
281 return true;
282 }
283 final String[] pomValues = pomVersion.split("\\.");
284 final String[] centralValues = centralVersion.split("\\.");
285 for (int i = 0; i < pomValues.length; i++) {
286 if (pomValues[i].startsWith("v")) {
287 pomValues[i] = pomValues[i].substring(1);
288 }
289 try {
290 Integer.parseInt(pomValues[i]);
291 }
292 catch (final NumberFormatException e) {
293 return false;
294 }
295 }
296 for (int i = 0; i < centralValues.length; i++) {
297 if (centralValues[i].startsWith("v")) {
298 centralValues[i] = centralValues[i].substring(1);
299 }
300 try {
301 Integer.parseInt(centralValues[i]);
302 }
303 catch (final NumberFormatException e) {
304 return true;
305 }
306 }
307 for (int i = 0; i < pomValues.length; i++) {
308 if (i == centralValues.length) {
309 return true;
310 }
311 final int pomValuePart = Integer.parseInt(pomValues[i]);
312 final int centralValuePart = Integer.parseInt(centralValues[i]);
313 if (pomValuePart < centralValuePart) {
314 return false;
315 }
316 if (pomValuePart > centralValuePart) {
317 return true;
318 }
319 }
320 return false;
321 }
322
323 private static boolean isIgnored(@SuppressWarnings("unused") final String groupId,
324 @SuppressWarnings("unused") final String artifactId, @SuppressWarnings("unused") final String version) {
325 if (groupId.startsWith("org.eclipse.jetty")
326 && (version.startsWith("11.") || version.startsWith("10."))) {
327 return true;
328 }
329
330
331 if ("maven-site-plugin".equals(artifactId)
332 && (version.startsWith("3.12.1") || version.startsWith("3.20.") || version.startsWith("3.21."))) {
333 return true;
334 }
335
336
337 if ("org.owasp".equals(groupId)
338 && (version.startsWith("11.") || version.startsWith("12."))) {
339 return true;
340 }
341
342
343 if ("org.apache.felix".equals(groupId)
344 && version.startsWith("6.")) {
345 return true;
346 }
347
348
349 if ("commons-io".equals(artifactId) && (version.startsWith("2003"))) {
350 return true;
351 }
352 if ("commons-net".equals(artifactId) && (version.startsWith("2003"))) {
353 return true;
354 }
355
356 return false;
357 }
358
359 private static String getValue(final String line) {
360 return line.substring(line.indexOf('>') + 1, line.lastIndexOf('<'));
361 }
362
363 private static WebClient buildWebClient() {
364 final WebClient webClient = new WebClient();
365 webClient.getOptions().setThrowExceptionOnScriptError(false);
366 return webClient;
367 }
368 }