source: josm/trunk/test/unit/org/openstreetmap/josm/io/OsmReaderTest.java@ 18225

Last change on this file since 18225 was 18037, checked in by Don-vip, 3 years ago

fix #21064 - Add JUnit 5 extension for preferences (patch by taylor.smock)

  • Property svn:eol-style set to native
File size: 16.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.hamcrest.CoreMatchers.anyOf;
5import static org.hamcrest.CoreMatchers.is;
6import static org.hamcrest.MatcherAssert.assertThat;
7import static org.junit.jupiter.api.Assertions.assertEquals;
8import static org.junit.jupiter.api.Assertions.assertFalse;
9import static org.junit.jupiter.api.Assertions.assertNull;
10import static org.junit.jupiter.api.Assertions.assertTrue;
11import static org.junit.jupiter.api.Assertions.fail;
12
13import java.io.ByteArrayInputStream;
14import java.io.InputStream;
15import java.nio.charset.StandardCharsets;
16import java.nio.file.Files;
17import java.nio.file.Paths;
18import java.util.Arrays;
19
20import org.openstreetmap.josm.TestUtils;
21import org.openstreetmap.josm.data.osm.DataSet;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
26import org.openstreetmap.josm.io.OsmReader.Options;
27import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
28
29import org.junit.jupiter.api.Test;
30
31/**
32 * Unit tests of {@link OsmReader} class.
33 */
34@BasicPreferences
35class OsmReaderTest {
36 private static Options[][] options() {
37 return new Options[][]{
38 new Options[]{},
39 new Options[]{Options.CONVERT_UNKNOWN_TO_TAGS},
40 new Options[]{Options.SAVE_ORIGINAL_ID},
41 new Options[]{Options.CONVERT_UNKNOWN_TO_TAGS, Options.SAVE_ORIGINAL_ID},
42 };
43 }
44
45 private static final class PostProcessorStub implements OsmServerReadPostprocessor {
46 boolean called;
47
48 @Override
49 public void postprocessDataSet(DataSet ds, ProgressMonitor progress) {
50 called = true;
51 }
52 }
53
54 /**
55 * Unit test of {@link OsmReader#registerPostprocessor} / {@link OsmReader#deregisterPostprocessor}.
56 * @throws Exception if any error occurs
57 */
58 @Test
59 void testPostProcessors() throws Exception {
60 PostProcessorStub registered = new PostProcessorStub();
61 PostProcessorStub unregistered = new PostProcessorStub();
62
63 OsmReader.registerPostprocessor(registered);
64
65 OsmReader.registerPostprocessor(unregistered);
66 OsmReader.deregisterPostprocessor(unregistered);
67
68 try (InputStream in = Files.newInputStream(Paths.get(TestUtils.getTestDataRoot(), "empty.osm"))) {
69 OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
70 assertTrue(registered.called);
71 assertFalse(unregistered.called);
72 } finally {
73 OsmReader.deregisterPostprocessor(registered);
74 }
75 }
76
77 private static void testUnknown(String osm, Options[] options) throws Exception {
78 try (InputStream in = new ByteArrayInputStream(
79 ("<?xml version='1.0' encoding='UTF-8'?>" + osm).getBytes(StandardCharsets.UTF_8))) {
80 assertTrue(OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE, options).allPrimitives().isEmpty());
81 }
82 }
83
84 /**
85 * Unit test of {@link OsmReader#parseUnknown} - root case.
86 * @throws Exception if any error occurs
87 */
88 @Test
89 void testUnknownRoot() throws Exception {
90 for (Options[] options : options()) {
91 testUnknown("<nonosm/>", options);
92 }
93 }
94
95 /**
96 * Unit test of {@link OsmReader#parseUnknown} - meta case from Overpass API.
97 * @throws Exception if any error occurs
98 */
99 @Test
100 void testUnknownMeta() throws Exception {
101 for (Options[] options : options()) {
102 testUnknown("<osm version='0.6'><meta osm_base='2017-03-29T19:04:03Z'/></osm>", options);
103 }
104 }
105
106 /**
107 * Unit test of {@link OsmReader#parseUnknown} - note case from Overpass API.
108 * @throws Exception if any error occurs
109 */
110 @Test
111 void testUnknownNote() throws Exception {
112 for (Options[] options : options()) {
113 testUnknown("<osm version='0.6'><note>The data included in this document is from www.openstreetmap.org.</note></osm>", options);
114 }
115 }
116
117 /**
118 * Unit test of {@link OsmReader#parseUnknown} - other cases.
119 * @throws Exception if any error occurs
120 */
121 @Test
122 void testUnknownTag() throws Exception {
123 for (Options[] options : options()) {
124 testUnknown("<osm version='0.6'><foo>bar</foo></osm>", options);
125 testUnknown("<osm version='0.6'><foo><bar/></foo></osm>", options);
126 }
127 }
128
129 /**
130 * Test valid data.
131 * @param osm OSM data without XML prefix
132 * @return parsed data set
133 * @throws Exception if any error occurs
134 */
135 private static DataSet testValidData(String osm, Options[] options) throws Exception {
136 try (InputStream in = new ByteArrayInputStream(
137 ("<?xml version='1.0' encoding='UTF-8'?>" + osm).getBytes(StandardCharsets.UTF_8))) {
138 return OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE, options);
139 }
140 }
141
142 /**
143 * Test invalid data.
144 * @param osm OSM data without XML prefix
145 * @param expectedError expected error message
146 * @throws Exception if any error occurs
147 */
148 private static void testInvalidData(String osm, String expectedError) throws Exception {
149 try (InputStream in = new ByteArrayInputStream(
150 ("<?xml version='1.0' encoding='UTF-8'?>" + osm).getBytes(StandardCharsets.UTF_8))) {
151 OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
152 fail("should throw exception");
153 } catch (IllegalDataException e) {
154 assertEquals(expectedError, e.getMessage());
155 }
156 }
157
158 /**
159 * Test invalid UID.
160 * @throws Exception if any error occurs
161 */
162 @Test
163 void testInvalidUid() throws Exception {
164 testInvalidData("<osm version='0.6'><node id='1' uid='nan'/></osm>",
165 "Illegal value for attribute 'uid'. Got 'nan'. (at line 1, column 82). 82 bytes have been read");
166 }
167
168 /**
169 * Test missing ID.
170 * @throws Exception if any error occurs
171 */
172 @Test
173 void testMissingId() throws Exception {
174 testInvalidData("<osm version='0.6'><node/></osm>",
175 "Missing required attribute 'id'. (at line 1, column 65). 64 bytes have been read");
176 }
177
178 /**
179 * Test missing ref.
180 * @throws Exception if any error occurs
181 */
182 @Test
183 void testMissingRef() throws Exception {
184 testInvalidData("<osm version='0.6'><way id='1' version='1'><nd/></way></osm>",
185 "Missing mandatory attribute 'ref' on <nd> of way 1. (at line 1, column 87). 88 bytes have been read");
186 testInvalidData("<osm version='0.6'><relation id='1' version='1'><member/></relation></osm>",
187 "Missing attribute 'ref' on member in relation 1. (at line 1, column 96). 101 bytes have been read");
188 }
189
190 /**
191 * Test illegal ref.
192 * @throws Exception if any error occurs
193 */
194 @Test
195 void testIllegalRef() throws Exception {
196 testInvalidData("<osm version='0.6'><way id='1' version='1'><nd ref='0'/></way></osm>",
197 "Illegal value of attribute 'ref' of element <nd>. Got 0. (at line 1, column 95). 96 bytes have been read");
198 testInvalidData("<osm version='0.6'><way id='1' version='1'><nd ref='nan'/></way></osm>",
199 "Illegal long value for attribute 'ref'. Got 'nan'. (at line 1, column 97). 98 bytes have been read");
200
201 testInvalidData("<osm version='0.6'><relation id='1' version='1'><member type='node' ref='0'/></relation></osm>",
202 "Incomplete <member> specification with ref=0 (at line 1, column 116). 121 bytes have been read");
203 testInvalidData("<osm version='0.6'><relation id='1' version='1'><member type='node' ref='nan'/></relation></osm>",
204 "Illegal value for attribute 'ref' on member in relation 1. Got nan (at line 1, column 118). 123 bytes have been read");
205 }
206
207 /**
208 * Test missing member type.
209 * @throws Exception if any error occurs
210 */
211 @Test
212 void testMissingType() throws Exception {
213 testInvalidData("<osm version='0.6'><relation id='1' version='1'><member ref='1'/></relation></osm>",
214 "Missing attribute 'type' on member 1 in relation 1. (at line 1, column 104). 109 bytes have been read");
215 }
216
217 /**
218 * Test illegal member type.
219 * @throws Exception if any error occurs
220 */
221 @Test
222 void testIllegalType() throws Exception {
223 testInvalidData("<osm version='0.6'><relation id='1' version='1'><member type='foo' ref='1'/></relation></osm>",
224 "Illegal value for attribute 'type' on member 1 in relation 1. Got foo. (at line 1, column 115). 120 bytes have been read");
225 }
226
227 /**
228 * Test missing key/value.
229 * @throws Exception if any error occurs
230 */
231 @Test
232 void testMissingKeyValue() throws Exception {
233 testInvalidData("<osm version='0.6'><node id='1' version='1'><tag/></node></osm>",
234 "Missing key or value attribute in tag. (at line 1, column 89). 89 bytes have been read");
235 testInvalidData("<osm version='0.6'><node id='1' version='1'><tag k='foo'/></node></osm>",
236 "Missing key or value attribute in tag. (at line 1, column 97). 97 bytes have been read");
237 testInvalidData("<osm version='0.6'><node id='1' version='1'><tag v='bar'/></node></osm>",
238 "Missing key or value attribute in tag. (at line 1, column 97). 97 bytes have been read");
239 }
240
241 /**
242 * Test missing version.
243 * @throws Exception if any error occurs
244 */
245 @Test
246 void testMissingVersion() throws Exception {
247 testInvalidData("<osm/>",
248 "Missing mandatory attribute 'version'. (at line 1, column 45). 44 bytes have been read");
249 testInvalidData("<osm version='0.6'><node id='1'/></osm>",
250 "Missing attribute 'version' on OSM primitive with ID 1. (at line 1, column 72). 72 bytes have been read");
251 }
252
253 /**
254 * Test unsupported version.
255 * @throws Exception if any error occurs
256 */
257 @Test
258 void testUnsupportedVersion() throws Exception {
259 testInvalidData("<osm version='0.1'/>",
260 "Unsupported version: 0.1 (at line 1, column 59). 58 bytes have been read");
261 }
262
263 /**
264 * Test illegal version.
265 * @throws Exception if any error occurs
266 */
267 @Test
268 void testIllegalVersion() throws Exception {
269 testInvalidData("<osm version='0.6'><node id='1' version='nan'/></osm>",
270 "Illegal value for attribute 'version' on OSM primitive with ID 1. Got nan. (at line 1, column 86). 86 bytes have been read");
271 }
272
273 /**
274 * Test illegal changeset.
275 * @throws Exception if any error occurs
276 */
277 @Test
278 void testIllegalChangeset() throws Exception {
279 testInvalidData("<osm version='0.6'><node id='1' version='1' changeset='nan'/></osm>",
280 "Illegal value for attribute 'changeset'. Got nan. (at line 1, column 100). 100 bytes have been read");
281 testInvalidData("<osm version='0.6'><node id='1' version='1' changeset='-1'/></osm>",
282 "Illegal value for attribute 'changeset'. Got -1. (at line 1, column 99). 99 bytes have been read");
283 }
284
285 /**
286 * Test GDPR-compliant changeset.
287 * @throws Exception if any error occurs
288 */
289 @Test
290 void testGdprChangeset() throws Exception {
291 String gdprChangeset = "<osm version='0.6'><node id='1' version='1' changeset='0'/></osm>";
292 for (Options[] options : options()) {
293 testValidData(gdprChangeset, options);
294 }
295 }
296
297 /**
298 * Test invalid bounds.
299 * @throws Exception if any error occurs
300 */
301 @Test
302 void testInvalidBounds() throws Exception {
303 testInvalidData("<osm version='0.6'><bounds/></osm>",
304 "Missing mandatory attributes on element 'bounds'. " +
305 "Got minlon='null',minlat='null',maxlon='null',maxlat='null', origin='null'. (at line 1, column 67). 72 bytes have been read");
306 testInvalidData("<osm version='0.6'><bounds minlon='0'/></osm>",
307 "Missing mandatory attributes on element 'bounds'. " +
308 "Got minlon='0',minlat='null',maxlon='null',maxlat='null', origin='null'. (at line 1, column 78). 83 bytes have been read");
309 testInvalidData("<osm version='0.6'><bounds minlon='0' minlat='0'/></osm>",
310 "Missing mandatory attributes on element 'bounds'. " +
311 "Got minlon='0',minlat='0',maxlon='null',maxlat='null', origin='null'. (at line 1, column 89). 94 bytes have been read");
312 testInvalidData("<osm version='0.6'><bounds minlon='0' minlat='0' maxlon='1'/></osm>",
313 "Missing mandatory attributes on element 'bounds'. " +
314 "Got minlon='0',minlat='0',maxlon='1',maxlat='null', origin='null'. (at line 1, column 100). 105 bytes have been read");
315 }
316
317 /**
318 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/14199">Bug #14199</a>.
319 * @throws Exception if any error occurs
320 */
321 @Test
322 void testTicket14199() throws Exception {
323 try (InputStream in = TestUtils.getRegressionDataStream(14199, "emptytag.osm")) {
324 Way w = OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE).getWays().iterator().next();
325 assertEquals(1, w.getKeys().size());
326 assertNull(w.get(" "));
327 assertTrue(w.isModified());
328 }
329 }
330
331 /**
332 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/14754">Bug #14754</a>.
333 * @throws Exception if any error occurs
334 */
335 @Test
336 void testTicket14754() throws Exception {
337 try (InputStream in = TestUtils.getRegressionDataStream(14754, "malformed_for_14754.osm")) {
338 OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
339 fail("should throw exception");
340 } catch (IllegalDataException e) {
341 String prefix = "Illegal value for attributes 'lat', 'lon' on node with ID 1425146006. Got '550.3311950157', '10.49428298298'.";
342 assertThat(e.getMessage(), anyOf(
343 is(prefix + " (at line 5, column 179). 578 bytes have been read"),
344 is(prefix + " (at line 5, column 179). 581 bytes have been read") // GitHub Actions
345 ));
346 }
347 }
348
349 /**
350 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/14788">Bug #14788</a>.
351 * @throws Exception if any error occurs
352 */
353 @Test
354 void testTicket14788() throws Exception {
355 try (InputStream in = TestUtils.getRegressionDataStream(14788, "remove_sign_test_4.osm")) {
356 OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
357 fail("should throw exception");
358 } catch (IllegalDataException e) {
359 String prefix = "Illegal value for attributes 'lat', 'lon' on node with ID 978. Got 'nan', 'nan'.";
360 assertThat(e.getMessage(), anyOf(
361 is(prefix + " (at line 4, column 151). 336 bytes have been read"),
362 is(prefix + " (at line 4, column 151). 338 bytes have been read") // GitHub Actions
363 ));
364 }
365 }
366
367 /**
368 * Test reading remark from Overpass API.
369 * @throws Exception if any error occurs
370 */
371 @Test
372 void testRemark() throws Exception {
373 String query = "<osm version=\"0.6\" generator=\"Overpass API 0.7.55.4 3079d8ea\">\r\n" +
374 "<note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note>\r\n" +
375 "<meta osm_base=\"2018-08-30T12:46:02Z\" areas=\"2018-08-30T12:40:02Z\"/>\r\n" +
376 "<remark>runtime error: Query ran out of memory in \"query\" at line 5.</remark>\r\n" +
377 "</osm>";
378 for (Options[] options : options()) {
379 DataSet ds = testValidData(query, options);
380 assertEquals("runtime error: Query ran out of memory in \"query\" at line 5.", ds.getRemark());
381 }
382 }
383
384 /**
385 * Test reading a file with unknown attributes in osm primitives
386 * @throws Exception if any error occurs
387 */
388 @Test
389 void testUnknownAttributeTags() throws Exception {
390 String testData = "<osm version=\"0.6\" generator=\"fake generator\">"
391 + "<node id='1' version='1' visible='true' changeset='82' randomkey='randomvalue'></node>" + "</osm>";
392 for (Options[] options : options()) {
393 DataSet ds = testValidData(testData, options);
394 Node firstNode = ds.getNodes().iterator().next();
395 if (Arrays.asList(options).contains(Options.CONVERT_UNKNOWN_TO_TAGS)) {
396 assertEquals("randomvalue", firstNode.get("randomkey"));
397 } else {
398 assertNull(firstNode.get("randomkey"));
399 }
400 if (Arrays.asList(options).contains(Options.SAVE_ORIGINAL_ID)) {
401 assertEquals("1", firstNode.get("current_id"));
402 } else {
403 assertNull(firstNode.get("current_id"));
404 }
405 }
406 }
407}
Note: See TracBrowser for help on using the repository browser.