source: josm/trunk/test/unit/org/openstreetmap/josm/data/validation/ValidatorCLITest.java@ 18723

Last change on this file since 18723 was 18723, checked in by taylor.smock, 13 months ago

Fix #22432, see #22941: Start migrating from javax to jakarta

Parsson was split out from the JSONP repository in 2021 (see
https://github.com/jakartaee/jsonp-api/issues/285 ). It is the default provider,
and will "just work" without additional configuration.

Many plugins use javax.json, so the scheduled removal of the javax.json
dependencies is set to milestone:"24.12" (see #22941).

Changes between javax.json and jakarta.json 2.0:

  • Rename of javax.json to jakarta.json
  • Some additional bug fixes

This will enable us to move easily to jakarta.json 2.1 in the future.
The changes of note with 2.1 includes:

  • Better handling of duplicated keys
  • Additional APIs around primitive types
  • API to get current event from JsonParser

We cannot currently move to jakarta.json 2.1 since it requires Java 11+.

File size: 10.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation;
3
4import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
5import static org.junit.jupiter.api.Assertions.assertEquals;
6import static org.junit.jupiter.api.Assertions.assertTrue;
7
8import java.io.ByteArrayInputStream;
9import java.io.File;
10import java.io.IOException;
11import java.io.OutputStream;
12import java.io.PrintWriter;
13import java.io.UncheckedIOException;
14import java.nio.charset.StandardCharsets;
15import java.nio.file.Files;
16import java.nio.file.Path;
17import java.nio.file.Paths;
18import java.util.ArrayList;
19import java.util.Collections;
20import java.util.List;
21import java.util.Objects;
22import java.util.logging.Handler;
23import java.util.logging.LogRecord;
24import java.util.stream.Collectors;
25import java.util.stream.Stream;
26
27import jakarta.json.Json;
28import jakarta.json.JsonObject;
29import jakarta.json.JsonReader;
30
31import mockit.Mock;
32import mockit.MockUp;
33import org.junit.jupiter.api.AfterEach;
34import org.junit.jupiter.api.BeforeEach;
35import org.junit.jupiter.api.Test;
36import org.junit.jupiter.api.extension.RegisterExtension;
37import org.junit.jupiter.api.io.TempDir;
38import org.junit.jupiter.params.ParameterizedTest;
39import org.junit.jupiter.params.provider.Arguments;
40import org.junit.jupiter.params.provider.MethodSource;
41import org.junit.jupiter.params.provider.ValueSource;
42import org.openstreetmap.josm.TestUtils;
43import org.openstreetmap.josm.data.coor.LatLon;
44import org.openstreetmap.josm.data.osm.DataSet;
45import org.openstreetmap.josm.data.osm.Node;
46import org.openstreetmap.josm.io.OsmWriter;
47import org.openstreetmap.josm.io.OsmWriterFactory;
48import org.openstreetmap.josm.spi.lifecycle.Lifecycle;
49import org.openstreetmap.josm.spi.preferences.Config;
50import org.openstreetmap.josm.testutils.annotations.AnnotationUtils;
51import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
52import org.openstreetmap.josm.testutils.annotations.ThreadSync;
53import org.openstreetmap.josm.tools.Logging;
54import org.openstreetmap.josm.tools.Utils;
55
56/**
57 * Test class for {@link ValidatorCLI}
58 * @author Taylor Smock
59 */
60@BasicPreferences
61class ValidatorCLITest {
62 @RegisterExtension
63 ThreadSync.ThreadSyncExtension threadSync = new ThreadSync.ThreadSyncExtension();
64
65 @TempDir
66 static File temporaryDirectory;
67
68 TestHandler handler;
69
70 @BeforeEach
71 void setup() {
72 TestUtils.assumeWorkingJMockit();
73 new LifecycleMock();
74 this.handler = new TestHandler();
75 Logging.getLogger().addHandler(this.handler);
76 }
77
78 @AfterEach
79 void tearDown() {
80 Logging.getLogger().removeHandler(this.handler);
81 this.handler.close();
82 this.handler = null;
83 }
84
85 @ParameterizedTest
86 @ValueSource(strings = {"resources/styles/standard/elemstyles.mapcss", "resources/styles/standard/potlatch2.mapcss"})
87 void testInternalMapcss(final String resourceLocation) {
88 new ValidatorCLI().processArguments(new String[]{"--input", resourceLocation});
89 assertEquals(2, this.handler.logRecordList.size());
90 assertEquals(resourceLocation + " had no errors", this.handler.logRecordList.get(0).getMessage());
91 assertTrue(this.handler.logRecordList.get(1).getMessage().contains("Finishing task"));
92 }
93
94 static Stream<Arguments> testInternalValidatorMapcss() {
95 return Stream.of(Objects.requireNonNull(new File("resources/data/validator").listFiles()))
96 .filter(file -> file.getPath().endsWith(".mapcss"))
97 .map(file -> {
98 // External validator mapcss files must have validator.mapcss as the extension.
99 final String renamedValidator = file.getName().endsWith(".validator.mapcss") ?
100 file.getName() : file.getName().replace(".mapcss", ".validator.mapcss");
101 try {
102 return Files.copy(file.toPath(), Paths.get(temporaryDirectory.getPath(), renamedValidator)).getFileName().toString();
103 } catch (IOException e) {
104 throw new UncheckedIOException(e);
105 }
106 }).map(Arguments::of);
107 }
108
109 @ParameterizedTest
110 @MethodSource
111 void testInternalValidatorMapcss(final String resourceLocation) {
112 final String path = Paths.get(temporaryDirectory.getPath(), resourceLocation).toString();
113 new ValidatorCLI().processArguments(new String[]{"--input", path});
114 assertEquals(2, this.handler.logRecordList.size(), this.handler.logRecordList.stream().map(LogRecord::getMessage).collect(
115 Collectors.joining(",\n")));
116 assertEquals(path + " had no errors", this.handler.logRecordList.get(0).getMessage());
117 assertTrue(this.handler.logRecordList.get(1).getMessage().contains("Finishing task"));
118 }
119
120 @Test
121 void testBadDataTicket13165() {
122 // Ticket #13165 was a validator non-regression test.
123 final String dataPath = TestUtils.getRegressionDataFile(13165, "13165.osm");
124 final String outputPath = Paths.get(temporaryDirectory.getPath(), "testBadDataTicket13165.geojson").toString();
125 new ValidatorCLI().processArguments(new String[]{"--input", dataPath, "--output", outputPath});
126 final File outputFile = new File(outputPath);
127 assertTrue(outputFile.exists());
128 threadSync.threadSync();
129 final List<JsonObject> errors = readJsonObjects(outputFile.toPath());
130 assertEquals(3, errors.stream().map(ValidatorCLITest::getMessage).filter("Overlapping Identical Landuses"::equals).count());
131 assertEquals(3, errors.size(), errors.stream().map(ValidatorCLITest::getMessage).collect(Collectors.joining("\n")));
132 }
133
134 @Test
135 void testBadDataPlusChangeFile() throws IOException {
136 final ValidatorCLI validatorCLI = new ValidatorCLI();
137 // Write test data out
138 final String osmPath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.osm").toString();
139 final String changePath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.osc").toString();
140 final String errorPath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.geojson").toString();
141 final DataSet dataSet = new DataSet();
142 final Node node = new Node(LatLon.ZERO);
143 node.setOsmId(1, 1);
144 dataSet.addPrimitive(node);
145 final PrintWriter printWriter = new PrintWriter(Files.newOutputStream(Paths.get(osmPath)), true);
146 final OsmWriter writer = OsmWriterFactory.createOsmWriter(printWriter, true, "0.6");
147 writer.write(dataSet);
148 printWriter.flush();
149 final PrintWriter changeWriter = new PrintWriter(Files.newOutputStream(Paths.get(changePath)), true);
150 changeWriter.write("<osmChange version=\"0.6\" generator=\"JOSM testBadDataPlusChangeFile\">");
151 changeWriter.write("<delete><node id=\"1\"/></delete>");
152 changeWriter.write("</osmChange>");
153 changeWriter.flush();
154
155 validatorCLI.processArguments(new String[] {"--input", osmPath, "--output", errorPath});
156 final List<JsonObject> errors = readJsonObjects(Paths.get(errorPath));
157 // There is already a mapped weather buoy at 0,0 (3000), and the node has no tags (201).
158 assertEquals(2, errors.size());
159 Files.deleteIfExists(Paths.get(errorPath));
160
161 validatorCLI.processArguments(new String[] {"--input", osmPath, "--change-file", changePath, "--output", errorPath});
162 errors.clear();
163 errors.addAll(readJsonObjects(Paths.get(errorPath)));
164 assertEquals(0, errors.size());
165 Files.deleteIfExists(Paths.get(errorPath));
166 }
167
168 /**
169 * A non-regression test for #22898: Validator CLI errors out when is run with --load-preferences argument
170 */
171 @Test
172 void testNonRegression22898(final @TempDir Path preferencesLocation) throws IOException, ReflectiveOperationException {
173 AnnotationUtils.resetStaticClass(Config.class);
174 final ValidatorCLI validatorCLI = new ValidatorCLI();
175 final Path preferences = preferencesLocation.resolve("preferences.xml");
176 try (OutputStream fos = Files.newOutputStream(preferences)) {
177 final String pref = "<config>\n" +
178 " <preferences operation=\"replace\">\n" +
179 " <list key='plugins'>\n" +
180 " <entry value='baz'/>\n" +
181 " </list>\n" +
182 " </preferences>\n" +
183 "</config>";
184 fos.write(pref.getBytes(StandardCharsets.UTF_8));
185 }
186 validatorCLI.processArguments(new String[]{"--load-preferences=" + preferences,
187 "--input", "resources/styles/standard/elemstyles.mapcss"});
188 assertEquals(Collections.singletonList("baz"), Config.getPref().getList("plugins"));
189 }
190
191 /**
192 * Read json objects from a file
193 * @param path The file to read
194 * @return The json objects
195 */
196 private static List<JsonObject> readJsonObjects(final Path path) {
197 if (Files.exists(path)) {
198 final List<String> lines = assertDoesNotThrow(() -> Files.readAllLines(path));
199 lines.replaceAll(line -> Utils.strip(line.replace((char) 0x1e, ' ')));
200 return lines.stream().map(str -> Json.createReader(new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))))
201 .map(JsonReader::readObject).collect(Collectors.toList());
202 }
203 return Collections.emptyList();
204 }
205
206 /**
207 * Get the validation message from a json object
208 * @param jsonObject The json object to parse
209 * @return The validator message
210 */
211 private static String getMessage(JsonObject jsonObject) {
212 return jsonObject.getJsonArray("features").getValuesAs(JsonObject.class)
213 .stream().filter(feature -> feature.containsKey("properties")).map(feature -> feature.getJsonObject("properties"))
214 .filter(properties -> properties.containsKey("message")).map(properties -> properties.getJsonString("message").getString())
215 .collect(Collectors.joining(","));
216 }
217
218 /**
219 * This exists to avoid exiting the tests.
220 */
221 private static final class LifecycleMock extends MockUp<Lifecycle> {
222 @Mock
223 public static boolean exitJosm(boolean exit, int exitCode) {
224 // No-op for now
225 return true;
226 }
227 }
228
229 private static final class TestHandler extends Handler {
230 final List<LogRecord> logRecordList = new ArrayList<>();
231
232 @Override
233 public void publish(LogRecord record) {
234 this.logRecordList.add(record);
235 }
236
237 @Override
238 public void flush() {
239 this.logRecordList.clear();
240 }
241
242 @Override
243 public void close() throws SecurityException {
244 this.flush();
245 }
246 }
247}
Note: See TracBrowser for help on using the repository browser.