diff options
author | Christian W. Damus | 2015-09-15 15:59:28 +0000 |
---|---|---|
committer | Christian W. Damus | 2015-12-02 20:31:46 +0000 |
commit | 399f3e502bb551969f19b5b63d2842bcb951619a (patch) | |
tree | f6b5176e3fa5a3eee34ff3a94c03056a86bfb6b2 | |
parent | 1b4984078ceba09b18e1447ace561d885d176876 (diff) | |
download | org.eclipse.papyrus-399f3e502bb551969f19b5b63d2842bcb951619a.tar.gz org.eclipse.papyrus-399f3e502bb551969f19b5b63d2842bcb951619a.tar.xz org.eclipse.papyrus-399f3e502bb551969f19b5b63d2842bcb951619a.zip |
Bug 476683: [AOF Sync] Undo/redo tests for diagram synchronization
https://bugs.eclipse.org/bugs/show_bug.cgi?id=476683
Add undo/redo tests for synchronization in diagrams, on the complete matrix of:
* undo and redo
* changes induced by synchronization and installation of mappings
* diagrams visualizing the same elements and diagrams visualizing
elements related by a more complex correspondence relation
3 files changed, 421 insertions, 19 deletions
diff --git a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/AbstractDiagramSyncTest.java b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/AbstractDiagramSyncTest.java index aa2be397811..f0d98e85995 100644 --- a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/AbstractDiagramSyncTest.java +++ b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/AbstractDiagramSyncTest.java @@ -20,11 +20,13 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeThat; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -37,6 +39,7 @@ import javax.inject.Inject; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.common.command.CommandStack; +import org.eclipse.emf.common.command.CommandWrapper; import org.eclipse.emf.common.util.AbstractTreeIterator; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; @@ -81,6 +84,7 @@ import org.eclipse.papyrus.infra.gmfdiag.common.utils.DiagramEditPartsUtil; import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils; import org.eclipse.papyrus.infra.services.edit.service.IElementEditService; import org.eclipse.papyrus.infra.services.edit.utils.ElementTypeUtils; +import org.eclipse.papyrus.infra.tools.util.StreamUtil; import org.eclipse.papyrus.junit.framework.classification.tests.AbstractPapyrusTest; import org.eclipse.papyrus.junit.utils.JUnitUtils; import org.eclipse.papyrus.junit.utils.rules.AbstractHouseKeeperRule.CleanUp; @@ -475,18 +479,13 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { EditPart regionEditPart = requireEditPart(container); EditPart contentsEditPart = getShapeCompartment(regionEditPart); - Command command = findNodeCreationCommand(contentsEditPart, container, + Command createCommand = findNodeCreationCommand(contentsEditPart, container, UMLPackage.Literals.FINAL_STATE, org.eclipse.papyrus.uml.diagram.statemachine.providers.UMLElementTypes::isKnownElementType); - assertThat("No executable command provided to create final state", command, notNullValue()); - execute(command); - - FinalState result = getNewObject(command, FinalState.class); - - execute(SetCommand.create(editor.getEditingDomain(), result, UMLPackage.Literals.NAMED_ELEMENT__NAME, name)); + assertThat("No executable command provided to create final state", createCommand, notNullValue()); - return result; + return create(FinalState.class, createCommand, name); } protected EditPart getShapeCompartment(EditPart parent) { @@ -501,18 +500,13 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { EditPart sourceEditPart = requireEditPart(source); EditPart targetEditPart = requireEditPart(target); - Command command = findConnectionCreationCommand(sourceEditPart, targetEditPart, + Command createCommand = findConnectionCreationCommand(sourceEditPart, targetEditPart, UMLPackage.Literals.TRANSITION, org.eclipse.papyrus.uml.diagram.statemachine.providers.UMLElementTypes::isKnownElementType); - assertThat("No executable command provided to create transition", command, notNullValue()); - execute(command); - - Transition result = getNewObject(command, Transition.class); - - execute(SetCommand.create(editor.getEditingDomain(), result, UMLPackage.Literals.NAMED_ELEMENT__NAME, name)); + assertThat("No executable command provided to create transition", createCommand, notNullValue()); - return result; + return create(Transition.class, createCommand, name); } /** @@ -538,9 +532,11 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { if (object instanceof ENamedElement) { result = ((UMLUtil.getQualifiedName((ENamedElement) object, NamedElement.SEPARATOR))); - } else { + } else if (object != null) { IItemLabelProvider labels = (IItemLabelProvider) adapterFactory.adapt(object, IItemLabelProvider.class); result = (labels == null) ? String.valueOf(object) : labels.getText(object); + } else { + result = String.valueOf(object); } return result; @@ -611,6 +607,11 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { assertThat("View exists for " + label(object), view, nullValue()); } + protected void assumeNoView(EObject object) { + View view = getView(object); + assumeThat("View exists for " + label(object), view, nullValue()); + } + protected Map<EObject, View> getViews(Iterable<? extends EObject> objects) { Map<EObject, View> result = Maps.newHashMap(); @@ -686,6 +687,19 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { assertDetached(Lists.asList(first, second, rest)); } + protected void assumeDetached(EObject element) { + assumeThat("Model must not contain " + label(element), element.eResource(), nullValue()); + } + + protected <E extends NamedElement> E create(Class<E> resultType, Command creationCommand, String name) { + CreateAndConfigureCommand<E> command = new CreateAndConfigureCommand<>(resultType, creationCommand, + newObject -> SetCommand.create(editor.getEditingDomain(), newObject, UMLPackage.Literals.NAMED_ELEMENT__NAME, name)); + + execute(command); + + return command.getNewObject(); + } + // // Nested types // @@ -695,4 +709,65 @@ public abstract class AbstractDiagramSyncTest extends AbstractPapyrusTest { protected @interface NeedsUIEvents { // Empty } + + public class CreateAndConfigureCommand<E extends EObject> extends org.eclipse.emf.common.command.CompoundCommand { + private final Command creationCommand; + private final Class<E> resultType; + private final Function<? super E, org.eclipse.emf.common.command.Command> configureCommandFunction; + + public CreateAndConfigureCommand(Class<E> resultType, Command creationCommand, Function<? super E, org.eclipse.emf.common.command.Command> configureCommandFunction) { + super(org.eclipse.emf.common.command.CompoundCommand.LAST_COMMAND_ALL, creationCommand.getLabel()); + + this.creationCommand = creationCommand; + this.resultType = resultType; + this.configureCommandFunction = configureCommandFunction; + + append(GEFtoEMFCommandWrapper.wrap(creationCommand)); + } + + public E getNewObject() { + return StreamUtil.select(getResult().stream(), resultType).findFirst().orElse(null); + } + + @Override + protected boolean prepare() { + boolean result = super.prepare(); + + if (result) { + append(new CommandWrapper() { + + E newObject; + + @Override + public boolean canExecute() { + return true; + } + + @Override + public void execute() { + assertThat(prepare(), is(true)); + super.execute(); + } + + @Override + protected org.eclipse.emf.common.command.Command createCommand() { + newObject = AbstractDiagramSyncTest.this.getNewObject(creationCommand, resultType); + + return createConfigureCommand(newObject); + } + + @Override + public Collection<?> getResult() { + return Collections.singletonList(newObject); + } + }); + } + + return result; + } + + protected org.eclipse.emf.common.command.Command createConfigureCommand(E newObject) { + return configureCommandFunction.apply(newObject); + } + } } diff --git a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/CapsuleDiagramMappingTest.java b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/CapsuleDiagramMappingTest.java index 61f8434c061..774fa3f8eab 100644 --- a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/CapsuleDiagramMappingTest.java +++ b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/CapsuleDiagramMappingTest.java @@ -121,6 +121,195 @@ public class CapsuleDiagramMappingTest extends AbstractDiagramSyncTest { assertThat(otherBounds.getHeight(), is(newH)); } + @Test + public void undoSynchronizedChange() { + // First, grab the original location of the doppelganger + editor.activateDiagram(DOPPELGANGERS); + Shape other = requireShape(capsule1); + Bounds otherBounds = (Bounds) other.getLayoutConstraint(); + final int oldX = otherBounds.getX(); + final int oldY = otherBounds.getY(); + + // Move a capsule in the original diagram + editor.activateDiagram(CAPSULES); + Shape original = requireShape(capsule1); + + Bounds bounds = (Bounds) original.getLayoutConstraint(); + int newX = bounds.getX() + 30; + int newY = bounds.getY() + 30; + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // Assume the new location in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + otherBounds = (Bounds) other.getLayoutConstraint(); + assumeThat(otherBounds.getX(), is(newX)); + assumeThat(otherBounds.getY(), is(newY)); + + // Undo the move + undo(); + + // Verify both diagrams + otherBounds = (Bounds) other.getLayoutConstraint(); + assertThat(otherBounds.getX(), is(oldX)); + assertThat(otherBounds.getY(), is(oldY)); + Bounds originalBounds = (Bounds) original.getLayoutConstraint(); + assertThat(originalBounds.getX(), not(newX)); + assertThat(originalBounds.getY(), not(newY)); + } + + @Test + public void redoSynchronizedChange() { + // First, grab the original location of the doppelganger + editor.activateDiagram(DOPPELGANGERS); + Shape other = requireShape(capsule1); + Bounds otherBounds = (Bounds) other.getLayoutConstraint(); + final int oldX = otherBounds.getX(); + final int oldY = otherBounds.getY(); + + // Move a capsule in the original diagram + editor.activateDiagram(CAPSULES); + Shape original = requireShape(capsule1); + + Bounds bounds = (Bounds) original.getLayoutConstraint(); + int newX = bounds.getX() + 30; + int newY = bounds.getY() + 30; + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // Assume the new location in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + otherBounds = (Bounds) other.getLayoutConstraint(); + assumeThat(otherBounds.getX(), is(newX)); + assumeThat(otherBounds.getY(), is(newY)); + + // Undo the move + undo(); + + // Assume both diagrams (verified by another test case) + otherBounds = (Bounds) other.getLayoutConstraint(); + assumeThat(otherBounds.getX(), is(oldX)); + assumeThat(otherBounds.getY(), is(oldY)); + Bounds originalBounds = (Bounds) original.getLayoutConstraint(); + assumeThat(originalBounds.getX(), not(newX)); + assumeThat(originalBounds.getY(), not(newY)); + + // Redo the move + redo(); + + editor.activateDiagram(DOPPELGANGERS); + otherBounds = (Bounds) other.getLayoutConstraint(); + assertThat(otherBounds.getX(), is(newX)); + assertThat(otherBounds.getY(), is(newY)); + } + + @Test + public void undoSynchronization() { + // First, grab the original location of the doppelganger + editor.activateDiagram(DOPPELGANGERS); + Shape other = requireShape(capsule1); + Bounds otherBounds = (Bounds) other.getLayoutConstraint(); + final int oldX = otherBounds.getX(); + final int oldY = otherBounds.getY(); + + // Move a capsule in the original diagram + editor.activateDiagram(CAPSULES); + Shape original = requireShape(capsule1); + + Bounds bounds = (Bounds) original.getLayoutConstraint(); + int newX = bounds.getX() + 30; + int newY = bounds.getY() + 30; + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // Assume the new location in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(otherBounds.getX(), is(newX)); + assumeThat(otherBounds.getY(), is(newY)); + + // Undo the move + undo(); + + // Assume the old location in the other (verified by another test case) + otherBounds = (Bounds) other.getLayoutConstraint(); + assumeThat(otherBounds.getX(), is(oldX)); + assumeThat(otherBounds.getY(), is(oldY)); + + // Undo the synchronization set-up + undo(); + + // Repeat the move + editor.activateDiagram(CAPSULES); + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // The doppelganger is no longer synchronized + otherBounds = (Bounds) other.getLayoutConstraint(); + assertThat(otherBounds.getX(), is(oldX)); + assertThat(otherBounds.getY(), is(oldY)); + } + + @Test + public void redoSynchronization() { + // First, grab the original location of the doppelganger + editor.activateDiagram(DOPPELGANGERS); + Shape other = requireShape(capsule1); + Bounds otherBounds = (Bounds) other.getLayoutConstraint(); + final int oldX = otherBounds.getX(); + final int oldY = otherBounds.getY(); + + // Move a capsule in the original diagram + editor.activateDiagram(CAPSULES); + Shape original = requireShape(capsule1); + + Bounds bounds = (Bounds) original.getLayoutConstraint(); + int newX = bounds.getX() + 30; + int newY = bounds.getY() + 30; + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // Assume the new location in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(otherBounds.getX(), is(newX)); + assumeThat(otherBounds.getY(), is(newY)); + + // Undo the move + undo(); + + // Assume the old location in the other (verified by another test case) + otherBounds = (Bounds) other.getLayoutConstraint(); + assumeThat(otherBounds.getX(), is(oldX)); + assumeThat(otherBounds.getY(), is(oldY)); + + // Undo the synchronization set-up + undo(); + + // Redo it + redo(); + + // Repeat the move + editor.activateDiagram(CAPSULES); + execute(() -> { + bounds.setX(newX); + bounds.setY(newY); + }); + + // The doppelganger is once again synchronized + otherBounds = (Bounds) other.getLayoutConstraint(); + assertThat(otherBounds.getX(), is(newX)); + assertThat(otherBounds.getY(), is(newY)); + } + // // Test framework // diff --git a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/StateMachineDiagramMappingTest.java b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/StateMachineDiagramMappingTest.java index 97ac22ee210..c44a5493073 100644 --- a/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/StateMachineDiagramMappingTest.java +++ b/tests/junit/extraplugins/aof/examples/org.eclipse.papyrus.aof.sync.examples.uml.ui.tests/src/org/eclipse/papyrus/aof/sync/examples/uml/ui/tests/StateMachineDiagramMappingTest.java @@ -17,6 +17,7 @@ import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assume.assumeThat; @@ -25,6 +26,7 @@ import java.util.Arrays; import org.eclipse.gef.ui.views.palette.PaletteView; import org.eclipse.gmf.runtime.notation.Bounds; import org.eclipse.gmf.runtime.notation.Shape; +import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.aof.sync.examples.uml.ui.internal.handlers.SynchronizeCapsulesHandler; import org.eclipse.papyrus.junit.utils.rules.ActiveDiagram; import org.eclipse.papyrus.junit.utils.rules.PluginResource; @@ -138,6 +140,139 @@ public class StateMachineDiagramMappingTest extends AbstractDiagramSyncTest { assertThat(otherBounds.getHeight(), is(newH)); } + @Test + public void undoSynchronizedChange() { + // Add a new shape in the original diagram + editor.activateDiagram(LIFECYCLE); + FinalState capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // Assume the new shape in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + FinalState capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + View capsule2EndView = getView(capsule2End); + assumeThat(capsule2EndView, notNullValue()); + + // Undo the creation + undo(); + + // Verify both diagrams + assertDetached(capsule2EndView); + assertDetached(capsule2End); + editor.activateDiagram(LIFECYCLE); + assertDetached(capsule1End); + assertNoView(capsule1End); + } + + @Test + public void redoSynchronizedChange() { + // Add a new shape in the original diagram + editor.activateDiagram(LIFECYCLE); + FinalState capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // Assume the new shape in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + FinalState capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + View capsule2EndView = getView(capsule2End); + assumeThat(capsule2EndView, notNullValue()); + + // Undo the creation + undo(); + + // Assume both diagrams (verified by another test case) + assumeDetached(capsule2EndView); + assumeDetached(capsule2End); + editor.activateDiagram(LIFECYCLE); + assumeDetached(capsule1End); + assumeNoView(capsule1End); + + // Redo the creation + redo(); + + editor.activateDiagram(DOPPELGANGERS); + assertThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + requireView(capsule2End); + } + + @Test + public void undoSynchronization() { + // Add a new shape in the original diagram + editor.activateDiagram(LIFECYCLE); + FinalState capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // Assume the new shape in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + FinalState capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + View capsule2EndView = getView(capsule2End); + assumeThat(capsule2EndView, notNullValue()); + + // Undo the creation + undo(); + + // Assume both diagrams (verified by another test case) + assumeThat(capsule2EndView.eResource(), nullValue()); + assumeThat(capsule2End.eResource(), nullValue()); + editor.activateDiagram(LIFECYCLE); + assumeThat(capsule1End.eResource(), nullValue()); + assumeThat(getView(capsule1End), nullValue()); + + // Undo the synchronization set-up + undo(); + + // Repeat the creation + editor.activateDiagram(LIFECYCLE); + capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // The doppelganger is no longer synchronized (but the state machine semantics is!) + editor.activateDiagram(DOPPELGANGERS); + assertThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + assertNoView(capsule2End); + } + + @Test + public void redoSynchronization() { + // Add a new shape in the original diagram + editor.activateDiagram(LIFECYCLE); + FinalState capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // Assume the new shape in the other (verified by another test case) + editor.activateDiagram(DOPPELGANGERS); + assumeThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + FinalState capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + View capsule2EndView = getView(capsule2End); + assumeThat(capsule2EndView, notNullValue()); + + // Undo the creation + undo(); + + // Assume both diagrams (verified by another test case) + assumeThat(capsule2EndView.eResource(), nullValue()); + assumeThat(capsule2End.eResource(), nullValue()); + editor.activateDiagram(LIFECYCLE); + assumeThat(capsule1End.eResource(), nullValue()); + assumeThat(getView(capsule1End), nullValue()); + + // Undo the synchronization set-up + undo(); + + // Redo it + redo(); + + // Repeat the creation + editor.activateDiagram(LIFECYCLE); + capsule1End = createFinalStateWithView(capsule1Region, "end"); + + // The doppelganger is once again synchronized + editor.activateDiagram(DOPPELGANGERS); + assertThat(capsule2Region.getSubvertex("end"), instanceOf(FinalState.class)); + capsule2End = (FinalState) capsule2Region.getSubvertex("end"); + requireView(capsule2End); + } + // // Test framework // @@ -147,8 +282,8 @@ public class StateMachineDiagramMappingTest extends AbstractDiagramSyncTest { editor.activateDiagram(DOPPELGANGERS); } - @Before - public void gatherElements() { + @Override + public void synchronizeActiveDiagram() { Class capsule1 = (Class) editor.getModel().getOwnedType("Capsule1"); capsule1Behavior = (StateMachine) capsule1.getClassifierBehavior(); capsule1Region = capsule1Behavior.getRegions().get(0); @@ -163,5 +298,8 @@ public class StateMachineDiagramMappingTest extends AbstractDiagramSyncTest { // Synchronize the capsules for creation of new elements new SynchronizeCapsulesHandler().synchronize(Arrays.asList(capsule1, capsule2)); + + // Synchronize the diagram + super.synchronizeActiveDiagram(); } } |