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.fail;
19
20 import java.awt.image.BufferedImage;
21 import java.io.ByteArrayInputStream;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.Serializable;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.nio.file.Files;
29 import java.nio.file.Paths;
30 import java.time.Duration;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Base64;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Objects;
37 import java.util.function.Supplier;
38
39 import javax.imageio.ImageIO;
40
41 import org.apache.commons.io.IOUtils;
42 import org.apache.commons.lang3.SerializationUtils;
43 import org.htmlunit.junit.RetryRule;
44 import org.junit.After;
45 import org.junit.AfterClass;
46 import org.junit.Assert;
47 import org.junit.BeforeClass;
48 import org.junit.Rule;
49 import org.junit.rules.MethodRule;
50 import org.junit.rules.TestName;
51
52 import com.github.romankh3.image.comparison.ImageComparison;
53 import com.github.romankh3.image.comparison.ImageComparisonUtil;
54 import com.github.romankh3.image.comparison.model.ImageComparisonResult;
55 import com.github.romankh3.image.comparison.model.ImageComparisonState;
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public abstract class WebTestCase {
70
71
72 public static final String DOCTYPE_HTML = "<!DOCTYPE html>\n";
73
74
75
76
77 @Rule
78 public TestName testMethodName_ = new TestName();
79
80
81
82
83 @Rule
84 public final RetryRule retryRule_ = new RetryRule(4);
85
86
87
88
89
90 private static final Locale SAVE_LOCALE = Locale.getDefault();
91
92
93
94
95 public static final int PORT = Integer.parseInt(System.getProperty("htmlunit.test.port", "22222"));
96
97
98 public static final int PORT2 = Integer.parseInt(System.getProperty("htmlunit.test.port2", "22223"));
99
100
101 public static final int PORT3 = Integer.parseInt(System.getProperty("htmlunit.test.port3", "22224"));
102
103
104 public static final int PORT_PRIMITIVE_SERVER = Integer.parseInt(
105 System.getProperty("htmlunit.test.port_primitive", "22225"));
106
107
108 public static final int PORT_PROXY_SERVER = Integer.parseInt(
109 System.getProperty("htmlunit.test.port_proxy", "22226"));
110
111
112 public static final int SOCKS_PROXY_PORT = Integer.parseInt(
113 System.getProperty("htmlunit.test.socksproxy.port", "22227"));
114
115
116 public static final String SOCKS_PROXY_HOST = System.getProperty("htmlunit.test.socksproxy.host", "localhost");
117
118
119 protected static final Duration DEFAULT_WAIT_TIME = Duration.ofSeconds(1);
120
121
122 public static final URL URL_FIRST;
123
124
125 public static final URL URL_SECOND;
126
127
128
129
130
131 public static final URL URL_THIRD;
132
133
134
135
136 public static final URL URL_CROSS_ORIGIN;
137
138
139
140
141
142 public static final URL URL_CROSS_ORIGIN2;
143
144
145
146
147 public static final URL URL_CROSS_ORIGIN_BASE;
148
149 private BrowserVersion browserVersion_;
150 private String[] expectedAlerts_;
151 private MockWebConnection mockWebConnection_;
152
153
154
155
156 @Rule
157 public final MethodRule errOutputChecker_ = new ErrorOutputChecker();
158
159 static {
160 try {
161 URL_FIRST = new URL("http://localhost:" + PORT + "/");
162 URL_SECOND = new URL("http://localhost:" + PORT + "/second/");
163 URL_THIRD = new URL("http://127.0.0.1:" + PORT + "/third/");
164 URL_CROSS_ORIGIN = new URL("http://127.0.0.1:" + PORT2 + "/corsAllowAll");
165 URL_CROSS_ORIGIN2 = new URL("http://localhost:" + PORT3 + "/");
166 URL_CROSS_ORIGIN_BASE = new URL("http://localhost:" + PORT2 + "/");
167 }
168 catch (final MalformedURLException e) {
169
170 throw new IllegalStateException("Unable to create URL constants");
171 }
172 }
173
174
175
176
177 protected WebTestCase() {
178 }
179
180
181
182
183
184 public static void assertNull(final Object object) {
185 Assert.assertNull("Expected null but found [" + object + "]", object);
186 }
187
188
189
190
191
192
193 public static void assertNull(final String message, final Object object) {
194 Assert.assertNull(message, object);
195 }
196
197
198
199
200
201 public static void assertNotNull(final Object object) {
202 Assert.assertNotNull(object);
203 }
204
205
206
207
208
209
210 public static void assertNotNull(final String message, final Object object) {
211 Assert.assertNotNull(message, object);
212 }
213
214
215
216
217
218
219 public static void assertSame(final Object expected, final Object actual) {
220 Assert.assertSame(expected, actual);
221 }
222
223
224
225
226
227
228
229 public static void assertSame(final String message, final Object expected, final Object actual) {
230 Assert.assertSame(message, expected, actual);
231 }
232
233
234
235
236
237
238 public static void assertNotSame(final Object expected, final Object actual) {
239 Assert.assertNotSame(expected, actual);
240 }
241
242
243
244
245
246
247
248 public static void assertNotSame(final String message, final Object expected, final Object actual) {
249 Assert.assertNotSame(message, expected, actual);
250 }
251
252
253
254
255
256
257
258 protected static void assertEquals(final URL expectedUrl, final URL actualUrl) {
259 Assert.assertEquals(expectedUrl.toExternalForm(), actualUrl.toExternalForm());
260 }
261
262
263
264
265
266
267 protected static void assertEquals(final Object expected, final Object actual) {
268 Assert.assertEquals(expected, actual);
269 }
270
271
272
273
274
275
276
277 protected static void assertEquals(final String message, final Object expected, final Object actual) {
278 Assert.assertEquals(message, expected, actual);
279 }
280
281
282
283
284
285
286 protected static void assertEquals(final int expected, final int actual) {
287 Assert.assertEquals(expected, actual);
288 }
289
290
291
292
293
294
295 protected void assertEquals(final boolean expected, final boolean actual) {
296 Assert.assertEquals(Boolean.valueOf(expected), Boolean.valueOf(actual));
297 }
298
299
300
301
302
303
304
305
306 protected void assertEquals(final String message, final URL expectedUrl, final URL actualUrl) {
307 Assert.assertEquals(message, expectedUrl.toExternalForm(), actualUrl.toExternalForm());
308 }
309
310
311
312
313
314
315 protected void assertEquals(final String expectedUrl, final URL actualUrl) {
316 Assert.assertEquals(expectedUrl, actualUrl.toExternalForm());
317 }
318
319
320
321
322
323
324
325
326
327 protected void assertEquals(final String[] expected, final List<String> actual) {
328 assertEquals(null, expected, actual);
329 }
330
331
332
333
334
335
336
337
338
339
340 protected void assertEquals(final String message, final String[] expected, final List<String> actual) {
341 Assert.assertEquals(message, Arrays.asList(expected).toString(), actual.toString());
342 }
343
344
345
346
347
348
349
350 protected void assertEquals(final String message, final String expectedUrl, final URL actualUrl) {
351 Assert.assertEquals(message, expectedUrl, actualUrl.toExternalForm());
352 }
353
354
355
356
357
358 protected void assertTrue(final boolean condition) {
359 Assert.assertTrue(condition);
360 }
361
362
363
364
365
366
367 protected void assertTrue(final String message, final boolean condition) {
368 Assert.assertTrue(message, condition);
369 }
370
371
372
373
374
375 protected void assertFalse(final boolean condition) {
376 Assert.assertFalse(condition);
377 }
378
379
380
381
382
383
384 protected void assertFalse(final String message, final boolean condition) {
385 Assert.assertFalse(message, condition);
386 }
387
388
389
390
391
392 public void setBrowserVersion(final BrowserVersion browserVersion) {
393 browserVersion_ = browserVersion;
394 }
395
396
397
398
399
400 protected final BrowserVersion getBrowserVersion() {
401 if (browserVersion_ == null) {
402 throw new IllegalStateException("You must annotate the test class with '@RunWith(BrowserRunner.class)'");
403 }
404 return browserVersion_;
405 }
406
407
408
409
410
411 public void setExpectedAlerts(final String... expectedAlerts) {
412 expectedAlerts_ = expectedAlerts;
413 }
414
415
416
417
418
419 protected String[] getExpectedAlerts() {
420 return expectedAlerts_;
421 }
422
423
424
425
426
427 protected void expandExpectedAlertsVariables(final URL url) {
428 expandExpectedAlertsVariables(url.toExternalForm());
429 }
430
431
432
433
434
435 protected void expandExpectedAlertsVariables(final String url) {
436 if (expectedAlerts_ == null) {
437 throw new IllegalStateException("You must annotate the test class with '@RunWith(BrowserRunner.class)'");
438 }
439 for (int i = 0; i < expectedAlerts_.length; i++) {
440 expectedAlerts_[i] = expectedAlerts_[i].replaceAll("§§URL§§", url);
441 }
442 }
443
444
445
446
447
448
449
450 protected <T extends Serializable> T clone(final T object) {
451 return SerializationUtils.clone(object);
452 }
453
454
455
456
457
458 @BeforeClass
459 public static void beforeClass() {
460 Locale.setDefault(Locale.US);
461 }
462
463
464
465
466 @AfterClass
467 public static void afterClass() {
468 Locale.setDefault(SAVE_LOCALE);
469 }
470
471
472
473
474
475
476
477 protected void verify(final Supplier<String> func, final String expected) throws Exception {
478 verify(func, expected, DEFAULT_WAIT_TIME);
479 }
480
481
482
483
484
485
486
487
488 protected void verify(final Supplier<String> func, final String expected,
489 final Duration maxWaitTime) throws Exception {
490 final long maxWait = System.currentTimeMillis() + maxWaitTime.toMillis();
491
492 String actual = null;
493 while (System.currentTimeMillis() < maxWait) {
494 actual = func.get();
495
496 if (Objects.equals(expected, actual)) {
497 break;
498 }
499
500 Thread.sleep(50);
501 }
502
503 assertEquals(expected, actual);
504 }
505
506
507
508
509
510 protected MockWebConnection getMockWebConnection() {
511 if (mockWebConnection_ == null) {
512 mockWebConnection_ = new MockWebConnection();
513 }
514 return mockWebConnection_;
515 }
516
517
518
519
520 @After
521 public void releaseResources() {
522 if (mockWebConnection_ != null) {
523 mockWebConnection_.clear();
524 }
525 }
526
527
528
529
530
531 protected List<Thread> getJavaScriptThreads() {
532 final Thread[] threads = new Thread[Thread.activeCount() + 10];
533 Thread.enumerate(threads);
534 final List<Thread> jsThreads = new ArrayList<>();
535 for (final Thread t : threads) {
536 if (t != null && t.getName().startsWith("JS executor for")) {
537 jsThreads.add(t);
538 }
539 }
540
541 return jsThreads;
542 }
543
544
545
546
547
548
549
550 protected String getFileContent(final String fileName) throws IOException {
551 final InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
552 assertNotNull(fileName, stream);
553 return IOUtils.toString(stream, ISO_8859_1);
554 }
555
556 protected void compareImages(final String expected, final String current) throws IOException {
557 final String currentBase64Image = current.split(",")[1];
558 final byte[] currentImageBytes = Base64.getDecoder().decode(currentBase64Image);
559
560 try (ByteArrayInputStream currentBis = new ByteArrayInputStream(currentImageBytes)) {
561 final BufferedImage currentImage = ImageIO.read(currentBis);
562
563 compareImages(expected, current, currentImage);
564 }
565 }
566
567 protected void compareImages(final String expected,
568 final String current, final BufferedImage currentImage) throws IOException {
569 final String expectedBase64Image = expected.split(",")[1];
570 final byte[] expectedImageBytes = Base64.getDecoder().decode(expectedBase64Image);
571
572 try (ByteArrayInputStream expectedBis = new ByteArrayInputStream(expectedImageBytes)) {
573 final BufferedImage expectedImage = ImageIO.read(expectedBis);
574
575 final ImageComparison imageComparison = new ImageComparison(expectedImage, currentImage);
576
577 imageComparison.setPixelToleranceLevel(0.2);
578 imageComparison.setAllowingPercentOfDifferentPixels(7);
579
580 final ImageComparisonResult imageComparisonResult = imageComparison.compareImages();
581 final ImageComparisonState imageComparisonState = imageComparisonResult.getImageComparisonState();
582
583 if (ImageComparisonState.SIZE_MISMATCH == imageComparisonState) {
584 final String dir = "target/" + testMethodName_.getMethodName();
585 Files.createDirectories(Paths.get(dir));
586
587 final File expectedOut = new File(dir, "expected.png");
588 final File currentOut = new File(dir, "current.png");
589 ImageComparisonUtil.saveImage(expectedOut, expectedImage);
590 ImageComparisonUtil.saveImage(currentOut, currentImage);
591
592 String fail = "The images are different in size - "
593 + "expected: " + expectedImage.getWidth() + "x" + expectedImage.getHeight()
594 + " current: " + currentImage.getWidth() + "x" + currentImage.getHeight()
595 + " (expected: " + expectedOut.getAbsolutePath()
596 + " current: " + currentOut.getAbsolutePath() + ")";
597 if (current != null) {
598 fail += "; current data: '" + current + "'";
599 }
600 fail(fail);
601 }
602 else if (ImageComparisonState.MISMATCH == imageComparisonState) {
603 final String dir = "target/" + testMethodName_.getMethodName();
604 Files.createDirectories(Paths.get(dir));
605
606 final File expectedOut = new File(dir, "expected.png");
607 final File currentOut = new File(dir, "current.png");
608 final File differenceOut = new File(dir, "difference.png");
609 ImageComparisonUtil.saveImage(expectedOut, expectedImage);
610 ImageComparisonUtil.saveImage(currentOut, currentImage);
611 ImageComparisonUtil.saveImage(differenceOut, imageComparisonResult.getResult());
612
613 String fail = "The images are different (expected: " + expectedOut.getAbsolutePath()
614 + " current: " + currentOut.getAbsolutePath()
615 + " difference: " + differenceOut.getAbsolutePath() + ")";
616 if (current != null) {
617 fail += "; current data: '" + current + "'";
618 }
619 fail(fail);
620 }
621 }
622 }
623 }