/****************************************************************************** * Copyright (c) 2002, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.tests.runtime.diagram.ui; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import junit.framework.TestCase; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Point; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.transaction.RunnableWithResult; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.Disposable; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.Request; import org.eclipse.gef.RootEditPart; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CommandStack; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.requests.ReconnectRequest; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.common.core.util.StringStatics; import org.eclipse.gmf.runtime.common.ui.action.IDisposableAction; import org.eclipse.gmf.runtime.common.ui.action.actions.global.GlobalActionManager; import org.eclipse.gmf.runtime.common.ui.action.global.GlobalActionId; import org.eclipse.gmf.runtime.diagram.ui.actions.internal.SelectAllAction; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; import org.eclipse.gmf.runtime.diagram.ui.requests.ChangePropertyValueRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.RefreshConnectionsRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.gmf.tests.runtime.diagram.ui.util.DiagramState; import org.eclipse.gmf.tests.runtime.diagram.ui.util.IPresentationTestFixture; import org.eclipse.gmf.tests.runtime.diagram.ui.util.ITestActionCallback; import org.eclipse.gmf.tests.runtime.diagram.ui.util.ITestCommandCallback; import org.eclipse.jface.action.IAction; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; /** * @author choang * * The abstract base class should be used by any tests that we write for the shapes team * It provides a framework for which the tests will run. * * It contains implementation of some ulitiy helper methods also. * * what's left for you to do? * <p>1. Implement the following abstract methods * <p>a. createConnectorViews - which setups up the diagram with the common shapes and connectors that will be used for your tests * <p>b. setDefaultDiagramExt - sets the diagram extension type which will be used for the tests. Based on the ext given * the diagram manager will use that as a hint to determine which DiagramEditor class to use to manuipulate the diagram. * <p>c. public static suite() - to return the Test that will be our Action Menus will run. Note this is not defined as an abstract * method here because it needs to be a static method in your test class. * d. setTestFixtureLogic() - which sets the <code>org.eclipse.gmf.tests.runtime.diagram.ui.util.IPresentataionTestFixtureLogic</code> class * that will be responsible for creating the fixture(i.e test data) for this test. * <p>2. Add your tests methods * You need to name your tests method like test*. The Junit framework will run all the methods that start with test*. For each test * in you class the Junit framework will first run the setup(), then your testName1() method and the tearDown(). * * */ public abstract class AbstractTestBase extends TestCase { protected IPresentationTestFixture testFixture = null; /** Verbose system property. */ public static final String SYSPROP_VERBOSE = "presentation.test.verbose";//$NON-NLS-1$ /** verbose flag. */ private static boolean _verbose = Boolean.getBoolean(SYSPROP_VERBOSE); /** * Constructor for AbstractTestBase. * @param TestName */ public AbstractTestBase(String arg0) { super(arg0); setTestFixture(); } /** * Enable verbose mode. If enabled, {@link junit.framework.Assert#fail(java.lang.String)} * will print the supplied string; otherwise the string is ignored. * * Verbose mode can also be enabled using the {@link #SYSPROP_VERBOSE} system property. * @param enabled boolean flag */ protected final void enableVerbose( boolean enabled ) { _verbose = enabled; } /** Return the verbose mode. */ public final boolean isVerbose() { return _verbose; } /** Calls <code>System.out.println(msg)</code> if in verbose mode. */ public static final void println( Object msg ) { if ( _verbose ) { System.out.println(msg); } } /** Calls <code>System.out.print(msg)</code> if in verbose mode. */ public static final void print( Object msg ) { if ( _verbose ) { System.out.print(msg); } } /** * Method getCommandStack. * @return CommandStack Command stack for the diagram editor */ protected CommandStack getCommandStack() { return getTestFixture().getCommandStack(); } /** * Method setTestFixtureLogic. * * Sets the fixture logic for the tests. A fixture is the set of "data" that the test will run against * Typically many tests will use the same fixture. * */ protected abstract void setTestFixture(); protected IPresentationTestFixture getTestFixture() { return testFixture; } protected IDiagramWorkbenchPart getDiagramWorkbenchPart() { return getTestFixture().getDiagramWorkbenchPart(); } protected IWorkbenchPage getWorkbenchPage() { return getDiagramWorkbenchPart().getSite().getPage(); } protected DiagramEditPart getDiagramEditPart() { return getTestFixture().getDiagramEditPart(); } protected Diagram getDiagram() { return getTestFixture().getDiagram(); } protected void saveDiagram() { if (getDiagramWorkbenchPart() instanceof IEditorPart) { IWorkbenchPage page = getDiagramWorkbenchPart().getSite().getPage(); page.saveEditor((IEditorPart) getDiagramWorkbenchPart(), false); flushEventQueue(); } } protected boolean isDirty() { if (getDiagramWorkbenchPart() instanceof IEditorPart) { return ((IEditorPart) getDiagramWorkbenchPart()).isDirty(); } return false; } protected DiagramState getDiagramState() { try { return (DiagramState) TransactionUtil .getEditingDomain(getDiagram()).runExclusive( new RunnableWithResult.Impl() { public void run() { setResult(new DiagramState(getDiagramEditPart())); } }); } catch (InterruptedException e) { e.printStackTrace(); assertTrue(false); } return null; } /** * Description: Will execute the <code>Command</code> and then the <code>ITestCommandCallBack</code>, which * has the logic to verify that the command executed successful. * <p>The command is executed within an UndoInterval and WriteAction model operation. * @throws <AssertFailError> if the command did not run successfully * @author choang */ protected void testCommand( final ICommand command, final ITestCommandCallback callback) { testCommand(new ICommandProxy(command), callback); } /** * Description: Will execute the <code>Command</code> and then the <code>ITestCommandCallBack</code>, which * has the logic to verify that the command executed successful. * <p>The command is executed within an UndoInterval and WriteAction model operation. * @throws <AssertFailError> if the command did not run successfully * @author choang */ protected void testCommand( final Command command, final ITestCommandCallback callback) { assertNotNull(command); // Had to wrap each command in separate model operations // as if we didn't we got some weird behavior in some of the tests // such as the ConnectorTests#testSelfConnections where we get a null pointer // exception. final DiagramState state1 = getDiagramState(); getCommandStack().execute(command); flushEventQueue(); try { TransactionUtil.getEditingDomain(getDiagram()).runExclusive( new Runnable() { public void run() { callback.onCommandExecution(); } }); } catch (InterruptedException e) { e.printStackTrace(); assertTrue(false); } DiagramState state2 = getDiagramState(); // checking if the command stack is in an undoable state first // not that selfConnections and deleteConnections tests are // failing .. if i do a check via command.canUndo() instead of // using getCommandSTack().canUndo() // which suggest that something is out of synch between the command // and the command stack .. need to look into it later. if (getCommandStack().canUndo()) { getCommandStack().undo(); flushEventQueue(); assertTrue(state1.equals(getDiagramState())); getCommandStack().redo(); flushEventQueue(); } assertTrue(state2.equals(getDiagramState())); } /** * Description: Will execute the <code>Action</code> and then the <code>ITestCommandCallBack</code>, which * has the logic to verify that the command executed successful. * This method will test if the action implements the Disposable interface from GEF * If it does it will call the dispose() method on the action. Callers should not * call it themselves * @throws <AssertFailError> if the command did not run successfully * */ protected void testAction(IAction action, ITestActionCallback callback) { flushEventQueue(); assertTrue(action.isEnabled()); action.run(); flushEventQueue(); if (action instanceof Disposable) ((Disposable) action).dispose(); if (callback != null) callback.onRunExecution(); } /** * Description: Will execute the <code>Action</code> and then the <code>ITestCommandCallBack</code>, which * has the logic to verify that the command executed successful. * This method will test if the action implements the IDisposableAction interface from common.ui * If it does it will first set active the diagrameditorpart of the diagram and then call the init() method before running the action. At the end, * it will call the dispose() method on the action. Callers should not * call these two methods themselves themselves * @throws <AssertFailError> if the command did not run successfully * */ protected void testAction(IDisposableAction action, ITestActionCallback callback) { getWorkbenchPage().activate(getDiagramWorkbenchPart()); action.init(); if( action.isEnabled() ) { action.run(); flushEventQueue(); } action.dispose(); if (callback != null) callback.onRunExecution(); } /** * Does the same as <code>testAction</code> but also does an undo and * redo afterwards and compares the diagram state. * * @param action * @param callback */ protected void testActionAndUndoRedo(IDisposableAction action, ITestActionCallback callback) { final DiagramState state1 = getDiagramState(); getWorkbenchPage().activate(getDiagramWorkbenchPart()); action.init(); if( action.isEnabled() ) { action.run(); flushEventQueue(); } action.dispose(); if (callback != null) callback.onRunExecution(); DiagramState state2 = getDiagramState(); assertTrue("testActionAndUndoRedo: Action cannot be undone.", getCommandStack().canUndo()); //$NON-NLS-1$ getCommandStack().undo(); assertTrue("diagram state different after undo of action", state1.equals(getDiagramState())); //$NON-NLS-1$ getCommandStack().redo(); assertTrue("diagram state different after redo of action", state2.equals(getDiagramState())); //$NON-NLS-1$ } /** * Method testProperty. * Generic method for testing a property change in a view. * * @param view IView to set the property value in * @param property String ID of the property to test * @param expectedValue Object that is the value of the property to test */ protected void testProperty( final View view, final String property, final Object expectedValue) { DiagramEditPart diagramEP = getDiagramEditPart(); assertNotNull( "The DiagramEditPart is null", diagramEP ); //$NON-NLS-1$ RootEditPart rootEP = diagramEP.getRoot(); assertNotNull( "The RootEditPart is null", rootEP ); //$NON-NLS-1$ EditPartViewer viewer = rootEP.getViewer(); assertNotNull( "The EditPartViewer is null", viewer ); //$NON-NLS-1$ Map epRegistry = viewer.getEditPartRegistry(); assertNotNull( "The EditPartRegistery is null", epRegistry ); //$NON-NLS-1$ final IGraphicalEditPart ep = (IGraphicalEditPart) epRegistry.get(view); assertNotNull( "Couldn't find the GraphicalEditPart in the Registery", ep ); //$NON-NLS-1$ Request request = new ChangePropertyValueRequest( StringStatics.BLANK, property, expectedValue ); Command cmd = ep.getCommand( request ); testCommand(cmd, new ITestCommandCallback() { public void onCommandExecution() { assertEquals( expectedValue, ep.getStructuralFeatureValue((EStructuralFeature)PackageUtil.getElement(property)) ); } }); } /** * @see TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); getTestFixture().setup(); } /** * Clears the display's event queue. * Same as calling <code>getTestFixture().flushEventQueue()</code> */ protected void flushEventQueue() { getTestFixture().flushEventQueue(); } /** Same as calling <code>getTestFixture().tearDown()</code>. */ protected void tearDown() throws Exception { flushEventQueue(); getTestFixture().tearDown(); } /** * Creates a new shape view as a child of the diagram at the given location * @param editor * @param semanticElement * @param location * @return IShapeView * @deprecated use createShapeView(IDiagramWorkbenchPart,Eobject,Point) */ /* protected IShapeView createShapeView( IDiagramWorkbenchPart editor, IElement semanticElement, Point location) { CompoundCommand cc = new CompoundCommand(); CreateViewRequest request = new CreateViewRequest(semanticElement); request.setLocation(location); cc.add(editor.getDiagramEditPart().getCommand(request)); RefreshConnectorsRequest rcRequest = new RefreshConnectorsRequest(request.getNewObject()); cc.add( getDiagramWorkbenchPart().getDiagramEditPart().getCommand( rcRequest)); getCommandStack().execute(cc); return (IShapeView) ((IAdaptable) (request.getNewObject()).get(0)).getAdapter( IShapeView.class); } */ /** * Creates a new shape view as a child of the diagram at the given location * @param editor * @param semanticElement * @param location * @return IShapeView */ protected View createShapeView( DiagramEditPart diagramEP, EObject semanticElement, Point location) { CompoundCommand cc = new CompoundCommand(); CreateViewRequest request = new CreateViewRequest(semanticElement, getTestFixture().getPreferencesHint()); request.setLocation(location); cc.add(diagramEP.getCommand(request)); RefreshConnectionsRequest rcRequest = new RefreshConnectionsRequest((List)request.getNewObject()); cc.add(getDiagramEditPart().getCommand(rcRequest)); getCommandStack().execute(cc); return (View) ((IAdaptable) ((List)request.getNewObject()).get(0)).getAdapter( View.class); } protected void clearDiagram() { testAction(SelectAllAction.createSelectAllAction(getWorkbenchPage()), null); testAction( GlobalActionManager.getInstance().createActionHandler( getWorkbenchPage(), GlobalActionId.DELETE), null); } /** * Return the figure in which elements are being added to. * @return <code>getDiagramEditPart().getFigure()</code>. */ protected IFigure getDrawSurfaceFigure() { return getDiagramEditPart().getFigure(); } /** * Return the editpart in which elements are being added to. * @return <code>getDiagramEditPart()</code>. */ protected IGraphicalEditPart getDrawSurfaceEditPart() { return getDiagramEditPart(); } /** Return the supplied editpart's {@link ShapeNodeEditPart}children. */ protected List getShapesIn(IGraphicalEditPart parent) { assertNotNull(parent); List shapes = new ArrayList(); Iterator it = parent.getChildren().iterator(); while (it.hasNext()) { Object child = it.next(); if (child instanceof ShapeNodeEditPart) { shapes.add(child); } } return shapes; } /** Return <code>getDiagramEditPart().getConnectors()</code>. */ protected List getConnectors() { return getDiagramEditPart().getConnections(); } /* Will run teardown if the setup fails. * @see junit.framework.TestCase#runBare() */ public void runBare() throws Throwable { try { setUp(); runTest(); } finally { tearDown(); } } /** * Reorients the connection to a new target. * * @param connectionEditPart * the connection editpart to be reoriented * @param targetEditPart * the new target editpart * @param supported * should this gesture be supported? * @return the command that was executed */ protected Command reorientConnectionTarget( final ConnectionEditPart connectionEditPart, final IGraphicalEditPart targetEditPart, boolean supported) { ReconnectRequest reconnectReq = new ReconnectRequest( RequestConstants.REQ_RECONNECT_TARGET); reconnectReq.setConnectionEditPart(connectionEditPart); reconnectReq.setTargetEditPart(targetEditPart); reconnectReq.setLocation(targetEditPart.getFigure().getBounds() .getTopRight()); Command cmd = targetEditPart.getCommand(reconnectReq); if (supported) { testCommand(cmd, new ITestCommandCallback() { public void onCommandExecution() { assertTrue(connectionEditPart.getTarget() == targetEditPart); } }); } else { assertTrue(cmd == null || !cmd.canExecute()); } return cmd; } /** * Reorients the connection to a new source. * * @param connectionEditPart * the connection editpart to be reoriented * @param sourceEditPart * the new source editpart * @param supported * should this gesture be supported? * @return the command that was executed */ protected Command reorientConnectionSource( final ConnectionEditPart connectionEditPart, final IGraphicalEditPart sourceEditPart, boolean supported) { ReconnectRequest reconnectReq = new ReconnectRequest( RequestConstants.REQ_RECONNECT_SOURCE); reconnectReq.setConnectionEditPart(connectionEditPart); reconnectReq.setTargetEditPart(sourceEditPart); reconnectReq.setLocation(sourceEditPart.getFigure().getBounds() .getTopRight()); Command cmd = sourceEditPart.getCommand(reconnectReq); if (supported) { testCommand(cmd, new ITestCommandCallback() { public void onCommandExecution() { assertTrue(connectionEditPart.getSource() == sourceEditPart); } }); } else { assertTrue(cmd == null || !cmd.canExecute()); } return cmd; } }