blob: 1245290c0ce32475e31518b4d8149357d8fb8bba [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 Obeo.
* All rights reserved.
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.tests.unit.diagram.layout;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.rulers.RulerProvider;
import org.eclipse.gmf.runtime.diagram.ui.actions.ActionIds;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.internal.properties.WorkspaceViewerProperties;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramGraphicalViewer;
import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart;
import org.eclipse.gmf.runtime.diagram.ui.requests.ArrangeRequest;
import org.eclipse.gmf.runtime.notation.Diagram;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.sirius.diagram.DDiagram;
import org.eclipse.sirius.diagram.DDiagramElement;
import org.eclipse.sirius.diagram.DNode;
import org.eclipse.sirius.diagram.tools.api.preferences.SiriusDiagramPreferencesKeys;
import org.eclipse.sirius.diagram.ui.internal.edit.parts.DNodeEditPart;
import org.eclipse.sirius.diagram.ui.tools.api.editor.DDiagramEditor;
import org.eclipse.sirius.tests.support.api.SiriusDiagramTestCase;
import org.eclipse.sirius.tests.support.api.TestsUtil;
import org.eclipse.sirius.ui.business.api.dialect.DialectUIManager;
import org.eclipse.sirius.ui.business.api.session.SessionUIManager;
import org.eclipse.sirius.viewpoint.DRepresentation;
import org.eclipse.ui.IEditorPart;
/**
* Tests to realize some verification of arrange result with basic ELK layouts.
*
* @author lredor
*/
@SuppressWarnings("restriction")
public class SimpleELKLayoutTest extends SiriusDiagramTestCase {
private static final String SEMANTIC_MODEL_PATH = "/org.eclipse.sirius.tests.junit/data/unit/layout/withELK/My.ecore";
private static final String VSM_PATH = "/org.eclipse.sirius.tests.junit/data/unit/layout/withELK/My.odesign";
private static final String REPRESENTATIONS_MODEL_PATH = "/org.eclipse.sirius.tests.junit/data/unit/layout/withELK/representations.aird";
private DDiagram diagram;
private IDiagramWorkbenchPart editorPart;
private boolean initialSnapToGridValue;
private double initialGridSpacingValue;
private int initialRulerUnitValue;
@Override
protected void setUp() throws Exception {
super.setUp();
genericSetUp(SEMANTIC_MODEL_PATH, VSM_PATH, REPRESENTATIONS_MODEL_PATH);
SessionUIManager.INSTANCE.createUISession(session);
}
@Override
protected void tearDown() throws Exception {
if (editorPart != null) {
SessionUIManager.INSTANCE.getUISession(session).closeEditors(false, Collections.singleton((DDiagramEditor) editorPart));
}
TestsUtil.emptyEventsFromUIThread();
super.tearDown();
}
/**
* Makes sure that activating the Snap to grid has no effect on the ELK layout.
*/
public void testArrangeWithSnapToWithELK() {
// We create a new diagram
EObject root = session.getSemanticResources().stream().findFirst().get().getContents().get(0);
DRepresentation representation = createRepresentation("SimpleDiagram", root);
// We open the editor and set the preferences for the test.
IEditorPart newEditorPart = DialectUIManager.INSTANCE.openEditor(session, representation, new NullProgressMonitor());
IPreferenceStore workspaceViewerPreferenceStore = ((DiagramGraphicalViewer) ((DiagramEditor) newEditorPart).getDiagramGraphicalViewer()).getWorkspaceViewerPreferenceStore();
changeSnapToPreferences(workspaceViewerPreferenceStore);
try {
arrangeAll((DiagramEditor) newEditorPart);
TestsUtil.synchronizationWithUIThread();
// We keep the figures bounds after the arrange all without the Snap to grid.
Map<DNode, Rectangle> DNodes2Bounds = computeNodesBounds(representation);
// We activate the Snap to Grid.
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.SNAPTOGRID, true);
// We perform the arrange all again.
arrangeAll((DiagramEditor) newEditorPart);
TestsUtil.synchronizationWithUIThread();
// We check that the layout did not change
Map<DNode, Rectangle> afterDNodes2Bounds = computeNodesBounds(representation);
afterDNodes2Bounds.forEach((dNode, rect) -> {
assertEquals("The layout should not change after having activated the snap to grid with ELK algorithm.", DNodes2Bounds.get(dNode), rect);
});
} finally {
restoreInitilaPreferences(workspaceViewerPreferenceStore);
}
}
/**
* Makes sure that activating the Snap to grid has effect on diagram without ELK algorithm
*/
public void testArrangeWithSnapToWithoutELK() {
// We create a new diagram
EObject root = session.getSemanticResources().stream().findFirst().get().getContents().get(0);
DRepresentation representation = createRepresentation("SimpleDiagramNoELK", root);
// We open the editor and set the preferences for the test.
IEditorPart newEditorPart = DialectUIManager.INSTANCE.openEditor(session, representation, new NullProgressMonitor());
IPreferenceStore workspaceViewerPreferenceStore = ((DiagramGraphicalViewer) ((DiagramEditor) newEditorPart).getDiagramGraphicalViewer()).getWorkspaceViewerPreferenceStore();
changeSnapToPreferences(workspaceViewerPreferenceStore);
try {
arrangeAll((DiagramEditor) newEditorPart);
TestsUtil.synchronizationWithUIThread();
// We keep the figures bounds after the arrange all without the Snap to grid.
Map<DNode, Rectangle> dNodes2Bounds = computeNodesBounds(representation);
// We activate the Snap to Grid.
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.SNAPTOGRID, true);
// We perform the arrange all again.
arrangeAll((DiagramEditor) newEditorPart);
TestsUtil.synchronizationWithUIThread();
// We check that the layout has changed
Map<DNode, Rectangle> afterDNodes2Bounds = computeNodesBounds(representation);
boolean atLeastOneElementHasChanged = false;
for (Iterator<Entry<DNode, Rectangle>> iterator = afterDNodes2Bounds.entrySet().iterator(); iterator.hasNext();) {
Entry<DNode, Rectangle> dNodeToRect = iterator.next();
if (!dNodeToRect.getValue().equals(dNodes2Bounds.get(dNodeToRect.getKey()))) {
atLeastOneElementHasChanged = true;
break;
}
}
assertTrue("The activation of the Snap to grid should have changed the layout", atLeastOneElementHasChanged);
} finally {
restoreInitilaPreferences(workspaceViewerPreferenceStore);
}
}
/**
* Check that the size of a Node under the root (under the diagram), is sufficiently large to read the label.
*/
public void testSizeOfRootNode() {
openDiagram("simpleDiagram");
Optional<DDiagramElement> c1Dde = diagram.getDiagramElements().stream().filter(dde -> dde.getName().equals("MyClass1")).findFirst();
assertTrue("The diagram should have a node named \"MyClass1\".", c1Dde.isPresent());
IGraphicalEditPart c1EditPart = getEditPart(c1Dde.get());
assertTrue("The node for \"MyClass1\" should be a DNodeEditPart.", c1EditPart instanceof DNodeEditPart);
Dimension minimumTextSize = ((DNodeEditPart) c1EditPart).getNodeLabel().getPreferredSize();
// Launch an arrange all
arrangeAll((DiagramEditor) editorPart);
// Check that the new Size is bigger than the minimum size to display the text
Dimension c1Dimension = c1EditPart.getFigure().getSize();
assertTrue("The size of \"MyClass1\" should be sufficiently large to read the label (minimul label size is " + minimumTextSize + " and node size is " + c1Dimension + ".",
c1Dimension.contains(minimumTextSize));
}
/**
* Check that the Note is moved with an arrange using ELK, when the preference "Move unlinked notes during layout"
* is enabled.
*
* @throws Exception
* in case of problem
*/
public void testNoteLayoutWithPrefTrue() throws Exception {
testNoteLayoutAccordingToPref(true);
}
/**
* Check that the Note is not moved with an arrange using ELK, when the preference "Move unlinked notes during
* layout" is disabled.
*
* @throws Exception
* in case of problem
*/
public void _testNoteLayoutWithPrefFalse() throws Exception {
testNoteLayoutAccordingToPref(false);
}
protected void openDiagram(String diagramName) {
diagram = (DDiagram) getRepresentationsByName(diagramName).toArray()[0];
editorPart = (IDiagramWorkbenchPart) DialectUIManager.INSTANCE.openEditor(session, diagram, new NullProgressMonitor());
TestsUtil.synchronizationWithUIThread();
}
/**
* Check that the Note is moved or not moved with an arrange using ELK, according to the value of the preference
* "Move unlinked notes during layout".
*
* @throws Exception
* in case of problem
*/
protected void testNoteLayoutAccordingToPref(boolean moveNoteDuringLayout) throws Exception {
openDiagram("simpleDiagramWithNote");
// Get the GMF node corresponding to the Note
Node noteNode = getNote(editorPart.getDiagram());
assertTrue("One note should exist on the diagram", noteNode != null);
// Get the corresponding edit part
Map editPartRegistry = editorPart.getDiagramEditPart().getRoot().getViewer().getEditPartRegistry();
final IGraphicalEditPart noteEditPart = (IGraphicalEditPart) editPartRegistry.get(noteNode);
// Get the initial note bounds (to be compare to the new bounds after the layout)
final Rectangle initialNoteBounds = noteEditPart.getFigure().getBounds().getCopy();
changeDiagramPreference(SiriusDiagramPreferencesKeys.PREF_MOVE_NOTES_DURING_LATOUT.name(), moveNoteDuringLayout);
// Launch an arrange all
arrangeAll((DiagramEditor) editorPart);
// Compare the new location with the expected result
Rectangle currentNoteBounds = noteEditPart.getFigure().getBounds().getCopy();
if (moveNoteDuringLayout) {
assertFalse("The Note should be moved during the arrange.", initialNoteBounds.getLocation().equals(currentNoteBounds.getLocation()));
} else {
assertTrue("The Note should not be moved during the arrange.", initialNoteBounds.getLocation().equals(currentNoteBounds.getLocation()));
Optional<DDiagramElement> c4Dde = diagram.getDiagramElements().stream().filter(dde -> dde.getName().equals("MyClass4")).findFirst();
if (c4Dde.isPresent()) {
IGraphicalEditPart c4EditPart = getEditPart(c4Dde.get());
Rectangle c4Bounds = c4EditPart.getFigure().getBounds().getCopy();
assertTrue("As the Note is not moved, it is expected to overlap \"MyClass4\" node.", currentNoteBounds.intersects(c4Bounds));
} else {
fail("The diagram should have a node named \"MyClass4\".");
}
}
}
private Node getNote(Diagram gmfDiagram) {
return getSpecificGmfNode(gmfDiagram, "Note");
}
private Node getSpecificGmfNode(Diagram gmfDiagram, String id) {
Node specificNode = null;
for (Iterator<Object> iterator = gmfDiagram.getChildren().iterator(); iterator.hasNext() && specificNode == null;) {
Object node = iterator.next();
if (node instanceof Node) {
if (((Node) node).getType().equals(id)) {
specificNode = (Node) node;
}
}
}
return specificNode;
}
private void restoreInitilaPreferences(IPreferenceStore workspaceViewerPreferenceStore) {
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.SNAPTOGRID, initialSnapToGridValue);
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.GRIDSPACING, initialGridSpacingValue);
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.RULERUNIT, initialRulerUnitValue);
}
private void changeSnapToPreferences(IPreferenceStore workspaceViewerPreferenceStore) {
initialSnapToGridValue = workspaceViewerPreferenceStore.getBoolean(WorkspaceViewerProperties.SNAPTOGRID);
initialGridSpacingValue = workspaceViewerPreferenceStore.getDouble(WorkspaceViewerProperties.GRIDSPACING);
initialRulerUnitValue = workspaceViewerPreferenceStore.getInt(WorkspaceViewerProperties.RULERUNIT);
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.SNAPTOGRID, false);
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.GRIDSPACING, 100.0);
workspaceViewerPreferenceStore.setValue(WorkspaceViewerProperties.RULERUNIT, RulerProvider.UNIT_PIXELS);
}
private Map<DNode, Rectangle> computeNodesBounds(DRepresentation representation) {
Map<DNode, Rectangle> dNodes2Bounds = new HashMap<>();
((DDiagram) representation).getNodes().stream().forEach(dNode -> {
IGraphicalEditPart editPart = getEditPart(dNode);
dNodes2Bounds.put(dNode, editPart.getFigure().getBounds().getCopy());
});
return dNodes2Bounds;
}
private void arrangeAll(final DiagramEditor editorPart) {
ArrangeRequest arrangeRequest = new ArrangeRequest(ActionIds.ACTION_ARRANGE_ALL);
arrangeRequest.setPartsToArrange(Collections.singletonList(editorPart));
editorPart.getDiagramEditPart().performRequest(arrangeRequest);
TestsUtil.synchronizationWithUIThread();
}
}