Ticket #21423: 21423.3.patch
| File 21423.3.patch, 27.4 KB (added by , 3 years ago) |
|---|
-
src/org/openstreetmap/josm/data/validation/OsmValidator.java
Subject: [PATCH] Fix #21423: Prevent error codes from clashing --- IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
a b 112 112 @SuppressWarnings("unchecked") 113 113 private static final Class<Test>[] CORE_TEST_CLASSES = new Class[] {// NOPMD 114 114 /* FIXME - unique error numbers for tests aren't properly unique - ignoring will not work as expected */ 115 /* Error codes are class.getName().hashCode() + "_" + oldCode. There should almost never be a collision. */ 115 116 DuplicateNode.class, // ID 1 .. 99 116 117 OverlappingWays.class, // ID 101 .. 199 117 118 UntaggedNode.class, // ID 201 .. 299 … … 217 218 218 219 private static void loadIgnoredErrors() { 219 220 ignoredErrors.clear(); 220 if ( ValidatorPrefHelper.PREF_USE_IGNORE.get()) {221 if (Boolean.TRUE.equals(ValidatorPrefHelper.PREF_USE_IGNORE.get())) { 221 222 Config.getPref().getListOfMaps(ValidatorPrefHelper.PREF_IGNORELIST).forEach(ignoredErrors::putAll); 222 223 Path path = Paths.get(getValidatorDir()).resolve("ignorederrors"); 223 224 try { 224 225 if (path.toFile().exists()) { 225 226 try { 226 TreeSet<String> treeSet = new TreeSet<>(); 227 treeSet.addAll(Files.readAllLines(path, StandardCharsets.UTF_8)); 227 TreeSet<String> treeSet = new TreeSet<>(Files.readAllLines(path, StandardCharsets.UTF_8)); 228 228 treeSet.forEach(ignore -> ignoredErrors.putIfAbsent(ignore, "")); 229 229 removeLegacyEntries(true); 230 230 … … 246 246 247 247 private static void removeLegacyEntries(boolean force) { 248 248 // see #19053: 249 boolean wasChanged = removeLegacyEntry(force, true, "3000"); 250 // see #18230 (pt_assistant, RightAngleBuildingTest) 251 wasChanged |= removeLegacyEntry(force, false, "3701"); 252 253 if (wasChanged) { 254 saveIgnoredErrors(); 255 } 256 } 257 258 private static boolean removeLegacyEntry(boolean force, boolean keep, String prefix) { 249 259 boolean wasChanged = false; 250 260 if (force) { 251 261 Iterator<Entry<String, String>> iter = ignoredErrors.entrySet().iterator(); 252 262 while (iter.hasNext()) { 253 263 Entry<String, String> entry = iter.next(); 254 if (entry.getKey().startsWith( "3000_")) {264 if (entry.getKey().startsWith(prefix + "_")) { 255 265 Logging.warn(tr("Cannot handle ignore list entry {0}", entry)); 256 266 iter.remove(); 257 267 wasChanged = true; 258 268 } 259 269 } 260 270 } 261 String legacyEntry = ignoredErrors.remove( "3000");262 if ( legacyEntry != null) {271 String legacyEntry = ignoredErrors.remove(prefix); 272 if (keep && legacyEntry != null) { 263 273 if (!legacyEntry.isEmpty()) { 264 addIgnoredError( "3000_" + legacyEntry, legacyEntry);274 addIgnoredError(prefix + "_" + legacyEntry, legacyEntry); 265 275 } 266 276 wasChanged = true; 267 277 } 268 if (wasChanged) { 269 saveIgnoredErrors(); 270 } 278 return wasChanged; 271 279 } 272 280 273 281 /** … … 502 510 List<Map<String, String>> list = new ArrayList<>(); 503 511 cleanupIgnoredErrors(); 504 512 ignoredErrors.remove("3000"); // see #19053 513 ignoredErrors.remove("3701"); // see #18230 505 514 list.add(ignoredErrors); 506 515 int i = 0; 507 516 while (i < list.size()) { … … 607 616 } 608 617 609 618 /** 610 * Initialize grid details based on current projection system. Values based on619 * Initialize grid details based on the current projection system. Values based on 611 620 * the original value fixed for EPSG:4326 (10000) using heuristics (that is, test&error 612 621 * until most bugs were discovered while keeping the processing time reasonable) 613 622 */ … … 636 645 private static boolean testsInitialized; 637 646 638 647 /** 639 * Initializes all tests if this operation shasn't been performed already.648 * Initializes all tests if this operation hasn't been performed already. 640 649 */ 641 650 public static synchronized void initializeTests() { 642 651 if (!testsInitialized) { -
src/org/openstreetmap/josm/data/validation/TestError.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/data/validation/TestError.java b/src/org/openstreetmap/josm/data/validation/TestError.java
a b 4 4 import java.awt.geom.Area; 5 5 import java.awt.geom.PathIterator; 6 6 import java.text.MessageFormat; 7 import java.time.Instant; 7 8 import java.util.ArrayList; 8 9 import java.util.Arrays; 9 10 import java.util.Collection; 10 11 import java.util.Collections; 11 12 import java.util.List; 12 13 import java.util.Locale; 14 import java.util.Map; 13 15 import java.util.TreeSet; 14 16 import java.util.function.Supplier; 15 17 import java.util.stream.Collectors; … … 33 35 * @since 3669 34 36 */ 35 37 public class TestError implements Comparable<TestError> { 38 /** 39 * Used to switch users over to new ignore system, UNIQUE_CODE_MESSAGE_STATE 40 * 1_704_067_200L -> 2024-01-01 41 * We can probably remove this and the supporting code in 2025. 42 */ 43 private static boolean switchOver = Instant.now().isAfter(Instant.ofEpochMilli(1_704_067_200L)); 36 44 /** is this error on the ignore list */ 37 45 private boolean ignored; 38 46 /** Severity */ … … 50 58 private final Test tester; 51 59 /** Internal code used by testers to classify errors */ 52 60 private final int code; 61 /** Internal code used by testers to classify errors. Used for moving between JOSM versions. */ 62 private final int uniqueCode; 53 63 /** If this error is selected */ 54 64 private boolean selected; 55 65 /** Supplying a command to fix the error */ … … 63 73 private final Test tester; 64 74 private final Severity severity; 65 75 private final int code; 76 private final int uniqueCode; 66 77 private String message; 67 78 private String description; 68 79 private String descriptionEn; … … 74 85 this.tester = tester; 75 86 this.severity = severity; 76 87 this.code = code; 88 this.uniqueCode = this.tester != null ? this.tester.getClass().getName().hashCode() : code; 77 89 } 78 90 79 91 /** … … 233 245 } 234 246 } 235 247 248 /** 249 * Update error codes on read and save. Used for tests. 250 * @param updateErrorCodes {@code true} to update error codes. See {@link #switchOver} for default. 251 */ 252 static void setUpdateErrorCodes(boolean updateErrorCodes) { 253 switchOver = updateErrorCodes; 254 } 255 236 256 /** 237 257 * Starts building a new {@code TestError} 238 258 * @param tester The tester … … 254 274 this.primitives = builder.primitives; 255 275 this.highlighted = builder.highlighted; 256 276 this.code = builder.code; 277 this.uniqueCode = builder.uniqueCode; 257 278 this.fixingCommand = builder.fixingCommand; 258 279 } 259 280 … … 306 327 * @return the ignore state for this error or null if any primitive is new 307 328 */ 308 329 public String getIgnoreState() { 330 return getIgnoreState(false); 331 } 332 333 /** 334 * Get the ignore state 335 * @param useOriginal if {@code true}, use the original code to get the ignore state 336 * @return The ignore state ({@link #getIgnoreGroup} + ignored object list) 337 */ 338 private String getIgnoreState(boolean useOriginal) { 309 339 Collection<String> strings = new TreeSet<>(); 310 340 for (OsmPrimitive o : primitives) { 311 341 // ignore data not yet uploaded … … 321 351 } 322 352 strings.add(type + '_' + o.getId()); 323 353 } 324 return strings.stream().map(o -> ':' + o).collect(Collectors.joining("", getIgnoreSubGroup( ), ""));354 return strings.stream().map(o -> ':' + o).collect(Collectors.joining("", getIgnoreSubGroup(useOriginal), "")); 325 355 } 326 356 327 357 /** … … 335 365 } 336 366 337 367 private boolean calcIgnored() { 368 // Begin code removal section (backwards compatibility) 369 if (OsmValidator.hasIgnoredError(getIgnoreGroup(true))) { 370 updateIgnoreList(getIgnoreGroup(true), getIgnoreGroup(false)); 371 return true; 372 } 373 if (OsmValidator.hasIgnoredError(getIgnoreSubGroup(true))) { 374 updateIgnoreList(getIgnoreSubGroup(true), getIgnoreSubGroup(false)); 375 return true; 376 } 377 String oldState = getIgnoreState(true); 378 String state = getIgnoreState(false); 379 if (oldState != null && OsmValidator.hasIgnoredError(oldState)) { 380 updateIgnoreList(oldState, state); 381 return true; 382 } 383 // End code removal section 338 384 if (OsmValidator.hasIgnoredError(getIgnoreGroup())) 339 385 return true; 340 386 if (OsmValidator.hasIgnoredError(getIgnoreSubGroup())) 341 387 return true; 342 String state = getIgnoreState();343 388 return state != null && OsmValidator.hasIgnoredError(state); 344 389 } 345 390 391 /** 392 * Convert old keys to new keys. Only takes effect when {@link #switchOver} is true 393 * @param oldKey The key to replace 394 * @param newKey The new key 395 */ 396 private static void updateIgnoreList(String oldKey, String newKey) { 397 if (switchOver) { 398 Map<String, String> errors = OsmValidator.getIgnoredErrors(); 399 if (errors.containsKey(oldKey)) { 400 String value = errors.remove(oldKey); 401 errors.put(newKey, value); 402 } 403 } 404 } 405 346 406 /** 347 407 * Gets the ignores subgroup that is more specialized than {@link #getIgnoreGroup()} 348 408 * @return The ignore sub group 349 409 */ 350 410 public String getIgnoreSubGroup() { 411 return getIgnoreSubGroup(false); 412 } 413 414 /** 415 * Get the subgroup for the error 416 * @param useOriginal if {@code true}, use the original code instead of the new unique codes. 417 * @return The ignore subgroup 418 */ 419 private String getIgnoreSubGroup(boolean useOriginal) { 351 420 if (code == 3000) { 352 421 // see #19053 353 422 return "3000_" + (description == null ? message : description); 354 423 } 355 String ignorestring = getIgnoreGroup( );424 String ignorestring = getIgnoreGroup(useOriginal); 356 425 if (descriptionEn != null) { 357 426 ignorestring += '_' + descriptionEn; 358 427 } … … 365 434 * @see TestError#getIgnoreSubGroup() 366 435 */ 367 436 public String getIgnoreGroup() { 437 return getIgnoreGroup(false); 438 } 439 440 /** 441 * Get the ignore group 442 * @param useOriginal if {@code true}, use the original code instead of a unique code + original code. 443 * Used for reading and understanding old ignore groups. 444 * @return The ignore group. 445 */ 446 private String getIgnoreGroup(boolean useOriginal) { 368 447 if (code == 3000) { 369 448 // see #19053 370 449 return "3000_" + getMessage(); 371 450 } 372 return Integer.toString(code); 451 if (useOriginal) { 452 return Integer.toString(this.code); 453 } 454 return this.uniqueCode + "_" + this.code; 373 455 } 374 456 375 457 /** … … 404 486 return code; 405 487 } 406 488 489 /** 490 * Get the unique code for this test. Used for ignore lists. 491 * @return The unique code (generated with {@code tester.getClass().getName().hashCode() + code}). 492 * @since xxx 493 */ 494 public int getUniqueCode() { 495 return this.uniqueCode; 496 } 497 407 498 /** 408 499 * Returns true if the error can be fixed automatically 409 500 * … … 546 637 * @return true if two errors are similar 547 638 */ 548 639 public boolean isSimilar(TestError other) { 549 return getCode() == other.getCode() 640 return getUniqueCode() == other.getUniqueCode() 641 && getCode() == other.getCode() 550 642 && getMessage().equals(other.getMessage()) 551 643 && getPrimitives().size() == other.getPrimitives().size() 552 644 && getPrimitives().containsAll(other.getPrimitives()) … … 570 662 571 663 @Override 572 664 public String toString() { 573 return "TestError [tester=" + tester + ", code=" + code + ", message=" + message + ']'; 665 return "TestError [tester=" + tester + ", unique code=" + this.uniqueCode + 666 ", code=" + code + ", message=" + message + ']'; 574 667 } 575 668 576 669 } -
src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java b/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java
a b 45 45 final JsonObjectBuilder propertiesBuilder = Json.createObjectBuilder(); 46 46 propertiesBuilder.add("message", testError.getMessage()); 47 47 Optional.ofNullable(testError.getDescription()).ifPresent(description -> propertiesBuilder.add("description", description)); 48 propertiesBuilder.add("code", testError.get Code());48 propertiesBuilder.add("code", testError.getUniqueCode()); 49 49 propertiesBuilder.add("fixable", testError.isFixable()); 50 50 propertiesBuilder.add("severity", testError.getSeverity().toString()); 51 51 propertiesBuilder.add("severityInteger", testError.getSeverity().getLevel()); -
new file test/unit/org/openstreetmap/josm/data/validation/TestErrorTest.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/test/unit/org/openstreetmap/josm/data/validation/TestErrorTest.java b/test/unit/org/openstreetmap/josm/data/validation/TestErrorTest.java new file mode 100644
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.validation; 3 4 import static org.junit.jupiter.api.Assertions.assertAll; 5 import static org.junit.jupiter.api.Assertions.assertEquals; 6 import static org.junit.jupiter.api.Assertions.assertFalse; 7 import static org.junit.jupiter.api.Assertions.assertNotEquals; 8 import static org.junit.jupiter.api.Assertions.assertNull; 9 import static org.junit.jupiter.api.Assertions.assertTrue; 10 11 import java.util.Arrays; 12 import java.util.Collections; 13 import java.util.List; 14 import java.util.stream.Stream; 15 16 import org.junit.jupiter.params.ParameterizedTest; 17 import org.junit.jupiter.params.provider.Arguments; 18 import org.junit.jupiter.params.provider.MethodSource; 19 import org.openstreetmap.josm.TestUtils; 20 import org.openstreetmap.josm.data.osm.OsmPrimitive; 21 import org.openstreetmap.josm.data.validation.tests.InternetTags; 22 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 23 import org.openstreetmap.josm.testutils.annotations.BasicPreferences; 24 25 /** 26 * Test class for {@link TestError} 27 */ 28 @BasicPreferences 29 class TestErrorTest { 30 static Stream<Arguments> testCodeCompatibility() { 31 return Stream.of(Arguments.of(InternetTags.class, 3301, 1166507262, false, Collections.singletonList(TestUtils.newNode("url=invalid"))), 32 Arguments.of(InternetTags.class, 3301, 1166507262, true, Collections.singletonList(TestUtils.newNode("url=invalid")))); 33 } 34 35 /** 36 * See #18230/#21423: Keep error codes unique 37 * 38 * @param testClass The test class to use 39 * @param originalCode The expected error code (original) 40 * @param expectedCode The expected error code (new, should be {@code testClass.getName().hashCode()}) 41 * @param switchOver {@code true} if the new code should be saved instead of the original code 42 * @param primitiveCollection The primitives to run the test on 43 * @throws ReflectiveOperationException If the test class could not be instantiated (no-op constructor) 44 */ 45 @ParameterizedTest 46 @MethodSource 47 void testCodeCompatibility(Class<? extends Test> testClass, int originalCode, int expectedCode, 48 boolean switchOver, List<OsmPrimitive> primitiveCollection) throws ReflectiveOperationException { 49 // Ensure that this test always works 50 TestError.setUpdateErrorCodes(switchOver); 51 assertEquals(expectedCode, testClass.getName().hashCode()); 52 // Run the test 53 final Test test = testClass.getConstructor().newInstance(); 54 test.startTest(NullProgressMonitor.INSTANCE); 55 test.visit(primitiveCollection); 56 test.endTest(); 57 assertFalse(test.getErrors().isEmpty()); 58 assertEquals(1, test.getErrors().size()); 59 final TestError testError = test.getErrors().get(0); 60 final String ignoreGroup = testError.getIgnoreGroup(); 61 final String ignoreSubGroup = testError.getIgnoreSubGroup(); 62 if (primitiveCollection.size() == 1 && primitiveCollection.get(0).isNew()) { 63 assertNull(testError.getIgnoreState()); 64 primitiveCollection.get(0).setOsmId(1, 1); 65 } 66 final String ignoreState = testError.getIgnoreState(); 67 final String startUniqueCode = expectedCode + "_"; 68 assertAll(() -> assertTrue(ignoreGroup.startsWith(startUniqueCode + originalCode)), 69 () -> assertTrue(ignoreSubGroup.startsWith(startUniqueCode + originalCode)), 70 () -> assertTrue(ignoreState.startsWith(startUniqueCode + originalCode))); 71 for (String ignore : Arrays.asList(ignoreGroup, ignoreSubGroup, ignoreState)) { 72 OsmValidator.clearIgnoredErrors(); 73 final String oldIgnore = ignore.replace(startUniqueCode, ""); 74 OsmValidator.addIgnoredError(oldIgnore); 75 // Add the ignored error 76 assertTrue(testError.updateIgnored()); 77 assertAll(() -> assertEquals(switchOver, OsmValidator.hasIgnoredError(ignore)), 78 () -> assertNotEquals(switchOver, OsmValidator.hasIgnoredError(oldIgnore))); 79 80 OsmValidator.clearIgnoredErrors(); 81 OsmValidator.addIgnoredError(ignore); 82 // Add the ignored error 83 assertTrue(testError.updateIgnored()); 84 assertAll(() -> assertTrue(OsmValidator.hasIgnoredError(ignore)), 85 () -> assertFalse(OsmValidator.hasIgnoredError(oldIgnore))); 86 } 87 } 88 } -
test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 diff --git a/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java b/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
a b 3 3 4 4 import static org.junit.jupiter.api.Assertions.assertEquals; 5 5 import static org.junit.jupiter.api.Assertions.assertFalse; 6 import static org.junit.jupiter.api.Assertions.assertNotNull; 6 7 import static org.junit.jupiter.api.Assertions.assertTrue; 7 8 8 9 import java.awt.GraphicsEnvironment; … … 16 17 import java.util.List; 17 18 import java.util.Map; 18 19 import java.util.Map.Entry; 20 import java.util.Objects; 19 21 import java.util.Set; 20 22 import java.util.function.Consumer; 23 import java.util.logging.Handler; 24 import java.util.logging.LogRecord; 21 25 import java.util.stream.Collectors; 22 26 23 27 import org.junit.jupiter.api.BeforeAll; 24 28 import org.junit.jupiter.api.Test; 25 29 import org.junit.jupiter.api.extension.RegisterExtension; 30 import org.junit.platform.commons.util.ReflectionUtils; 26 31 import org.openstreetmap.josm.TestUtils; 27 32 import org.openstreetmap.josm.data.Preferences; 28 33 import org.openstreetmap.josm.data.gpx.GpxData; … … 73 78 74 79 Map<String, Throwable> loadingExceptions = PluginHandler.pluginLoadingExceptions.entrySet().stream() 75 80 .filter(e -> !(Utils.getRootCause(e.getValue()) instanceof HeadlessException)) 76 .collect(Collectors.toMap( e -> e.getKey(), e -> Utils.getRootCause(e.getValue())));81 .collect(Collectors.toMap(Map.Entry::getKey, e -> Utils.getRootCause(e.getValue()))); 77 82 78 83 List<PluginInformation> loadedPlugins = PluginHandler.getPlugins(); 79 84 Map<String, List<String>> invalidManifestEntries = loadedPlugins.stream().filter(pi -> !pi.invalidManifestEntries.isEmpty()) … … 92 97 testPlugin(MainApplication.getLayerManager()::removeLayer, layer, layerExceptions, loadedPlugins); 93 98 } 94 99 100 Map<String, String> testCodeHashCollisions = checkForHashCollisions(); 101 95 102 Map<String, Throwable> noRestartExceptions = new HashMap<>(); 96 103 testCompletelyRestartlessPlugins(loadedPlugins, noRestartExceptions); 97 104 … … 99 106 debugPrint(loadingExceptions); 100 107 debugPrint(layerExceptions); 101 108 debugPrint(noRestartExceptions); 109 debugPrint(testCodeHashCollisions); 102 110 103 111 invalidManifestEntries = filterKnownErrors(invalidManifestEntries); 104 112 loadingExceptions = filterKnownErrors(loadingExceptions); 105 113 layerExceptions = filterKnownErrors(layerExceptions); 106 114 noRestartExceptions = filterKnownErrors(noRestartExceptions); 115 testCodeHashCollisions = filterKnownErrors(testCodeHashCollisions); 107 116 108 117 String msg = errMsg("invalidManifestEntries", invalidManifestEntries) + '\n' + 109 118 errMsg("loadingExceptions", loadingExceptions) + '\n' + 110 119 errMsg("layerExceptions", layerExceptions) + '\n' + 111 errMsg("noRestartExceptions", noRestartExceptions); 120 errMsg("noRestartExceptions", noRestartExceptions) + '\n' + 121 errMsg("testCodeHashCollisions", testCodeHashCollisions); 112 122 assertTrue(invalidManifestEntries.isEmpty() 113 123 && loadingExceptions.isEmpty() 114 124 && layerExceptions.isEmpty() 115 && noRestartExceptions.isEmpty(), msg); 125 && noRestartExceptions.isEmpty() 126 && testCodeHashCollisions.isEmpty(), msg); 116 127 } 117 128 118 129 private static String errMsg(String type, Map<String, ?> map) { … … 121 132 122 133 private static void testCompletelyRestartlessPlugins(List<PluginInformation> loadedPlugins, 123 134 Map<String, Throwable> noRestartExceptions) { 135 final List<LogRecord> records = new ArrayList<>(); 136 Handler tempHandler = new Handler() { 137 @Override 138 public void publish(LogRecord record) { 139 records.add(record); 140 } 141 142 @Override 143 public void flush() { /* Do nothing */ } 144 145 @Override 146 public void close() throws SecurityException { /* Do nothing */ } 147 }; 148 Logging.getLogger().addHandler(tempHandler); 124 149 try { 125 150 List<PluginInformation> restartable = loadedPlugins.parallelStream() 126 151 .filter(info -> PluginHandler.getPlugin(info.name) instanceof Destroyable) … … 141 166 Throwable root = Utils.getRootCause(t); 142 167 root.printStackTrace(); 143 168 noRestartExceptions.put(findFaultyPlugin(loadedPlugins, root), root); 169 records.removeIf(record -> Objects.equals(Utils.getRootCause(record.getThrown()), root)); 170 } catch (AssertionError assertionError) { 171 noRestartExceptions.put("Plugin load/unload failed", assertionError); 172 } finally { 173 Logging.getLogger().removeHandler(tempHandler); 174 for (LogRecord record : records) { 175 if (record.getThrown() != null) { 176 Throwable root = Utils.getRootCause(record.getThrown()); 177 root.printStackTrace(); 178 noRestartExceptions.put(findFaultyPlugin(loadedPlugins, root), root); 179 } 180 } 144 181 } 145 182 } 183 184 private static Map<String, String> checkForHashCollisions() { 185 Map<Integer, List<String>> codes = new HashMap<>(); 186 for (Class<?> clazz : ReflectionUtils.findAllClassesInPackage("org.openstreetmap", 187 org.openstreetmap.josm.data.validation.Test.class::isAssignableFrom, s -> true)) { 188 if (org.openstreetmap.josm.data.validation.Test.class.isAssignableFrom(clazz) 189 && !Objects.equals(org.openstreetmap.josm.data.validation.Test.class, clazz)) { 190 // clazz.getName().hashCode() is how the base error codes are calculated since xxx 191 // We want to avoid cases where the hashcode is too close, so we want to 192 // ensure that there is at least 1m available codes after the hashCode. 193 // This is needed since some plugins pick some really large number, and count up from there. 194 int hashCeil = (int) Math.ceil(clazz.getName().hashCode() / 1_000_000d); 195 int hashFloor = (int) Math.floor(clazz.getName().hashCode() / 1_000_000d); 196 codes.computeIfAbsent(hashCeil, k -> new ArrayList<>()).add(clazz.getName()); 197 codes.computeIfAbsent(hashFloor, k -> new ArrayList<>()).add(clazz.getName()); 198 } 199 } 200 return codes.entrySet().stream().filter(entry -> entry.getValue().size() > 1).collect( 201 Collectors.toMap(entry -> entry.getKey().toString(), entry -> String.join(", ", entry.getValue()))); 202 } 146 203 147 204 private static <T> Map<String, T> filterKnownErrors(Map<String, T> errorMap) { 148 205 return errorMap.entrySet().parallelStream() … … 153 210 private static void debugPrint(Map<String, ?> invalidManifestEntries) { 154 211 System.out.println(invalidManifestEntries.entrySet() 155 212 .stream() 156 .map( e -> convertEntryToString(e))213 .map(PluginHandlerTestIT::convertEntryToString) 157 214 .collect(Collectors.joining(", "))); 158 215 } 159 216 … … 203 260 if (GraphicsEnvironment.isHeadless()) { 204 261 for (Iterator<PluginInformation> it = plugins.iterator(); it.hasNext();) { 205 262 PluginInformation pi = it.next(); 206 if (pi.isExternal() ) {263 if (pi.isExternal() && (pi.name.equals("jogl") || pi.requires == null || pi.requires.contains("jogl"))) { 207 264 System.out.println("Ignoring " + pi.name + " (unofficial plugin in headless mode)"); 208 265 it.remove(); 209 266 } … … 241 298 for (PluginInformation p : plugins) { 242 299 try { 243 300 ClassLoader cl = PluginHandler.getPluginClassLoader(p.getName()); 301 assertNotNull(cl); 244 302 String pluginPackage = cl.loadClass(p.className).getPackage().getName(); 245 303 for (StackTraceElement e : root.getStackTrace()) { 246 304 try {
