/* * Copyright (c) 2006, 2010 Borland Software 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: * Alexander Shatalin (Borland) - initial API and implementation */ package org.eclipse.gmf.tests.gef; import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.junit.Assert; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.ContentHandler; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.emf.transaction.util.TransactionUtil; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartViewer; import org.eclipse.gef.Request; import org.eclipse.gef.Tool; import org.eclipse.gef.commands.Command; import org.eclipse.gef.palette.PaletteContainer; import org.eclipse.gef.palette.PaletteRoot; import org.eclipse.gef.palette.ToolEntry; import org.eclipse.gmf.codegen.gmfgen.GenCommonBase; import org.eclipse.gmf.codegen.gmfgen.GenCompartment; import org.eclipse.gmf.codegen.gmfgen.GenNode; import org.eclipse.gmf.runtime.diagram.ui.tools.CreationTool; import org.eclipse.gmf.runtime.emf.core.GMFEditingDomainFactory; import org.eclipse.gmf.runtime.notation.Diagram; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.NotationPackage; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.gmf.tests.JobTracker; import org.eclipse.gmf.tests.Utils; import org.eclipse.gmf.tests.setup.GeneratedDiagramPlugin; import org.eclipse.gmf.tests.setup.RuntimeBasedGeneratorConfiguration; import org.eclipse.gmf.tests.setup.ViewerConfiguration; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorPart; public class DiagramEditorTest extends AbstractDiagramEditorTest { public DiagramEditorTest(String name) { this(name, new RuntimeBasedGeneratorConfiguration()); } public DiagramEditorTest(String name, RuntimeBasedGeneratorConfiguration genConfig) { super(name, genConfig); } public void testSaveDiagramChanges() { IEditorPart editorPart = getEditor(); assertFalse("Created Editor is dirty", editorPart.isDirty()); Diagram diagram = getDiagram(); Command setNameCommand = getViewerConfiguration().getSetNotationalElementStructuralFeature(diagram, NotationPackage.eINSTANCE.getDiagram_Name(), getUniqueString()); checkEditorDirtyState(setNameCommand, editorPart, getViewerConfiguration().getViewer()); } private void checkEditorDirtyState(Command setNameCommand, IEditorPart editorPart, EditPartViewer viewer) { viewer.getEditDomain().getCommandStack().execute(setNameCommand); assertTrue("Editor was not marked as dirty", editorPart.isDirty()); editorPart.doSave(new NullProgressMonitor()); assertFalse("Editor was not saved", editorPart.isDirty()); } public void testSaveNotationElementChanges() { IEditorPart editorPart = getEditor(); ViewerConfiguration viewerConfiguration = getViewerConfiguration(); Diagram diagram = getDiagram(); Node nodeA = createNodeA(diagram, editorPart); assertTrue("Created node invisible", nodeA.isVisible()); Command setNameCommand = viewerConfiguration.getSetNotationalElementStructuralFeature(nodeA, NotationPackage.eINSTANCE.getView_Visible(), Boolean.FALSE); checkEditorDirtyState(setNameCommand, editorPart, getViewerConfiguration().getViewer()); } /** * Creating Node, saving editor */ private Node createNodeA(Diagram diagram, IEditorPart editorPart) { Node result = createNode(getSetup().getGenModel().getNodeA(), diagram); editorPart.doSave(new NullProgressMonitor()); assertFalse("Editor was not saved", editorPart.isDirty()); return result; } public void testSaveDomainElementChangesSeparateFiles() { useSameFileForDiagramAndModel(false); checkSaveDomainElementChanges(); } public void testSaveDomainElementChangesSameFile() { useSameFileForDiagramAndModel(true); checkSaveDomainElementChanges(); } private void checkSaveDomainElementChanges() { IEditorPart editorPart = getEditor(); ViewerConfiguration viewerConfiguration = getViewerConfiguration(); EditPartViewer viewer = viewerConfiguration.getViewer(); Diagram diagram = getDiagram(); Node nodeA = createNodeA(diagram, editorPart); Command setLabelCommand = viewerConfiguration.getSetBusinessElementStructuralFeatureCommand(nodeA, "label", getUniqueString()); checkEditorDirtyState(setLabelCommand, editorPart, viewer); } /** * Testing fix of request: * https://bugs.eclipse.org/bugs/show_bug.cgi?id=153893 */ public void testSaveWithUnloadedResource() throws IOException { IEditorPart editorPart = getEditor(); ViewerConfiguration viewerConfiguration = getViewerConfiguration(); EditPartViewer viewer = viewerConfiguration.getViewer(); Diagram diagram = getDiagram(); // Creating arbitrary model resource + unloading it URI anotherResourceURI = diagram.eResource().getURI().trimFileExtension().appendFileExtension("additional." + getSetup().getGenModel().getGenDiagram().getEditorGen().getDomainFileExtension()); createAdditionalModelResource(anotherResourceURI); Resource anotherResource = diagram.eResource().getResourceSet().getResource(anotherResourceURI, true); anotherResource.unload(); // Changing + saving editor Command setNameCommand = viewerConfiguration.getSetNotationalElementStructuralFeature(diagram, NotationPackage.eINSTANCE.getDiagram_Name(), getUniqueString()); checkEditorDirtyState(setNameCommand, editorPart, viewer); // Checking contents of unloaded resource. checkAdditionalModelResource(anotherResourceURI); } public void testUnspecifiedTypeRequest() { EditPartViewer viewer = getViewerConfiguration().getViewer(); Diagram diagram = getDiagram(); CreationTool creationTool = getNodeCreationTool(viewer); GenNode genNodeA = getSetup().getGenModel().getNodeA(); Node aNode = checkCreateNode(viewer, diagram, creationTool, genNodeA.getVisualID()); assertTrue("Incorrect setup passed", genNodeA.getCompartments().size() > 0); GenCompartment genCompartment = genNodeA.getCompartments().get(0); assertTrue("Incorrect setup passed", genCompartment.getChildNodes().size() > 0); GenNode childNode = genCompartment.getChildNodes().get(0); assertNotNull("Incorrect setup passed", childNode); Node compartment = findChildnode(aNode, genCompartment); checkCreateNode(viewer, compartment, creationTool, childNode.getVisualID()); } private Node findChildnode(Node parentNode, GenCommonBase genElement) { String visualID = String.valueOf(genElement.getVisualID()); for (Iterator<?> it = parentNode.getChildren().iterator(); it.hasNext();) { View nextView = (View) it.next(); if (nextView.getType().equals(visualID)) { assertTrue(nextView instanceof Node); return (Node) nextView; } } fail("Node not found" + visualID); return null; } private Node checkCreateNode(EditPartViewer viewer, View parentView, CreationTool creationTool, int newNodeVisualID) { Request request = creationTool.createCreateRequest(); EditPart parentEP = (EditPart) viewer.getEditPartRegistry().get(parentView); assertNotNull(parentEP); Command createANodeCommand = parentEP.getCommand(request); viewer.getEditDomain().getCommandStack().execute(createANodeCommand); assertTrue(parentView.getChildren().size() == 1); Node aNode = (Node) parentView.getChildren().get(0); assertEquals("Node with incorrect visual ID was created.", aNode.getType(), String.valueOf(newNodeVisualID)); return aNode; } private CreationTool getNodeCreationTool(EditPartViewer viewer) { PaletteRoot paletteRoot = viewer.getEditDomain().getPaletteViewer().getPaletteRoot(); assertNotNull("Palette root absent", paletteRoot); PaletteContainer container = findPaletteContainer(paletteRoot, "Default"); assertTrue("Incorrect palette was created", container.getChildren().size() > 0); assertTrue("Incorrect palette was created", container.getChildren().get(0) instanceof ToolEntry); ToolEntry toolEntry = (ToolEntry) container.getChildren().get(0); Tool tool = toolEntry.createTool(); assertTrue("Incorrect palette was created", tool instanceof CreationTool); CreationTool creationTool = (CreationTool) tool; creationTool.setViewer(viewer); creationTool.setEditDomain(viewer.getEditDomain()); return creationTool; } private PaletteContainer findPaletteContainer(PaletteRoot paletteRoot, String groupName) { for (Iterator<?> it = paletteRoot.getChildren().iterator(); it.hasNext();) { PaletteContainer nextContainer = (PaletteContainer) it.next(); if (groupName.equals(nextContainer.getLabel())) { return nextContainer; } } fail("No palette container " + groupName + " fourn in the palette"); return null; } private void checkAdditionalModelResource(URI anotherResourceURI) { ResourceSet resourceSet = new ResourceSetImpl(); Resource resource = resourceSet.getResource(anotherResourceURI, true); assertTrue("Unloaded resource was changed while saving editor", resource.getContents().size() == 1); } private void createAdditionalModelResource(URI anotherResourceURI) throws IOException { ResourceSet resourceSet = new ResourceSetImpl(); Resource resource = resourceSet.createResource(anotherResourceURI, ContentHandler.UNSPECIFIED_CONTENT_TYPE); EObject domainDiagramElement = createDiagramDomainObject(getSetup()); resource.getContents().add(domainDiagramElement); resource.save(Collections.EMPTY_MAP); } public void testDiagramAndModelExternalModificationSameResource() { useSameFileForDiagramAndModel(true); checkDiagramAndModelExternalModification(); } public void testDiagramAndModelExternalModificationSeparateResources() { useSameFileForDiagramAndModel(false); checkDiagramAndModelExternalModification(); } /* * Here's console output for regular (successful) run of testDiagramAndModelExternalModificationSeparateResources. * The difference in test...SameResource() is in single file notification * * Important thing to note: * - handleElementChanged is invoked asynchronously from a worker thread, albeit idea of saveResources() * with subsequent redispatchEvents was to have handleElementChanged (among others, of course) to be already invoked * by the moment we do getDiagram(). Nevertheless, on my Windows machine handleElementChanged() still runs prior to * getDiagram() call (see below). It is not true for Linux test machine, where we had failures for the long time. * [300887] Done saveResources, all resource notifications should be already dispatched [300887] added async handleElementChanged Worker-3 [300887] added async handleElementChanged Worker-3 [300887] L/AbstractDiagramEditorTest_1270580558187/AbstractDiagramEditorTest_1270580617468.samplemodel [300887] about to be replaced [300887] doc content set [300887] DiagramEditor.handleElementContentReplaced.1 [300887] DiagramEditor.handleElementContentReplaced.2 [300887] content replaced fired [300887] L/AbstractDiagramEditorTest_1270580558187/AbstractDiagramEditorTest_1270580617468.samplemodel_diagram [300887] about to be replaced [300887] doc content set [300887] DiagramEditor.handleElementContentReplaced.1 [300887] DiagramEditor.handleElementContentReplaced.2 [300887] content replaced fired [300887] Test is about to re-get top EP's model:testDiagramAndModelExternalModificationSeparateResources */ private void checkDiagramAndModelExternalModification() { IEditorPart editorPart = getEditor(); Diagram diagram = getDiagram(); assertTrue("Not empty diagram created", diagram.getChildren().size() == 0); assertTrue("Not empty domain model element created", diagram.getElement().eContents().size() == 0); Diagram diagramCopy = reloadInSeparateResourceSet(diagram); assertTrue("Passed diagram is not empty", diagramCopy.getChildren().size() == 0); try { ViewerConfiguration viewerConfiguration = getViewerConfigurationFactory().createViewerConfiguration(new Shell(SWT.NONE), diagramCopy, getSetup()); Command command = viewerConfiguration.getCreateNodeCommand(diagramCopy, getSetup().getGenModel().getNodeA()); viewerConfiguration.getViewer().getEditDomain().getCommandStack().execute(command); } catch (Exception e) { fail(e.getMessage()); } assertFalse("Diagram node was not created", diagramCopy.getChildren().size() == 0); new WorkspaceUtils(getProject()).atomicSave(diagramCopy.eResource().getResourceSet().getResources()); diagram = getDiagram(); assertFalse("Editor is dirty", editorPart.isDirty()); assertTrue("Diagram content was not refreshed", diagram.getChildren().size() > 0); assertTrue("Domain model content was not refreshed", diagram.getElement().eContents().size() > 0); } public void testDiagramResourceExternalModification() { Diagram diagram = getDiagram(); String newDiagramName = getUniqueString(); Diagram diagramCopy = reloadInSeparateResourceSet(diagram); try { ViewerConfiguration viewerConfiguration = getViewerConfigurationFactory().createViewerConfiguration(new Shell(SWT.NONE), diagramCopy, getSetup()); Command command = viewerConfiguration.getSetNotationalElementStructuralFeature(diagramCopy, NotationPackage.eINSTANCE.getDiagram_Name(), newDiagramName); viewerConfiguration.getViewer().getEditDomain().getCommandStack().execute(command); } catch (Exception e) { fail(e.getMessage()); } assertEquals("Diagram name was not set", newDiagramName, diagramCopy.getName()); new WorkspaceUtils(getProject()).atomicSave(diagramCopy.eResource()); diagram = getDiagram(); assertFalse("Editor is dirty", getEditor().isDirty()); assertEquals("Diagram name was not updated", newDiagramName, diagram.getName()); } public void testModelResorceExternalModification() { Diagram diagram = getDiagram(); Diagram diagramCopy = reloadInSeparateResourceSet(diagram); String newName = getUniqueString(); try { ViewerConfiguration viewerConfiguration = getViewerConfigurationFactory().createViewerConfiguration(new Shell(SWT.NONE), diagramCopy, getSetup()); Command command = viewerConfiguration.getSetBusinessElementStructuralFeatureCommand(diagramCopy, "diagramAttribute", newName); viewerConfiguration.getViewer().getEditDomain().getCommandStack().execute(command); } catch (Exception e) { fail(e.getMessage()); } new WorkspaceUtils(getProject()).atomicSave(diagramCopy.getElement().eResource()); diagram = getDiagram(); assertFalse("Editor is dirty", getEditor().isDirty()); EObject diagramModel = diagram.getElement(); EStructuralFeature stFeature = diagramModel.eClass().getEStructuralFeature("diagramAttribute"); assertNotNull("Name feature not found", stFeature); String nodeAName = (String) diagramModel.eGet(stFeature); assertEquals("Name was not refreshed", newName, nodeAName); } // utilities for workspace (IResource) operations private static class WorkspaceUtils { private final IProject myProject; WorkspaceUtils(IProject p) { assert p != null; myProject = p; } void atomicSave(Resource resource) { atomicSave(Collections.singletonList(resource)); } void atomicSave(final List<Resource> resources) { // Batching all the notifications from Eclipse resource subsystem. // Otherwise notifications will be dispatched on by one and just created // diagram node will be removed by CanonicalEditPolicy because // corresponding notification from the domain model file changes is // waiting to be dispatched later IWorkspaceRunnable runnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { for (Resource nextResource : resources) { try { nextResource.save(Collections.EMPTY_MAP); } catch (IOException e) { fail(e.getMessage()); } } } }; JobTracker jt = new JobTracker(); try { jt.start(); ResourcesPlugin.getWorkspace().run(runnable, myProject, IWorkspace.AVOID_UPDATE, new NullProgressMonitor()); jt.freeze(); // WorkspaceSynchronizer re-dispatches resource events using Jobs, let these jobs complete Utils.assertDispatchDisplayMessages(jt.getNonEmptyCondition(), 3); // hence DocumentProvider.ResourceSetInfo's delegate (from Job) invoked handleResourceChanged(), which in turn // posted asyncExec to invoke DocumentProvider.handleElementChanged(), thus need to give it a chance Utils.assertDispatchDisplayMessages(3); } catch (CoreException e) { Assert.fail(e.getMessage()); } finally { jt.stop(); } } } private Diagram reloadInSeparateResourceSet(Diagram diagram) { TransactionalEditingDomain editingDoman = GMFEditingDomainFactory.INSTANCE.createEditingDomain(); editingDoman.setID(getSetup().getGenModel().getGenDiagram().getEditingDomainID()); AdapterFactoryEditingDomain oldEditingDomain = (AdapterFactoryEditingDomain) TransactionUtil.getEditingDomain(diagram); ((AdapterFactoryEditingDomain) editingDoman).setResourceToReadOnlyMap(oldEditingDomain.getResourceToReadOnlyMap()); ResourceSet resourceSet = editingDoman.getResourceSet(); Resource newDiagramResource = resourceSet.getResource(diagram.eResource().getURI(), true); EObject newDiagram = newDiagramResource.getEObject(diagram.eResource().getURIFragment(diagram)); assertTrue("Unable to reload the diagram into another ResourceSet", newDiagram instanceof Diagram); return (Diagram) newDiagram; } @Override protected Diagram createDiagramView(EObject domainElement, GeneratedDiagramPlugin genPlugin) { return RuntimeBasedGeneratorConfiguration.createDiagram(domainElement, genPlugin); } }