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

Last change on this file was 18853, checked in by taylor.smock, 7 months ago

See #16567: Update to JUnit 5

This removes new JOSMTestRules() with no additional setup and most
JOSMFixture calls.

Removing the bare JOSMTestRules speeds up the test suite since there are two
fewer System.gc() calls per test.

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