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

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

See r18798: Actually use the @Territories annotation

This also fixes some tests that fail with specific options, but were passing due
to run order.

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 private ValidatorCLI validatorCLI;
70
71 @BeforeEach
72 void setup() {
73 TestUtils.assumeWorkingJMockit();
74 new LifecycleMock();
75 this.handler = new TestHandler();
76 Logging.getLogger().addHandler(this.handler);
77 validatorCLI = new ValidatorCLI();
78 }
79
80 @AfterEach
81 void tearDown() {
82 Logging.getLogger().removeHandler(this.handler);
83 this.handler.close();
84 this.handler = null;
85 }
86
87 @ParameterizedTest
88 @ValueSource(strings = {"resources/styles/standard/elemstyles.mapcss", "resources/styles/standard/potlatch2.mapcss"})
89 void testInternalMapcss(final String resourceLocation) {
90 validatorCLI.processArguments(new String[]{"--input", resourceLocation});
91 assertEquals(2, this.handler.logRecordList.size());
92 assertEquals(resourceLocation + " had no errors", this.handler.logRecordList.get(0).getMessage());
93 assertTrue(this.handler.logRecordList.get(1).getMessage().contains("Finishing task"));
94 }
95
96 static Stream<Arguments> testInternalValidatorMapcss() {
97 return Stream.of(Objects.requireNonNull(new File("resources/data/validator").listFiles()))
98 .filter(file -> file.getPath().endsWith(".mapcss"))
99 .map(file -> {
100 // External validator mapcss files must have validator.mapcss as the extension.
101 final String renamedValidator = file.getName().endsWith(".validator.mapcss") ?
102 file.getName() : file.getName().replace(".mapcss", ".validator.mapcss");
103 try {
104 return Files.copy(file.toPath(), Paths.get(temporaryDirectory.getPath(), renamedValidator)).getFileName().toString();
105 } catch (IOException e) {
106 throw new UncheckedIOException(e);
107 }
108 }).map(Arguments::of);
109 }
110
111 @ParameterizedTest
112 @MethodSource
113 void testInternalValidatorMapcss(final String resourceLocation) {
114 final String path = Paths.get(temporaryDirectory.getPath(), resourceLocation).toString();
115 validatorCLI.processArguments(new String[]{"--input", path});
116 assertEquals(2, this.handler.logRecordList.size(), this.handler.logRecordList.stream().map(LogRecord::getMessage).collect(
117 Collectors.joining(",\n")));
118 assertEquals(path + " had no errors", this.handler.logRecordList.get(0).getMessage());
119 assertTrue(this.handler.logRecordList.get(1).getMessage().contains("Finishing task"));
120 }
121
122 @Test
123 void testBadDataTicket13165() {
124 // Ticket #13165 was a validator non-regression test.
125 final String dataPath = TestUtils.getRegressionDataFile(13165, "13165.osm");
126 final String outputPath = Paths.get(temporaryDirectory.getPath(), "testBadDataTicket13165.geojson").toString();
127 validatorCLI.processArguments(new String[]{"--input", dataPath, "--output", outputPath});
128 final File outputFile = new File(outputPath);
129 assertTrue(outputFile.exists());
130 threadSync.threadSync();
131 final List<JsonObject> errors = readJsonObjects(outputFile.toPath());
132 assertEquals(3, errors.stream().map(ValidatorCLITest::getMessage).filter("Overlapping Identical Landuses"::equals).count());
133 assertEquals(3, errors.size(), errors.stream().map(ValidatorCLITest::getMessage).collect(Collectors.joining("\n")));
134 }
135
136 @Test
137 void testBadDataPlusChangeFile() throws IOException {
138 // Write test data out
139 final String osmPath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.osm").toString();
140 final String changePath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.osc").toString();
141 final String errorPath = Paths.get(temporaryDirectory.getPath(), "testBadDataPlusChangeFile.geojson").toString();
142 final DataSet dataSet = new DataSet();
143 final Node node = new Node(LatLon.ZERO);
144 node.setOsmId(1, 1);
145 dataSet.addPrimitive(node);
146 final PrintWriter printWriter = new PrintWriter(Files.newOutputStream(Paths.get(osmPath)), true);
147 final OsmWriter writer = OsmWriterFactory.createOsmWriter(printWriter, true, "0.6");
148 writer.write(dataSet);
149 printWriter.flush();
150 final PrintWriter changeWriter = new PrintWriter(Files.newOutputStream(Paths.get(changePath)), true);
151 changeWriter.write("<osmChange version=\"0.6\" generator=\"JOSM testBadDataPlusChangeFile\">");
152 changeWriter.write("<delete><node id=\"1\"/></delete>");
153 changeWriter.write("</osmChange>");
154 changeWriter.flush();
155
156 validatorCLI.processArguments(new String[] {"--input", osmPath, "--output", errorPath});
157 final List<JsonObject> errors = readJsonObjects(Paths.get(errorPath));
158 // There is already a mapped weather buoy at 0,0 (3000), and the node has no tags (201).
159 assertEquals(2, errors.size());
160 Files.deleteIfExists(Paths.get(errorPath));
161
162 validatorCLI.processArguments(new String[] {"--input", osmPath, "--change-file", changePath, "--output", errorPath});
163 errors.clear();
164 errors.addAll(readJsonObjects(Paths.get(errorPath)));
165 assertEquals(0, errors.size());
166 Files.deleteIfExists(Paths.get(errorPath));
167 }
168
169 /**
170 * A non-regression test for #22898: Validator CLI errors out when is run with --load-preferences argument
171 */
172 @Test
173 void testNonRegression22898(final @TempDir Path preferencesLocation) throws IOException, ReflectiveOperationException {
174 AnnotationUtils.resetStaticClass(Config.class);
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.