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