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

Last change on this file since 14922 was 14922, checked in by Don-vip, 5 years ago

fix #7481 - add missing conflict when merging a primitive modified and deleted in two data sets

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