Changeset 18636 in josm
- Timestamp:
- 2023-01-17T14:41:59+01:00 (2 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/OsmValidator.java
r18211 r18636 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 … … 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"); … … 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); … … 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) { … … 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(); … … 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 … … 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; … … 608 617 609 618 /** 610 * Initialize grid details based on current projection system. Values based on 619 * 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) … … 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() { -
trunk/src/org/openstreetmap/josm/data/validation/TestError.java
r17622 r18636 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; … … 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; … … 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; … … 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; … … 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; … … 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 … … 232 244 return new TestError(this); 233 245 } 246 } 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; 234 254 } 235 255 … … 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 } … … 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) { … … 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 … … 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); 389 } 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 } 344 404 } 345 405 … … 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; … … 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 … … 403 485 public int getCode() { 404 486 return code; 487 } 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; 405 496 } 406 497 … … 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() … … 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 -
trunk/src/org/openstreetmap/josm/io/GeoJSONMapRouletteWriter.java
r18365 r18636 46 46 propertiesBuilder.add("message", testError.getMessage()); 47 47 Optional.ofNullable(testError.getDescription()).ifPresent(description -> propertiesBuilder.add("description", description)); 48 propertiesBuilder.add("code", testError.getCode()); 48 propertiesBuilder.add("code", testError.getUniqueCode()); 49 49 propertiesBuilder.add("fixable", testError.isFixable()); 50 50 propertiesBuilder.add("severity", testError.getSeverity().toString()); -
trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
r18267 r18636 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 … … 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 … … 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; … … 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(); … … 93 98 } 94 99 100 Map<String, String> testCodeHashCollisions = checkForHashCollisions(); 101 95 102 Map<String, Throwable> noRestartExceptions = new HashMap<>(); 96 103 testCompletelyRestartlessPlugins(loadedPlugins, noRestartExceptions); … … 100 107 debugPrint(layerExceptions); 101 108 debugPrint(noRestartExceptions); 109 debugPrint(testCodeHashCollisions); 102 110 103 111 invalidManifestEntries = filterKnownErrors(invalidManifestEntries); … … 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 … … 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() … … 142 167 root.printStackTrace(); 143 168 noRestartExceptions.put(findFaultyPlugin(loadedPlugins, root), root); 144 } 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 } 181 } 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()))); 145 202 } 146 203 … … 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 } … … 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()) {
Note:
See TracChangeset
for help on using the changeset viewer.