source: josm/trunk/test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java

Last change on this file was 19228, checked in by taylor.smock, 10 months ago

Fix #23930: Merging duplicated layers with little differences stalls JOSM

This is fixed by keeping the "last" conflict in the problem if statement body.

  • Property svn:eol-style set to native
File size: 44.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
5import static org.junit.jupiter.api.Assertions.assertEquals;
6import static org.junit.jupiter.api.Assertions.assertFalse;
7import static org.junit.jupiter.api.Assertions.assertInstanceOf;
8import static org.junit.jupiter.api.Assertions.assertNotNull;
9import static org.junit.jupiter.api.Assertions.assertNotSame;
10import static org.junit.jupiter.api.Assertions.assertSame;
11import static org.junit.jupiter.api.Assertions.assertThrows;
12import static org.junit.jupiter.api.Assertions.assertTrue;
13import static org.junit.jupiter.api.Assertions.fail;
14
15import java.io.File;
16import java.io.IOException;
17import java.io.StringWriter;
18import java.time.Instant;
19import java.util.Arrays;
20import java.util.List;
21import java.util.function.BiConsumer;
22import java.util.stream.Collectors;
23import java.util.stream.Stream;
24
25import org.junit.jupiter.api.AfterEach;
26import org.junit.jupiter.api.BeforeEach;
27import org.junit.jupiter.api.Test;
28import org.junit.jupiter.params.ParameterizedTest;
29import org.junit.jupiter.params.provider.MethodSource;
30import org.openstreetmap.josm.TestUtils;
31import org.openstreetmap.josm.data.conflict.Conflict;
32import org.openstreetmap.josm.data.conflict.ConflictCollection;
33import org.openstreetmap.josm.data.coor.LatLon;
34import org.openstreetmap.josm.data.projection.ProjectionRegistry;
35import org.openstreetmap.josm.data.projection.Projections;
36import org.openstreetmap.josm.gui.layer.OsmDataLayer;
37import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
38import org.openstreetmap.josm.io.IllegalDataException;
39import org.openstreetmap.josm.io.session.SessionReader;
40import org.openstreetmap.josm.tools.Logging;
41
42/**
43 * Unit tests for class {@link DataSetMerger}.
44 */
45class DataSetMergerTest {
46 private DataSet my;
47 private DataSet their;
48
49 /**
50 * Setup test.
51 */
52 @BeforeEach
53 public void setUp() {
54 my = new DataSet();
55 my.setVersion("0.6");
56 their = new DataSet();
57 their.setVersion("0.6");
58 ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
59 }
60
61 private void runConsistencyTests(DataSet ds) {
62 StringWriter writer = new StringWriter();
63 DatasetConsistencyTest test = new DatasetConsistencyTest(ds, writer);
64 test.checkReferrers();
65 test.checkCompleteWaysWithIncompleteNodes();
66 test.searchNodes();
67 test.searchWays();
68 test.referredPrimitiveNotInDataset();
69 test.checkZeroNodesWays();
70 String result = writer.toString();
71 if (!result.isEmpty())
72 fail(result);
73 }
74
75 @AfterEach
76 public void checkDatasets() {
77 runConsistencyTests(my);
78 runConsistencyTests(their);
79 }
80
81 /**
82 * two identical nodes, even in id and version. No conflict expected.
83 * <p>
84 * Can happen if data is loaded in two layers and then merged from one layer
85 * on the other.
86 */
87 @Test
88 void testNodeSimpleIdenticalNoConflict() {
89 Node n = new Node(LatLon.ZERO);
90 n.setOsmId(1, 1);
91 n.setModified(false);
92 n.put("key1", "value1");
93 my.addPrimitive(n);
94
95 Node n1 = new Node(LatLon.ZERO);
96 n1.setOsmId(1, 1);
97 n1.setModified(false);
98 n1.put("key1", "value1");
99 their.addPrimitive(n1);
100
101
102 DataSetMerger visitor = new DataSetMerger(my, their);
103 visitor.merge();
104
105 Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
106 assertTrue(visitor.getConflicts().isEmpty());
107 assertNotSame(n1, n2); // make sure we have a clone
108 assertEquals(1, n2.getId());
109 assertEquals(1, n2.getVersion());
110 assertFalse(n2.isModified());
111 assertEquals("value1", n2.get("key1"));
112
113 // merge target not modified after merging
114 assertFalse(n2.isModified());
115 }
116
117 /**
118 * two nodes, my is unmodified, their is updated and has a higher version
119 * => their version is going to be the merged version
120 */
121 @Test
122 void testNodeSimpleLocallyUnmodifiedNoConflict() {
123 Node n = new Node(LatLon.ZERO);
124 n.setOsmId(1, 1);
125 n.setModified(false);
126 n.put("key1", "value1");
127 my.addPrimitive(n);
128
129 Node n1 = new Node(LatLon.ZERO);
130 n1.setOsmId(1, 2);
131 n1.setModified(false);
132 n1.put("key1", "value1-new");
133 n1.put("key2", "value2");
134 their.addPrimitive(n1);
135
136
137 DataSetMerger visitor = new DataSetMerger(my, their);
138 visitor.merge();
139
140 Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
141 assertTrue(visitor.getConflicts().isEmpty());
142 assertSame(n, n2); // make sure the merged node is still the original node
143 assertSame(n2.getDataSet(), my);
144 assertEquals(1, n2.getId());
145 assertEquals(2, n2.getVersion());
146 assertFalse(n2.isModified());
147 assertEquals("value1-new", n2.get("key1"));
148 assertEquals("value2", n2.get("key2"));
149
150 // the merge target should not be modified
151 assertFalse(n2.isModified());
152 }
153
154 /**
155 * Node with same id, my is modified, their has a higher version
156 * => results in a conflict
157 * <p>
158 * Use case: node which is modified locally and updated by another mapper on
159 * the server
160 */
161 @Test
162 void testNodeSimpleTagConflict() {
163 Node n = new Node(LatLon.ZERO);
164 n.setOsmId(1, 1);
165 n.setModified(true);
166 n.put("key1", "value1");
167 n.put("key2", "value2");
168 my.addPrimitive(n);
169
170 Node n1 = new Node(LatLon.ZERO);
171 n1.setOsmId(1, 2);
172 n1.setModified(false);
173 n1.put("key1", "value1-new");
174
175 their.addPrimitive(n1);
176
177
178 DataSetMerger visitor = new DataSetMerger(my, their);
179 visitor.merge();
180
181 Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
182 assertEquals(1, visitor.getConflicts().size());
183 assertSame(n, n2);
184 assertNotSame(n1, n2);
185 assertSame(n1.getDataSet(), their);
186 }
187
188 /**
189 * node with same id, my is deleted, their has a higher version
190 * => results in a conflict
191 * <p>
192 * Use case: node which is deleted locally and updated by another mapper on
193 * the server
194 */
195 @Test
196 void testNodeSimpleDeleteConflict() {
197 Node n = new Node(1, 1);
198 n.setCoor(LatLon.ZERO);
199 n.setDeleted(true);
200 n.put("key1", "value1");
201 my.addPrimitive(n);
202
203 Node n1 = new Node(LatLon.ZERO);
204 n1.setOsmId(1, 2);
205 n1.setModified(false);
206 n1.put("key1", "value1-new");
207 n1.put("key2", "value2");
208 their.addPrimitive(n1);
209
210
211 DataSetMerger visitor = new DataSetMerger(my, their);
212 visitor.merge();
213
214 Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
215 assertEquals(1, visitor.getConflicts().size());
216 assertSame(n, n2);
217 assertNotSame(n1, n2);
218 assertSame(n1.getDataSet(), their);
219 }
220
221 /**
222 * My node is deleted, their node has the same id and version and is not deleted.
223 * => mine has precedence
224 */
225 @Test
226 void testNodeSimpleDeleteConflict2() {
227 Node n = new Node(LatLon.ZERO);
228 n.setOsmId(1, 1);
229 n.setDeleted(true);
230 my.addPrimitive(n);
231
232 Node n1 = new Node(LatLon.ZERO);
233 n1.setOsmId(1, 1);
234 their.addPrimitive(n1);
235
236
237 DataSetMerger visitor = new DataSetMerger(my, their);
238 visitor.merge();
239
240 Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
241 assertEquals(0, visitor.getConflicts().size());
242 assertTrue(n2.isVisible());
243 assertSame(n, n2);
244 assertSame(n.getDataSet(), my);
245 assertSame(n1.getDataSet(), their);
246 }
247
248 /**
249 * My and their node are new but semantically equal. My node is deleted.
250 * <p>
251 * => Ignore my node, no conflict
252 */
253 @Test
254 void testNodeSimpleDeleteConflict3() {
255 Node n = new Node(new LatLon(1, 1));
256 n.setDeleted(true);
257 my.addPrimitive(n);
258
259 Node n1 = new Node(new LatLon(1, 1));
260 their.addPrimitive(n1);
261
262
263 DataSetMerger visitor = new DataSetMerger(my, their);
264 visitor.merge();
265
266 assertEquals(0, visitor.getConflicts().size());
267 assertSame(n.getDataSet(), my);
268 assertSame(n1.getDataSet(), their);
269 }
270
271 /**
272 * My and their node are new but semantically equal. Both are deleted.
273 * <p>
274 * => take mine
275 */
276 @Test
277 void testNodeSimpleDeleteConflict4() {
278 Node n = new Node(new LatLon(1, 1));
279 n.setDeleted(true);
280 my.addPrimitive(n);
281
282 Node n1 = new Node(new LatLon(1, 1));
283 n1.setDeleted(true);
284 their.addPrimitive(n1);
285
286 DataSetMerger visitor = new DataSetMerger(my, their);
287 visitor.merge();
288
289 assertEquals(0, visitor.getConflicts().size());
290 Node n2 = (Node) my.getNodes().toArray()[0];
291 assertSame(n2, n);
292 assertTrue(n2.isDeleted());
293 }
294
295 /**
296 * their node has no assigned id (id == 0) and is semantically equal to one of my
297 * nodes with id == 0
298 * <p>
299 * => merge it onto my node.
300 */
301 @Test
302 void testNodeSimpleNoIdSemanticallyEqual() {
303
304 User myUser = User.createOsmUser(1111, "my");
305
306 User theirUser = User.createOsmUser(222, "their");
307
308 Node n = new Node();
309 n.setCoor(LatLon.ZERO);
310 n.put("key1", "value1");
311 n.setUser(myUser);
312 n.setInstant(Instant.now());
313
314 my.addPrimitive(n);
315
316 Node n1 = new Node();
317 n1.setCoor(LatLon.ZERO);
318 n1.put("key1", "value1");
319 n1.setInstant(Instant.now().plusSeconds(3600));
320 n1.setUser(theirUser);
321 their.addPrimitive(n1);
322
323 DataSetMerger visitor = new DataSetMerger(my, their);
324 visitor.merge();
325
326 Node n2 = my.getNodes().iterator().next();
327 assertEquals(0, visitor.getConflicts().size());
328 assertEquals("value1", n2.get("key1"));
329 assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp());
330 assertEquals(theirUser, n2.getUser());
331 assertSame(n2, n);
332 assertNotSame(n2, n1);
333 assertSame(n2.getDataSet(), my);
334 }
335
336 /**
337 * my node is incomplete, their node is complete
338 * <p>
339 * => merge it onto my node. My node becomes complete
340 */
341 @Test
342 void testNodeSimpleIncompleteNode() {
343
344 Node n = new Node(1);
345 my.addPrimitive(n);
346
347 Node n1 = new Node();
348 n1.setCoor(LatLon.ZERO);
349 n1.setOsmId(1, 1);
350 n1.put("key1", "value1");
351 n1.setInstant(Instant.now());
352 their.addPrimitive(n1);
353
354 DataSetMerger visitor = new DataSetMerger(my, their);
355 visitor.merge();
356
357 Node n2 = my.getNodes().iterator().next();
358 assertEquals(0, visitor.getConflicts().size());
359 assertEquals("value1", n2.get("key1"));
360 assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp());
361 assertFalse(n2.isIncomplete());
362 assertSame(n2, n);
363 }
364
365 /**
366 * their way has a higher version and different tags. the nodes are the same. My
367 * way is not modified. Merge is possible. No conflict.
368 * <p>
369 * => merge it onto my way.
370 */
371 @Test
372 void testWaySimpleIdenticalNodesDifferentTags() {
373
374 // -- the target dataset
375
376 Node n1 = new Node();
377 n1.setCoor(LatLon.ZERO);
378 n1.setOsmId(1, 1);
379 my.addPrimitive(n1);
380
381 Node n2 = new Node();
382 n2.setCoor(LatLon.ZERO);
383 n2.setOsmId(2, 1);
384
385 my.addPrimitive(n2);
386
387 Way myWay = new Way();
388 myWay.setOsmId(3, 1);
389 myWay.put("key1", "value1");
390 myWay.addNode(n1);
391 myWay.addNode(n2);
392 my.addPrimitive(myWay);
393
394 // -- the source data set
395
396 Node n3 = new Node(LatLon.ZERO);
397 n3.setOsmId(1, 1);
398 their.addPrimitive(n3);
399
400 Node n4 = new Node(new LatLon(1, 1));
401 n4.setOsmId(2, 1);
402 their.addPrimitive(n4);
403
404 Way theirWay = new Way();
405 theirWay.setOsmId(3, 2);
406 theirWay.put("key1", "value1");
407 theirWay.put("key2", "value2");
408 theirWay.addNode(n3);
409 theirWay.addNode(n4);
410 their.addPrimitive(theirWay);
411
412
413 DataSetMerger visitor = new DataSetMerger(my, their);
414 visitor.merge();
415
416 // -- tests
417 Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
418 assertEquals(0, visitor.getConflicts().size());
419 assertEquals("value1", merged.get("key1"));
420 assertEquals("value2", merged.get("key2"));
421 assertEquals(3, merged.getId());
422 assertEquals(2, merged.getVersion());
423 assertEquals(2, merged.getNodesCount());
424 assertEquals(1, merged.getNode(0).getId());
425 assertEquals(2, merged.getNode(1).getId());
426 assertSame(merged, myWay);
427 assertSame(merged.getDataSet(), my);
428
429 Node mergedNode = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
430 assertSame(mergedNode, n1);
431 mergedNode = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE);
432 assertSame(mergedNode, n2);
433
434 assertFalse(merged.isModified());
435 }
436
437 /**
438 * their way has a higher version and different tags. And it has more nodes. Two
439 * of the existing nodes are modified.
440 * <p>
441 * => merge it onto my way, no conflict
442 */
443 @Test
444 void testWaySimpleAdditionalNodesAndChangedNodes() {
445
446 // -- my data set
447
448 Node n1 = new Node(LatLon.ZERO);
449 n1.setOsmId(1, 1);
450 my.addPrimitive(n1);
451
452 Node n2 = new Node(new LatLon(1, 1));
453 n2.setOsmId(2, 1);
454 my.addPrimitive(n2);
455
456 Way myWay = new Way();
457 myWay.setOsmId(3, 1);
458 myWay.addNode(n1);
459 myWay.addNode(n2);
460 my.addPrimitive(myWay);
461
462 // --- their data set
463
464 Node n3 = new Node(LatLon.ZERO);
465 n3.setOsmId(1, 1);
466 their.addPrimitive(n3);
467
468 Node n5 = new Node(new LatLon(1, 1));
469 n5.setOsmId(4, 1);
470
471 their.addPrimitive(n5);
472
473 Node n4 = new Node(new LatLon(2, 2));
474 n4.setOsmId(2, 2);
475 n4.put("key1", "value1");
476 their.addPrimitive(n4);
477
478
479 Way theirWay = new Way();
480 theirWay.setOsmId(3, 2);
481 theirWay.addNode(n3);
482 theirWay.addNode(n5); // insert a node
483 theirWay.addNode(n4); // this one is updated
484 their.addPrimitive(theirWay);
485
486 DataSetMerger visitor = new DataSetMerger(my, their);
487 visitor.merge();
488
489 // -- tests
490 Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
491 assertEquals(0, visitor.getConflicts().size());
492 assertEquals(3, merged.getId());
493 assertEquals(2, merged.getVersion());
494 assertEquals(3, merged.getNodesCount());
495 assertEquals(1, merged.getNode(0).getId());
496 assertEquals(4, merged.getNode(1).getId());
497 assertEquals(2, merged.getNode(2).getId());
498 assertEquals("value1", merged.getNode(2).get("key1"));
499
500 assertSame(merged.getNode(0), n1);
501 assertNotSame(merged.getNode(1), n5); // must be a clone of the original node in their
502 assertSame(merged.getNode(2), n2);
503
504 assertFalse(merged.isModified()); // the target wasn't modified before merging, it mustn't be after merging
505 }
506
507 /**
508 * their way has a higher version and different nodes. My way is modified.
509 * <p>
510 * => merge onto my way not possible, create a conflict
511 */
512 @Test
513 void testWaySimpleDifferentNodesAndMyIsModified() {
514
515 // -- the target dataset
516
517 Node n1 = new Node(LatLon.ZERO);
518 n1.setOsmId(1, 1);
519 my.addPrimitive(n1);
520
521 Node n2 = new Node(new LatLon(1, 1));
522 n2.setOsmId(2, 1);
523 my.addPrimitive(n2);
524
525 Way myWay = new Way();
526 myWay.setOsmId(3, 1);
527
528 myWay.addNode(n1);
529 myWay.addNode(n2);
530 myWay.setModified(true);
531 myWay.put("key1", "value1");
532 my.addPrimitive(myWay);
533
534 // -- the source dataset
535
536 Node n3 = new Node(LatLon.ZERO);
537 n3.setOsmId(1, 1);
538 their.addPrimitive(n3);
539
540 Node n5 = new Node(new LatLon(1, 1));
541 n5.setOsmId(4, 1);
542 their.addPrimitive(n5);
543
544 Node n4 = new Node(new LatLon(2, 2));
545 n4.setOsmId(2, 1);
546 n4.put("key1", "value1");
547 their.addPrimitive(n4);
548
549 Way theirWay = new Way();
550 theirWay.setOsmId(3, 2);
551
552 theirWay.addNode(n3);
553 theirWay.addNode(n5); // insert a node
554 theirWay.addNode(n4); // this one is updated
555 their.addPrimitive(theirWay);
556
557
558 DataSetMerger visitor = new DataSetMerger(my, their);
559 visitor.merge();
560
561 Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
562 assertEquals(1, visitor.getConflicts().size());
563 assertEquals(3, merged.getId());
564 assertEquals(1, merged.getVersion());
565 assertEquals(2, merged.getNodesCount());
566 assertEquals(1, merged.getNode(0).getId());
567 assertEquals(2, merged.getNode(1).getId());
568 assertEquals("value1", merged.get("key1"));
569 }
570
571 /**
572 * their way is not visible anymore.
573 * <p>
574 * => conflict
575 */
576 @Test
577 void testWaySimpleTheirVersionNotVisibleMyIsModified() {
578
579 Node mn1 = new Node(LatLon.ZERO);
580 mn1.setOsmId(1, 1);
581 my.addPrimitive(mn1);
582
583 Node mn2 = new Node(new LatLon(1, 1));
584 mn2.setOsmId(2, 1);
585 my.addPrimitive(mn2);
586
587 Way myWay = new Way();
588 myWay.setOsmId(3, 1);
589 myWay.addNode(mn1);
590 myWay.addNode(mn2);
591 myWay.setModified(true);
592 my.addPrimitive(myWay);
593
594 Way theirWay = new Way();
595 theirWay.setOsmId(3, 2);
596 theirWay.setVisible(false);
597 /* Invisible objects fetched from the server should be marked as "deleted".
598 * Otherwise it's an error.
599 */
600 theirWay.setDeleted(true);
601 their.addPrimitive(theirWay);
602
603 DataSetMerger visitor = new DataSetMerger(my, their);
604 visitor.merge();
605
606 Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
607 assertEquals(1, visitor.getConflicts().size());
608 assertTrue(visitor.getConflicts().hasConflictForMy(myWay));
609 assertTrue(visitor.getConflicts().hasConflictForTheir(theirWay));
610 assertEquals(myWay, merged);
611 }
612
613 /**
614 * my and their way have no ids, nodes they refer to have an id. but
615 * my and their way are semantically equal. so technical attributes of
616 * their way can be merged on my way. No conflict.
617 */
618 @Test
619 void testWaySimpleTwoWaysWithNoIdNodesWithId() {
620
621 // -- my data set
622
623 Node n1 = new Node(LatLon.ZERO);
624 n1.setOsmId(1, 1);
625 my.addPrimitive(n1);
626
627 Node n2 = new Node(new LatLon(1, 1));
628 n2.setOsmId(2, 1);
629 my.addPrimitive(n2);
630
631 Way myWay = new Way();
632 myWay.addNode(n1);
633 myWay.addNode(n2);
634 my.addPrimitive(myWay);
635
636 // -- their data set
637
638 Node n3 = new Node(LatLon.ZERO);
639 n3.setOsmId(1, 1);
640 their.addPrimitive(n3);
641
642 Node n4 = new Node(new LatLon(1, 1));
643 n4.setOsmId(2, 1);
644 their.addPrimitive(n4);
645
646 Way theirWay = new Way();
647 theirWay.addNode(n3);
648 theirWay.addNode(n4);
649 User user = User.createOsmUser(1111, "their");
650 theirWay.setUser(user);
651 theirWay.setInstant(Instant.now());
652 their.addPrimitive(theirWay);
653
654 DataSetMerger visitor = new DataSetMerger(my, their);
655 visitor.merge();
656
657 // -- tests
658 Way merged = (Way) my.getWays().toArray()[0];
659 assertEquals(0, visitor.getConflicts().size());
660 assertEquals("their", merged.getUser().getName());
661 assertEquals(1111, merged.getUser().getId());
662 assertEquals(theirWay.getRawTimestamp(), merged.getRawTimestamp());
663 assertSame(merged, myWay);
664 assertSame(merged.getNode(0), n1);
665 assertSame(merged.getNode(1), n2);
666
667 assertFalse(merged.isModified());
668 }
669
670 /**
671 * my and their way have no ids, neither do the nodes they refer to. but
672 * my and their way are semantically equal. so technical attributes of
673 * their way can be merged on my way. No conflict.
674 */
675 @Test
676 void testWaySimpleTwoWaysWithNoIdNodesWithoutId() {
677
678 // -- my data set
679
680 Node n1 = new Node(LatLon.ZERO);
681 my.addPrimitive(n1);
682
683 Node n2 = new Node(new LatLon(1, 1));
684 my.addPrimitive(n2);
685
686 Way myWay = new Way();
687 myWay.addNode(n1);
688 myWay.addNode(n2);
689 my.addPrimitive(myWay);
690
691 // -- their data set
692
693 Node n3 = new Node(LatLon.ZERO);
694 their.addPrimitive(n3);
695
696 Node n4 = new Node(new LatLon(1, 1));
697 their.addPrimitive(n4);
698
699 Way theirWay = new Way();
700 theirWay.addNode(n3);
701 theirWay.addNode(n4);
702 User user = User.createOsmUser(1111, "their");
703 theirWay.setUser(user);
704 theirWay.setInstant(Instant.now());
705 their.addPrimitive(theirWay);
706
707 DataSetMerger visitor = new DataSetMerger(my, their);
708 visitor.merge();
709
710 // -- tests
711 Way merged = (Way) my.getWays().toArray()[0];
712 assertEquals(0, visitor.getConflicts().size());
713 assertEquals("their", merged.getUser().getName());
714 assertEquals(1111, merged.getUser().getId());
715 assertEquals(theirWay.getRawTimestamp(), merged.getRawTimestamp());
716 assertSame(merged, myWay);
717 assertSame(merged.getNode(0), n1);
718 assertSame(merged.getNode(1), n2);
719
720 assertFalse(merged.isModified());
721 }
722
723 /**
724 * My dataset includes a deleted node.
725 * Their dataset includes a way with three nodes, the first one being my node.
726 * <p>
727 * => the merged way should include all three nodes. Deleted node should have deleted=false and
728 * special conflict with isDeleted should exist
729 */
730 @Test
731 void testWayComplexMergingADeletedNode() {
732
733 // -- my dataset
734
735 Node mn1 = new Node(LatLon.ZERO);
736 mn1.setOsmId(1, 1);
737 mn1.setDeleted(true);
738 my.addPrimitive(mn1);
739
740 Node tn1 = new Node(LatLon.ZERO);
741 tn1.setOsmId(1, 1);
742 their.addPrimitive(tn1);
743
744 Node tn2 = new Node(new LatLon(1, 1));
745 tn2.setOsmId(2, 1);
746 their.addPrimitive(tn2);
747
748 Node tn3 = new Node(new LatLon(2, 2));
749 tn3.setOsmId(3, 1);
750 their.addPrimitive(tn3);
751
752 // -- their data set
753 Way theirWay = new Way();
754 theirWay.setOsmId(4, 1);
755 theirWay.addNode(tn1);
756 theirWay.addNode(tn2);
757 theirWay.addNode(tn3);
758 theirWay.setUser(User.createOsmUser(1111, "their"));
759 theirWay.setInstant(Instant.now());
760 their.addPrimitive(theirWay);
761
762 DataSetMerger visitor = new DataSetMerger(my, their);
763 visitor.merge();
764
765 assertEquals(1, visitor.getConflicts().size());
766 assertTrue(visitor.getConflicts().get(0).isMyDeleted());
767
768 Way myWay = (Way) my.getPrimitiveById(4, OsmPrimitiveType.WAY);
769 assertEquals(3, myWay.getNodesCount());
770
771 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
772 assertTrue(myWay.getNodes().contains(n));
773
774 assertFalse(myWay.isModified());
775 }
776
777 /**
778 * My dataset includes a deleted node.
779 * Their dataset includes a relation with three nodes, the first one being my node.
780 * <p>
781 * => the merged relation should include all three nodes. There should be conflict for deleted
782 * node with isMyDeleted set
783 */
784 @Test
785 void testRelationComplexMergingADeletedNode() {
786
787 Node mn1 = new Node(LatLon.ZERO);
788 mn1.setOsmId(1, 1);
789 mn1.setDeleted(true);
790 my.addPrimitive(mn1);
791
792 Node tn1 = new Node(LatLon.ZERO);
793 tn1.setOsmId(1, 1);
794 their.addPrimitive(tn1);
795
796 Node tn2 = new Node(new LatLon(1, 1));
797 tn2.setOsmId(2, 1);
798 their.addPrimitive(tn2);
799
800 Node tn3 = new Node(new LatLon(2, 2));
801 tn3.setOsmId(3, 1);
802 their.addPrimitive(tn3);
803
804 Relation theirRelation = new Relation();
805 theirRelation.setOsmId(4, 1);
806
807 theirRelation.addMember(new RelationMember("", tn1));
808 theirRelation.addMember(new RelationMember("", tn2));
809 theirRelation.addMember(new RelationMember("", tn3));
810 their.addPrimitive(theirRelation);
811
812 DataSetMerger visitor = new DataSetMerger(my, their);
813 visitor.merge();
814
815 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
816 assertNotNull(n);
817
818 assertEquals(1, visitor.getConflicts().size());
819 assertTrue(visitor.getConflicts().hasConflictForMy(n));
820 assertTrue(visitor.getConflicts().get(0).isMyDeleted());
821
822 Relation r = (Relation) my.getPrimitiveById(4, OsmPrimitiveType.RELATION);
823 assertEquals(3, r.getMembersCount());
824
825 assertFalse(r.isModified());
826 }
827
828 /**
829 * Merge an incomplete way with two incomplete nodes into an empty dataset.
830 * <p>
831 * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456
832 */
833 @Test
834 void testNewIncompleteWay() {
835
836 Node n1 = new Node(1);
837 their.addPrimitive(n1);
838
839 Node n2 = new Node(2);
840 their.addPrimitive(n2);
841
842 Way w3 = new Way(3);
843 w3.setNodes(Arrays.asList(n1, n2));
844 their.addPrimitive(w3);
845 assertTrue(w3.isIncomplete());
846
847 DataSetMerger visitor = new DataSetMerger(my, their);
848 visitor.merge();
849
850 assertEquals(0, visitor.getConflicts().size());
851
852 OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE);
853 assertNotNull(p);
854 assertTrue(p.isIncomplete());
855 p = my.getPrimitiveById(2, OsmPrimitiveType.NODE);
856 assertNotNull(p);
857 assertTrue(p.isIncomplete());
858 p = my.getPrimitiveById(3, OsmPrimitiveType.WAY);
859 assertNotNull(p);
860 assertTrue(p.isIncomplete());
861
862 Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
863 assertNotNull(w);
864 assertTrue(p.isIncomplete());
865 assertEquals(2, w.getNodesCount());
866 assertTrue(w.getNode(0).isIncomplete());
867 assertTrue(w.getNode(1).isIncomplete());
868 }
869
870 /**
871 * Merge an incomplete way with two incomplete nodes into a dataset where the way already exists as complete way.
872 * <p>
873 * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 after an "Update selection " of this way
874 */
875 @Test
876 void testIncompleteWayOntoCompleteWay() {
877
878 // an incomplete node
879 Node n1 = new Node(1);
880 their.addPrimitive(n1);
881
882 // another incomplete node
883 Node n2 = new Node(2);
884 their.addPrimitive(n2);
885
886 // an incomplete way with two incomplete nodes
887 Way w3 = new Way(3);
888 w3.setNodes(Arrays.asList(n1, n2));
889 their.addPrimitive(w3);
890
891 Node n4 = new Node(LatLon.ZERO);
892 n4.setOsmId(1, 1);
893 my.addPrimitive(n4);
894
895 Node n5 = new Node(new LatLon(1, 1));
896 n5.setOsmId(2, 1);
897 my.addPrimitive(n5);
898
899 Way w6 = new Way(3, 1);
900 w6.setNodes(Arrays.asList(n4, n5));
901 my.addPrimitive(w6);
902
903 DataSetMerger visitor = new DataSetMerger(my, their);
904 visitor.merge();
905
906 assertEquals(0, visitor.getConflicts().size());
907
908 OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE);
909 assertNotNull(p);
910 assertFalse(p.isIncomplete());
911 p = my.getPrimitiveById(2, OsmPrimitiveType.NODE);
912 assertNotNull(p);
913 assertFalse(p.isIncomplete());
914 p = my.getPrimitiveById(3, OsmPrimitiveType.WAY);
915 assertNotNull(p);
916 assertFalse(p.isIncomplete());
917
918 Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
919 assertNotNull(w);
920 assertFalse(p.isIncomplete());
921 assertEquals(2, w.getNodesCount());
922 assertFalse(w.getNode(0).isIncomplete());
923 assertFalse(w.getNode(1).isIncomplete());
924 }
925
926 /**
927 * merge to complete nodes onto an incomplete way with the same two nodes, but incomplete.
928 * => both the nodes and the way should be complete in the target dataset after merging
929 */
930 @Test
931 void testTwoCompleteNodesOntoAnIncompleteWay() {
932
933 // -- source dataset
934
935 // a complete node
936 Node n1 = new Node(1, 1);
937 n1.setCoor(new LatLon(1, 1));
938 their.addPrimitive(n1);
939
940 // another complete node
941 Node n2 = new Node(2, 1);
942 n2.setCoor(new LatLon(2, 2));
943 their.addPrimitive(n2);
944
945 // --- target dataset
946
947 Node n4 = new Node(1);
948 my.addPrimitive(n4);
949
950 Node n5 = new Node(2);
951 my.addPrimitive(n5);
952
953 Way w6 = new Way(3, 1);
954 w6.addNode(n4);
955 w6.addNode(n5);
956 my.addPrimitive(w6);
957
958 //-- merge it
959 DataSetMerger visitor = new DataSetMerger(my, their);
960 visitor.merge();
961
962 // -- test it
963 assertEquals(0, visitor.getConflicts().size());
964
965 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
966 assertNotNull(n);
967 assertFalse(n.isIncomplete());
968
969 n = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE);
970 assertNotNull(n);
971 assertFalse(n.isIncomplete());
972
973 Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY);
974 assertNotNull(w);
975 assertFalse(w.hasIncompleteNodes());
976 assertTrue(w.isUsable());
977 assertEquals(2, w.getNodesCount());
978 assertEquals(1, w.getNode(0).getId());
979 assertEquals(2, w.getNode(1).getId());
980 }
981
982 /**
983 * merge two equal objects with different visibility,
984 * => make sure that DataIntegrityProblemException is thrown.
985 */
986 @Test
987 void testSameVersionAndDifferentVisibility() {
988
989 // -- source dataset
990
991 // a complete node
992 Node n1 = new Node(1, 2);
993 n1.setCoor(new LatLon(1, 1));
994 n1.setVisible(true);
995 assertFalse(n1.isModified());
996 their.addPrimitive(n1);
997
998 // --- target dataset
999 // a complete node
1000 Node n1b = new Node(1, 2);
1001 n1b.setCoor(new LatLon(1, 1));
1002 n1b.setVisible(false);
1003 assertFalse(n1b.isModified());
1004 my.addPrimitive(n1b);
1005
1006 //-- merge it
1007 DataSetMerger visitor = new DataSetMerger(my, their);
1008 assertThrows(DataIntegrityProblemException.class, visitor::merge);
1009 }
1010
1011 /**
1012 * node without referrers deleted in source
1013 */
1014 @Test
1015 void testDeletedSingleNode() {
1016
1017 // -- source dataset
1018
1019 // a complete node
1020 Node n1 = new Node(1, 1);
1021 n1.setCoor(new LatLon(1, 1));
1022 n1.setVisible(true);
1023 assertFalse(n1.isModified());
1024 their.addPrimitive(n1);
1025
1026 //-- merge it to create identical copies
1027 DataSetMerger visitor = new DataSetMerger(my, their);
1028 visitor.merge();
1029
1030 n1.setDeleted(true);
1031
1032 visitor = new DataSetMerger(my, their);
1033 visitor.merge();
1034 OsmPrimitive n1b = my.getPrimitiveById(n1);
1035 assertTrue(n1b.isDeleted());
1036 }
1037
1038 /**
1039 * node without referrers deleted in source
1040 */
1041 @Test
1042 void testDeletedSingleNodeWithMonitor() {
1043
1044 // -- source dataset
1045
1046 // a complete node
1047 Node n1 = new Node(1, 1);
1048 n1.setCoor(new LatLon(1, 1));
1049 n1.setVisible(true);
1050 assertFalse(n1.isModified());
1051 their.addPrimitive(n1);
1052
1053 //-- merge it to create identical copies
1054 DataSetMerger visitor = new DataSetMerger(my, their);
1055 visitor.merge(NullProgressMonitor.INSTANCE);
1056
1057 n1.setDeleted(true);
1058
1059 visitor = new DataSetMerger(my, their);
1060 visitor.merge(NullProgressMonitor.INSTANCE);
1061 OsmPrimitive n1b = my.getPrimitiveById(n1);
1062 assertTrue(n1b.isDeleted());
1063 }
1064
1065 /**
1066 * Way without referrers deleted in source
1067 */
1068 @Test
1069 void testDeletedWayNoReferrers() {
1070
1071 // -- source dataset
1072
1073 // a complete way with two nodes
1074 Node n1 = new Node(1, 1);
1075 n1.setCoor(new LatLon(1, 1));
1076 Node n2 = new Node(2, 1);
1077 n2.setCoor(new LatLon(1, 1));
1078 n1.setVisible(true);
1079 n2.setVisible(true);
1080 their.addPrimitive(n1);
1081 their.addPrimitive(n2);
1082 Way w1 = new Way(1, 1);
1083 their.addPrimitive(w1);
1084 w1.addNode(n1);
1085 w1.addNode(n2);
1086 w1.setVisible(true);
1087 w1.setModified(false);
1088 assertFalse(n1.isModified());
1089 assertFalse(n2.isModified());
1090 assertFalse(w1.isModified());
1091
1092 //-- merge it to create identical copies
1093 DataSetMerger visitor = new DataSetMerger(my, their);
1094 visitor.merge();
1095
1096 w1.setDeleted(true);
1097
1098 visitor = new DataSetMerger(my, their);
1099 visitor.merge();
1100 OsmPrimitive w1b = my.getPrimitiveById(w1);
1101 assertTrue(w1b.isDeleted());
1102 }
1103
1104 /**
1105 * Dependency loop in relations, both deleted in source
1106 * => make sure that DataIntegrityProblemException is thrown.
1107 */
1108 @Test
1109 void testDeletedRelationLoop() {
1110
1111 // -- source dataset
1112 Relation r1 = new Relation(1, 1);
1113 Relation r2 = new Relation(2, 1);
1114 their.addPrimitive(r1);
1115 their.addPrimitive(r2);
1116 // create dependency loop
1117 r1.addMember(new RelationMember("", r2));
1118 r2.addMember(new RelationMember("", r1));
1119
1120 //-- merge it to create identical copies
1121 DataSetMerger visitor = new DataSetMerger(my, their);
1122 visitor.merge();
1123
1124 r1.setMembers(null);
1125 r2.setMembers(null);
1126 r1.setDeleted(true);
1127 r2.setDeleted(true);
1128 visitor = new DataSetMerger(my, their);
1129 visitor.merge();
1130
1131 OsmPrimitive r1b = my.getPrimitiveById(r1);
1132 OsmPrimitive r2b = my.getPrimitiveById(r2);
1133 assertTrue(r1b.isDeleted());
1134 assertTrue(r2b.isDeleted());
1135 }
1136
1137 /**
1138 * Way is deleted in my but member of relation in their
1139 */
1140 @Test
1141 void testDeletedWayStillMemberOfRelation() {
1142
1143 // -- source dataset
1144 Node n1 = new Node(1, 1);
1145 n1.setCoor(new LatLon(1, 1));
1146 Node n2 = new Node(2, 1);
1147 n2.setCoor(new LatLon(1, 1));
1148 n1.setVisible(true);
1149 n2.setVisible(true);
1150 their.addPrimitive(n1);
1151 their.addPrimitive(n2);
1152 Way w1 = new Way(1, 1);
1153 their.addPrimitive(w1);
1154 w1.addNode(n1);
1155 w1.addNode(n2);
1156 w1.setVisible(true);
1157 w1.setModified(false);
1158 assertFalse(n1.isModified());
1159 assertFalse(n2.isModified());
1160 assertFalse(w1.isModified());
1161
1162 //-- merge it to create identical copies
1163 DataSetMerger visitor = new DataSetMerger(my, their);
1164 visitor.merge();
1165
1166 // let relation use the way that is in my dataset
1167 Relation r1 = new Relation(1, 1);
1168 their.addPrimitive(r1);
1169 r1.addMember(new RelationMember("", w1));
1170
1171 Way myWay = (Way) my.getPrimitiveById(w1);
1172 myWay.setNodes(null);
1173 myWay.setDeleted(true);
1174
1175 visitor = new DataSetMerger(my, their);
1176 visitor.merge();
1177 assertFalse(visitor.getConflicts().isEmpty());
1178 assertFalse(myWay.isDeleted());
1179 assertTrue(myWay.isEmpty());
1180 my.clear(); // prevent error from consistency test
1181 }
1182
1183 private void doTestTicket7481(DataSet source, DataSet target) {
1184 // Local node A
1185 Node nA = new Node(2848219691L, 1);
1186 nA.setCoor(LatLon.ZERO);
1187 nA.setInstant(Instant.parse("2014-05-10T14:25:40Z"));
1188 nA.setChangesetId(22251108);
1189 nA.setUser(User.createOsmUser(385987, "yaho"));
1190 nA.put("name", "Mionga");
1191 nA.put("tourism", "hotel");
1192
1193 // Local node B, duplicated
1194 Node nB = new Node(nA);
1195
1196 // Move and delete node A
1197 nA.setCoor(new LatLon(0.1321894855, 6.64627402075));
1198 nA.setDeleted(true);
1199 my.addPrimitive(nA);
1200
1201 // Move and modify node B
1202 nB.setCoor(new LatLon(0.1322066, 6.6462202));
1203 nB.put("phone", "999");
1204 nB.setModified(true);
1205 their.addPrimitive(nB);
1206
1207 // Merge
1208 DataSetMerger visitor = new DataSetMerger(source, target);
1209 visitor.merge();
1210
1211 assertEquals(1, visitor.getConflicts().size());
1212 }
1213
1214 /**
1215 * Non-regression test 1 for <a href="https://josm.openstreetmap.de/ticket/7481">Bug #7481</a>.
1216 */
1217 @Test
1218 void testTicket07481ab() {
1219 doTestTicket7481(my, their);
1220 }
1221
1222 /**
1223 * Non-regression test 2 for <a href="https://josm.openstreetmap.de/ticket/7481">Bug #7481</a>.
1224 */
1225 @Test
1226 void testTicket07481ba() {
1227 doTestTicket7481(their, my);
1228 }
1229
1230 /**
1231 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12599">Bug #12599</a>.
1232 */
1233 @Test
1234 void testTicket12599() {
1235 // Server node: no modifications
1236 Node n1 = new Node(1, 1);
1237 n1.setCoor(LatLon.ZERO);
1238 assertFalse(n1.isModified());
1239 their.addPrimitive(n1);
1240
1241 // Local node: one modification: addition of an uninteresting tag
1242 Node n1b = new Node(n1);
1243 n1b.setModified(true);
1244 n1b.put("note", "something");
1245 assertTrue(n1b.isModified());
1246 assertEquals(0, n1b.getInterestingTags().size());
1247 my.addPrimitive(n1b);
1248
1249 // Merge
1250 DataSetMerger visitor = new DataSetMerger(my, their);
1251 visitor.merge();
1252
1253 // Check that modification is still here
1254 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
1255 assertNotNull(n);
1256 assertEquals("something", n.get("note"));
1257 assertTrue(n.isModified());
1258
1259 // Merge again
1260 visitor = new DataSetMerger(my, their);
1261 visitor.merge();
1262
1263 // Check that modification is still here
1264 n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
1265 assertNotNull(n);
1266 assertEquals("something", n.get("note"));
1267 assertTrue(n.isModified());
1268 }
1269
1270 /**
1271 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12616">Bug #12616</a>.
1272 */
1273 @Test
1274 void testTicket12616() {
1275 // Server node: no modifications
1276 Node n1 = new Node(1, 1);
1277 n1.setCoor(LatLon.ZERO);
1278 assertFalse(n1.isModified());
1279 their.addPrimitive(n1);
1280
1281 // Local node: one modification: move
1282 Node n1b = new Node(n1);
1283 n1b.setCoor(new LatLon(1, 1));
1284 n1b.setModified(true);
1285 assertTrue(n1b.isModified());
1286 assertEquals(new LatLon(1, 1), n1b.getCoor());
1287 my.addPrimitive(n1b);
1288
1289 // Merge
1290 DataSetMerger visitor = new DataSetMerger(my, their);
1291 visitor.merge();
1292
1293 // Check that modification is still here
1294 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
1295 assertNotNull(n);
1296 assertEquals(new LatLon(1, 1), n.getCoor());
1297 assertTrue(n.isModified());
1298
1299 // Merge again
1300 visitor = new DataSetMerger(my, their);
1301 visitor.merge();
1302
1303 // Check that modification is still here
1304 n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
1305 assertNotNull(n);
1306 assertEquals(new LatLon(1, 1), n.getCoor());
1307 assertTrue(n.isModified());
1308 }
1309
1310 /**
1311 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/19783">Bug #19783</a>.
1312 */
1313 @Test
1314 void testTicket19783() {
1315 // Server node: deleted
1316 Node n1 = new Node(1, 2);
1317 n1.setDeleted(true);
1318 n1.setVisible(false);
1319 n1.setModified(false);
1320 assertFalse(n1.isModified());
1321 their.addPrimitive(n1);
1322
1323 // Local node: one modification: move
1324 Node n1b = new Node(1, 1);
1325 n1b.setCoor(new LatLon(1, 1));
1326 n1.setIncomplete(false);
1327 assertFalse(n1b.isModified());
1328 my.addPrimitive(n1b);
1329 n1b.setDeleted(true);
1330 assertTrue(n1b.isModified());
1331
1332 // Merge
1333 DataSetMerger visitor = new DataSetMerger(my, their);
1334 visitor.merge();
1335
1336 // Check that modification is gone and version was merged
1337 Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE);
1338 assertNotNull(n);
1339 assertFalse(n.isModified());
1340 assertFalse(n.isVisible());
1341 assertTrue(n.isDeleted());
1342 assertEquals(2, n.getVersion());
1343 }
1344
1345 /**
1346 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/20091">Bug #20091</a>.
1347 * Relation refers to incomplete way that ways deleted on server.
1348 */
1349 @Test
1350 void testTicket20091() {
1351 // Server way: deleted
1352 Way w1 = new Way(1, 2);
1353 w1.setDeleted(true);
1354 w1.setVisible(false);
1355 w1.setModified(false);
1356 assertFalse(w1.isModified());
1357 their.addPrimitive(w1);
1358
1359 Way w1b = new Way(1);
1360 w1b.setIncomplete(true);
1361 my.addPrimitive(w1b);
1362 Relation r1 = new Relation(1, 1);
1363 r1.addMember(new RelationMember("outer", w1b));
1364 r1.setModified(true);
1365 my.addPrimitive(r1);
1366
1367 // Merge
1368 DataSetMerger visitor = new DataSetMerger(my, their);
1369 visitor.merge();
1370
1371 assertEquals(1, visitor.getConflicts().size());
1372 assertTrue(r1.isModified());
1373 assertEquals(w1b, visitor.getConflicts().iterator().next().getMy());
1374 }
1375
1376 static Stream<BiConsumer<Node, Node>> testTicket23846() {
1377 return Stream.of(
1378 (firstNode, secondNode) -> firstNode.setModified(true),
1379 (firstNode, secondNode) -> { /* No modifications */ }
1380 );
1381 }
1382
1383 @ParameterizedTest
1384 @MethodSource
1385 void testTicket23846(BiConsumer<Node, Node> nodeSetup) {
1386 final Node firstNode = new Node(1234, 1);
1387 final Node secondNode = new Node(1234, 1);
1388 final DataSetMerger merge = new DataSetMerger(my, their);
1389 firstNode.setCoor(LatLon.ZERO);
1390 secondNode.setCoor(LatLon.ZERO);
1391 nodeSetup.accept(firstNode, secondNode);
1392 my.addPrimitive(firstNode);
1393 their.addPrimitive(secondNode);
1394 secondNode.setReferrersDownloaded(true);
1395 assertFalse(firstNode.isReferrersDownloaded());
1396 assertTrue(secondNode.isReferrersDownloaded());
1397 merge.merge();
1398 assertTrue(firstNode.isReferrersDownloaded());
1399 assertTrue(secondNode.isReferrersDownloaded());
1400 }
1401
1402 /**
1403 * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/23930">#23930</a>
1404 */
1405 @Test
1406 void testTicket23930() throws IOException, IllegalDataException {
1407 final File file = new File(TestUtils.getRegressionDataFile(23930, "JOSM_conflict.joz"));
1408 final SessionReader reader = new SessionReader();
1409 reader.loadSession(file, true, NullProgressMonitor.INSTANCE);
1410 final List<OsmDataLayer> layers = reader.getLayers().stream()
1411 .filter(OsmDataLayer.class::isInstance).map(OsmDataLayer.class::cast).collect(Collectors.toList());
1412 final DataSet newWay = layers.stream().filter(layer -> layer.getName().equals("new_way.osm"))
1413 .map(OsmDataLayer::getDataSet).findFirst().orElseThrow();
1414 final DataSet nodeDeleted = layers.stream().filter(layer -> layer.getName().equals("node_deleted.osm"))
1415 .map(OsmDataLayer::getDataSet).findFirst().orElseThrow();
1416 final DataSetMerger merge = new DataSetMerger(nodeDeleted, newWay);
1417 Logging.clearLastErrorAndWarnings();
1418 assertDoesNotThrow(() -> merge.merge(NullProgressMonitor.INSTANCE));
1419 assertTrue(Logging.getLastErrorAndWarnings().isEmpty(), String.join("\n", Logging.getLastErrorAndWarnings()));
1420 final ConflictCollection conflicts = merge.getConflicts();
1421 // There are a few differences in the files
1422 // 1. New node in layer 2: No need for conflict
1423 // 2. node 2427358529: layer 1 deletes it, layer 2 modifies it (conflict required)
1424 // 3. new way in layer 2 with new node and node 2427358529 (conflict required)
1425 // 4. Modification of way 32277602 in layer 1 removing node 2427358529 (conflict required)
1426 // Therefore, conflicts are as follows:
1427 // 1. A deleted node (n2427358529) with referrers (w32277602 and new way) and new tags ("fix tag=recheck position")
1428 assertEquals(1, conflicts.size());
1429 final Conflict<?> conflict = conflicts.iterator().next();
1430 final Node myNode = assertInstanceOf(Node.class, conflict.getMy());
1431 final Node theirNode = assertInstanceOf(Node.class, conflict.getTheir());
1432 assertFalse(theirNode.isDeleted());
1433 assertFalse(myNode.isDeleted());
1434 }
1435}
Note: See TracBrowser for help on using the repository browser.