// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.data.osm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.StringWriter; import java.time.Instant; import java.util.Arrays; import java.util.Date; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.projection.Projections; import org.openstreetmap.josm.testutils.JOSMTestRules; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Unit tests for class {@link DataSetMerger}. */ public class DataSetMergerTest { /** * Setup test. */ @Rule @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public JOSMTestRules test = new JOSMTestRules(); private DataSet my; private DataSet their; /** * Setup test. */ @Before public void setUp() { User.clearUserMap(); my = new DataSet(); my.setVersion("0.6"); their = new DataSet(); their.setVersion("0.6"); Main.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator } private void runConsistencyTests(DataSet ds) { StringWriter writer = new StringWriter(); DatasetConsistencyTest test = new DatasetConsistencyTest(ds, writer); test.checkReferrers(); test.checkCompleteWaysWithIncompleteNodes(); test.searchNodes(); test.searchWays(); test.referredPrimitiveNotInDataset(); test.checkZeroNodesWays(); String result = writer.toString(); if (!result.isEmpty()) fail(result); } @After public void checkDatasets() { runConsistencyTests(my); runConsistencyTests(their); } /** * two identical nodes, even in id and version. No confict expected. * * Can happen if data is loaded in two layers and then merged from one layer * on the other. */ @Test public void testNodeSimpleIdenticalNoConflict() { Node n = new Node(LatLon.ZERO); n.setOsmId(1, 1); n.setModified(false); n.put("key1", "value1"); my.addPrimitive(n); Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 1); n1.setModified(false); n1.put("key1", "value1"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(visitor.getConflicts().isEmpty()); assertNotSame(n1, n2); // make sure we have a clone assertEquals(1, n2.getId()); assertEquals(1, n2.getVersion()); assertFalse(n2.isModified()); assertEquals("value1", n2.get("key1")); // merge target not modified after merging assertFalse(n2.isModified()); } /** * two nodes, my is unmodified, their is updated and has a higher version * => their version is going to be the merged version */ @Test public void testNodeSimpleLocallyUnmodifiedNoConflict() { Node n = new Node(LatLon.ZERO); n.setOsmId(1, 1); n.setModified(false); n.put("key1", "value1"); my.addPrimitive(n); Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 2); n1.setModified(false); n1.put("key1", "value1-new"); n1.put("key2", "value2"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(visitor.getConflicts().isEmpty()); assertSame(n, n2); // make sure the merged node is still the original node assertSame(n2.getDataSet(), my); assertEquals(1, n2.getId()); assertEquals(2, n2.getVersion()); assertFalse(n2.isModified()); assertEquals("value1-new", n2.get("key1")); assertEquals("value2", n2.get("key2")); // the merge target should not be modified assertFalse(n2.isModified()); } /** * Node with same id, my is modified, their has a higher version * => results in a conflict * * Use case: node which is modified locally and updated by another mapper on * the server */ @Test public void testNodeSimpleTagConflict() { Node n = new Node(LatLon.ZERO); n.setOsmId(1, 1); n.setModified(true); n.put("key1", "value1"); n.put("key2", "value2"); my.addPrimitive(n); Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 2); n1.setModified(false); n1.put("key1", "value1-new"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertEquals(1, visitor.getConflicts().size()); assertSame(n, n2); assertNotSame(n1, n2); assertSame(n1.getDataSet(), their); } /** * node with same id, my is deleted, their has a higher version * => results in a conflict * * Use case: node which is deleted locally and updated by another mapper on * the server */ @Test public void testNodeSimpleDeleteConflict() { Node n = new Node(1, 1); n.setCoor(LatLon.ZERO); n.setDeleted(true); n.put("key1", "value1"); my.addPrimitive(n); Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 2); n1.setModified(false); n1.put("key1", "value1-new"); n1.put("key2", "value2"); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertEquals(1, visitor.getConflicts().size()); assertSame(n, n2); assertNotSame(n1, n2); assertSame(n1.getDataSet(), their); } /** * My node is deleted, their node has the same id and version and is not deleted. * => mine has precedence */ @Test public void testNodeSimpleDeleteConflict2() { Node n = new Node(LatLon.ZERO); n.setOsmId(1, 1); n.setDeleted(true); my.addPrimitive(n); Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 1); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertEquals(0, visitor.getConflicts().size()); assertTrue(n2.isVisible()); assertSame(n, n2); assertSame(n.getDataSet(), my); assertSame(n1.getDataSet(), their); } /** * My and their node are new but semantically equal. My node is deleted. * * => Ignore my node, no conflict */ @Test public void testNodeSimpleDeleteConflict3() { Node n = new Node(new LatLon(1, 1)); n.setDeleted(true); my.addPrimitive(n); Node n1 = new Node(new LatLon(1, 1)); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(0, visitor.getConflicts().size()); assertSame(n.getDataSet(), my); assertSame(n1.getDataSet(), their); } /** * My and their node are new but semantically equal. Both are deleted. * * => take mine */ @Test public void testNodeSimpleDeleteConflict4() { Node n = new Node(new LatLon(1, 1)); n.setDeleted(true); my.addPrimitive(n); Node n1 = new Node(new LatLon(1, 1)); n1.setDeleted(true); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(0, visitor.getConflicts().size()); Node n2 = (Node) my.getNodes().toArray()[0]; assertSame(n2, n); assertTrue(n2.isDeleted()); } /** * their node has no assigned id (id == 0) and is semantically equal to one of my * nodes with id == 0 * * => merge it onto my node. */ @Test public void testNodeSimpleNoIdSemanticallyEqual() { User myUser = User.createOsmUser(1111, "my"); User theirUser = User.createOsmUser(222, "their"); Node n = new Node(); n.setCoor(LatLon.ZERO); n.put("key1", "value1"); n.setUser(myUser); n.setTimestamp(new Date()); my.addPrimitive(n); Node n1 = new Node(); n1.setCoor(LatLon.ZERO); n1.put("key1", "value1"); n1.setTimestamp(Date.from(Instant.now().plusSeconds(3600))); n1.setUser(theirUser); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = my.getNodes().iterator().next(); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", n2.get("key1")); assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp()); assertEquals(theirUser, n2.getUser()); assertSame(n2, n); assertNotSame(n2, n1); assertSame(n2.getDataSet(), my); } /** * my node is incomplete, their node is complete * * => merge it onto my node. My node becomes complete */ @Test public void testNodeSimpleIncompleteNode() { Node n = new Node(1); my.addPrimitive(n); Node n1 = new Node(); n1.setCoor(LatLon.ZERO); n1.setOsmId(1, 1); n1.put("key1", "value1"); Date timestamp = new Date(); n1.setTimestamp(timestamp); their.addPrimitive(n1); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n2 = my.getNodes().iterator().next(); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", n2.get("key1")); assertEquals(n1.getRawTimestamp(), n2.getRawTimestamp()); assertFalse(n2.isIncomplete()); assertSame(n2, n); } /** * their way has a higher version and different tags. the nodes are the same. My * way is not modified. Merge is possible. No conflict. * * => merge it onto my way. */ @Test public void testWaySimpleIdenticalNodesDifferentTags() { // -- the target dataset Node n1 = new Node(); n1.setCoor(LatLon.ZERO); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(); n2.setCoor(LatLon.ZERO); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.put("key1", "value1"); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // -- the source data set Node n3 = new Node(LatLon.ZERO); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n4 = new Node(new LatLon(1, 1)); n4.setOsmId(2, 1); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.put("key1", "value1"); theirWay.put("key2", "value2"); theirWay.addNode(n3); theirWay.addNode(n4); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(0, visitor.getConflicts().size()); assertEquals("value1", merged.get("key1")); assertEquals("value2", merged.get("key2")); assertEquals(3, merged.getId()); assertEquals(2, merged.getVersion()); assertEquals(2, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(2, merged.getNode(1).getId()); assertSame(merged, myWay); assertSame(merged.getDataSet(), my); Node mergedNode = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertSame(mergedNode, n1); mergedNode = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertSame(mergedNode, n2); assertFalse(merged.isModified()); } /** * their way has a higher version and different tags. And it has more nodes. Two * of the existing nodes are modified. * * => merge it onto my way, no conflict */ @Test public void testWaySimpleAdditionalNodesAndChangedNodes() { // -- my data set Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // --- their data set Node n3 = new Node(LatLon.ZERO); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 2); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(0, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(2, merged.getVersion()); assertEquals(3, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(4, merged.getNode(1).getId()); assertEquals(2, merged.getNode(2).getId()); assertEquals("value1", merged.getNode(2).get("key1")); assertSame(merged.getNode(0), n1); assertNotSame(merged.getNode(1), n5); // must be clone of the original node in their assertSame(merged.getNode(2), n2); assertFalse(merged.isModified()); // the target wasn't modified before merging, it mustn't be after merging } /** * their way has a higher version and different nodes. My way is modified. * * => merge onto my way not possible, create a conflict */ @Test public void testWaySimpleDifferentNodesAndMyIsModified() { // -- the target dataset Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(n1); myWay.addNode(n2); myWay.setModified(true); myWay.put("key1", "value1"); my.addPrimitive(myWay); // -- the source dataset Node n3 = new Node(LatLon.ZERO); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(4, 1); their.addPrimitive(n5); Node n4 = new Node(new LatLon(2, 2)); n4.setOsmId(2, 1); n4.put("key1", "value1"); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.addNode(n3); theirWay.addNode(n5); // insert a node theirWay.addNode(n4); // this one is updated their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(1, visitor.getConflicts().size()); assertEquals(3, merged.getId()); assertEquals(1, merged.getVersion()); assertEquals(2, merged.getNodesCount()); assertEquals(1, merged.getNode(0).getId()); assertEquals(2, merged.getNode(1).getId()); assertEquals("value1", merged.get("key1")); } /** * their way is not visible anymore. * * => conflict */ @Test public void testWaySimpleTheirVersionNotVisibleMyIsModified() { Node mn1 = new Node(LatLon.ZERO); mn1.setOsmId(1, 1); my.addPrimitive(mn1); Node mn2 = new Node(new LatLon(1, 1)); mn2.setOsmId(2, 1); my.addPrimitive(mn2); Way myWay = new Way(); myWay.setOsmId(3, 1); myWay.addNode(mn1); myWay.addNode(mn2); myWay.setModified(true); my.addPrimitive(myWay); Way theirWay = new Way(); theirWay.setOsmId(3, 2); theirWay.setVisible(false); /* Invisible objects fetched from the server should be marked as "deleted". * Otherwise it's an error. */ theirWay.setDeleted(true); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Way merged = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertEquals(1, visitor.getConflicts().size()); assertTrue(visitor.getConflicts().hasConflictForMy(myWay)); assertTrue(visitor.getConflicts().hasConflictForTheir(theirWay)); assertEquals(myWay, merged); } /** * my and their way have no ids, nodes they refer to have an id. but * my and their way are semantically equal. so technical attributes of * their way can be merged on my way. No conflict. */ @Test public void testWaySimpleTwoWaysWithNoIdNodesWithId() { // -- my data set Node n1 = new Node(LatLon.ZERO); n1.setOsmId(1, 1); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); n2.setOsmId(2, 1); my.addPrimitive(n2); Way myWay = new Way(); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // -- their data set Node n3 = new Node(LatLon.ZERO); n3.setOsmId(1, 1); their.addPrimitive(n3); Node n4 = new Node(new LatLon(1, 1)); n4.setOsmId(2, 1); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.addNode(n3); theirWay.addNode(n4); User user = User.createOsmUser(1111, "their"); theirWay.setUser(user); theirWay.setTimestamp(new Date()); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getWays().toArray()[0]; assertEquals(0, visitor.getConflicts().size()); assertEquals("their", merged.getUser().getName()); assertEquals(1111, merged.getUser().getId()); assertEquals(theirWay.getRawTimestamp(), merged.getRawTimestamp()); assertSame(merged, myWay); assertSame(merged.getNode(0), n1); assertSame(merged.getNode(1), n2); assertFalse(merged.isModified()); } /** * my and their way have no ids, neither do the nodes they refer to. but * my and their way are semantically equal. so technical attributes of * their way can be merged on my way. No conflict. */ @Test public void testWaySimpleTwoWaysWithNoIdNodesWithoutId() { // -- my data set Node n1 = new Node(LatLon.ZERO); my.addPrimitive(n1); Node n2 = new Node(new LatLon(1, 1)); my.addPrimitive(n2); Way myWay = new Way(); myWay.addNode(n1); myWay.addNode(n2); my.addPrimitive(myWay); // -- their data set Node n3 = new Node(LatLon.ZERO); their.addPrimitive(n3); Node n4 = new Node(new LatLon(1, 1)); their.addPrimitive(n4); Way theirWay = new Way(); theirWay.addNode(n3); theirWay.addNode(n4); User user = User.createOsmUser(1111, "their"); theirWay.setUser(user); theirWay.setTimestamp(new Date()); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- tests Way merged = (Way) my.getWays().toArray()[0]; assertEquals(0, visitor.getConflicts().size()); assertEquals("their", merged.getUser().getName()); assertEquals(1111, merged.getUser().getId()); assertEquals(theirWay.getRawTimestamp(), merged.getRawTimestamp()); assertSame(merged, myWay); assertSame(merged.getNode(0), n1); assertSame(merged.getNode(1), n2); assertFalse(merged.isModified()); } /** * My dataset includes a deleted node. * Their dataset includes a way with three nodes, the first one being my node. * * => the merged way should include all three nodes. Deleted node should have deleted=false and * special conflict with isDeleted should exist */ @Test public void testWayComplexMergingADeletedNode() { // -- my dataset Node mn1 = new Node(LatLon.ZERO); mn1.setOsmId(1, 1); mn1.setDeleted(true); my.addPrimitive(mn1); Node tn1 = new Node(LatLon.ZERO); tn1.setOsmId(1, 1); their.addPrimitive(tn1); Node tn2 = new Node(new LatLon(1, 1)); tn2.setOsmId(2, 1); their.addPrimitive(tn2); Node tn3 = new Node(new LatLon(2, 2)); tn3.setOsmId(3, 1); their.addPrimitive(tn3); // -- their data set Way theirWay = new Way(); theirWay.setOsmId(4, 1); theirWay.addNode(tn1); theirWay.addNode(tn2); theirWay.addNode(tn3); theirWay.setUser(User.createOsmUser(1111, "their")); theirWay.setTimestamp(new Date()); their.addPrimitive(theirWay); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(1, visitor.getConflicts().size()); assertTrue(visitor.getConflicts().get(0).isMyDeleted()); Way myWay = (Way) my.getPrimitiveById(4, OsmPrimitiveType.WAY); assertEquals(3, myWay.getNodesCount()); Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertTrue(myWay.getNodes().contains(n)); assertFalse(myWay.isModified()); } /** * My dataset includes a deleted node. * Their dataset includes a relation with three nodes, the first one being my node. * * => the merged relation should include all three nodes. There should be conflict for deleted * node with isMyDeleted set */ @Test public void testRelationComplexMergingADeletedNode() { Node mn1 = new Node(LatLon.ZERO); mn1.setOsmId(1, 1); mn1.setDeleted(true); my.addPrimitive(mn1); Node tn1 = new Node(LatLon.ZERO); tn1.setOsmId(1, 1); their.addPrimitive(tn1); Node tn2 = new Node(new LatLon(1, 1)); tn2.setOsmId(2, 1); their.addPrimitive(tn2); Node tn3 = new Node(new LatLon(2, 2)); tn3.setOsmId(3, 1); their.addPrimitive(tn3); Relation theirRelation = new Relation(); theirRelation.setOsmId(4, 1); theirRelation.addMember(new RelationMember("", tn1)); theirRelation.addMember(new RelationMember("", tn2)); theirRelation.addMember(new RelationMember("", tn3)); their.addPrimitive(theirRelation); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertEquals(1, visitor.getConflicts().size()); assertTrue(visitor.getConflicts().hasConflictForMy(n)); assertTrue(visitor.getConflicts().get(0).isMyDeleted()); Relation r = (Relation) my.getPrimitiveById(4, OsmPrimitiveType.RELATION); assertEquals(3, r.getMembersCount()); assertFalse(r.isModified()); } /** * Merge an incomplete way with two incomplete nodes into an empty dataset. * * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 */ @Test public void testNewIncompleteWay() { Node n1 = new Node(1); their.addPrimitive(n1); Node n2 = new Node(2); their.addPrimitive(n2); Way w3 = new Way(3); w3.setNodes(Arrays.asList(n1, n2)); their.addPrimitive(w3); assertTrue(w3.isIncomplete()); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(0, visitor.getConflicts().size()); OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(p); assertTrue(p.isIncomplete()); p = my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertNotNull(p); assertTrue(p.isIncomplete()); p = my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(p); assertTrue(p.isIncomplete()); Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(w); assertTrue(p.isIncomplete()); assertEquals(2, w.getNodesCount()); assertTrue(w.getNode(0).isIncomplete()); assertTrue(w.getNode(1).isIncomplete()); } /** * Merge an incomplete way with two incomplete nodes into a dataset where the way already exists as complete way. * * Use case: a way loaded with a multiget, i.e. GET /api/0.6/ways?ids=123456 after a "Update selection " of this way */ @Test public void testIncompleteWayOntoCompleteWay() { // an incomplete node Node n1 = new Node(1); their.addPrimitive(n1); // another incomplete node Node n2 = new Node(2); their.addPrimitive(n2); // an incomplete way with two incomplete nodes Way w3 = new Way(3); w3.setNodes(Arrays.asList(n1, n2)); their.addPrimitive(w3); Node n4 = new Node(LatLon.ZERO); n4.setOsmId(1, 1); my.addPrimitive(n4); Node n5 = new Node(new LatLon(1, 1)); n5.setOsmId(2, 1); my.addPrimitive(n5); Way w6 = new Way(3, 1); w6.setNodes(Arrays.asList(n4, n5)); my.addPrimitive(w6); DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); assertEquals(0, visitor.getConflicts().size()); OsmPrimitive p = my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(p); assertFalse(p.isIncomplete()); p = my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertNotNull(p); assertFalse(p.isIncomplete()); p = my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(p); assertFalse(p.isIncomplete()); Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(w); assertFalse(p.isIncomplete()); assertEquals(2, w.getNodesCount()); assertFalse(w.getNode(0).isIncomplete()); assertFalse(w.getNode(1).isIncomplete()); } /** * merge to complete nodes onto an incomplete way with the same two nodes, but incomplete. * => both the nodes and the way should be complete in the target dataset after merging */ @Test public void testTwoCompleteNodesOntoAnIncompleteWay() { // -- source dataset // an complete node Node n1 = new Node(1, 1); n1.setCoor(new LatLon(1, 1)); their.addPrimitive(n1); // another complete node Node n2 = new Node(2, 1); n2.setCoor(new LatLon(2, 2)); their.addPrimitive(n2); // --- target dataset Node n4 = new Node(1); my.addPrimitive(n4); Node n5 = new Node(2); my.addPrimitive(n5); Way w6 = new Way(3, 1); w6.addNode(n4); w6.addNode(n5); my.addPrimitive(w6); //-- merge it DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // -- test it assertEquals(0, visitor.getConflicts().size()); Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertFalse(n.isIncomplete()); n = (Node) my.getPrimitiveById(2, OsmPrimitiveType.NODE); assertNotNull(n); assertFalse(n.isIncomplete()); Way w = (Way) my.getPrimitiveById(3, OsmPrimitiveType.WAY); assertNotNull(w); assertFalse(w.hasIncompleteNodes()); assertTrue(w.isUsable()); assertEquals(2, w.getNodesCount()); assertEquals(1, w.getNode(0).getId()); assertEquals(2, w.getNode(1).getId()); } /** * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12599">Bug #12599</a>. */ @Test public void testTicket12599() { // Server node: no modifications Node n1 = new Node(1, 1); n1.setCoor(LatLon.ZERO); assertFalse(n1.isModified()); their.addPrimitive(n1); // Local node: one modification: addition of an uninteresting tag Node n1b = new Node(n1); n1b.setModified(true); n1b.put("note", "something"); assertTrue(n1b.isModified()); assertEquals(0, n1b.getInterestingTags().size()); my.addPrimitive(n1b); // Merge DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // Check that modification is still here Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertEquals("something", n.get("note")); assertTrue(n.isModified()); // Merge again visitor = new DataSetMerger(my, their); visitor.merge(); // Check that modification is still here n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertEquals("something", n.get("note")); assertTrue(n.isModified()); } /** * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12616">Bug #12616</a>. */ @Test public void testTicket12616() { // Server node: no modifications Node n1 = new Node(1, 1); n1.setCoor(LatLon.ZERO); assertFalse(n1.isModified()); their.addPrimitive(n1); // Local node: one modification: move Node n1b = new Node(n1); n1b.setCoor(new LatLon(1, 1)); n1b.setModified(true); assertTrue(n1b.isModified()); assertEquals(new LatLon(1, 1), n1b.getCoor()); my.addPrimitive(n1b); // Merge DataSetMerger visitor = new DataSetMerger(my, their); visitor.merge(); // Check that modification is still here Node n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertEquals(new LatLon(1, 1), n.getCoor()); assertTrue(n.isModified()); // Merge again visitor = new DataSetMerger(my, their); visitor.merge(); // Check that modification is still here n = (Node) my.getPrimitiveById(1, OsmPrimitiveType.NODE); assertNotNull(n); assertEquals(new LatLon(1, 1), n.getCoor()); assertTrue(n.isModified()); } }