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

Last change on this file since 17360 was 17346, checked in by GerdP, 3 years ago

checkstyle issues

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