// 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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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.User;
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 SequenceCommand} class.
*/
public class SequenceCommandTest {
/**
* We need prefs for nodes.
*/
@Rule
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
public JOSMTestRules test = new JOSMTestRules().preferences();
private CommandTestDataWithRelation testData;
/**
* Set up the test data.
*/
@Before
public void createTestData() {
testData = new CommandTestDataWithRelation();
}
/**
* Test {@link SequenceCommand#executeCommand()}
*/
@Test
public void testExecute() {
final TestCommand command1 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode));
TestCommand command2 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode2)) {
@Override
public boolean executeCommand() {
assertTrue(command1.executed);
return super.executeCommand();
}
};
SequenceCommand command = new SequenceCommand("seq", Arrays.<Command>asList(command1, command2));
command.executeCommand();
assertTrue(command1.executed);
assertTrue(command2.executed);
}
/**
* Test {@link SequenceCommand#undoCommand()}
*/
@Test
public void testUndo() {
final TestCommand command2 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode2));
TestCommand command1 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode)) {
@Override
public void undoCommand() {
assertFalse(command2.executed);
super.undoCommand();
}
};
SequenceCommand command = new SequenceCommand("seq", Arrays.<Command>asList(command1, command2));
command.executeCommand();
command.undoCommand();
assertFalse(command1.executed);
assertFalse(command2.executed);
command.executeCommand();
assertTrue(command1.executed);
assertTrue(command2.executed);
}
/**
* Test {@link SequenceCommand#executeCommand()} rollback if case of subcommand failure.
*/
@Test
public void testExecuteRollback() {
TestCommand command1 = new TestCommand(null);
FailingCommand command2 = new FailingCommand();
TestCommand command3 = new TestCommand(null);
SequenceCommand command = new SequenceCommand("seq", Arrays.<Command>asList(command1, command2, command3));
assertFalse(command.executeCommand());
assertFalse(command1.executed);
// Don't check command2 executed state as it's possible but not necessary to undo failed commands
assertFalse(command3.executed);
command.undoCommand();
}
/**
* Test {@link SequenceCommand#executeCommand()} with continueOnError = true
*/
@Test
public void testContinueOnErrors() {
TestCommand command1 = new TestCommand(null);
FailingCommand command2 = new FailingCommand();
TestCommand command3 = new TestCommand(null);
SequenceCommand command = new SequenceCommand("seq", Arrays.<Command>asList(command1, command2, command3), true);
assertTrue(command.executeCommand());
assertTrue(command1.executed);
assertTrue(command3.executed);
command.undoCommand();
assertFalse(command1.executed);
// Don't check command2 executed state as it's possible but not necessary to undo failed commands
assertFalse(command3.executed);
}
/**
* Test {@link SequenceCommand#undoCommand()}
*/
@Test
public void testGetLastCommand() {
final TestCommand command1 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode));
final TestCommand command2 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode2));
assertEquals(command2, new SequenceCommand("seq", command1, command2).getLastCommand());
assertNull(new SequenceCommand("seq").getLastCommand());
}
/**
* Tests {@link SequenceCommand#fillModifiedData(java.util.Collection, java.util.Collection, java.util.Collection)}
*/
@Test
public void testFillModifiedData() {
Command command1 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode));
Command command2 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode2));
Command command3 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingWay)) {
@Override
public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
Collection<OsmPrimitive> added) {
deleted.addAll(primitives);
}
};
Command command4 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingRelation)) {
@Override
public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
Collection<OsmPrimitive> added) {
added.addAll(primitives);
}
};
ArrayList<OsmPrimitive> modified = new ArrayList<>();
ArrayList<OsmPrimitive> deleted = new ArrayList<>();
ArrayList<OsmPrimitive> added = new ArrayList<>();
SequenceCommand command = new SequenceCommand("seq", command1, command2, command3, command4);
command.fillModifiedData(modified, deleted, added);
assertArrayEquals(new Object[] {testData.existingNode, testData.existingNode2}, modified.toArray());
assertArrayEquals(new Object[] {testData.existingWay}, deleted.toArray());
assertArrayEquals(new Object[] {testData.existingRelation}, added.toArray());
}
/**
* Tests {@link SequenceCommand#getParticipatingPrimitives()}
*/
@Test
public void testGetParticipatingPrimitives() {
Command command1 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode));
Command command2 = new TestCommand(Arrays.<OsmPrimitive>asList(testData.existingNode2));
SequenceCommand command = new SequenceCommand("seq", command1, command2);
command.executeCommand();
Collection<? extends OsmPrimitive> primitives = command.getParticipatingPrimitives();
assertEquals(2, primitives.size());
assertTrue(primitives.contains(testData.existingNode));
assertTrue(primitives.contains(testData.existingNode2));
}
/**
* Test {@link SequenceCommand#getDescriptionText()}
*/
@Test
public void testDescription() {
assertTrue(new SequenceCommand("test").getDescriptionText().matches("Sequence: test"));
}
/**
* Unit test of methods {@link SequenceCommand#equals} and {@link SequenceCommand#hashCode}.
*/
@Test
public void testEqualsContract() {
EqualsVerifier.forClass(SequenceCommand.class).usingGetClass()
.withPrefabValues(Command.class,
new AddCommand(new Node(1)), new AddCommand(new Node(2)))
.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();
}
private static class TestCommand extends Command {
protected final Collection<? extends OsmPrimitive> primitives;
protected boolean executed;
TestCommand(Collection<? extends OsmPrimitive> primitives) {
super();
this.primitives = primitives;
}
@Override
public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
Collection<OsmPrimitive> added) {
modified.addAll(primitives);
}
@Override
public String getDescriptionText() {
fail("Should not be called");
return "";
}
@Override
public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
return primitives;
}
@Override
public boolean executeCommand() {
assertFalse("Cannot execute twice", executed);
executed = true;
return true;
}
@Override
public void undoCommand() {
assertTrue("Cannot undo without execute", executed);
executed = false;
}
@Override
public String toString() {
return "TestCommand [primitives=" + primitives + "]";
}
}
private static class FailingCommand extends TestCommand {
FailingCommand() {
super(null);
}
@Override
public boolean executeCommand() {
executed = true;
return false;
}
@Override
public void undoCommand() {
assertTrue("Cannot undo without execute", executed);
executed = false;
}
@Override
public String toString() {
return "FailingCommand";
}
}
}