// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.command;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.openstreetmap.josm.command.CommandTest.CommandTestDataWithRelation;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.User;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.WaySegment;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.testutils.JOSMTestRules;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import nl.jqno.equalsverifier.EqualsVerifier;
import nl.jqno.equalsverifier.Warning;
/**
* Unit tests of {@link DeleteCommand} class.
*/
public class DeleteCommandTest {
/**
* We need prefs for nodes.
*/
@Rule
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
private CommandTestDataWithRelation testData;
/**
* Set up the test data.
*/
@Before
public void createTestData() {
testData = new CommandTestDataWithRelation();
}
/**
* A simple deletion test with no references
*/
@Test
public void testSimpleDelete() {
Node node = testData.createNode(15);
assertTrue(testData.layer.data.allPrimitives().contains(node));
new DeleteCommand(node).executeCommand();
assertTrue(node.isDeleted());
assertTrue(node.isModified());
assertFalse(testData.layer.data.allNonDeletedPrimitives().contains(node));
}
/**
* A delete should not delete referred objects but should should remove the reference.
*/
@Test
public void testDeleteIgnoresReferences() {
assertTrue(testData.existingNode.getReferrers().contains(testData.existingRelation));
new DeleteCommand(testData.existingRelation).executeCommand();
assertTrue(testData.existingRelation.isDeleted());
assertEquals(0, testData.existingRelation.getMembersCount());
assertFalse(testData.existingNode.isDeleted());
assertFalse(testData.existingWay.isDeleted());
assertFalse(testData.existingNode.getReferrers().contains(testData.existingRelation));
// same for the way
assertTrue(testData.existingNode.getReferrers().contains(testData.existingWay));
new DeleteCommand(testData.existingWay).executeCommand();
assertEquals(0, testData.existingWay.getNodesCount());
assertFalse(testData.existingNode.getReferrers().contains(testData.existingWay));
}
/**
* A delete should delete all objects with references to the deleted one
*/
@Test(expected = IllegalArgumentException.class)
public void testDeleteFailsOnDelted() {
new DeleteCommand(testData.existingRelation).executeCommand();
new DeleteCommand(testData.existingRelation).executeCommand();
}
/**
* A delete should delete all objects with references to the deleted one
*/
@Test
public void testReferredDelete() {
DeleteCommand.deleteWithReferences(testData.layer, Arrays.asList(testData.existingNode), true).executeCommand();
assertTrue(testData.existingNode.isDeleted());
assertEquals(0, testData.existingWay.getNodesCount());
assertTrue(testData.existingWay.isDeleted());
}
/**
* Delete nodes that would be without reference afterwards.
*/
@Test
public void testDelteNodesInWay() {
testData.existingNode.removeAll();
// That untagged node should be deleted.
testData.existingNode2.removeAll();
DeleteCommand.delete(testData.layer, Arrays.asList(testData.existingWay), true, true).executeCommand();
assertTrue(testData.existingWay.isDeleted());
assertTrue(testData.existingNode2.isDeleted());
assertFalse(testData.existingNode.isDeleted());
assertFalse(testData.existingRelation.isDeleted());
// Same test, now with tagged nodes
Node node1 = testData.createNode(15);
Node node2 = testData.createNode(16);
Node node3 = testData.createNode(17);
Node node4 = testData.createNode(18);
node2.removeAll();
node4.removeAll();
Way way1 = new Way(25, 1);
way1.setNodes(Arrays.asList(node1, node2, node3));
testData.layer.data.addPrimitive(way1);
Way way2 = new Way(26, 1);
way2.setNodes(Arrays.asList(node2, node3, node4));
testData.layer.data.addPrimitive(way2);
DeleteCommand.delete(testData.layer, Arrays.asList(way1, way2), true, true).executeCommand();
assertTrue(way1.isDeleted());
assertTrue(way2.isDeleted());
assertFalse(node1.isDeleted());
assertTrue(node2.isDeleted());
assertFalse(node3.isDeleted());
assertTrue(node4.isDeleted());
}
/**
* Test that {@link DeleteCommand} checks for non-null.
*/
@Test(expected = IllegalArgumentException.class)
public void testConsistency() {
new DeleteCommand(Arrays.asList(testData.existingNode, testData.existingWay, null));
}
/**
* Test that {@link DeleteCommand} checks for the dataset
*/
@Test(expected = IllegalArgumentException.class)
public void testConsistencyDataset() {
testData.layer.data.removePrimitive(testData.existingNode);
new DeleteCommand(Arrays.asList(testData.existingNode, testData.existingWay));
}
/**
* Test that {@link DeleteCommand} checks for non-empty list
*/
@Test(expected = IllegalArgumentException.class)
public void testConsistencyNonEmpty() {
new DeleteCommand(Arrays.<OsmPrimitive>asList());
}
/**
* Test that {@link DeleteCommand} checks for non-null list
*/
@Test(expected = IllegalArgumentException.class)
public void testConsistencyNonNull() {
new DeleteCommand((Collection<OsmPrimitive>) null);
}
/**
* Test {@link DeleteCommand#undoCommand()}
*/
@Test
public void testUndo() {
DeleteCommand command = new DeleteCommand(
Arrays.asList(testData.existingNode, testData.existingNode2, testData.existingWay));
command.executeCommand();
assertTrue(testData.existingNode.isDeleted());
assertTrue(testData.existingWay.isDeleted());
command.undoCommand();
assertFalse(testData.existingNode.isDeleted());
assertFalse(testData.existingWay.isDeleted());
assertEquals("existing", testData.existingNode.get("existing"));
command.executeCommand();
assertTrue(testData.existingNode.isDeleted());
assertTrue(testData.existingWay.isDeleted());
}
/**
* Test {@link DeleteCommand#deleteWaySegment(OsmDataLayer, org.openstreetmap.josm.data.osm.WaySegment)}
* Way with only 1 segment
*/
@Test
public void testDeleteWaySegment() {
Way way1 = testData.createWay(100, testData.createNode(101), testData.createNode(102));
WaySegment ws = new WaySegment(way1, 0);
Command command = DeleteCommand.deleteWaySegment(testData.layer, ws);
command.executeCommand();
assertTrue(way1.isDeleted());
}
/**
* Test {@link DeleteCommand#deleteWaySegment(OsmDataLayer, org.openstreetmap.josm.data.osm.WaySegment)}
* Delete end of way
*/
@Test
public void testDeleteWaySegmentEndOfWay() {
Way way = testData.createWay(200, testData.createNode(201), testData.createNode(202), testData.createNode(203),
testData.createNode(204));
WaySegment ws = new WaySegment(way, 2);
Command command = DeleteCommand.deleteWaySegment(testData.layer, ws);
command.executeCommand();
assertEquals(3, way.getNodesCount());
assertEquals(201, way.getNodeId(0));
assertEquals(202, way.getNodeId(1));
assertEquals(203, way.getNodeId(2));
}
/**
* Test {@link DeleteCommand#deleteWaySegment(OsmDataLayer, org.openstreetmap.josm.data.osm.WaySegment)}
* Delete start of way
*/
@Test
public void testDeleteWaySegmentStartOfWay() {
Way way = testData.createWay(100, testData.createNode(101), testData.createNode(102), testData.createNode(103),
testData.createNode(104));
WaySegment ws = new WaySegment(way, 0);
Command command = DeleteCommand.deleteWaySegment(testData.layer, ws);
command.executeCommand();
assertEquals(3, way.getNodesCount());
assertEquals(102, way.getNodeId(0));
assertEquals(103, way.getNodeId(1));
assertEquals(104, way.getNodeId(2));
}
/**
* Test {@link DeleteCommand#deleteWaySegment(OsmDataLayer, org.openstreetmap.josm.data.osm.WaySegment)}
* Delete start of way
*/
@Test
public void testDeleteWaySegmentSplit() {
Node node103 = testData.createNode(103);
Node node104 = testData.createNode(104);
Way way = testData.createWay(100, testData.createNode(101), testData.createNode(102), node103, node104);
WaySegment ws = new WaySegment(way, 1);
Command command = DeleteCommand.deleteWaySegment(testData.layer, ws);
command.executeCommand();
assertEquals(2, way.getNodesCount());
assertEquals(101, way.getNodeId(0));
assertEquals(102, way.getNodeId(1));
// there needs to be a new way
assertEquals(1, node104.getReferrers().size());
Way newWay = (Way) node104.getReferrers().get(0);
assertEquals(2, newWay.getNodesCount());
assertEquals(103, newWay.getNodeId(0));
assertEquals(104, newWay.getNodeId(1));
}
/**
* Test {@link DeleteCommand#deleteWaySegment(OsmDataLayer, org.openstreetmap.josm.data.osm.WaySegment)}
* Delete start of way
*/
@Test
public void testDeleteWaySegmentCycle() {
Node n = testData.createNode(101);
Way way = testData.createWay(100, n, testData.createNode(102), testData.createNode(103),
testData.createNode(104), n);
WaySegment ws = new WaySegment(way, 2);
Command command = DeleteCommand.deleteWaySegment(testData.layer, ws);
command.executeCommand();
assertEquals(4, way.getNodesCount());
assertEquals(104, way.getNodeId(0));
assertEquals(101, way.getNodeId(1));
assertEquals(102, way.getNodeId(2));
assertEquals(103, way.getNodeId(3));
}
/**
* Tests {@link DeleteCommand#getChildren()}
*/
@Test
public void testGetChildren() {
testData.existingNode.put("name", "xy");
Collection<PseudoCommand> children = new DeleteCommand(Arrays.<OsmPrimitive>asList(testData.existingNode, testData.existingNode2))
.getChildren();
assertEquals(2, children.size());
assertTrue(children.stream().allMatch(c -> c.getParticipatingPrimitives().size() == 1));
assertTrue(children.stream().anyMatch(c -> c.getParticipatingPrimitives().iterator().next() == testData.existingNode));
assertTrue(children.stream().anyMatch(c -> c.getParticipatingPrimitives().iterator().next() == testData.existingNode2));
assertTrue(children.stream().anyMatch(c -> c.getDescriptionText().matches("Deleted '.*xy.*'")));
}
/**
* Tests {@link DeleteCommand#fillModifiedData(java.util.Collection, java.util.Collection, java.util.Collection)}
*/
@Test
public void testFillModifiedData() {
ArrayList<OsmPrimitive> modified = new ArrayList<>();
ArrayList<OsmPrimitive> deleted = new ArrayList<>();
ArrayList<OsmPrimitive> added = new ArrayList<>();
new DeleteCommand(Arrays.<OsmPrimitive>asList(testData.existingNode)).fillModifiedData(modified, deleted, added);
// intentionally left empty.
assertArrayEquals(new Object[] {}, modified.toArray());
assertArrayEquals(new Object[] {}, deleted.toArray());
assertArrayEquals(new Object[] {}, added.toArray());
}
/**
* Tests {@link DeleteCommand#getParticipatingPrimitives()}
*/
@Test
public void testGetParticipatingPrimitives() {
DeleteCommand command = new DeleteCommand(Arrays.<OsmPrimitive>asList(testData.existingNode));
assertArrayEquals(new Object[] {testData.existingNode }, command.getParticipatingPrimitives().toArray());
DeleteCommand command2 = new DeleteCommand(
Arrays.<OsmPrimitive>asList(testData.existingNode, testData.existingWay));
assertArrayEquals(new Object[] {testData.existingNode, testData.existingWay},
command2.getParticipatingPrimitives().toArray());
}
/**
* Test {@link DeleteCommand#getDescriptionText()}
*/
@Test
public void testDescription() {
Node node = testData.createNode(100);
node.put("name", "xy");
Way way = testData.createWay(101);
way.put("name", "xy");
Relation relation = testData.createRelation(102);
relation.put("name", "xy");
List<OsmPrimitive> nodeList = Arrays.<OsmPrimitive>asList(node);
assertTrue(new DeleteCommand(nodeList).getDescriptionText().matches("Delete node .*xy.*"));
List<OsmPrimitive> wayList = Arrays.<OsmPrimitive>asList(way);
assertTrue(new DeleteCommand(wayList).getDescriptionText().matches("Delete way .*xy.*"));
List<OsmPrimitive> relationList = Arrays.<OsmPrimitive>asList(relation);
assertTrue(new DeleteCommand(relationList).getDescriptionText().matches("Delete relation .*xy.*"));
List<OsmPrimitive> nodesList = Arrays.<OsmPrimitive>asList(node, testData.createNode(110));
assertTrue(new DeleteCommand(nodesList).getDescriptionText().matches("Delete 2 nodes"));
List<OsmPrimitive> waysList = Arrays.<OsmPrimitive>asList(way, testData.createWay(111));
assertTrue(new DeleteCommand(waysList).getDescriptionText().matches("Delete 2 ways"));
List<OsmPrimitive> relationsList = Arrays.<OsmPrimitive>asList(relation, testData.createRelation(112));
assertTrue(new DeleteCommand(relationsList).getDescriptionText().matches("Delete 2 relations"));
List<OsmPrimitive> mixed = Arrays.<OsmPrimitive>asList(node, way, relation);
assertTrue(new DeleteCommand(mixed).getDescriptionText().matches("Delete 3 objects"));
}
/**
* Unit test of methods {@link DeleteCommand#equals} and {@link DeleteCommand#hashCode}.
*/
@Test
public void testEqualsContract() {
EqualsVerifier.forClass(DeleteCommand.class).usingGetClass()
.withPrefabValues(DataSet.class,
new DataSet(), new DataSet())
.withPrefabValues(User.class,
User.createOsmUser(1, "foo"), User.createOsmUser(2, "bar"))
.withPrefabValues(OsmDataLayer.class,
new OsmDataLayer(new DataSet(), "1", null), new OsmDataLayer(new DataSet(), "2", null))
.suppress(Warning.NONFINAL_FIELDS)
.verify();
}
}