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