source: josm/trunk/test/unit/org/openstreetmap/josm/TestUtils.java@ 19267

Last change on this file since 19267 was 19109, checked in by taylor.smock, 6 months ago

See #17858: Remove old workarounds for javabugs and update Utils#getLatestVersion

  • JDK-8180379: Fixed in Java 9+
  • JDK-8251377: Fixed in Java 11.0.14 (2021-09-02)
  • JDK-8262085: Fixed in Java 11.0.17 (2022-07-19)
  • Property svn:eol-style set to native
File size: 26.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm;
3
4import static org.junit.jupiter.api.Assertions.assertAll;
5import static org.junit.jupiter.api.Assertions.assertArrayEquals;
6import static org.junit.jupiter.api.Assertions.assertEquals;
7import static org.junit.jupiter.api.Assertions.assertTrue;
8import static org.junit.jupiter.api.Assertions.fail;
9import static org.junit.jupiter.api.Assumptions.assumeFalse;
10import static org.junit.jupiter.api.Assumptions.assumeTrue;
11
12import java.awt.Component;
13import java.awt.Container;
14import java.awt.Graphics2D;
15import java.io.File;
16import java.io.FileInputStream;
17import java.io.IOException;
18import java.io.InputStream;
19import java.lang.reflect.Field;
20import java.lang.reflect.Method;
21import java.security.AccessController;
22import java.security.PrivilegedAction;
23import java.time.Instant;
24import java.time.ZoneOffset;
25import java.time.format.DateTimeFormatter;
26import java.time.temporal.Temporal;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Collection;
30import java.util.Comparator;
31import java.util.List;
32import java.util.Objects;
33import java.util.Set;
34import java.util.concurrent.ExecutionException;
35import java.util.concurrent.ThreadPoolExecutor;
36import java.util.concurrent.atomic.AtomicInteger;
37import java.util.function.Function;
38import java.util.stream.Collectors;
39import java.util.stream.IntStream;
40import java.util.stream.Stream;
41
42import org.junit.jupiter.api.Assertions;
43import org.openstreetmap.josm.command.Command;
44import org.openstreetmap.josm.data.osm.DataSet;
45import org.openstreetmap.josm.data.osm.Node;
46import org.openstreetmap.josm.data.osm.OsmPrimitive;
47import org.openstreetmap.josm.data.osm.OsmUtils;
48import org.openstreetmap.josm.data.osm.Relation;
49import org.openstreetmap.josm.data.osm.RelationMember;
50import org.openstreetmap.josm.data.osm.Way;
51import org.openstreetmap.josm.gui.MainApplication;
52import org.openstreetmap.josm.gui.progress.AbstractProgressMonitor;
53import org.openstreetmap.josm.gui.progress.CancelHandler;
54import org.openstreetmap.josm.gui.progress.ProgressMonitor;
55import org.openstreetmap.josm.gui.progress.ProgressTaskId;
56import org.openstreetmap.josm.gui.util.GuiHelper;
57import org.openstreetmap.josm.io.Compression;
58import org.openstreetmap.josm.testutils.FakeGraphics;
59import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
60import org.openstreetmap.josm.testutils.mockers.WindowMocker;
61import org.openstreetmap.josm.tools.JosmRuntimeException;
62import org.openstreetmap.josm.tools.ReflectionUtils;
63import org.openstreetmap.josm.tools.Utils;
64import org.openstreetmap.josm.tools.WikiReader;
65
66import com.github.tomakehurst.wiremock.WireMockServer;
67import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
68import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
69import io.github.classgraph.ClassGraph;
70import io.github.classgraph.ClassInfoList;
71import io.github.classgraph.ScanResult;
72import mockit.integration.TestRunnerDecorator;
73
74/**
75 * Various utils, useful for unit tests.
76 */
77public final class TestUtils {
78 private static Boolean workingJMockit;
79
80 private TestUtils() {
81 // Hide constructor for utility classes
82 }
83
84 /**
85 * Returns the path to test data root directory.
86 * @return path to test data root directory
87 */
88 public static String getTestDataRoot() {
89 String testDataRoot = System.getProperty("josm.test.data");
90 if (Utils.isEmpty(testDataRoot)) {
91 testDataRoot = "test/data";
92 System.out.println("System property josm.test.data is not set, using '" + testDataRoot + "'");
93 }
94 return testDataRoot.endsWith("/") ? testDataRoot : testDataRoot + "/";
95 }
96
97 /**
98 * Gets path to test data directory for given ticket id.
99 * @param ticketid Ticket numeric identifier
100 * @return path to test data directory for given ticket id
101 */
102 public static String getRegressionDataDir(int ticketid) {
103 return TestUtils.getTestDataRoot() + "/regress/" + ticketid;
104 }
105
106 /**
107 * Gets path to given file in test data directory for given ticket id.
108 * @param ticketid Ticket numeric identifier
109 * @param filename File name
110 * @return path to given file in test data directory for given ticket id
111 */
112 public static String getRegressionDataFile(int ticketid, String filename) {
113 return getRegressionDataDir(ticketid) + '/' + filename;
114 }
115
116 /**
117 * Gets input stream to given file in test data directory for given ticket id.
118 * @param ticketid Ticket numeric identifier
119 * @param filename File name
120 * @return path to given file in test data directory for given ticket id
121 * @throws IOException if any I/O error occurs
122 */
123 public static InputStream getRegressionDataStream(int ticketid, String filename) throws IOException {
124 return Compression.getUncompressedFileInputStream(new File(getRegressionDataDir(ticketid), filename));
125 }
126
127 /**
128 * Checks that the given Comparator respects its contract on the given table.
129 * @param <T> type of elements
130 * @param comparator The comparator to test
131 * @param array The array sorted for test purpose
132 */
133 @SuppressFBWarnings(value = "RV_NEGATING_RESULT_OF_COMPARETO")
134 public static <T> void checkComparableContract(Comparator<T> comparator, T[] array) {
135 System.out.println("Validating Comparable contract on array of "+array.length+" elements");
136 // Check each compare possibility
137 for (int i = 0; i < array.length; i++) {
138 T r1 = array[i];
139 for (int j = i; j < array.length; j++) {
140 T r2 = array[j];
141 int a = comparator.compare(r1, r2);
142 int b = comparator.compare(r2, r1);
143 if (i == j || a == b) {
144 if (a != 0 || b != 0) {
145 fail(getFailMessage(r1, r2, a, b));
146 }
147 } else {
148 if (a != -b) {
149 fail(getFailMessage(r1, r2, a, b));
150 }
151 }
152 for (int k = j; k < array.length; k++) {
153 T r3 = array[k];
154 int c = comparator.compare(r1, r3);
155 int d = comparator.compare(r2, r3);
156 if (a > 0 && d > 0) {
157 if (c <= 0) {
158 fail(getFailMessage(r1, r2, r3, a, b, c, d));
159 }
160 } else if (a == 0 && d == 0) {
161 if (c != 0) {
162 fail(getFailMessage(r1, r2, r3, a, b, c, d));
163 }
164 } else if (a < 0 && d < 0) {
165 if (c >= 0) {
166 fail(getFailMessage(r1, r2, r3, a, b, c, d));
167 }
168 }
169 }
170 }
171 }
172 // Sort relation array
173 Arrays.sort(array, comparator);
174 }
175
176 /**
177 * Create a test matrix for parameterized tests.
178 * <br />
179 * <b>WARNING:</b> This can quickly become <i>very</i> large (this is combinatorial,
180 * so the returned {@link Stream} length is the size of the object collections multiplied by each other.
181 * So if you have three lists of size 3, 4, and 5, the stream size would be {@code 3 * 4 * 5} or 60 elements.
182 * <br />
183 * Generally speaking, you should avoid putting expected values into the test matrix.
184 *
185 * @param objectCollections The collections of objects. May include/provide {@code null}.
186 * @return The object arrays to be used as arguments. Note: The returned stream might not be thread-safe.
187 */
188 public static Stream<Object[]> createTestMatrix(List<?>... objectCollections) {
189 // Create the original object arrays
190 final AtomicInteger size = new AtomicInteger(1);
191 Stream.of(objectCollections).mapToInt(Collection::size).forEach(i -> size.set(size.get() * i));
192 final List<Object[]> testMatrix = new ArrayList<>(size.get());
193 final int[] indexes = IntStream.range(0, objectCollections.length).map(i -> 0).toArray();
194
195 // It is important to make a new object array each time (we modify them)
196 return IntStream.range(0, size.get()).mapToObj(index -> new Object[objectCollections.length]).peek(args -> {
197 // Just in case someone tries to make this parallel, synchronize on indexes to avoid most issues.
198 synchronized (indexes) {
199 // Set the args
200 for (int listIndex = 0; listIndex < objectCollections.length; listIndex++) {
201 args[listIndex] = objectCollections[listIndex].get(indexes[listIndex]);
202 }
203 // Increment indexes
204 for (int listIndex = 0; listIndex < objectCollections.length; listIndex++) {
205 indexes[listIndex] = indexes[listIndex] + 1;
206 if (indexes[listIndex] >= objectCollections[listIndex].size()) {
207 indexes[listIndex] = 0;
208 } else {
209 break;
210 }
211 }
212 }
213 });
214 }
215
216 private static <T> String getFailMessage(T o1, T o2, int a, int b) {
217 return new StringBuilder("Compared\no1: ").append(o1).append("\no2: ")
218 .append(o2).append("\ngave: ").append(a).append("/").append(b)
219 .toString();
220 }
221
222 private static <T> String getFailMessage(T o1, T o2, T o3, int a, int b, int c, int d) {
223 return new StringBuilder(getFailMessage(o1, o2, a, b))
224 .append("\nCompared\no1: ").append(o1).append("\no3: ").append(o3).append("\ngave: ").append(c)
225 .append("\nCompared\no2: ").append(o2).append("\no3: ").append(o3).append("\ngave: ").append(d)
226 .toString();
227 }
228
229 /**
230 * Returns a private field value.
231 * @param obj object
232 * @param fieldName private field name
233 * @return private field value
234 * @throws ReflectiveOperationException if a reflection operation error occurs
235 */
236 public static Object getPrivateField(Object obj, String fieldName) throws ReflectiveOperationException {
237 return getPrivateField(obj.getClass(), obj, fieldName);
238 }
239
240 /**
241 * Returns a private field value.
242 * @param cls object class
243 * @param obj object
244 * @param fieldName private field name
245 * @return private field value
246 * @throws ReflectiveOperationException if a reflection operation error occurs
247 */
248 public static Object getPrivateField(Class<?> cls, Object obj, String fieldName) throws ReflectiveOperationException {
249 Field f = cls.getDeclaredField(fieldName);
250 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
251 f.setAccessible(true);
252 return null;
253 });
254 return f.get(obj);
255 }
256
257 /**
258 * Sets a private field value.
259 * @param obj object
260 * @param fieldName private field name
261 * @param value replacement value
262 * @throws ReflectiveOperationException if a reflection operation error occurs
263 */
264 public static void setPrivateField(
265 final Object obj,
266 final String fieldName,
267 final Object value
268 ) throws ReflectiveOperationException {
269 setPrivateField(obj.getClass(), obj, fieldName, value);
270 }
271
272 /**
273 * Sets a private field value.
274 * @param cls object class
275 * @param obj object
276 * @param fieldName private field name
277 * @param value replacement value
278 * @throws ReflectiveOperationException if a reflection operation error occurs
279 */
280 public static void setPrivateField(
281 final Class<?> cls,
282 final Object obj,
283 final String fieldName,
284 final Object value
285 ) throws ReflectiveOperationException {
286 Field f = cls.getDeclaredField(fieldName);
287 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
288 f.setAccessible(true);
289 return null;
290 });
291 f.set(obj, value);
292 }
293
294 /**
295 * Returns a private static field value.
296 * @param cls object class
297 * @param fieldName private field name
298 * @return private field value
299 * @throws ReflectiveOperationException if a reflection operation error occurs
300 */
301 public static Object getPrivateStaticField(Class<?> cls, String fieldName) throws ReflectiveOperationException {
302 Field f = cls.getDeclaredField(fieldName);
303 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
304 f.setAccessible(true);
305 return null;
306 });
307 return f.get(null);
308 }
309
310 /**
311 * Sets a private static field value.
312 * @param cls object class
313 * @param fieldName private field name
314 * @param value replacement value
315 * @throws ReflectiveOperationException if a reflection operation error occurs
316 */
317 public static void setPrivateStaticField(Class<?> cls, String fieldName, final Object value) throws ReflectiveOperationException {
318 Field f = cls.getDeclaredField(fieldName);
319 AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
320 f.setAccessible(true);
321 return null;
322 });
323 f.set(null, value);
324 }
325
326 /**
327 * Returns an instance of {@link AbstractProgressMonitor} which keeps track of the monitor state,
328 * but does not show the progress.
329 * @return a progress monitor
330 */
331 public static ProgressMonitor newTestProgressMonitor() {
332 return new AbstractProgressMonitor(new CancelHandler()) {
333
334 @Override
335 protected void doBeginTask() {
336 }
337
338 @Override
339 protected void doFinishTask() {
340 }
341
342 @Override
343 protected void doSetIntermediate(boolean value) {
344 }
345
346 @Override
347 protected void doSetTitle(String title) {
348 }
349
350 @Override
351 protected void doSetCustomText(String title) {
352 }
353
354 @Override
355 protected void updateProgress(double value) {
356 }
357
358 @Override
359 public void setProgressTaskId(ProgressTaskId taskId) {
360 }
361
362 @Override
363 public ProgressTaskId getProgressTaskId() {
364 return null;
365 }
366
367 @Override
368 public Component getWindowParent() {
369 return null;
370 }
371 };
372 }
373
374 /**
375 * Returns an instance of {@link Graphics2D}.
376 * @return a mockup graphics instance
377 */
378 public static Graphics2D newGraphics() {
379 return new FakeGraphics();
380 }
381
382 /**
383 * Makes sure the given primitive belongs to a data set.
384 * @param <T> OSM primitive type
385 * @param osm OSM primitive
386 * @return OSM primitive, attached to a new {@code DataSet}
387 */
388 public static <T extends OsmPrimitive> T addFakeDataSet(T osm) {
389 new DataSet(osm);
390 return osm;
391 }
392
393 /**
394 * Creates a new node with the given tags (see {@link OsmUtils#createPrimitive(java.lang.String)})
395 *
396 * @param tags the tags to set
397 * @return a new node
398 */
399 public static Node newNode(String tags) {
400 return (Node) OsmUtils.createPrimitive("node " + tags);
401 }
402
403 /**
404 * Creates a new way with the given tags (see {@link OsmUtils#createPrimitive(java.lang.String)}) and the nodes added
405 *
406 * @param tags the tags to set
407 * @param nodes the nodes to add
408 * @return a new way
409 */
410 public static Way newWay(String tags, Node... nodes) {
411 final Way way = (Way) OsmUtils.createPrimitive("way " + tags);
412 for (Node node : nodes) {
413 way.addNode(node);
414 }
415 return way;
416 }
417
418 /**
419 * Creates a new relation with the given tags (see {@link OsmUtils#createPrimitive(java.lang.String)}) and the members added
420 *
421 * @param tags the tags to set
422 * @param members the members to add
423 * @return a new relation
424 */
425 public static Relation newRelation(String tags, RelationMember... members) {
426 final Relation relation = (Relation) OsmUtils.createPrimitive("relation " + tags);
427 for (RelationMember member : members) {
428 relation.addMember(member);
429 }
430 return relation;
431 }
432
433 /**
434 * Creates a new empty command.
435 * @param ds data set
436 * @return a new empty command
437 */
438 public static Command newCommand(DataSet ds) {
439 return new Command(ds) {
440 @Override
441 public String getDescriptionText() {
442 return "";
443 }
444
445 @Override
446 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
447 Collection<OsmPrimitive> added) {
448 // Do nothing
449 }
450 };
451 }
452
453 /**
454 * Ensures 100% code coverage for enums.
455 * @param enumClass enum class to cover
456 */
457 public static void superficialEnumCodeCoverage(Class<? extends Enum<?>> enumClass) {
458 try {
459 Method values = enumClass.getMethod("values");
460 Method valueOf = enumClass.getMethod("valueOf", String.class);
461 ReflectionUtils.setObjectsAccessible(values, valueOf);
462 for (Object o : (Object[]) values.invoke(null)) {
463 assertEquals(o, valueOf.invoke(null, ((Enum<?>) o).name()));
464 }
465 } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException e) {
466 throw new JosmRuntimeException(e);
467 }
468 }
469
470 /**
471 * Get a descendant component by name.
472 * @param root The root component to start searching from.
473 * @param name The component name
474 * @return The component with that name or null if it does not exist.
475 * @since 12045
476 */
477 public static Component getComponentByName(Component root, String name) {
478 if (name.equals(root.getName())) {
479 return root;
480 } else if (root instanceof Container) {
481 Container container = (Container) root;
482 return Stream.of(container.getComponents())
483 .map(child -> getComponentByName(child, name))
484 .filter(Objects::nonNull)
485 .findFirst().orElse(null);
486 } else {
487 return null;
488 }
489 }
490
491 /**
492 * Use to assume that EqualsVerifier is working with the current JVM.
493 */
494 @SuppressWarnings("null")
495 public static void assumeWorkingEqualsVerifier() {
496 // See https://github.com/raphw/byte-buddy/blob/master/byte-buddy-dep/src/main/java/net/bytebuddy/ClassFileVersion.java
497 // for currently supported Java versions.
498 if (Utils.getJavaVersion() >= 22) {
499 // Byte Buddy often supports new class file versions for current EA releases if its experimental flag is set to true
500 System.setProperty("net.bytebuddy.experimental", "true");
501 } else {
502 return;
503 }
504 try {
505 // Workaround to https://github.com/jqno/equalsverifier/issues/177
506 // Inspired by https://issues.apache.org/jira/browse/SOLR-11606
507 // Note: if we change to the equalsverifier fat jar, use nl.jqno.equalsverifier.internal.lib instead of net
508 net.bytebuddy.ClassFileVersion.ofThisVm();
509 } catch (IllegalArgumentException e) {
510 assumeFalse(e != null);
511 }
512 }
513
514 /**
515 * Use to assume that JMockit is working with the current JVM.
516 */
517 @SuppressWarnings("null")
518 public static void assumeWorkingJMockit() {
519 if (workingJMockit == null) {
520 try {
521 // Workaround to https://github.com/jmockit/jmockit1/issues/534
522 // Inspired by https://issues.apache.org/jira/browse/SOLR-11606
523 new WindowMocker();
524 new JOptionPaneSimpleMocker();
525 workingJMockit = true;
526 } catch (UnsupportedOperationException e) {
527 workingJMockit = false;
528 } finally {
529 TestRunnerDecorator.cleanUpAllMocks();
530 }
531 }
532 assumeTrue(workingJMockit);
533 }
534
535 /**
536 * Return WireMock server serving files under ticket directory
537 * @param ticketId Ticket numeric identifier
538 * @return WireMock HTTP server on dynamic port
539 */
540 public static WireMockServer getWireMockServer(int ticketId) {
541 return new WireMockServer(
542 WireMockConfiguration.options()
543 .dynamicPort()
544 .usingFilesUnderDirectory(getRegressionDataDir(ticketId))
545 );
546 }
547
548 /**
549 * Return WireMock server
550 * @return WireMock HTTP server on dynamic port
551 */
552 public static WireMockServer getWireMockServer() {
553 return new WireMockServer(
554 WireMockConfiguration.options()
555 .withRootDirectory("test/data")
556 .dynamicPort()
557 );
558 }
559
560 /**
561 * Renders Temporal to RFC 1123 Date Time
562 * @param time to convert
563 * @return string representation according to RFC1123 of time
564 */
565 public static String getHTTPDate(Temporal time) {
566 return DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC).format(time);
567 }
568
569 /**
570 * Renders java time stamp to RFC 1123 Date Time
571 * @param time java timestamp (milliseconds from Epoch)
572 * @return string representation according to RFC1123 of time
573 */
574 public static String getHTTPDate(long time) {
575 return getHTTPDate(Instant.ofEpochMilli(time));
576 }
577
578 /**
579 * Throws AssertionError if contents of both files are not equal
580 * @param fileA File A
581 * @param fileB File B
582 */
583 public static void assertFileContentsEqual(final File fileA, final File fileB) {
584 assertTrue(fileA.exists());
585 assertTrue(fileA.canRead());
586 assertTrue(fileB.exists());
587 assertTrue(fileB.canRead());
588 try {
589 try (
590 FileInputStream streamA = new FileInputStream(fileA);
591 FileInputStream streamB = new FileInputStream(fileB);
592 ) {
593 assertArrayEquals(
594 streamA.readAllBytes(),
595 streamB.readAllBytes()
596 );
597 }
598 } catch (IOException e) {
599 throw new RuntimeException(e);
600 }
601 }
602
603 /**
604 * Replaces {@linkplain System#lineSeparator() system dependent line separators} with {@code \n}
605 * and calls {@link Assertions#assertEquals(java.lang.Object, java.lang.Object)}.
606 * @param expected expected value
607 * @param actual the value to check against <code>expected</code>
608 */
609 public static void assertEqualsNewline(String expected, String actual) {
610 String[] actualArray = actual.replace(System.lineSeparator(), "\n").split("\n", -1);
611 String[] expectedArray = expected.split("\n", -1);
612 assertAll(() -> assertEquals(expectedArray.length, actualArray.length, "Different number of newlines"),
613 () -> assertArrayEquals(expectedArray, actualArray),
614 () -> assertEquals(expected, actual.replace(System.lineSeparator(), "\n")));
615 }
616
617 /**
618 * Waits until any asynchronous operations launched by the test on the EDT or worker threads have
619 * (almost certainly) completed.
620 */
621 public static void syncEDTAndWorkerThreads() {
622 boolean workerQueueEmpty = false;
623 while (!workerQueueEmpty) {
624 try {
625 // once our own task(s) have made it to the front of their respective queue(s),
626 // they're both executing at the same time and we know there aren't any outstanding
627 // worker tasks, then presumably the only way there could be incomplete operations
628 // is if the EDT had launched a deferred task to run on itself or perhaps set up a
629 // swing timer - neither are particularly common patterns in JOSM (?)
630 //
631 // there shouldn't be a risk of creating a deadlock in doing this as there shouldn't
632 // (...couldn't?) be EDT operations waiting on the results of a worker task.
633 workerQueueEmpty = MainApplication.worker.submit(
634 () -> GuiHelper.runInEDTAndWaitAndReturn(
635 () -> ((ThreadPoolExecutor) MainApplication.worker).getQueue().isEmpty()
636 )
637 ).get();
638 } catch (InterruptedException | ExecutionException e) {
639 // inconclusive - retry...
640 workerQueueEmpty = false;
641 }
642 }
643 }
644
645 /**
646 * Returns all JOSM subtypes of the given class.
647 * @param <T> class
648 * @param superClass class
649 * @return all JOSM subtypes of the given class
650 */
651 public static <T> Set<Class<? extends T>> getJosmSubtypes(Class<T> superClass) {
652 try (ScanResult scan = new ClassGraph().acceptPackages("org.openstreetmap.josm").ignoreClassVisibility().scan()) {
653 Function<String, ClassInfoList> lambda = superClass.isInterface() ? scan::getClassesImplementing : scan::getSubclasses;
654 return lambda.apply(superClass.getName())
655 .asMap().values().stream().map(x -> x.loadClass(superClass)).collect(Collectors.toSet());
656 }
657 }
658
659 /**
660 * Determines if OSM DEV_API credential have been provided. Required for functional tests.
661 * @return {@code true} if {@code osm.oauth2} have been defined on the command line
662 */
663 public static boolean areCredentialsProvided() {
664 return Utils.getSystemProperty("osm.oauth2") != null;
665 }
666
667 /**
668 * Returns the ignored error messages listed on
669 * <a href="https://josm.openstreetmap.de/wiki/IntegrationTestIgnores">JOSM wiki</a> for a given test.
670 * @param integrationTest The integration test class
671 * @return the ignored error messages listed on JOSM wiki for this test.
672 * @throws IOException in case of I/O error
673 */
674 public static List<String> getIgnoredErrorMessages(Class<?> integrationTest) throws IOException {
675 return Arrays.stream(new WikiReader()
676 .read("https://josm.openstreetmap.de/wiki/IntegrationTestIgnores?format=txt").split("\\n", -1))
677 .filter(s -> s.startsWith("|| " + integrationTest.getSimpleName() + " ||"))
678 .map(s -> s.substring(s.indexOf("{{{") + 3, s.indexOf("}}}")))
679 .collect(Collectors.toList());
680 }
681}
Note: See TracBrowser for help on using the repository browser.