Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit')
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/DiagramMatchers.java104
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java175
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Activator.java83
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DiagramUtils.java172
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DisplayUtils.java38
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Duck.java255
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/EditorUtils.java109
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/FilesUtils.java48
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/GenericUtils.java64
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/HandlerUtils.java95
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/JUnitUtils.java159
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/LogTracker.java132
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelExplorerUtils.java214
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelUtils.java110
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PackageExplorerUtils.java91
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PapyrusProjectUtils.java93
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PrintingProgressMonitor.java137
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ProjectUtils.java68
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/SynchronousExecutorService.java188
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/TableUtils.java82
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/ChangeCapture.java84
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/EcoreModel.java82
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/WorkspaceModificationAssertion.java128
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractHouseKeeperRule.java723
-rwxr-xr-xtests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java649
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveDiagram.java33
-rwxr-xr-xtests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveTable.java33
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AnnotationRule.java152
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ExecutorRule.java56
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HideViewRule.java72
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HouseKeeper.java143
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/JavaResource.java32
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ModelSetFixture.java127
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionFixture.java95
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionRule.java59
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java1415
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PluginResource.java36
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ProjectFixture.java256
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ResourceSetFixture.java34
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/RuleUtil.java132
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ServiceRegistryModelSetFixture.java41
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowView.java74
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowViewRule.java72
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/StandaloneResourceSetFixture.java179
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThread.java31
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThreadRule.java94
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEMFResourceTest.java57
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEditorTest.java244
48 files changed, 7550 insertions, 0 deletions
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/DiagramMatchers.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/DiagramMatchers.java
new file mode 100644
index 00000000000..b78720e078a
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/DiagramMatchers.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014 CEA 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.matchers;
+
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.EditPartViewer;
+import org.eclipse.gef.palette.PaletteDrawer;
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+
+/**
+ * Hamcrest matchers for assertions on GMF diagrams.
+ */
+public class DiagramMatchers {
+
+ private DiagramMatchers() {
+ super();
+ }
+
+ /**
+ * Match an edit part that is selected in its viewer.
+ */
+ public static Matcher<EditPart> editPartSelected() {
+ return EditPartSelected.INSTANCE;
+ }
+
+ /**
+ * Match a drawer that is expanded in the specified {@code viewer}.
+ */
+ public static Matcher<PaletteDrawer> expandedIn(PaletteViewer viewer) {
+ return new DrawerExpansion(viewer, true);
+ }
+
+ /**
+ * Match a drawer that is collapsed (closed) in the specified {@code viewer}.
+ */
+ public static Matcher<PaletteDrawer> collapsedIn(PaletteViewer viewer) {
+ return new DrawerExpansion(viewer, false);
+ }
+
+ //
+ // Nested types
+ //
+
+ static class EditPartSelected extends BaseMatcher<EditPart> {
+
+ private static final EditPartSelected INSTANCE = new EditPartSelected();
+
+ private EditPartSelected() {
+ super();
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("edit-part is selected");
+ }
+
+ public boolean matches(Object item) {
+ return (item instanceof EditPart) && isSelected((EditPart)item);
+ }
+
+ boolean isSelected(EditPart editPart) {
+ EditPartViewer viewer = editPart.getViewer();
+ return (viewer != null) && viewer.getSelectedEditParts().contains(editPart);
+ }
+ }
+
+ static class DrawerExpansion extends BaseMatcher<PaletteDrawer> {
+
+ private final PaletteViewer viewer;
+
+ private final boolean expanded;
+
+ DrawerExpansion(PaletteViewer viewer, boolean expanded) {
+ this.viewer = viewer;
+ this.expanded = expanded;
+ }
+
+ public void describeTo(Description description) {
+ description.appendText("drawer is ");
+ description.appendText(expanded ? "expanded" : "collapsed");
+ }
+
+ public boolean matches(Object item) {
+ return (item instanceof PaletteDrawer) && (isExpanded((PaletteDrawer)item) == expanded);
+ }
+
+ boolean isExpanded(PaletteDrawer drawer) {
+ return viewer.isExpanded(drawer);
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
new file mode 100644
index 00000000000..9765f99db05
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
@@ -0,0 +1,175 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.matchers;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IStatus;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.CombinableMatcher;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+
+/**
+ * Some useful matchers that Hamcrest doesn't provide.
+ */
+public class MoreMatchers {
+
+ private MoreMatchers() {
+ super();
+ }
+
+ public static <N extends Number & Comparable<N>> Matcher<N> greaterThan(final N minimum) {
+ return new BaseMatcher<N>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("greater than ").appendValue(minimum);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean matches(Object item) {
+ return ((N) item).compareTo(minimum) > 0;
+ }
+ };
+ }
+
+ public static <N extends Number & Comparable<N>> Matcher<N> lessThan(final N maximum) {
+ return new BaseMatcher<N>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("less than ").appendValue(maximum);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean matches(Object item) {
+ return ((N) item).compareTo(maximum) < 0;
+ }
+ };
+ }
+
+ /**
+ * Match empty iterables of any kind.
+ *
+ * @see #emptyIterable()
+ */
+ public static Matcher<Iterable<?>> isEmpty() {
+ return new BaseMatcher<Iterable<?>>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is empty");
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ return Iterables.isEmpty((Iterable<?>) item);
+ }
+ };
+ }
+
+ /**
+ * The {@link CombinableMatcher}s of Hamcrest require matching generic signatures,
+ * for which the wildcard of the {@link #isEmpty()} matcher doesn't work very well,
+ * so this equivalent matcher may be used instead in those cases.
+ *
+ * @see #isEmpty()
+ */
+ public static <E> Matcher<Iterable<E>> emptyIterable() {
+ return new BaseMatcher<Iterable<E>>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is empty");
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ return Iterables.isEmpty((Iterable<?>) item);
+ }
+ };
+ }
+
+ public static Matcher<String> regexMatches(final String pattern) {
+ return new BaseMatcher<String>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("matches /").appendText(pattern).appendText("/");
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ String string = (String) item;
+ return !Strings.isNullOrEmpty(string) && string.matches(pattern);
+ }
+ };
+ }
+
+ public static Matcher<String> regexContains(final String pattern) {
+ final Pattern regex = Pattern.compile(pattern);
+
+ return new BaseMatcher<String>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("contains /").appendText(pattern).appendText("/");
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ String string = (String) item;
+ return !Strings.isNullOrEmpty(string) && regex.matcher(string).find();
+ }
+ };
+ }
+
+ public static Matcher<IStatus> statusWithMessage(final Matcher<? super String> matcher) {
+ return new BaseMatcher<IStatus>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("status with message ").appendDescriptionOf(matcher);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ boolean result = false;
+ if (item instanceof IStatus) {
+ IStatus status = (IStatus) item;
+ result = matcher.matches(status.getMessage());
+ }
+ return result;
+ }
+ };
+ }
+
+ public static Matcher<IStatus> statusWithException(final Matcher<?> matcher) {
+ return new BaseMatcher<IStatus>() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("status with exception ").appendDescriptionOf(matcher);
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ boolean result = false;
+ if (item instanceof IStatus) {
+ IStatus status = (IStatus) item;
+ result = matcher.matches(status.getException());
+ }
+ return result;
+ }
+ };
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Activator.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Activator.java
new file mode 100644
index 00000000000..3f1a9deceb6
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Activator.java
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * Copyright (c) 2012, 2015 CEA LIST, Christian W. Damus, 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ * Christian W. Damus - bug 433206
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.papyrus.infra.core.log.LogHelper;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.application.ApplicationHandle;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.papyrus.junit.utils"; //$NON-NLS-1$
+
+ // The shared instance
+ private static Activator plugin;
+
+ public static LogHelper log;
+
+ private String runningApplicationID;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ log = new LogHelper(this);
+
+ // Get the running application ID
+ ServiceReference<ApplicationHandle> appRef = context.getServiceReference(ApplicationHandle.class);
+ if (appRef != null) {
+ ApplicationHandle appHandle = context.getService(appRef);
+ if (appHandle != null) {
+ try {
+ runningApplicationID = appHandle.getApplicationDescriptor().getApplicationId();
+ } finally {
+ context.ungetService(appRef);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ public String getRunningApplicationID() {
+ return (runningApplicationID == null) ? "" : runningApplicationID; //$NON-NLS-1$
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DiagramUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DiagramUtils.java
new file mode 100644
index 00000000000..a44656558aa
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DiagramUtils.java
@@ -0,0 +1,172 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST.
+ *
+ * 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:
+ * CEA LIST - Initial API and implementation
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
+import org.eclipse.gmf.runtime.notation.Diagram;
+import org.eclipse.gmf.runtime.notation.Edge;
+import org.eclipse.gmf.runtime.notation.Shape;
+import org.eclipse.gmf.runtime.notation.View;
+import org.eclipse.papyrus.infra.core.resource.AbstractBaseModel;
+import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.uml2.uml.NamedElement;
+import org.junit.Assert;
+
+/**
+ * Utility class for diagrams
+ */
+public class DiagramUtils {
+
+ public static IGraphicalEditPart findEditPartforView(IMultiDiagramEditor papyrusEditor, View view, Class<? extends EditPart> editPartClass) {
+ DiagramEditPart diagramEditPart = findDiagramEditPart(papyrusEditor);
+ Object part = diagramEditPart.getViewer().getEditPartRegistry().get(view);
+ Assert.assertNotNull("Cannot find the edit part", part);
+ Assert.assertNotNull("part has not the right class", editPartClass.isAssignableFrom(part.getClass()));
+ return (IGraphicalEditPart) part;
+ }
+
+ public static DiagramEditPart findDiagramEditPart(IMultiDiagramEditor papyrusEditor) {
+ DiagramEditPart diagramEditPart = (DiagramEditPart) papyrusEditor.getAdapter(DiagramEditPart.class);
+ Assert.assertNotNull("Cannot find the Diagram edit part", diagramEditPart);
+ return diagramEditPart;
+ }
+
+ /**
+ * Search and returns the first view in the specified container view with the given name
+ *
+ * @param container
+ * the view container of the searched view
+ * @param elementName
+ * the name of the element represented by the search view
+ * @return the found view or <code>null</code> if none was found
+ */
+ public static Shape findShape(View container, String elementName) {
+ for (Object viewObject : container.getChildren()) {
+ View view = (View) viewObject;
+ if (view instanceof Shape && view.getElement() instanceof NamedElement) {
+ NamedElement element = (NamedElement) view.getElement();
+ if (elementName.equals(element.getName())) {
+ return (Shape) view;
+ }
+ }
+ }
+
+
+ // Assert.fail("Cannot find the view associated to " + elementName);
+ return null;
+ }
+
+
+
+ /**
+ * Search and returns the first view in the specified container view with the given name
+ *
+ * @param container
+ * the container
+ * @param elementName
+ * the element name
+ * @return the edge
+ */
+ public static Edge findEdge(View container, String elementName) {
+ for (Object viewObject : container.eContents()) {
+ if (viewObject instanceof View) {
+ View view = (View) viewObject;
+ if (view instanceof Edge && view.getElement() instanceof NamedElement) {
+ NamedElement element = (NamedElement) view.getElement();
+ if (elementName.equals(element.getName())) {
+ return (Edge) view;
+ }
+ }
+ }
+ }
+
+
+ // Assert.fail("Cannot find the view associated to " + elementName);
+ return null;
+ }
+
+
+ /**
+ * Returns the int corresponding to the given tuple
+ *
+ * @param red
+ * @param green
+ * @param blue
+ * @return
+ */
+ public static int rgb(int red, int green, int blue) {
+ return red | green << 8 | blue << 16;
+ }
+
+ public static RGB integerToRGB(int value) {
+ int blue = value & 255;
+ int green = (value >> 8) & 255;
+ int red = (value >> 16) & 255;
+ return new RGB(red, green, blue);
+ }
+
+ public static String integerToRGBString(int value) {
+ RGB rgb = integerToRGB(value);
+ return rgb.toString();
+
+ }
+
+ public static Diagram getNotationDiagram(ModelSet modelSet, String string) {
+ IModel notationModel = modelSet.getModel(NotationModel.MODEL_ID);
+
+ AbstractBaseModel notationBaseModel = null;
+ if (notationModel instanceof AbstractBaseModel) {
+ notationBaseModel = (AbstractBaseModel) notationModel;
+ } else {
+ Assert.fail("notation model is not an abstract base model"); //$NON-NLS-1$
+ return null;
+ }
+ Assert.assertTrue("notation resource contains nothing", notationBaseModel.getResource().getContents().size() >= 1); //$NON-NLS-1$
+ for (EObject object : notationBaseModel.getResource().getContents()) {
+ if (object instanceof Diagram && string.equals(((Diagram) object).getName())) {
+ return (Diagram) object;
+ }
+ }
+ return null;
+ }
+
+ public static Collection<Diagram> getAllNotationDiagram(ModelSet modelSet, String string) {
+ IModel notationModel = modelSet.getModel(NotationModel.MODEL_ID);
+ Collection<Diagram> arrayList = new ArrayList<Diagram>();
+ AbstractBaseModel notationBaseModel = null;
+ if (notationModel instanceof AbstractBaseModel) {
+ notationBaseModel = (AbstractBaseModel) notationModel;
+ } else {
+ Assert.fail("notation model is not an abstract base model");
+ return null;
+ }
+ Assert.assertTrue("notation resource contains nothing", notationBaseModel.getResource().getContents().size() >= 1);
+ for (EObject object : notationBaseModel.getResource().getContents()) {
+ if (object instanceof Diagram && string.equals(((Diagram) object).getName())) {
+ arrayList.add((Diagram) object);
+ }
+ }
+ return arrayList;
+ }
+
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DisplayUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DisplayUtils.java
new file mode 100644
index 00000000000..e9ff430657d
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/DisplayUtils.java
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.swt.widgets.Display;
+
+
+public class DisplayUtils {
+
+ /** Processes all events waiting in the Display's event loop and then returns. */
+ public static void flushEventLoop() {
+ final Display display = Display.getDefault();
+ display.syncExec(new Runnable() {
+
+ public void run() {
+ try {
+ while(display.readAndDispatch()) {
+ // nothing
+ }
+ } catch (Exception ex) {
+ //Do not fail the test for invalid runnables
+ }
+ }
+ });
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Duck.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Duck.java
new file mode 100644
index 00000000000..6da762ab6d7
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/Duck.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 485214
+ *
+ */
+package org.eclipse.papyrus.junit.utils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.emf.common.util.WrappedException;
+
+import com.google.common.primitives.Primitives;
+
+
+/**
+ * A clumsy Java implementation of <em>Duck Typing</em>, in which objects are manipulated reflectively according to their public interface (the
+ * operations they provide) rather than their types.
+ */
+public class Duck {
+
+ private final Object target;
+
+ /**
+ * Wraps an object as a duck.
+ *
+ * @param target
+ * the object to wrap. Must not be {@code null}
+ *
+ * @throws NullPointerException
+ * on attempt to duck-wrap a {@code null}
+ */
+ public Duck(Object target) {
+ super();
+
+ if (target == null) {
+ throw new NullPointerException();
+ }
+
+ this.target = target;
+ }
+
+ /**
+ * Queries whether the duck understands the named message (implying that it can {@linkplain #quack(String, Object...) quack} it.
+ * This accounts for signature overloading by finding the first method that accepts the given arguments.
+ *
+ * @param methodName
+ * the method name
+ * @param arg
+ * the arguments to the method
+ * @return whether I can invoke the named method with these arguments
+ */
+ public boolean understands(String methodName, Object... arg) {
+ return lookup(methodName, null, arg) != null;
+ }
+
+ /**
+ * Queries whether the duck understands a message matching the given regex(implying that it can {@linkplain #quackp(String, Object...) quack} it.
+ * This accounts for signature overloading by finding the first method that accepts the given arguments.
+ *
+ * @param methodPattern
+ * the method name pattern
+ * @param arg
+ * the arguments to the method
+ * @return whether I can invoke the indicated method with these arguments
+ */
+ public boolean understandsp(String methodPattern, Object... arg) {
+ return lookup(Pattern.compile(methodPattern), null, arg) != null;
+ }
+
+ /**
+ * Reflectively invokes a method by name. This accounts for signature overloading by finding the first method that accepts the given arguments.
+ *
+ * @param methodName
+ * the method name
+ * @param arg
+ * the arguments to the method
+ * @return the method result, which would be {@code null} in the case of a {@code void} method
+ */
+ public <T> T quack(String methodName, Object... arg) {
+ return invoke(lookup(methodName, null, arg), arg);
+ }
+
+ /**
+ * Reflectively invokes a method by regex (matching the method name). This accounts for signature overloading by finding the first method that
+ * accepts the given arguments.
+ *
+ * @param methodPattern
+ * the method name pattern
+ * @param arg
+ * the arguments to the method
+ * @return the method result, which would be {@code null} in the case of a {@code void} method
+ */
+ public <T> T quackp(String methodPattern, Object... arg) {
+ return invoke(lookup(Pattern.compile(methodPattern), null, arg), arg);
+ }
+
+ /**
+ * Reflectively invokes a method by name {@code returning} a type conforming to the given type. This accounts for signature overloading by finding
+ * the first method that accepts the given arguments.
+ *
+ * @param methodName
+ * the method name
+ * @param returning
+ * the required return type, or {@code null} if the return type doesn't matter
+ * @param arg
+ * the arguments to the method
+ * @return the method result, which would be {@code null} in the case of a {@code void} method
+ */
+ public <T> T quack(String methodName, Class<T> returning, Object... arg) {
+ return invoke(lookup(methodName, returning, arg), arg);
+ }
+
+ /**
+ * Reflectively invokes a method by regex (matching the method name) {@code returning} a type conforming to the given type. This accounts for
+ * signature overloading by finding the first method that
+ * accepts the given arguments.
+ *
+ * @param methodPattern
+ * the method name pattern
+ * @param returning
+ * the required return type, or {@code null} if the return type doesn't matter
+ * @param arg
+ * the arguments to the method
+ * @return the method result, which would be {@code null} in the case of a {@code void} method
+ */
+ public <T> T quackp(String methodPattern, Class<T> returning, Object... arg) {
+ return invoke(lookup(Pattern.compile(methodPattern), returning, arg), arg);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T invoke(Method method, Object[] args) {
+ try {
+ return (method == null) ? null : (T) method.invoke(target, args);
+ } catch (IllegalAccessException e) {
+ throw new WrappedException(e);
+ } catch (InvocationTargetException e) {
+ Throwable toThrow = e.getTargetException();
+ if (toThrow instanceof Error) {
+ throw (Error) toThrow;
+ }
+ throw new WrappedException((Exception) toThrow);
+ }
+ }
+
+ private Method lookup(String methodName, Class<?> returning, Object[] args) {
+ Method result = null;
+ final Class<?>[] signature = signature(args);
+
+ Method[] scope = target.getClass().getMethods();
+ for (int i = 0; (result == null) && (i < scope.length); i++) {
+ Method next = scope[i];
+ if (next.getName().equals(methodName) && matchReturn(next.getReturnType(), returning) && match(next, signature)) {
+ result = next;
+ }
+ }
+
+ return result;
+ }
+
+ private Method lookup(Pattern methodPattern, Class<?> returning, Object[] args) {
+ final Matcher m = methodPattern.matcher(""); //$NON-NLS-1$
+
+ Method result = null;
+ final Class<?>[] signature = signature(args);
+
+ Method[] scope = target.getClass().getMethods();
+ for (int i = 0; (result == null) && (i < scope.length); i++) {
+ Method next = scope[i];
+ m.reset(next.getName());
+ if (m.matches() && matchReturn(next.getReturnType(), returning) && match(next, signature)) {
+ result = next;
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean match(Method method, Class<?>[] signature) {
+ Class<?>[] params = method.getParameterTypes();
+ boolean result = params.length == signature.length;
+
+ if (result) {
+ for (int i = 0; result && (i < signature.length); i++) {
+ result = matchParameter(params[i], signature[i]);
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean matchReturn(Class<?> returnType, Class<?> expectedType) {
+ boolean result;
+
+ if (expectedType == null) {
+ // Wildcard: take any method
+ result = true;
+ } else if ((returnType == void.class) || (returnType == Void.class)) {
+ // Handle void methods
+ result = (expectedType == void.class) || (expectedType == Void.class);
+ } else {
+ // Compare the unwrapped primitive types
+ result = Primitives.unwrap(expectedType).isAssignableFrom(Primitives.unwrap(returnType));
+ }
+
+ return result;
+ }
+
+ private static boolean matchParameter(Class<?> paramType, Class<?> argType) {
+ boolean result;
+
+ if (argType == Void.class) {
+ // Handle null arguments: null is assignable to any object type (not primitive)
+ result = !paramType.isPrimitive();
+ } else if (paramType.isPrimitive()) {
+ // Compare the wrapper type
+ result = Primitives.wrap(paramType).isAssignableFrom(argType);
+ } else {
+ // Straight-forward object types
+ result = paramType.isAssignableFrom(argType);
+ }
+
+ return result;
+ }
+
+ private static Class<?>[] signature(Object[] args) {
+ Class<?>[] result = new Class<?>[args.length];
+
+ for (int i = 0; i < args.length; i++) {
+ result[i] = (args[i] == null) ? Void.class : args[i].getClass();
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof Duck) && target.equals(((Duck) obj).target);
+ }
+
+ @Override
+ public int hashCode() {
+ return target.hashCode();
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/EditorUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/EditorUtils.java
new file mode 100644
index 00000000000..b388df6b639
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/EditorUtils.java
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ * Copyright (c) 2012, 2015 CEA LIST, Christian W. Damus, 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ * Christian W. Damus - bug 434983
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.papyrus.editor.PapyrusMultiDiagramEditor;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.junit.Assert;
+
+/**
+ *
+ * useful methods for Editors
+ *
+ */
+public class EditorUtils {
+
+ private EditorUtils() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @param file
+ * a file
+ * @return
+ * the opened editor for this file
+ * @throws PartInitException
+ */
+ public static final IEditorPart openEditor(final IFile file) throws PartInitException {
+ return withoutLayoutStoragePopup(() -> {
+ GenericUtils.closeIntroPart();
+ final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IEditorPart editor = null;
+ editor = IDE.openEditor(activePage, file);
+ Assert.assertNotNull(editor);
+ return editor;
+ });
+ }
+
+ /**
+ * Opens the file with the Papyrus Editor
+ *
+ * @param file
+ * @return
+ * @throws PartInitException
+ */
+ public static final IMultiDiagramEditor openPapyrusEditor(final IFile file) throws PartInitException {
+ return withoutLayoutStoragePopup(() -> {
+ GenericUtils.closeIntroPart();
+ final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IEditorPart editor = null;
+ editor = IDE.openEditor(activePage, file, PapyrusMultiDiagramEditor.EDITOR_ID);
+ Assert.assertNotNull(editor);
+ return (IMultiDiagramEditor) editor;
+ });
+ }
+
+ /**
+ * Opens an editor without the possibility of it showing a prompt dialog to convert
+ * DI-file storage of the page layout to private sash-file storage.
+ */
+ @SuppressWarnings("restriction")
+ private static <E extends IEditorPart> E withoutLayoutStoragePopup(EditorOpener<E> editorOpener) throws PartInitException {
+ E result;
+ boolean posted = false;
+
+ org.eclipse.papyrus.infra.ui.internal.preferences.YesNo originalPreference = org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().getConvertSharedPageLayoutToPrivate();
+ org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(org.eclipse.papyrus.infra.ui.internal.preferences.YesNo.NO);
+
+ try {
+ result = editorOpener.openEditor();
+ result.getSite().getShell().getDisplay().asyncExec(() -> org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(originalPreference));
+ posted = true;
+ } finally {
+ if (!posted) {
+ // Revert now because the editor failed to open and we won't be reverting asynchronously
+ org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(originalPreference);
+ }
+ }
+
+ return result;
+ }
+
+ //
+ // Nested types
+ //
+
+ @FunctionalInterface
+ private interface EditorOpener<E extends IEditorPart> {
+ E openEditor() throws PartInitException;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/FilesUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/FilesUtils.java
new file mode 100644
index 00000000000..f3ba910ea0b
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/FilesUtils.java
@@ -0,0 +1,48 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+/**
+ *
+ * Useful methods to manipulate files
+ *
+ */
+public class FilesUtils {
+
+ /**
+ *
+ * @param testProject
+ * the destination projecr
+ * @param newFilename
+ * the new name of the copied file
+ * @param fileURL
+ * the URl of the file to copy
+ * @throws IOException
+ * @throws CoreException
+ */
+ public static final void copyFiles(final IProject testProject, final String newFilename, final URL fileURL) throws CoreException, IOException {
+ // Copy EmptyModel from bundle to the test project
+ final IFile emptyModel = testProject.getFile(newFilename);
+ emptyModel.create(fileURL.openStream(), true, new NullProgressMonitor());
+
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/GenericUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/GenericUtils.java
new file mode 100644
index 00000000000..fd3c865820a
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/GenericUtils.java
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.intro.IIntroPart;
+
+/**
+ *
+ Some useful methods for Tests
+ *
+ */
+public class GenericUtils {
+
+ private GenericUtils() {
+ //to prevent instanciation
+ }
+
+ /**
+ * close the Intro part
+ */
+ public static final void closeIntroPart() {
+ final IIntroPart introPart = PlatformUI.getWorkbench().getIntroManager().getIntro();
+ PlatformUI.getWorkbench().getIntroManager().closeIntro(introPart);
+ }
+
+ /**
+ * Close all the editors
+ */
+ public static final void closeAllEditors() {
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().closeAllEditors(false);
+ }
+
+ /**
+ * Clean the workspace
+ *
+ * @throws CoreException
+ */
+ public static final void cleanWorkspace() throws CoreException {
+ // we clean the workspace and create a new project to test the handlers
+ final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ for(final IProject project : workspace.getRoot().getProjects()) {
+
+ project.delete(true, new NullProgressMonitor());
+
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/HandlerUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/HandlerUtils.java
new file mode 100644
index 00000000000..681c3fa9539
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/HandlerUtils.java
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.commands.ParameterizedCommand;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.junit.Assert;
+
+/**
+ *
+ * Useful methods for the handlers and the Eclipse commands
+ *
+ */
+public class HandlerUtils {
+
+ /**
+ * Warning : you should be sure that the correct Editor/partView... has the focus to test the enablement if the handler!
+ *
+ * @param commandId
+ * the command id
+ * @return
+ * the current handler for this command
+ */
+ public static final IHandler getActiveHandlerFor(final String commandId) {
+ return HandlerUtils.getCommand(commandId).getHandler();
+ }
+
+ /**
+ *
+ * @param commandId
+ * the commandId
+ * @return
+ * the real handler for the command
+ */
+ public static final IHandler getRealHandlerFor(final String commandId) {
+ //commented because it works only on Eclipse4
+ //imports to add :
+ // - org.eclipse.e4.ui.model.application.MApplication;
+ // - org.eclipse.e4.core.contexts.IEclipseContext
+ // - org.eclipse.e4.core.commands.internal.HandlerServiceImpl
+ // - org.eclipse.ui.internal.handlers.E4HandlerProxy
+ // MApplication appl = (MApplication)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(MApplication.class);
+ // IEclipseContext context = appl.getContext();
+ // E4HandlerProxy handler = HandlerServiceImpl.lookUpHandler(context, commandId);
+ // return handler.getHandler();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ *
+ * @param commandId
+ * the command id
+ * @return
+ * the command object for this command id
+ */
+ public static final Command getCommand(final String commandId) {
+ final ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getService(ICommandService.class);
+ commandService.refreshElements(commandId, null);
+ Assert.assertNotNull("CommandService can't be found", commandService);
+ Command cmd = commandService.getCommand(commandId);
+ Assert.assertNotNull("Command can't be found", cmd);
+ return cmd;
+ }
+
+ /**
+ * Execute the command
+ *
+ * @param cmd
+ * a command
+ */
+ public static final void executeCommand(final Command cmd) throws Exception {
+ IHandlerService handlerService = (IHandlerService)PlatformUI.getWorkbench().getService(IHandlerService.class);
+ Assert.assertNotNull("Impossible to find handler service", handlerService);
+ final ParameterizedCommand parameterizedCommand = new ParameterizedCommand(cmd, null);
+ Assert.assertEquals("Command is not executable as expected", cmd.isEnabled(), true);
+ //execute the command
+ handlerService.executeCommand(parameterizedCommand, null);
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/JUnitUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/JUnitUtils.java
new file mode 100644
index 00000000000..aaaac0121a3
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/JUnitUtils.java
@@ -0,0 +1,159 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Utilities for working with the JUnit data model and execution environment.
+ */
+public class JUnitUtils {
+
+ /**
+ * Not instantiable by clients.
+ */
+ private JUnitUtils() {
+ super();
+ }
+
+ /**
+ * Obtains the test class implied by a {@code description} that is supplied to a {@link TestRule}.
+ *
+ * @param description
+ * a rule's owning description, which generally would be a test method or a test class
+ * (as these are the contexts in which rules are invoked)
+ *
+ * @return the test class, or {@code null} in the unlikely event that none can be found
+ */
+ public static Class<?> getTestClass(Description description) {
+ Class<?> result = description.getTestClass();
+
+ if (result == null) {
+ for (Description child : description.getChildren()) {
+ result = getTestClass(child);
+ if (result != null) {
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Obtains the annotations applied to a {@code description} that is supplied to a {@link TestRule}.
+ * If the description is for a test method, the annotations of its owning class are included,
+ * excepting annotations of the same type applied to the method.
+ *
+ * @param description
+ * a rule's owning description, which generally would be a test method or a test class
+ * (as these are the contexts in which rules are invoked)
+ *
+ * @return all of the annotations applied to the test description
+ */
+ public static Iterable<Annotation> getAnnotations(Description description) {
+ Map<Class<? extends Annotation>, Annotation> result = Maps.newLinkedHashMap();
+
+ for (Annotation next : description.getAnnotations()) {
+ result.put(next.annotationType(), next);
+ }
+
+ if (description.getTestClass() != null) {
+ for (Annotation next : description.getTestClass().getAnnotations()) {
+ if (!result.containsKey(next.annotationType())) {
+ result.put(next.annotationType(), next);
+ }
+ }
+ }
+
+ return result.values();
+ }
+
+ /**
+ * Obtains the annotation of the specified {@code type} applicable to a {@code description} that is supplied to a {@link TestRule}.
+ * If the description is for a test method, then if that method doesn't have the requested annotation, its owning class is searched
+ * for the annotation.
+ *
+ * @param description
+ * a rule's owning description, which generally would be a test method or a test class
+ * (as these are the contexts in which rules are invoked)
+ * @param type
+ * the annotation type to look for
+ *
+ * @return the requested annotation, or {@code null} if none was found
+ */
+ public static <A extends Annotation> A getAnnotation(Description description, Class<A> type) {
+ A result = description.getAnnotation(type);
+
+ if ((result == null) && (description.getTestClass() != null)) {
+ result = description.getTestClass().getAnnotation(type);
+ }
+
+ return result;
+ }
+
+ /**
+ * Obtains the annotation of any one of the specified {@code types} applicable to a {@code description} that is supplied to a {@link TestRule}.
+ * If the description is for a test method, then if that method doesn't have any of the requested annotations, its owning class is searched
+ * for the annotations.
+ *
+ * @param description
+ * a rule's owning description, which generally would be a test method or a test class
+ * (as these are the contexts in which rules are invoked)
+ * @param types
+ * the annotation types to look for
+ *
+ * @return the first available of the requested annotations, or {@code null} if none was found
+ */
+ @SafeVarargs
+ public static Annotation getAnyAnnotation(Description description, final Class<? extends Annotation>... types) {
+ Annotation result = null;
+
+ for (Class<? extends Annotation> next : types) {
+ result = description.getAnnotation(next);
+ if (result != null) {
+ break;
+ }
+ }
+
+ if (result == null) {
+ out: for (Class<?> testClass = description.getTestClass(); testClass != null; testClass = testClass.getSuperclass()) {
+ for (Class<? extends Annotation> next : types) {
+ result = testClass.getAnnotation(next);
+ if (result != null) {
+ break out;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Queries whether the current JUnit test execution is running in the automated build environment
+ * (whether actually on the build server or not; users can run local builds on their development systems, too).
+ *
+ * @return whether the tests are running in the automated build environment
+ */
+ public static boolean isAutomatedBuildExecution() {
+ return Activator.getDefault().getRunningApplicationID().startsWith("org.eclipse.tycho."); //$NON-NLS-1$
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/LogTracker.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/LogTracker.java
new file mode 100644
index 00000000000..36ee26cc460
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/LogTracker.java
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.CoreMatchers.either;
+import static org.hamcrest.CoreMatchers.everyItem;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.ILogListener;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.papyrus.junit.matchers.MoreMatchers;
+import org.hamcrest.Matcher;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Lists;
+
+/**
+ * A configurable log listener to help tests to make assertions about the
+ * messages that are logged.
+ */
+public class LogTracker implements ILogListener {
+
+ private String bundle;
+
+ private Predicate<? super IStatus> filter;
+
+ private ILog log;
+
+ private final List<IStatus> messages = Lists.newArrayList();
+
+ public LogTracker() {
+ super();
+ }
+
+ /**
+ * Start tracking the specified {@code bundle}'s log.
+ *
+ * @param bundle
+ * the symbolic name of the bundle whose log is to be tracked
+ */
+ public void start(String bundle) {
+ start(bundle, null);
+ }
+
+ /**
+ * Start tracking the specified {@code bundle}'s log for particular messages.
+ *
+ * @param bundle
+ * the symbolic name of the bundle whose log is to be tracked
+ * @param filter
+ * a filter matching messages that should be recorded, or {@code null} to record all messages
+ */
+ public void start(String bundle, Predicate<? super IStatus> filter) {
+ if (filter == null) {
+ filter = Predicates.alwaysTrue();
+ }
+
+ this.bundle = bundle;
+ this.filter = filter;
+ this.log = Platform.getLog(Platform.getBundle(bundle));
+
+ // Individual ILog instances don't notify listeners
+ Platform.addLogListener(this);
+ }
+
+ public void dispose() {
+ if (log != null) {
+ Platform.removeLogListener(this);
+ log = null;
+
+ clear();
+ bundle = null;
+ filter = null;
+ }
+ }
+
+ public void clear() {
+ messages.clear();
+ }
+
+ @Override
+ public void logging(IStatus status, String plugin) {
+ if ((plugin.equals(bundle) || status.getPlugin().equals(bundle)) && filter.apply(status)) {
+ messages.add(status);
+ }
+ }
+
+ /**
+ * Assert that either there were no messages recorded, or they all satisfy an {@code assertion}.
+ */
+ public void assertAll(Matcher<? super IStatus> assertion) {
+ @SuppressWarnings("unchecked")
+ Matcher<IStatus> hamcrestSignatureWorkaround = (Matcher<IStatus>) assertion;
+ assertThat(messages, either(MoreMatchers.<IStatus> emptyIterable()).or(everyItem(hamcrestSignatureWorkaround)));
+ }
+
+ /**
+ * Assert at least one message was recorded, and all recorded messages satisfy an {@code assertion}.
+ */
+ public void assertExistAll(Matcher<? super IStatus> assertion) {
+ @SuppressWarnings("unchecked")
+ Matcher<IStatus> hamcrestSignatureWorkaround = (Matcher<IStatus>) assertion;
+ assertThat(messages, both(not(MoreMatchers.<IStatus> emptyIterable())).and(everyItem(hamcrestSignatureWorkaround)));
+ }
+
+ /**
+ * Assert that either there were no messages recorded, or they all satisfy an {@code assertion}.
+ */
+ public void assertNone(Matcher<? super IStatus> assertion) {
+ @SuppressWarnings("unchecked")
+ Matcher<IStatus> hamcrestSignatureWorkaround = (Matcher<IStatus>) assertion;
+ assertThat(messages, everyItem(not(hamcrestSignatureWorkaround)));
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelExplorerUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelExplorerUtils.java
new file mode 100644
index 00000000000..e52615587ce
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelExplorerUtils.java
@@ -0,0 +1,214 @@
+/*****************************************************************************
+ * Copyright (c) 2012, 2014 CEA LIST 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 386118
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.transaction.RunnableWithResult;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPage;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPageBookView;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerView;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.part.IPage;
+import org.junit.Assert;
+
+/**
+ * Useful methods for the ModelExplorer view
+ */
+public class ModelExplorerUtils {
+
+ /**
+ * the ID of the ModelExplorerView
+ */
+ private static final String ModelExplorerViewId = "org.eclipse.papyrus.views.modelexplorer.modelexplorer"; //$NON-NLS-1$
+
+ private ModelExplorerUtils() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @return
+ * the opened modelexplorer. Warning, it should be better that Papyrus was opened yet
+ * @throws PartInitException
+ */
+ public static ModelExplorerView openModelExplorerView() throws PartInitException {
+ IViewPart modelexplorer = null;
+ modelexplorer = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(ModelExplorerViewId);
+ final ModelExplorerPageBookView view = (ModelExplorerPageBookView)modelexplorer;
+ final IPage currentPage = view.getCurrentPage();
+ final ModelExplorerPage page = (ModelExplorerPage)currentPage;
+ final IViewPart viewer = page.getViewer();
+ Assert.assertNotNull(viewer);
+ viewer.setFocus();
+ return (ModelExplorerView)viewer;
+ }
+
+ /**
+ *
+ * @param view
+ * the modelexplorer to manipulate
+ * @param elements
+ * the elements to select
+ */
+ public static void setSelectionInTheModelexplorer(final ModelExplorerView view, List<?> elements) {
+ view.revealSemanticElement(elements);
+ final List<?> currentSelection = getCurrentSelectionInTheModelExplorer();
+ Assert.assertTrue("The current selection is not the wanted selection", elements.containsAll(currentSelection)); //$NON-NLS-1$
+ Assert.assertTrue("The current selection is not the wanted selection", currentSelection.containsAll(elements)); //$NON-NLS-1$
+ }
+
+ /**
+ *
+ * @return
+ * the object selected in the ModelExplorer
+ * //TODO : should be moved in the ModelExplorer
+ */
+ public static List<?> getCurrentSelectionInTheModelExplorer() {
+ final List<Object> selection = new ArrayList<Object>();
+ IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWorkbenchWindow!=null){
+ final IStructuredSelection currentSelection = (IStructuredSelection)activeWorkbenchWindow.getSelectionService().getSelection(ModelExplorerViewId);
+ final Iterator<?> iter = currentSelection.iterator();
+ while(iter.hasNext()) {
+ final Object current = iter.next();
+ EObject eObject = EMFHelper.getEObject(current);
+ if(eObject != null) {
+ selection.add(eObject);
+ } else {
+ selection.add(current);
+ }
+ }
+ }
+ return selection;
+ }
+
+ /**
+ *
+ * @param view
+ * the ModelExplorerView
+ * @return
+ * the root of the Model
+ * //TODO : should be moved in the ModelExplorer
+ */
+ public static final EObject getRootInModelExplorer(final ModelExplorerView view) {
+ view.getCommonViewer().expandToLevel(2);
+
+ // store the root of the model
+ final Object[] visibleElement = view.getCommonViewer().getVisibleExpandedElements();
+ EObject modelRoot = null;
+ if(visibleElement.length > 0) {
+ modelRoot = EMFHelper.getEObject(visibleElement[0]);
+ }
+ Assert.assertNotNull(modelRoot);
+ while(modelRoot.eContainer() != null) {
+ modelRoot = modelRoot.eContainer();
+ }
+ return modelRoot;
+ }
+
+ /**
+ *
+ * @param actionContext
+ * the creation context
+ * @param wantedResult
+ * the wanted result
+ */
+ public static final void testHandlerStatusInModelExplorer(final ModelExplorerView view, final String commandToTest, final EObject actionContext, boolean wantedResult) {
+ setSelectionInTheModelexplorer(view, Collections.singletonList(actionContext));
+ ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class);
+ Command cmd = commandService.getCommand(commandToTest);
+ IHandler handler = cmd.getHandler();
+ if(handler instanceof AbstractHandler) {
+ ((AbstractHandler)handler).setEnabled(commandToTest);
+ }
+ boolean res = handler.isEnabled();
+ Assert.assertEquals(wantedResult, res);
+ }
+
+ /**
+ * Execute an editor command creation and returns the current papyrus nested editor (you must verify that it is the correct editor to be sure of
+ * the command execution)
+ *
+ * @param currentPapyrusEditor
+ * the current PapyrusEditor
+ * @param view
+ * the model explorer view
+ * @param commandToExecute
+ * the command to execute
+ * @param actionContext
+ * the context used for the commadn (the selected elements)
+ * @param bundelID
+ * the bundle id
+ *
+ * @return
+ * the current papyrus nested editor (you must verify that it is the correct editor to be sure of
+ * the command execution)
+ */
+ public static final Object executeCreateNestedEditorHandlerInModelExplorer(final IMultiDiagramEditor currentPapyrusEditor, final ModelExplorerView view, final String commandToExecute, final EObject actionContext, final String bundelID) {
+ setSelectionInTheModelexplorer(view, Collections.singletonList(actionContext));
+ ICommandService commandService = (ICommandService)PlatformUI.getWorkbench().getService(ICommandService.class);
+ final Command cmd = commandService.getCommand(commandToExecute);
+ final IHandler handler = cmd.getHandler();
+ if(handler instanceof AbstractHandler) {
+ ((AbstractHandler)handler).setEnabled(commandToExecute);
+ }
+ final RunnableWithResult<?> runnableWithResult = new RunnableWithResult.Impl<Object>() {
+
+ public void run() {
+ try {
+ handler.execute(new ExecutionEvent(cmd, Collections.emptyMap(), null, null));
+ } catch (ExecutionException e) {
+ setStatus(new Status(IStatus.ERROR, bundelID, e.getMessage()));
+ }
+
+ IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IEditorPart activeEditor = activePage.getActiveEditor();
+ if(currentPapyrusEditor != activeEditor) {
+ setStatus(new Status(IStatus.ERROR, bundelID, "The current active editor is not the wanted Papyrus Editor")); //$NON-NLS-1$
+ }
+
+ setResult(currentPapyrusEditor.getActiveEditor());
+ setStatus(Status.OK_STATUS);
+ }
+ };
+ Display.getDefault().syncExec(runnableWithResult);
+ Assert.assertEquals(runnableWithResult.getStatus().getMessage(), IStatus.OK, runnableWithResult.getStatus().getSeverity());
+ Object result = runnableWithResult.getResult();
+ Assert.assertNotNull(result);
+ return result;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelUtils.java
new file mode 100644
index 00000000000..b5113e57722
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ModelUtils.java
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * Copyright (c) 2013, 2014 CEA LIST 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:
+ * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ * Christian W. Damus (CEA) - bug 437052
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler2;
+import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis;
+import org.eclipse.papyrus.infra.core.utils.DiResourceSet;
+
+/**
+ * Helper class for manipulating Papyrus ModelSets
+ *
+ * @author Camille Letavernier
+ *
+ */
+public class ModelUtils {
+
+ /**
+ * Loads a ModelSet and associates a TransactionalEditingDomain to it
+ *
+ * Use {@link #getEditingDomain(ModelSet)} to retrieve the EditingDomain
+ *
+ * @param uri
+ * @param resolveAll
+ * @return
+ * @throws ModelMultiException
+ */
+ public static ModelSet loadModelSet(URI uri, boolean resolveAll) throws ModelMultiException {
+ ModelSet modelSet = new DiResourceSet();
+ TransactionalEditingDomain.Factory.INSTANCE.createEditingDomain(modelSet);
+
+ modelSet.loadModels(uri);
+
+ if(resolveAll) {
+ EcoreUtil.resolveAll(modelSet);
+ }
+
+ return modelSet;
+ }
+
+ public static ModelSet loadModelSet(IPath workspacePath, boolean resolveAll) throws ModelMultiException {
+ URI workspaceURI = URI.createPlatformResourceURI(workspacePath.toString(), true);
+ return loadModelSet(workspaceURI, resolveAll);
+ }
+
+ public static TransactionalEditingDomain getEditingDomain(ModelSet modelSet) {
+ return TransactionalEditingDomain.Factory.INSTANCE.getEditingDomain(modelSet);
+ }
+
+ /**
+ * Attempts to set the specified resource URIs as writable according to the referenced-model handler.
+ *
+ * @param modelSet
+ * a model set
+ * @param uri
+ * referenced-model resource URIs to make writable
+ */
+ public static void makeReferencedModelsWritable(ModelSet modelSet, URI... uris) {
+ IReadOnlyHandler2 handler = modelSet.getReadOnlyHandler();
+ if(handler != null) {
+ try {
+ // It will have this field if it's a ReadOnlyManager
+ Field orderedHandlersByAxisField = handler.getClass().getDeclaredField("orderedHandlersByAxis"); //$NON-NLS-1$
+ orderedHandlersByAxisField.setAccessible(true);
+
+ // Find the referenced-model handler and make it non-interactive so that we don't attempt to pop up a user dialog
+ @SuppressWarnings("unchecked")
+ Map<ReadOnlyAxis, IReadOnlyHandler2[]> orderedHandlersByAxis = (Map<ReadOnlyAxis, IReadOnlyHandler2[]>)orderedHandlersByAxisField.get(handler);
+ for(IReadOnlyHandler2 next : orderedHandlersByAxis.get(ReadOnlyAxis.DISCRETION)) {
+ // If this handler supports user interaction, try to suppress it
+ Duck nextHandler = new Duck(next);
+ if(nextHandler.understands("setInteractive", false)) {
+ nextHandler.quack("setInteractive", false);
+ }
+
+ // And make the resources writable in this handler
+ if(next.canMakeWritable(ReadOnlyAxis.discretionAxes(), uris).or(false)) {
+ next.makeWritable(ReadOnlyAxis.discretionAxes(), uris);
+ }
+ }
+ } catch (Exception e) {
+ // OK, didn't work. Fine. It's expected for non-ReadOnlyManager
+ }
+
+ assertThat("Could not make referenced models writable: " + uris, handler.anyReadOnly(ReadOnlyAxis.discretionAxes(), uris).or(true), is(false));
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PackageExplorerUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PackageExplorerUtils.java
new file mode 100644
index 00000000000..cb3e3f1b8c6
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PackageExplorerUtils.java
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.util.List;
+
+import org.eclipse.jdt.ui.IPackagesViewPart;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.junit.Assert;
+
+/**
+ *
+ * Utils Methods for the Package Explorer
+ *
+ */
+public class PackageExplorerUtils {
+
+ /** ID of the Package Explorer View */
+ private static final String PACKAGE_EXPLORER_VIEW_ID = "org.eclipse.jdt.ui.PackageExplorer"; //$NON-NLS-1$
+
+ /**
+ * This methods opens the PackageExplorerView, and give it the focus
+ *
+ * @throws PartInitException
+ */
+ public static final IPackagesViewPart openPackageExplorerView() throws PartInitException {
+ final IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ IPackagesViewPart pack = null;
+ IViewPart packageExplorer = activeWorkbenchWindow.getActivePage().showView(PACKAGE_EXPLORER_VIEW_ID);
+ pack = (IPackagesViewPart)packageExplorer;
+ Assert.assertNotNull(pack);
+ pack.setFocus();
+ return pack;
+
+ }
+
+ /**
+ * Set the selection in the PackageExplorer
+ *
+ * @param packageExplorer
+ * the package explorer
+ * @param newSelection
+ * the new selection
+ */
+ public static final void setSelectionInPackageExplorerView(final IPackagesViewPart packageExplorer, final IStructuredSelection newSelection) {
+ packageExplorer.getTreeViewer().expandAll();
+ packageExplorer.getTreeViewer().setSelection(newSelection);
+ //we verify that the current selection is correct in the PackageExplorer
+ IStructuredSelection currentSelection = (IStructuredSelection)packageExplorer.getTreeViewer().getSelection();
+ Assert.assertEquals("Package Explorer: The current selection is not the same as the wanted selection", currentSelection.toList(), newSelection.toList()); //$NON-NLS-1$
+
+ //we verify that the current selection is correct using the selection service
+ currentSelection = getCurrentSelectionInPackageExplorerView();
+ Assert.assertEquals("Package Explorer: The SelectionService doesn't return the wanted selection", currentSelection.toList(), newSelection.toList()); //$NON-NLS-1$
+ }
+
+ /**
+ *
+ * @return
+ * the current selection in the PackageExplorer
+ */
+ public static final IStructuredSelection getCurrentSelectionInPackageExplorerView() {
+ return (IStructuredSelection)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(PACKAGE_EXPLORER_VIEW_ID);
+ }
+
+ /**
+ *
+ * @return
+ * the current selection in the PackageExplorer as List
+ */
+ public static final List<?> getCurrentSelectionAsListInPackageExplorerView() {
+ final IStructuredSelection selection = (IStructuredSelection)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService().getSelection(PACKAGE_EXPLORER_VIEW_ID);
+ final List<?> list = selection.toList();
+ return list;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PapyrusProjectUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PapyrusProjectUtils.java
new file mode 100644
index 00000000000..b7208951f4a
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PapyrusProjectUtils.java
@@ -0,0 +1,93 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Locale;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel;
+import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.internationalization.utils.PropertiesFilesUtils;
+import org.eclipse.papyrus.uml.tools.model.UmlModel;
+import org.junit.Assert;
+import org.osgi.framework.Bundle;
+
+public class PapyrusProjectUtils {
+
+ private PapyrusProjectUtils() {
+ //to prevent instanciation
+ }
+
+ /**
+ *
+ * @param project
+ * @param bundle
+ * @param sourcePath
+ * should be something like /model/
+ * @param fileRootName
+ * @throws IOException
+ * @throws CoreException
+ */
+ public static final IFile copyPapyrusModel(final IProject project, final Bundle bundle, final String sourcePath, final String fileRootName) throws CoreException, IOException {
+
+ String diSourcePath = sourcePath + fileRootName + "." + DiModel.MODEL_FILE_EXTENSION;
+ String notationSourcePath = sourcePath + fileRootName + "." + NotationModel.NOTATION_FILE_EXTENSION;
+ String umlSourcePath = sourcePath + fileRootName + "." + UmlModel.UML_FILE_EXTENSION;
+
+ final IFile emptyModel_di = copyIFile(diSourcePath, bundle, project, fileRootName + "." + DiModel.MODEL_FILE_EXTENSION);
+ copyIFile(notationSourcePath, bundle, project, fileRootName + "." + NotationModel.NOTATION_FILE_EXTENSION);
+ copyIFile(umlSourcePath, bundle, project, fileRootName + "." + UmlModel.UML_FILE_EXTENSION);
+
+ // Load existing properties files
+ for(final Locale locale : Locale.getAvailableLocales()){
+ String propertiesSourcePath = sourcePath + fileRootName + "_" + locale.toString() + "." + PropertiesFilesUtils.PROPERTIES_FILE_EXTENSION;
+ final URL bundleResource = bundle.getResource(propertiesSourcePath);
+ if(null != bundleResource){
+ copyIFile(propertiesSourcePath, bundle, project, fileRootName + "_" + locale.toString() + "." + PropertiesFilesUtils.PROPERTIES_FILE_EXTENSION);
+ }
+ }
+
+ return emptyModel_di;
+ }
+
+ public static IFile copyIFile(String sourcePath, Bundle sourceBundle, IProject targetProject, String targetFileName) throws CoreException, IOException {
+ final IFile createdFile = targetProject.getFile(targetFileName);
+ if(createdFile.getParent() instanceof IFolder) {
+ createRecursiveFolder((IFolder)createdFile.getParent());
+ }
+ URL bundleResource = sourceBundle.getResource(sourcePath);
+ Assert.assertNotNull("Cannot find bundle resource: " + sourcePath, bundleResource);
+ InputStream bundleResourceStream = bundleResource.openStream();
+ createdFile.create(bundleResourceStream, true, new NullProgressMonitor());
+ return createdFile;
+ }
+
+ public static void createRecursiveFolder(IFolder folderToCreate) throws CoreException {
+ if(folderToCreate.exists()) {
+ return;
+ }
+
+ if(folderToCreate.getParent() instanceof IFolder) {
+ createRecursiveFolder((IFolder)folderToCreate.getParent());
+ }
+ folderToCreate.create(true, true, new NullProgressMonitor());
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PrintingProgressMonitor.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PrintingProgressMonitor.java
new file mode 100644
index 00000000000..e2ca54127c3
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/PrintingProgressMonitor.java
@@ -0,0 +1,137 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils;
+
+import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.ProgressMonitorWrapper;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+
+/**
+ * A progress monitor that prints progress to standard output or some other
+ * {@link PrintStream}, optionally wrapping some other monitor.
+ */
+public class PrintingProgressMonitor extends ProgressMonitorWrapper {
+ private final PrintStream printTo;
+
+ private boolean first;
+
+ private Predicate<String> filter = Predicates.alwaysTrue();
+
+ /**
+ * Initializes me to print to standard output.
+ */
+ public PrintingProgressMonitor() {
+ this(System.out, new NullProgressMonitor());
+ }
+
+ /**
+ * Initializes me to print to some stream.
+ */
+ public PrintingProgressMonitor(PrintStream printTo) {
+ this(printTo, new NullProgressMonitor());
+ }
+
+ /**
+ * Initializes me to print to some stream and wrap another {@code monitor).
+ */
+ public PrintingProgressMonitor(PrintStream printTo, IProgressMonitor monitor) {
+ super(monitor);
+
+ this.printTo = printTo;
+ }
+
+ /**
+ * Adds a filter regular expression that matches task messages to exclude from
+ * the output (to promote quieter progress when appropriate).
+ *
+ * @param pattern
+ * a regular expression pattern for task messages to suppress
+ *
+ * @return myself, for the convenience of call chaining
+ */
+ public PrintingProgressMonitor filter(String pattern) {
+ Pattern regex = Pattern.compile(pattern);
+ final Matcher m = regex.matcher(""); //$NON-NLS-1$
+
+ Predicate<String> filter = new Predicate<String>() {
+ @Override
+ public boolean apply(String input) {
+ m.reset(input);
+ return !m.find();
+ }
+ };
+
+ this.filter = Predicates.and(filter, this.filter);
+
+ return this;
+ }
+
+ private void echo(boolean dashN, String text) {
+ echo(true, false, text);
+ }
+
+ private void echo(boolean initialNewline, boolean terminalNewline, String text) {
+ if (filter.apply(text)) {
+ if (first) {
+ first = false;
+ } else if (initialNewline) {
+ printTo.println();
+ }
+
+ printTo.print(text);
+
+ if (terminalNewline) {
+ printTo.println();
+ }
+ }
+ }
+
+ @Override
+ public void beginTask(String name, int totalWork) {
+ echo(true, name);
+ super.beginTask(name, totalWork);
+ }
+
+ @Override
+ public void setTaskName(String name) {
+ echo(true, name);
+ super.setTaskName(name);
+ }
+
+ @Override
+ public void subTask(String name) {
+ echo(true, name);
+ super.subTask(name);
+ }
+
+ @Override
+ public void worked(int work) {
+ echo(false, false, ".");
+ super.worked(work);
+ }
+
+ @Override
+ public void done() {
+ echo(false, true, " Done.");
+ super.done();
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ProjectUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ProjectUtils.java
new file mode 100644
index 00000000000..19bbcbf7309
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/ProjectUtils.java
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Copyright (c) 2012 CEA LIST.
+ *
+ *
+ * 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:
+ * Vincent Lorenzo (CEA LIST) Vincent.Lorenzo@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.junit.Assert;
+
+/**
+ *
+ * Useful methods for projects
+ *
+ */
+public class ProjectUtils {
+
+ private ProjectUtils() {
+ // to prevent instanciation
+ }
+
+ /**
+ *
+ * @param projectName
+ * the name of the projecy
+ * @return
+ * the created project
+ * @throws CoreException
+ */
+ public static final IProject createProject(final String projectName) throws CoreException {
+ final IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ final IProject testProject = workspace.getRoot().getProject(projectName);
+
+ if(testProject.exists()) {
+ testProject.delete(true, new NullProgressMonitor());
+ }
+ testProject.create(new NullProgressMonitor());
+ testProject.open(new NullProgressMonitor());
+
+ Assert.assertNotNull(testProject);
+ return testProject;
+ }
+
+ /**
+ * Remove all the projects in a workspace
+ *
+ * @throws CoreException
+ */
+ public static final void removeAllProjectFromTheWorkspace() throws CoreException {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ for(IProject project : workspace.getRoot().getProjects()) {
+ project.delete(true, new NullProgressMonitor());
+ }
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/SynchronousExecutorService.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/SynchronousExecutorService.java
new file mode 100644
index 00000000000..a6cddd29d97
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/SynchronousExecutorService.java
@@ -0,0 +1,188 @@
+/*****************************************************************************
+ * Copyright (c) 2014 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * A convenient {@link ExecutorService} implementation for test cases where we want to control
+ * when asynchronous tasks run.
+ */
+public class SynchronousExecutorService extends AbstractExecutorService {
+
+ /**
+ * A runnable to post to me to cause me to run all pending tasks. This lets the caller
+ * synchronize with me, to run and/or wait for all tasks up to that point.
+ *
+ * @see #flush()
+ */
+ public static final Runnable FLUSH = new Runnable() {
+ public void run() {
+ // Pass
+ }
+ };
+
+ private final AtomicBoolean isShutdown = new AtomicBoolean();
+ private final ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue<Runnable>();
+
+ private final Lock lock = new ReentrantLock();
+ private final Condition done = lock.newCondition();
+
+ /**
+ * Constructor.
+ */
+ public SynchronousExecutorService() {
+ super();
+ }
+
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ lock.lockInterruptibly();
+ try {
+ long now = System.currentTimeMillis();
+ long deadline = now + unit.toMillis(timeout);
+ while (!isTerminated()) {
+ if (done.await(deadline - now, TimeUnit.MILLISECONDS)) {
+ break;
+ }
+ now = System.currentTimeMillis();
+ if (now >= deadline) {
+ break;
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return isTerminated();
+ }
+
+ public boolean isShutdown() {
+ return isShutdown.get();
+ }
+
+ public boolean isTerminated() {
+ return isShutdown() && queue.isEmpty();
+ }
+
+ public void shutdown() {
+ if (isShutdown.compareAndSet(false, true)) {
+ queue.clear();
+ }
+ }
+
+ public List<Runnable> shutdownNow() {
+ List<Runnable> result;
+
+ lock.lock();
+ try {
+ if (isShutdown.compareAndSet(false, true)) {
+ result = ImmutableList.copyOf(queue);
+ queue.clear();
+ done.signalAll();
+ } else {
+ result = Collections.emptyList();
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return result;
+ }
+
+ public void execute(Runnable command) {
+ final boolean flush = isFlush(command);
+
+ lock.lock();
+ try {
+ if (isShutdown()) {
+ throw new RejectedExecutionException("executor is shut down");
+ }
+
+ // Even if it's FLUSH, enqueue it because somebody may be synchronizing on a Future wrapping it
+ queue.add(command);
+ } finally {
+ lock.unlock();
+ }
+
+ if (flush) {
+ flush();
+ }
+ }
+
+ public void flush() {
+ lock.lock();
+ try {
+ for (Runnable next = queue.poll(); next != null; next = queue.poll()) {
+ lock.unlock();
+
+ try {
+ next.run();
+ } catch (Exception e) {
+ final String bsn = "org.eclipse.papyrus.junit.utils";
+ IStatus status = new Status(IStatus.ERROR, bsn, "Uncaught exception in async runnable.", e);
+ Platform.getLog(Platform.getBundle(bsn)).log(status);
+ } finally {
+ lock.lock();
+ }
+ }
+
+ if (isShutdown()) {
+ done.signalAll();
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ protected <T> RunnableFuture<T> newTaskFor(Runnable task, T value) {
+ return new MyFutureTask<T>(task, value);
+ }
+
+ boolean isFlush(Runnable task) {
+ return (task == FLUSH) || ((task instanceof MyFutureTask<?>) && ((MyFutureTask<?>) task).task == FLUSH);
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class MyFutureTask<V> extends FutureTask<V> {
+ final Runnable task;
+
+ MyFutureTask(Runnable task, V value) {
+ super(task, value);
+
+ this.task = task;
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/TableUtils.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/TableUtils.java
new file mode 100644
index 00000000000..bec37f68246
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/TableUtils.java
@@ -0,0 +1,82 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST.
+ *
+ *
+ * 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:
+ * Benoit Maggi (CEA LIST) benoit.maggi@cea.fr - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.papyrus.infra.core.resource.AbstractBaseModel;
+import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.nattable.model.nattable.Table;
+import org.junit.Assert;
+
+/**
+ * Utility class for diagrams
+ */
+public class TableUtils {
+
+ /**
+ * Return the first table found with the specified name
+ * @param modelSet
+ * @param tableName
+ * @return
+ */
+ public static Table getNotationFirstTable(ModelSet modelSet, String tableName) {
+ IModel notationModel = modelSet.getModel(NotationModel.MODEL_ID);
+
+ AbstractBaseModel notationBaseModel = null;
+ if (notationModel instanceof AbstractBaseModel) {
+ notationBaseModel = (AbstractBaseModel) notationModel;
+ } else {
+ Assert.fail("notation model is not an abstract base model");
+ return null;
+ }
+ Assert.assertTrue("notation resource contains nothing", notationBaseModel.getResource().getContents().size() >= 1);
+ for (EObject eObject : notationBaseModel.getResource().getContents()) {
+ if (eObject instanceof Table && tableName.equals(((Table) eObject).getName())) {
+ return (Table) eObject;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the all tables found with the specified name
+ * @param modelSet
+ * @param tableName
+ * @return
+ */
+ public static Collection<Table> getAllNotationTable(ModelSet modelSet, String tableName) {
+ IModel notationModel = modelSet.getModel(NotationModel.MODEL_ID);
+ Collection<Table> tableList = new ArrayList<Table>();
+ AbstractBaseModel notationBaseModel = null;
+ if (notationModel instanceof AbstractBaseModel) {
+ notationBaseModel = (AbstractBaseModel) notationModel;
+ } else {
+ Assert.fail("notation model is not an abstract base model");
+ return null;
+ }
+ Assert.assertTrue("notation resource contains nothing", notationBaseModel.getResource().getContents().size() >= 1);
+ for (EObject eObject : notationBaseModel.getResource().getContents()) {
+ if (eObject instanceof Table && tableName.equals(((Table) eObject).getName())) {
+ tableList.add((Table) eObject);
+ }
+ }
+ return tableList;
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/ChangeCapture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/ChangeCapture.java
new file mode 100644
index 00000000000..c8d99f3f537
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/ChangeCapture.java
@@ -0,0 +1,84 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.resources;
+
+import org.eclipse.emf.ecore.change.ChangeDescription;
+import org.eclipse.emf.transaction.NotificationFilter;
+import org.eclipse.emf.transaction.ResourceSetChangeEvent;
+import org.eclipse.emf.transaction.ResourceSetListenerImpl;
+import org.eclipse.emf.transaction.Transaction;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+
+/**
+ * A listener that captures the {@link ChangeDescription}s of EMF {@link Transaction}s.
+ * it is {@link AutoCloseable} for convenience of ensuring that it is removed from the editing domain
+ * when no longer needed.
+ *
+ * @see #getChangeDescription()
+ */
+public class ChangeCapture extends ResourceSetListenerImpl implements AutoCloseable {
+
+ private TransactionalEditingDomain domain;
+
+ private ChangeDescription changeDescription;
+
+ /**
+ * Initializes me with my editing {@code domain}, to which I immediately begin listening for transactions
+ * (there is no need to add me as a listener explicitly). I only capture the changes of a transaction
+ * that actually makes non-read-only-compatible changes.
+ *
+ * @param domain
+ * my editing domain
+ */
+ public ChangeCapture(TransactionalEditingDomain domain) {
+ super(NotificationFilter.NOT_TOUCH);
+
+ this.domain = domain;
+ domain.addResourceSetListener(this);
+ }
+
+ @Override
+ public boolean isPostcommitOnly() {
+ return true;
+ }
+
+ @Override
+ public void resourceSetChanged(ResourceSetChangeEvent event) {
+ // Ignore unbatched (non-transactional) changes
+ if (event.getTransaction() != null) {
+ this.changeDescription = event.getTransaction().getChangeDescription();
+ }
+ }
+
+ /**
+ * Obtains the change description of the last committed transaction, if any.
+ *
+ * @return the last transaction's changes, or {@code null} if none
+ */
+ public ChangeDescription getChangeDescription() {
+ return changeDescription;
+ }
+
+ /**
+ * Detaches me from my editing domain.
+ */
+ @Override
+ public void close() {
+ if (domain != null) {
+ domain.removeResourceSetListener(this);
+ domain = null;
+ }
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/EcoreModel.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/EcoreModel.java
new file mode 100644
index 00000000000..bf36fb787a5
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/EcoreModel.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 485220
+ *
+ */
+package org.eclipse.papyrus.junit.utils.resources;
+
+import static org.junit.Assert.fail;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.papyrus.infra.core.resource.EMFLogicalModel;
+import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.utils.TransactionHelper;
+
+
+/**
+ * An {@link IModel} implementation for Ecore models that test cases may add to their {@link ModelSet}s for cases where it is expedient to
+ * work with models that are not UML.
+ */
+public class EcoreModel extends EMFLogicalModel {
+
+ public EcoreModel() {
+ super();
+ }
+
+ @Override
+ public String getIdentifier() {
+ return "test.ecore";
+ }
+
+ @Override
+ public String getModelFileExtension() {
+ return "ecore";
+ }
+
+ public EPackage getRoot() {
+ return (EPackage) EcoreUtil.getObjectByType(getResource().getContents(), EcorePackage.Literals.EPACKAGE);
+ }
+
+ @Override
+ public void createModel(URI uri) {
+ resourceURI = uri.appendFileExtension(getModelFileExtension());
+ resource = getResourceSet().createResource(resourceURI, EcorePackage.eCONTENT_TYPE);
+
+ final EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage();
+ ePackage.setName("package1");
+ ePackage.setNsPrefix("pkg1");
+ ePackage.setNsURI("http://www.eclipse.org/papyrus/test/fakemodel/ecore/package1");
+
+ try {
+ TransactionHelper.run(getModelManager().getTransactionalEditingDomain(), new Runnable() {
+
+ @Override
+ public void run() {
+ resource.getContents().add(ePackage);
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Creation of Ecore model failed: " + e.getLocalizedMessage());
+ }
+ }
+
+ @Override
+ protected boolean isSupportedRoot(EObject object) {
+ return EcorePackage.Literals.EPACKAGE.isInstance(object);
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/WorkspaceModificationAssertion.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/WorkspaceModificationAssertion.java
new file mode 100644
index 00000000000..7bc8e6257e7
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/resources/WorkspaceModificationAssertion.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014 CEA 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.resources;
+
+import static org.hamcrest.CoreMatchers.anything;
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+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.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.junit.utils.rules.AbstractHouseKeeperRule;
+import org.hamcrest.CoreMatchers;
+
+import com.google.common.collect.Sets;
+
+/**
+ * A simple fixture for making assertions on the workspace resource changes made (or not) by {@link ModelSet}s upon saving.
+ */
+public class WorkspaceModificationAssertion implements IResourceChangeListener {
+
+ private final IWorkspace ws = ResourcesPlugin.getWorkspace();
+
+ private final Set<URI> requireChange = Sets.newHashSet();
+
+ private final Set<URI> requireNoChange = Sets.newHashSet();
+
+ private final Set<URI> changed = Sets.newHashSet();
+
+ private CoreException exception;
+
+ public WorkspaceModificationAssertion(AbstractHouseKeeperRule houseKeeper) {
+ ws.addResourceChangeListener(this);
+ houseKeeper.cleanUpLater(this);
+ }
+
+ public void dispose() {
+ ws.removeResourceChangeListener(this);
+ reset();
+ }
+
+ public void requireChange(URI uri) {
+ assertThat("conflicting change requirement for " + uri, requireNoChange, not(hasItem(uri)));
+ requireChange.add(uri);
+ }
+
+ public void requireNoChange(URI uri) {
+ assertThat("conflicting change requirement for " + uri, requireChange, not(hasItem(uri)));
+ requireNoChange.add(uri);
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ event.getDelta().accept(new IResourceDeltaVisitor() {
+
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ if(delta.getResource().getType() == IResource.FILE) {
+ changed.add(URI.createPlatformResourceURI(delta.getFullPath().toString(), true));
+ }
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ e.printStackTrace();
+ exception = e;
+ }
+ }
+
+ public void reset() {
+ requireChange.clear();
+ requireNoChange.clear();
+ changed.clear();
+ exception = null;
+ }
+
+ public void save(final ModelSet modelSet) {
+ try {
+ try {
+ ws.run(new IWorkspaceRunnable() {
+
+ public void run(IProgressMonitor monitor) throws CoreException {
+ try {
+ modelSet.save(monitor);
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, "org.eclipse.papyrus.junit.utils", "Save failed.", e));
+ }
+ }
+ }, new NullProgressMonitor());
+ } catch (CoreException e) {
+ e.printStackTrace();
+ exception = e;
+ }
+
+ assertThat("Resource(s) saved that should not have been", Sets.intersection(requireNoChange, changed), not(CoreMatchers.<URI> hasItem(anything())));
+ assertThat("Resource(s) not saved that should have been", Sets.difference(requireChange, changed), not(CoreMatchers.<URI> hasItem(anything())));
+ assertThat("Save assertion failed with an exception", exception, nullValue());
+ } finally {
+ reset();
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractHouseKeeperRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractHouseKeeperRule.java
new file mode 100644
index 00000000000..13d90e49f78
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractHouseKeeperRule.java
@@ -0,0 +1,723 @@
+/*
+ * Copyright (c) 2014, 2015 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 476683
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.util.WrappedException;
+import org.eclipse.emf.ecore.EObject;
+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.EditingDomain;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gmf.runtime.common.core.command.ICommand;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.junit.utils.Duck;
+import org.eclipse.papyrus.junit.utils.EditorUtils;
+import org.eclipse.papyrus.junit.utils.PapyrusProjectUtils;
+import org.eclipse.papyrus.junit.utils.ProjectUtils;
+import org.eclipse.papyrus.junit.utils.rules.HouseKeeper.Disposer;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.osgi.framework.FrameworkUtil;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public abstract class AbstractHouseKeeperRule {
+
+ private static final LoadingCache<Class<?>, Field[]> leakProneInstanceFields = CacheBuilder.newBuilder().maximumSize(128).build(fieldCacheLoader(false));
+
+ private static final LoadingCache<Class<?>, Field[]> leakProneStaticFields = CacheBuilder.newBuilder().maximumSize(128).build(fieldCacheLoader(true));
+
+ private static final Function<Object, Disposer<Object>> DISPOSER_FUNCTION;
+
+ Object test;
+
+ String testName;
+
+ private List<Runnable> cleanUpActions;
+
+ static {
+ final Map<Class<?>, Function<Object, Disposer<?>>> disposers = Maps.newLinkedHashMap();
+
+ ResourceSetDisposer.register(disposers);
+ TransactionalEditingDomainDisposer.register(disposers);
+ WorkspaceResourceDisposer.register(disposers);
+ EditorDisposer.register(disposers);
+ CollectionDisposer.register(disposers);
+ MapDisposer.register(disposers);
+
+ // This one must be last because it matches any object
+ ReflectiveDisposer.register(disposers);
+
+ DISPOSER_FUNCTION = new Function<Object, Disposer<Object>>() {
+
+ private final Function<Object, Disposer<?>> nullFunction = Functions.constant(null);
+
+ @Override
+ public Disposer<Object> apply(Object input) {
+ Function<Object, Disposer<?>> resultFunction = nullFunction;
+
+ for (Map.Entry<Class<?>, Function<Object, Disposer<?>>> next : disposers.entrySet()) {
+ if (next.getKey().isInstance(input)) {
+ resultFunction = next.getValue();
+ break;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ Disposer<Object> result = (Disposer<Object>) resultFunction.apply(input);
+ return result;
+ }
+ };
+ }
+
+ AbstractHouseKeeperRule() {
+ super();
+ }
+
+ /**
+ * Obtains the test name (may as well provide it, since we are a test rule).
+ *
+ * @return the current test name
+ */
+ public final String getTestName() {
+ return testName;
+ }
+
+ /**
+ * Adds an {@code object} to clean up later, with a {@code disposer} method that is invoked reflectively to do the cleaning up.
+ *
+ * @param object
+ * an object to dispose after the test has completed
+ * @param disposer
+ * the disposal method name
+ * @param arg
+ * arguments (if any) to the {@code disposer} method
+ *
+ * @return the {@code object}, for convenience
+ */
+ public <T> T cleanUpLater(T object, String disposer, Object... arg) {
+ assertThat("No such disposal method", new Duck(object).understands(disposer, arg), is(true));
+ return cleanUpLater(object, new ReflectiveDisposer(disposer, arg));
+ }
+
+ /**
+ * Adds an {@code object} to clean up later, with a {@code disposer} that does the cleaning up.
+ *
+ * @param object
+ * an object to dispose after the test has completed
+ * @param disposer
+ * the disposal behaviour
+ *
+ * @return the {@code object}, for convenience
+ */
+ public <T> T cleanUpLater(T object, Disposer<? super T> disposer) {
+ if (cleanUpActions == null) {
+ cleanUpActions = Lists.newLinkedList();
+ }
+
+ // Clean up in reverse order to best manage dependencies between cleaned-up objects
+ cleanUpActions.add(0, new CleanUpAction(object, disposer));
+ return object;
+ }
+
+ /**
+ * Adds an {@code object} to clean up later, using the appropriate built-in disposer.
+ * Fails if the {@code object} does not have a corresponding built-in disposer.
+ *
+ * @param object
+ * an object to dispose after the test has completed
+ *
+ * @return the {@code object}, for convenience
+ */
+ public <T> T cleanUpLater(T object) {
+ @SuppressWarnings("unchecked")
+ Disposer<T> disposer = (Disposer<T>) DISPOSER_FUNCTION.apply(object);
+ assertThat("No built-in disposer available", disposer, notNullValue());
+ return cleanUpLater(object, disposer);
+ }
+
+ /**
+ * Obtains a new resource set that will be disposed of automatically after the test completes.
+ *
+ * @return the new resource set
+ */
+ public ResourceSet createResourceSet() {
+ return cleanUpLater(new ResourceSetImpl(), ResourceSetDisposer.INSTANCE);
+ }
+
+ /**
+ * Creates a new editing domain that will be disposed of automatically after the test completes.
+ *
+ * @return the editing domain
+ */
+ public TransactionalEditingDomain createSimpleEditingDomain() {
+ return createSimpleEditingDomain(null);
+ }
+
+ /**
+ * Creates a new editing domain that will be disposed of automatically after the test completes.
+ *
+ * @param resourceSet
+ * the resource set on which to create the editing domain (or {@code null} to create a default one)
+ *
+ * @return the editing domain
+ */
+ public TransactionalEditingDomain createSimpleEditingDomain(ResourceSet resourceSet) {
+ if (resourceSet == null) {
+ resourceSet = createResourceSet();
+ }
+
+ return cleanUpLater(TransactionalEditingDomain.Factory.INSTANCE.createEditingDomain(resourceSet), TransactionalEditingDomainDisposer.INSTANCE);
+ }
+
+ /**
+ * Creates a project that will be disposed of automatically after the test completes.
+ *
+ * @param name
+ * the name of the project
+ *
+ * @return the project
+ */
+ public IProject createProject(String name) {
+ try {
+ return cleanUpLater(ProjectUtils.createProject(name), WorkspaceResourceDisposer.INSTANCE);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ return null; // Unreachable
+ }
+ }
+
+ /**
+ * Creates a file in the specified {@code project} with the given {@code fileName}, initialized by copying a
+ * template resource from the test class's originating bundle.
+ *
+ * @param project
+ * the test project in which to create the file
+ * @param fileName
+ * the name of the file to create
+ * @param templatePath
+ * the path in the test bundle of the template file to copy
+ *
+ * @return the new file
+ */
+ public IFile createFile(IProject project, String fileName, String templatePath) {
+ Class<?> testClass = (test instanceof Class<?>) ? (Class<?>) test : test.getClass();
+
+ try {
+ return cleanUpLater(PapyrusProjectUtils.copyIFile(templatePath, FrameworkUtil.getBundle(testClass), project, fileName), //
+ WorkspaceResourceDisposer.INSTANCE);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ return null; // Unreachable
+ }
+ }
+
+ /**
+ * Opens the default editor on the given {@code file} and ensures that it will be closed after the test terminates.
+ *
+ * @param file
+ * the file to open in its editor
+ *
+ * @return the editor
+ */
+ public IEditorPart openEditor(final IFile file) {
+ final IEditorPart[] result = { null };
+
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ result[0] = cleanUpLater(EditorUtils.openEditor(file), EditorDisposer.INSTANCE);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+ });
+
+ return result[0];
+ }
+
+ /**
+ * Opens the Papyrus editor on the given {@code file} and ensures that it will be closed after the test terminates.
+ *
+ * @param file
+ * the file to open in the Papyrus editor
+ *
+ * @return the editor
+ */
+ public IMultiDiagramEditor openPapyrusEditor(final IFile file) throws Exception {
+ final IMultiDiagramEditor[] result = { null };
+ final AtomicReference<Exception> syncExecException = new AtomicReference<Exception>();
+
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ result[0] = cleanUpLater(EditorUtils.openPapyrusEditor(file), EditorDisposer.INSTANCE);
+ } catch (Exception ex) {
+ syncExecException.set(ex);
+ }
+ }
+ });
+
+ if (syncExecException.get() != null) {
+ throw syncExecException.get();
+ }
+
+ return result[0];
+ }
+
+ /**
+ * Obtains the value of the named field of the test instance and ensures that it will be automatically cleared after the test completes.
+ *
+ * @param fieldName
+ * the field to access now and clear later
+ *
+ * @return the value of the field
+ *
+ * @deprecated Use the {@link CleanUp @CleanUp} annotation on the field and access it directly.
+ */
+ @Deprecated
+ public <T> T getField(String fieldName) {
+ try {
+ Field field = field(fieldName);
+
+ @SuppressWarnings("unchecked")
+ T result = (T) field.get(test);
+ cleanUpLater(field, new FieldDisposer());
+
+ return result;
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(String.format("Could not access field %s of test instance.", fieldName));
+ return null; // Unreachable
+ }
+ }
+
+ Field field(String fieldName) {
+ Field result = null;
+
+ for (Class<?> next = getTestClass(); (result == null) && (next != null) && (next != Object.class); next = next.getSuperclass()) {
+ try {
+ result = next.getDeclaredField(fieldName);
+ if (result != null) {
+ result.setAccessible(true);
+ }
+ } catch (Exception e) {
+ // Keep looking
+ result = null;
+ }
+ }
+
+ assertThat(String.format("Could not access field %s of test instance.", fieldName), result, notNullValue());
+ assertThat(String.format("Field is not %sstatic", isStatic() ? "" : "non-"), Modifier.isStatic(result.getModifiers()), is(isStatic()));
+
+ return result;
+ }
+
+ /**
+ * Sets the value of the named field of the test instance and ensures that it will be automatically cleared after the test completes.
+ *
+ * @param fieldName
+ * the field to access now and clear later
+ * @param value
+ * the value to set
+ *
+ * @return the new value of the field
+ *
+ * @deprecated Use the {@link CleanUp @CleanUp} annotation on the field and access it directly.
+ */
+ @Deprecated
+ public <T> T setField(String fieldName, T value) {
+ try {
+ Field field = field(fieldName);
+ field.set(test, value);
+ cleanUpLater(field, new FieldDisposer());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(String.format("Could not access field %s of test instance.", fieldName));
+ }
+
+ return value;
+ }
+
+ abstract boolean isStatic();
+
+ abstract Class<?> getTestClass();
+
+
+ void registerAutoCleanups() {
+ try {
+ final boolean staticFields = isStatic();
+
+ // Get all inherited fields, too
+ for (Class<?> next = getTestClass(); (next != null) && (next != Object.class); next = next.getSuperclass()) {
+ for (Field field : next.getDeclaredFields()) {
+ CleanUp cleanUp = field.getAnnotation(CleanUp.class);
+
+ if ((cleanUp != null) && (Modifier.isStatic(field.getModifiers()) == staticFields) && !Modifier.isFinal(field.getModifiers())) {
+ try {
+ field.setAccessible(true);
+
+ Class<? extends Disposer<?>> disposerClass = cleanUp.value();
+ if (disposerClass == FieldDisposer.class) {
+ // Default case
+ cleanUpLater(field, new FieldDisposer());
+ } else {
+ // Custom case
+
+ // Handle inner classes
+ Constructor<? extends Disposer<?>> ctor;
+ Object[] args;
+ if (disposerClass.getDeclaringClass() != null && ((disposerClass.getModifiers() & Modifier.STATIC) == 0)) {
+ ctor = disposerClass.getDeclaredConstructor(disposerClass.getDeclaringClass());
+ args = new Object[] { this };
+ } else {
+ ctor = disposerClass.getConstructor();
+ args = new Object[0];
+ }
+ ctor.setAccessible(true);
+
+ @SuppressWarnings("unchecked")
+ Disposer<Object> disposer = (Disposer<Object>) ctor.newInstance(args);
+ cleanUpLater(field.get(test), disposer);
+ }
+ } catch (Exception e) {
+ // Can't make it accessible? Then it's of no use.
+ // Likewise any problem in creating the disposer
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ // We tried our best. Don't propagate as a test failure because the test didn't ask for this
+ }
+ }
+
+ void cleanUp() throws Exception {
+ cleanUpLeakProneFields();
+
+ if (cleanUpActions != null) {
+ Exception toThrow = null;
+
+ for (Runnable next : cleanUpActions) {
+ try {
+ next.run();
+ } catch (Exception e) {
+ // Unwrap
+ if (e instanceof WrappedException) {
+ e = ((WrappedException) e).exception();
+ }
+
+ e.printStackTrace();
+ if (toThrow == null) {
+ toThrow = e;
+ }
+ }
+ }
+
+ cleanUpActions = null;
+
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+ }
+
+ /**
+ * Automatically clear all fields of the test instance that are of some {@link EObject} type.
+ */
+ private void cleanUpLeakProneFields() {
+ try {
+ final Field[] fields = isStatic() ? leakProneStaticFields.get(getTestClass()) : leakProneInstanceFields.get(getTestClass());
+
+ for (int i = 0; i < fields.length; i++) {
+ fields[i].set(test, null);
+ }
+ } catch (Exception e) {
+ // We tried our best. Don't propagate as a test failure because the test didn't ask for this
+ }
+ }
+
+ private static CacheLoader<Class<?>, Field[]> fieldCacheLoader(final boolean staticFields) {
+ return new CacheLoader<Class<?>, Field[]>() {
+
+ @Override
+ public Field[] load(Class<?> key) {
+ List<Field> result = Lists.newArrayList();
+
+ // Get all inherited fields, too
+ for (Class<?> next = key; (next != null) && (next != Object.class); next = next.getSuperclass()) {
+ for (Field field : next.getDeclaredFields()) {
+ if ((Modifier.isStatic(field.getModifiers()) == staticFields) && !Modifier.isFinal(field.getModifiers()) && isLeakProne(field)) {
+ try {
+ field.setAccessible(true);
+ result.add(field);
+ } catch (Exception e) {
+ // Can't make it accessible? Then it's of no use
+ }
+ }
+ }
+ }
+
+ return Iterables.toArray(result, Field.class);
+ }
+ };
+ }
+
+ private static boolean isLeakProne(Field field) {
+ Class<?> type = field.getType();
+ return EObject.class.isAssignableFrom(type) || Resource.class.isAssignableFrom(type) //
+ || ResourceSet.class.isAssignableFrom(type) || EditingDomain.class.isAssignableFrom(type) //
+ || EditPart.class.isAssignableFrom(type) //
+ || Command.class.isAssignableFrom(type) || org.eclipse.gef.commands.Command.class.isAssignableFrom(type) //
+ || IUndoableOperation.class.isAssignableFrom(type) || ICommand.class.isAssignableFrom(type);
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * Annotates fields for automatic clean-up.
+ */
+ @Retention(RUNTIME)
+ @Target(FIELD)
+ public @interface CleanUp {
+ /**
+ * Optionally specifies a disposer class to instantiate to clean
+ * up the annotated field. By default, the field is simply
+ * cleared to {@code null}.
+ */
+ Class<? extends Disposer<?>>value() default FieldDisposer.class;
+ }
+
+ private static final class CleanUpAction implements Runnable {
+
+ private final Object target;
+
+ private final Disposer<Object> disposer;
+
+ @SuppressWarnings("unchecked")
+ <T> CleanUpAction(T object, Disposer<? super T> disposer) {
+ this.target = object;
+ this.disposer = (Disposer<Object>) disposer;
+ }
+
+ @Override
+ public void run() {
+ try {
+ disposer.dispose(target);
+ } catch (Exception e) {
+ throw new WrappedException(e);
+ }
+ }
+ }
+
+ private static final class ResourceSetDisposer implements Disposer<ResourceSet> {
+
+ static final ResourceSetDisposer INSTANCE = new ResourceSetDisposer();
+
+ private ResourceSetDisposer() {
+ super();
+ }
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(ResourceSet.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(ResourceSet object) {
+ if (object instanceof ModelSet) {
+ ((ModelSet) object).unload();
+ }
+
+ // No harm in hitting a ModelSet again
+ EMFHelper.unload(object);
+ }
+ }
+
+ private static final class TransactionalEditingDomainDisposer implements Disposer<TransactionalEditingDomain> {
+
+ static final TransactionalEditingDomainDisposer INSTANCE = new TransactionalEditingDomainDisposer();
+
+ private TransactionalEditingDomainDisposer() {
+ super();
+ }
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(TransactionalEditingDomain.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(TransactionalEditingDomain object) {
+ object.dispose();
+ }
+ }
+
+ private final class FieldDisposer implements Disposer<Field> {
+
+ @Override
+ public void dispose(Field object) throws Exception {
+ object.set(test, null);
+ }
+ }
+
+ private static final class WorkspaceResourceDisposer implements Disposer<IResource> {
+
+ static final WorkspaceResourceDisposer INSTANCE = new WorkspaceResourceDisposer();
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(IResource.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(IResource object) throws Exception {
+ switch (object.getType()) {
+ case IResource.PROJECT:
+ case IResource.FOLDER:
+ case IResource.FILE:
+ object.delete(true, null);
+ break;
+ default:
+ // Delete the workspace? No, I don't think so
+ fail("Cannot delete resource " + object);
+ break;
+ }
+ }
+ }
+
+ private static final class EditorDisposer implements Disposer<IEditorPart> {
+
+ static final EditorDisposer INSTANCE = new EditorDisposer();
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(IEditorPart.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(final IEditorPart object) throws Exception {
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ IWorkbenchPage page = (object.getSite() == null) ? null : object.getSite().getPage();
+ if (page != null) {
+ try {
+ page.closeEditor(object, false);
+ } catch (Exception e) {
+ // Best effort
+ }
+ }
+ }
+ });
+ }
+ }
+
+ private static final class CollectionDisposer implements Disposer<Collection<?>> {
+
+ static final CollectionDisposer INSTANCE = new CollectionDisposer();
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(Collection.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(final Collection<?> object) throws Exception {
+ object.clear();
+ }
+ }
+
+ private static final class MapDisposer implements Disposer<Map<?, ?>> {
+
+ static final MapDisposer INSTANCE = new MapDisposer();
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(Map.class, Functions.<Disposer<?>> constant(INSTANCE));
+ }
+
+ @Override
+ public void dispose(final Map<?, ?> object) throws Exception {
+ object.clear();
+ }
+ }
+
+ private static final class ReflectiveDisposer implements Disposer<Object> {
+
+ static final ReflectiveDisposer INSTANCE = new ReflectiveDisposer("dispose"); //$NON-NLS-1$
+
+ private final String disposeMethod;
+
+ private final Object[] arguments;
+
+ ReflectiveDisposer(String methodName, Object... arguments) {
+ this.disposeMethod = methodName;
+ this.arguments = arguments;
+ }
+
+ static void register(Map<Class<?>, Function<Object, Disposer<?>>> disposers) {
+ disposers.put(Object.class, new Function<Object, Disposer<?>>() {
+
+ @Override
+ public Disposer<?> apply(Object input) {
+ Duck duck = new Duck(input);
+
+ return duck.understands(INSTANCE.disposeMethod, INSTANCE.arguments) ? INSTANCE : null;
+ }
+ });
+ }
+
+ @Override
+ public void dispose(Object object) throws Exception {
+ new Duck(object).quack(disposeMethod, arguments);
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java
new file mode 100755
index 00000000000..f220d1d9088
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AbstractModelFixture.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bugs 399859, 451230, 458685, 469188, 485220, 496299
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+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.ecore.util.EcoreUtil;
+import org.eclipse.emf.ecore.xmi.XMLResource;
+import org.eclipse.emf.ecore.xml.type.AnyType;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.workspace.IWorkspaceCommandStack;
+import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.tools.util.TypeUtils;
+import org.eclipse.papyrus.junit.utils.JUnitUtils;
+import org.eclipse.papyrus.uml.tools.model.UmlModel;
+import org.eclipse.uml2.uml.Package;
+import org.eclipse.uml2.uml.Profile;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.junit.Rule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.ByteSource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import com.google.common.io.Resources;
+
+
+/**
+ * Abstract superclass for JUnit test fixture rules that provide:
+ * <ul>
+ * <li>an editing domain of some kind (subclasses must create it)</li>
+ * <li>a test project in the workspace, exposed via a nested {@link ProjectFixture} rule</li>
+ * <li>a test {@link Package} loaded from a resource in the plug-in and saved as <tt>model.uml</tt> in the test project. This model is specified using an annotation on the test, as described below</li>
+ * </ul>
+ * The test model template to load into the editing domain and project must be specified by one of the following annotations:
+ * <ul>
+ * <li>{@link JavaResource @JavaResource}: specifies the path to a resource to be loaded from the test class's classpath, using the {@link Class#getResource(String)} API</li>
+ * <li>{@link PluginResource @PluginResource}: specifies a path relative to the root of the OSGi bundle containing the test class, to be loaded via the {@link Bundle#getEntry(String)} API</li>
+ * </ul>
+ * The resource annotation may be specified either on the test method, in which case it applies to that test case, or on the test
+ * class, in which case it applies to all test methods in the class that do not have a resource annotation of their own (method
+ * annotations take precedence over the class annotation).
+ */
+public abstract class AbstractModelFixture<T extends EditingDomain> extends TestWatcher {
+
+ private final ProjectFixture project = new ProjectFixture();
+
+ private T domain;
+
+ private EObject root;
+
+ private Package model;
+
+ private Class<?> testClass;
+
+ private Iterable<URI> initialResourceURIs;
+
+ public AbstractModelFixture() {
+ super();
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ testClass = JUnitUtils.getTestClass(description);
+
+ // Wrap myself in the project rule so that the project exists when I start
+ Statement result = super.apply(base, description);
+ result = project.apply(result, description);
+ return result;
+ }
+
+ /**
+ * Obtains the nested project fixture rule. If stored in a field of the test class, it must not be annotated as a {@link Rule @Rule} because that
+ * would result in double initialization of the rule.
+ *
+ * @return the nested project fixture
+ */
+ public ProjectFixture getProject() {
+ return project;
+ }
+
+ public T getEditingDomain() {
+ return domain;
+ }
+
+ public void execute(Command command) {
+ assertThat("Command not executable", command.canExecute(), is(true));
+ getEditingDomain().getCommandStack().execute(command);
+ }
+
+ public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) {
+ assertThat("Operation not executable", operation.canExecute(), is(true));
+ assertThat("No operation history available", getEditingDomain().getCommandStack(), instanceOf(IWorkspaceCommandStack.class));
+
+ try {
+ IWorkspaceCommandStack stack = (IWorkspaceCommandStack) getEditingDomain().getCommandStack();
+ operation.addContext(stack.getDefaultUndoContext());
+ return stack.getOperationHistory().execute(operation, monitor, info);
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ fail("Command execution failed: " + e.getLocalizedMessage());
+ return null; // Unreachable
+ }
+ }
+
+ public IStatus execute(IUndoableOperation operation) {
+ return execute(operation, null, null);
+ }
+
+ public boolean canUndo() {
+ return getEditingDomain().getCommandStack().canUndo();
+ }
+
+ public void undo() {
+ assertThat("Cannot undo", canUndo(), is(true));
+ getEditingDomain().getCommandStack().undo();
+ }
+
+ public boolean canRedo() {
+ return getEditingDomain().getCommandStack().canRedo();
+ }
+
+ public void redo() {
+ assertThat("Cannot redo", canRedo(), is(true));
+ getEditingDomain().getCommandStack().redo();
+ }
+
+ public ResourceSet getResourceSet() {
+ EditingDomain domain = getEditingDomain();
+ return (domain == null) ? null : domain.getResourceSet();
+ }
+
+ /**
+ * Obtains the first root of the main test resource.
+ *
+ * @return the first test resource root
+ */
+ public EObject getRoot() {
+ return root;
+ }
+
+ /**
+ * Obtains the test model, which is resident in the <tt>model.uml</tt> file in the test project (as indicated by its {@linkplain #getModelResourceURI() URI}).
+ *
+ * @return the test model
+ */
+ public Package getModel() {
+ return model;
+ }
+
+ public Resource getModelResource() {
+ return getRoot().eResource();
+ }
+
+ public URI getModelResourceURI() {
+ return getModelResource().getURI();
+ }
+
+ public URI getModelURI() {
+ return EcoreUtil.getURI(getRoot());
+ }
+
+ protected abstract T createEditingDomain();
+
+ @Override
+ protected void starting(Description description) {
+ domain = createEditingDomain();
+
+ Resource main = Iterables.getFirst(initModelResources(description), null);
+ assertThat("No main UML resource in model fixture", main, notNullValue());
+
+ root = main.getContents().get(0);
+ if (root instanceof Package) {
+ model = (Package) root;
+ }
+
+ // We have finished initializing
+ initialResourceURIs = null;
+
+ didLoadResourceSet();
+ }
+
+ protected void didLoadResourceSet() {
+ // Pass
+ }
+
+ protected Iterable<Resource> initModelResources(Description description) {
+ List<Resource> result;
+
+ // Don't initialize the resources more than once (subclasses such as PapyrusEditorFixture can repeat this)
+ if (initialResourceURIs == null) {
+ Annotation resourceAnnotation = getResourceAnnotation(description);
+ ResourceKind kind = ResourceKind.getResourceKind(resourceAnnotation);
+
+ final String[] paths = kind.getResourcePaths(resourceAnnotation);
+ result = Lists.newArrayListWithCapacity(paths.length);
+
+ for (String path : paths) {
+ // Ensure that the bundle ID prefix, if any, is taken as the "device"
+ result.add(initModelResource(Path.forWindows(path), kind));
+ }
+
+ List<URI> uris = Lists.newArrayListWithCapacity(result.size());
+ for (Resource next : result) {
+ uris.add(next.getURI());
+ }
+ initialResourceURIs = uris;
+
+ // Ensure that the ModelSet's IModels are started
+ ModelSet modelSet = TypeUtils.as(getResourceSet(), ModelSet.class);
+ if (modelSet != null) {
+ // It doesn't matter that the resource is already loaded
+ try {
+ modelSet.loadModels(result.get(0).getURI());
+ } catch (ModelMultiException e) {
+ // Continue with the test as well as we can
+ e.printStackTrace();
+ }
+ }
+ } else {
+ ResourceSet rset = getResourceSet();
+ boolean bootstrapResourceSet = rset == null;
+ if (bootstrapResourceSet) {
+ // Bootstrap the initialization of the test model with a plain resource set
+ rset = new ResourceSetImpl();
+ rset.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, true);
+ rset.getLoadOptions().put(XMLResource.OPTION_LAX_FEATURE_PROCESSING, true);
+ }
+ result = Lists.newArrayList();
+ try {
+ for (URI next : initialResourceURIs) {
+ result.add(rset.getResource(next, true));
+ }
+ } finally {
+ if (bootstrapResourceSet) {
+ EMFHelper.unload(rset);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Resource initModelResource(IPath resourcePath, ResourceKind kind) {
+ String targetResourceName = "model";
+ if (isDIModel(resourcePath)) {
+ // We will be initializing all three resources, and they have cross-references, so must not change
+ // resource name
+ targetResourceName = resourcePath.removeFileExtension().lastSegment();
+ }
+
+ return initModelResource(targetResourceName, kind, resourcePath.toString());
+ }
+
+ protected boolean isDIModel(IPath path) {
+ String fileExtension = path.getFileExtension();
+ return DiModel.DI_FILE_EXTENSION.equals(fileExtension);
+ }
+
+ protected Resource initModelResource(String targetPath, ResourceKind resourceKind, String resourcePath) {
+ Resource result = null;
+
+ ResourceSet resourceSet = getResourceSet();
+ final boolean bootstrapResourceSet = resourceSet == null;
+ if (bootstrapResourceSet) {
+ // Bootstrap the initialization of the test model with a plain resource set
+ resourceSet = new ResourceSetImpl();
+ resourceSet.getLoadOptions().put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, true);
+ resourceSet.getLoadOptions().put(XMLResource.OPTION_LAX_FEATURE_PROCESSING, true);
+ }
+
+ Set<Resource> toUnload = Sets.newHashSet();
+ try {
+ // Ensure that the bundle ID prefix, if any, is taken as the "device"
+ IPath resourceIPath = Path.forWindows(resourcePath);
+ if (isDIModel(resourceIPath)) {
+ // Try to initialize the entire collection of files
+ resourceIPath = resourceIPath.removeFileExtension();
+
+ Map<IPath, Boolean> manifest = loadManifest(resourceKind, resourceIPath);
+ for (Map.Entry<IPath, Boolean> next : manifest.entrySet()) {
+ Resource resource = doInitModelResource(resourceSet, targetPath, resourceKind, next.getKey());
+
+ if ((result == null) && UmlModel.UML_FILE_EXTENSION.equals(next.getKey().getFileExtension())) {
+ // We should always have this one, at least, and it's the one we most care about
+ result = resource;
+ }
+
+ if (!next.getValue()) {
+ // Unload this resource
+ toUnload.add(resource);
+ }
+ }
+ } else {
+ result = doInitModelResource(resourceSet, targetPath, resourceKind, resourceIPath);
+ }
+
+ if (result == null) {
+ fail("No UML resource in test model");
+ }
+
+ // Look for any other dependencies (libraries, profiles, etc.) that also need to be copied
+ Queue<Resource> dependents = new LinkedList<>();
+ Set<Resource> scanned = new HashSet<>();
+ dependents.add(result);
+ boolean loadedProfiles = false;
+ for (Resource dependent = dependents.poll(); dependent != null; dependent = dependents.poll()) {
+ if (scanned.add(dependent)) {
+ URI baseURI = result.getURI().trimSegments(1);
+ if (!baseURI.isPrefix()) {
+ baseURI = baseURI.appendSegment("");
+ }
+
+ for (EObject proxy : EcoreUtil.UnresolvedProxyCrossReferencer.find(dependent).keySet()) {
+ URI dependencyURI = EcoreUtil.getURI(proxy).trimFragment();
+ if (dependencyURI.toString().startsWith(baseURI.toString())) {
+ Resource dependency = resourceSet.getResource(dependencyURI, false);
+ if ((dependency == null) || !dependency.isLoaded() || !dependency.getErrors().isEmpty()) {
+ // It should be available in the test bundle. Try to get it
+ URI relative = dependencyURI.deresolve(baseURI);
+ IPath depPath = resourceIPath.removeLastSegments(1).append(URI.decode(relative.toString()));
+ if (resourceKind.exists(testClass, depPath)) {
+ if (dependency == null) {
+ dependency = resourceSet.createResource(dependencyURI);
+ } else {
+ dependency.unload();
+ }
+
+ dependency = doInitModelResource(resourceSet, URI.decode(relative.toString()), resourceKind, depPath);
+ loadedProfiles = loadedProfiles || Iterables.any(dependency.getContents(), Predicates.instanceOf(Profile.class));
+
+ // Enqueue this for recursive dependency processing
+ dependents.add(dependency);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If we depend on profiles, then we may have stereotype applications that need to resolve against that schema.
+ // In such case, re-load the model resource to resolve the stereotype schema
+ if (loadedProfiles && Iterables.any(result.getContents(), Predicates.instanceOf(AnyType.class))) {
+ try {
+ result.unload();
+ result.load(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Error re-loading resource to resolve stereotype schema: " + e.getLocalizedMessage());
+ }
+ }
+
+ // Now unload resources that the manifest indicates should not be loaded initially
+ for (Resource next : toUnload) {
+ next.unload();
+ next.getResourceSet().getResources().remove(next);
+ next.eAdapters().clear();
+ }
+ } finally {
+ if (bootstrapResourceSet) {
+ EMFHelper.unload(resourceSet);
+ }
+ }
+
+ return result;
+ }
+
+ private Map<IPath, Boolean> loadManifest(ResourceKind resourceKind, IPath resourceIPath) {
+ Map<IPath, Boolean> result = null;
+ IPath manifestPath = resourceIPath.addFileExtension("manifest");
+
+ URL manifestURL = resourceKind.getResourceURL(testClass, manifestPath);
+ if (manifestURL != null) {
+ try {
+ result = parseManifest(manifestPath.removeLastSegments(1), manifestURL);
+ } catch (IOException e) {
+ e.printStackTrace();
+ // Create a default manifest
+ }
+ }
+
+ if (result == null) {
+ // Default manifest
+ result = Maps.newHashMap();
+ IPath basePath = manifestPath.removeFileExtension();
+ result.put(basePath.addFileExtension(DiModel.DI_FILE_EXTENSION), true);
+ result.put(basePath.addFileExtension(UmlModel.UML_FILE_EXTENSION), true);
+ result.put(basePath.addFileExtension(NotationModel.NOTATION_FILE_EXTENSION), true);
+ }
+
+ return result;
+ }
+
+ private Map<IPath, Boolean> parseManifest(IPath baseResourcePath, URL manifestURL) throws IOException {
+ Map<IPath, Boolean> result = Maps.newLinkedHashMap();
+
+ List<String> lines = Resources.asByteSource(manifestURL).asCharSource(Charsets.UTF_8).readLines();
+ Pattern pattern = Pattern.compile("([^=]+)(?:=(true|false))?");
+ Matcher m = pattern.matcher("");
+ for (String line : lines) {
+ m.reset(line);
+ if (m.matches()) {
+ IPath path = baseResourcePath.append(m.group(1));
+
+ boolean load = true;
+ if (m.group(2) != null) {
+ load = Boolean.valueOf(m.group(2));
+ }
+
+ result.put(path, load);
+ }
+ }
+
+ return result;
+ }
+
+ private Resource doInitModelResource(ResourceSet resourceSet, String targetPath, ResourceKind resourceKind, IPath resourceIPath) {
+ IPath targetIPath = new Path(targetPath);
+ if (!resourceIPath.getFileExtension().equals(targetIPath.getFileExtension())) {
+ targetIPath = targetIPath.addFileExtension(resourceIPath.getFileExtension());
+ }
+
+ // If the file name is different from the core model name, then use it as is. It's an extra resource for some purpose
+ // (perhaps such as a library model)
+ if (!targetIPath.lastSegment().equals(resourceIPath.lastSegment())) {
+ targetIPath = targetIPath.removeLastSegments(1).append(resourceIPath.lastSegment());
+ }
+
+ final URI modelURI = project.getURI(targetIPath);
+ Resource result = resourceSet.getResource(modelURI, false);
+
+ if (result == null) {
+ String extension = modelURI.fileExtension();
+ if (DiModel.DI_FILE_EXTENSION.equals(extension) || UmlModel.UML_FILE_EXTENSION.equals(extension) || NotationModel.NOTATION_FILE_EXTENSION.equals(extension)) {
+ // Default load behaviour
+ result = resourceSet.createResource(modelURI);
+ } else {
+ // Assume it's a fragment of UML content (such as a profile-application resource)
+ result = resourceSet.createResource(modelURI, UMLPackage.eCONTENT_TYPE);
+ }
+ }
+
+ if (!result.isLoaded()) {
+ if (resourceSet instanceof ModelSet) {
+ ModelSet modelSet = (ModelSet) resourceSet;
+ if (modelSet.getURIWithoutExtension() == null) {
+ modelSet.getInternal().setPrimaryModelResourceURI(modelURI);
+ }
+ }
+
+ try {
+ InputStream input = resourceKind.getResourceURL(testClass, resourceIPath).openStream();
+ OutputStream output = resourceSet.getURIConverter().createOutputStream(result.getURI());
+
+ try {
+ ByteStreams.copy(input, output);
+ } finally {
+ input.close();
+ output.close();
+ }
+
+ result.load(null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to load test resource: " + resourceIPath.toString());
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void finished(Description description) {
+ final ResourceSet resourceSet = getResourceSet();
+
+ initialResourceURIs = null;
+ model = null;
+ root = null;
+
+ if (domain instanceof TransactionalEditingDomain) {
+ ((TransactionalEditingDomain) domain).dispose();
+ }
+ domain = null;
+
+ if (resourceSet != null) {
+ EMFHelper.unload(resourceSet);
+ }
+ }
+
+ private Annotation getResourceAnnotation(Description description) {
+ Annotation result = JUnitUtils.getAnyAnnotation(description, JavaResource.class, PluginResource.class);
+
+ assertThat("No JavaResource or PluginResource annotation found on test.", result, notNullValue());
+
+ return result;
+ }
+
+ public static enum ResourceKind {
+ JAVA, BUNDLE;
+
+ static ResourceKind getResourceKind(Annotation resourceAnnotation) {
+ return (resourceAnnotation instanceof JavaResource) ? JAVA : (resourceAnnotation instanceof PluginResource) ? BUNDLE : null;
+ }
+
+ String[] getResourcePaths(Annotation resourceAnnotation) {
+ switch (this) {
+ case JAVA:
+ return ((JavaResource) resourceAnnotation).value();
+ case BUNDLE:
+ PluginResource plugin = (PluginResource) resourceAnnotation;
+ return plugin.bundle().isEmpty()
+ ? plugin.value()
+ : Stream.of(plugin.value()).map(path -> String.format("%s:%s", plugin.bundle(), path)).toArray(String[]::new);
+ }
+
+ fail("Not a resource annotation: " + resourceAnnotation);
+ return null; // Not reachable
+ }
+
+ boolean exists(Class<?> context, IPath path) {
+ return getResourceURL(context, path) != null;
+ }
+
+ URL getResourceURL(Class<?> context, IPath path) {
+ URL result = null;
+
+ switch (this) {
+ case JAVA:
+ result = context.getResource(path.toString());
+ break;
+ case BUNDLE:
+ result = getBundleURL(context, path);
+ break;
+ }
+
+ return result;
+ }
+
+ private URL getBundleURL(Class<?> testClass, IPath resourcePath) {
+ URL result = null;
+
+ String bundleID = resourcePath.getDevice();
+ if (bundleID != null) {
+ resourcePath = resourcePath.setDevice(null);
+
+ int colon = bundleID.lastIndexOf(':');
+ if (colon >= 0) {
+ bundleID = bundleID.substring(0, colon);
+ }
+ }
+
+ String pattern = resourcePath.lastSegment();
+ IPath search;
+ if (resourcePath.segmentCount() > 1) {
+ search = resourcePath.removeLastSegments(1);
+ } else {
+ search = Path.ROOT;
+ }
+
+ Bundle testBundle = (bundleID != null)
+ ? Platform.getBundle(bundleID)
+ : FrameworkUtil.getBundle(testClass);
+ Enumeration<URL> urls = testBundle.findEntries(search.toPortableString(), pattern, false);
+ if ((urls != null) && urls.hasMoreElements()) {
+ result = urls.nextElement();
+ }
+
+ if (result == null) {
+ // A test case can override a resource in a base test bundle with a corresponding resource of its
+ // own. But, it may also just use the resource provided by the base test bundle, so look for it
+ Bundle lastBundle = testBundle;
+ for (Class<?> baseClass = testClass.getSuperclass(); (baseClass != null); baseClass = baseClass.getSuperclass()) {
+ testBundle = FrameworkUtil.getBundle(baseClass);
+ if (testBundle == null) {
+ break;
+ } else if (testBundle != lastBundle) {
+ lastBundle = testBundle;
+ urls = testBundle.findEntries(search.toPortableString(), pattern, false);
+ if ((urls != null) && urls.hasMoreElements()) {
+ result = urls.nextElement();
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveDiagram.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveDiagram.java
new file mode 100644
index 00000000000..5ae3344d156
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveDiagram.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotation on a test indicating the diagram to activate.
+ *
+ * @see PapyrusEditorFixture
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ActiveDiagram {
+ /**
+ * The name of the diagram to activate.
+ */
+ String value();
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveTable.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveTable.java
new file mode 100755
index 00000000000..44d26fb35fa
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ActiveTable.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotation on a test indicating the table to activate.
+ *
+ * @see PapyrusEditorFixture
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ActiveTable {
+ /**
+ * The name of the table to activate.
+ */
+ String value();
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AnnotationRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AnnotationRule.java
new file mode 100644
index 00000000000..ae3e6017797
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/AnnotationRule.java
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.junit.Assert.fail;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import org.eclipse.papyrus.junit.utils.JUnitUtils;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import com.google.common.base.Defaults;
+import com.google.common.base.Supplier;
+
+/**
+ * A rule that finds annotation metadata of a test. If the value of the annotation is a
+ * class, then the rule instantiates that class in order to provide a value.
+ */
+public class AnnotationRule<T> implements TestRule, Supplier<T> {
+ private final Class<? extends Annotation> annotationType;
+ private final Method valueAccessor;
+
+ private T value;
+
+ private AnnotationRule(Class<? extends Annotation> annotationType, Method accessor, T default_) {
+ super();
+
+ this.annotationType = annotationType;
+ this.valueAccessor = accessor;
+
+ this.value = default_;
+ }
+
+ /**
+ * Creates a new rule that extracts the value of the specified annotation type from
+ * a test case.
+ *
+ * @param annotationType
+ * the annotation type from which to extract the value
+ * @param default_
+ * the value to return in the case that the annotation is absent. If null
+ * and the annotation value is of a primitive type, the appropriate primitive default is substituted
+ *
+ * @return the rule
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> AnnotationRule<T> create(Class<? extends Annotation> annotationType, T default_) {
+ try {
+ Method method = annotationType.getDeclaredMethod("value");
+
+ Class<?> resultType = method.getReturnType();
+
+ if ((default_ == null) && (resultType != Class.class)) {
+ default_ = (T) Defaults.defaultValue(resultType);
+ }
+
+ return new AnnotationRule<T>(annotationType, method, default_);
+ } catch (Exception e) {
+ fail("Cannot get annotation value: " + e.getMessage());
+ throw new Error();// unreachable
+ }
+ }
+
+ /**
+ * Creates a new rule that extracts the value of the specified annotation type from
+ * a test case. This rule does not have a default: if the annotation is absent, it
+ * returns {@code null}.
+ *
+ * @param annotationType
+ * the annotation type from which to extract the value
+ * @return the rule
+ */
+ public static <T> AnnotationRule<T> create(Class<? extends Annotation> annotationType) {
+ return create(annotationType, null);
+ }
+
+ /**
+ * Creates a new rule that just determines whether the specified annotation is applied to
+ * a test case.
+ *
+ * @param annotationType
+ * the annotation type to detect
+ *
+ * @return the rule
+ */
+ public static AnnotationRule<Boolean> createExists(Class<? extends Annotation> annotationType) {
+ return new AnnotationRule<Boolean>(annotationType, null, false);
+ }
+
+ @Override
+ public final T get() {
+ return value;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ Statement result = base;
+ final Annotation annotation = JUnitUtils.getAnnotation(description, annotationType);
+
+ if (annotation != null) {
+ result = new Statement() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ // If we have no accessor method, we're just checking existence of the annotation,
+ // which is "extracted" as a boolean
+ if (valueAccessor == null) {
+ value = (T) Boolean.TRUE;
+ } else {
+ Object extracted = valueAccessor.invoke(annotation);
+ if (extracted instanceof Class<?>) {
+ // Instantiate the class
+ extracted = ((Class<?>) extracted).newInstance();
+ }
+ value = (T) extracted;
+ }
+ } catch (Exception e) {
+ fail("Cannot get annotation value: " + e.getMessage());
+ }
+
+ try {
+ base.evaluate();
+ } finally {
+ if (valueAccessor != null) {
+ // Clean up. Note that if the annotation value is a class, the
+ // default is null anyways, so the cast doesn't matter
+ value = (T) Defaults.defaultValue(valueAccessor.getReturnType());
+ }
+ }
+ }
+ };
+ }
+
+ return result;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ExecutorRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ExecutorRule.java
new file mode 100644
index 00000000000..77347df01ce
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ExecutorRule.java
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+
+import org.eclipse.papyrus.junit.utils.Activator;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import com.google.common.collect.Queues;
+
+/**
+ * A JUnit {@linkplain TestRule rule} that is an {@link Executor} running tasks at clean-up of the
+ * test execution.
+ */
+public class ExecutorRule extends TestWatcher implements Executor {
+ private final BlockingQueue<Runnable> queue = Queues.newLinkedBlockingQueue();
+
+ public ExecutorRule() {
+ super();
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ queue.add(command);
+ }
+
+ protected void runPending() {
+ for (Runnable next = queue.poll(); next != null; next = queue.poll()) {
+ try {
+ next.run();
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in test shutdown runnable.", e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ @Override
+ protected void finished(Description description) {
+ runPending();
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HideViewRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HideViewRule.java
new file mode 100644
index 00000000000..66830a5159b
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HideViewRule.java
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.junit.Assert.fail;
+
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+
+/**
+ * A rule that hides a view for the duration of a test.
+ */
+public class HideViewRule extends TestWatcher {
+
+ private final String viewID;
+
+ private IWorkbenchPage page;
+
+ public HideViewRule(String viewID) {
+ super();
+
+ this.viewID = viewID;
+ }
+
+ @Override
+ protected void starting(Description description) {
+ IWorkbench bench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = bench.getActiveWorkbenchWindow();
+ if(window == null) {
+ window = bench.getWorkbenchWindows()[0];
+ }
+
+ IWorkbenchPage page = window.getActivePage();
+ IViewPart viewPart = page.findView(viewID);
+
+ if(viewPart != null) {
+ this.page = page;
+ page.hideView(viewPart);
+ }
+ }
+
+ @Override
+ protected void finished(Description description) {
+ if(page != null) {
+ try {
+ page.showView(viewID);
+ } catch (PartInitException e) {
+ fail(String.format("Failed to restore view %s: %s", viewID, e.getLocalizedMessage()));
+ }
+ }
+
+ page = null;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HouseKeeper.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HouseKeeper.java
new file mode 100644
index 00000000000..c52cc8d17e5
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/HouseKeeper.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2015 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 476683
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.util.Iterator;
+
+import org.eclipse.emf.ecore.EObject;
+import org.junit.ClassRule;
+import org.junit.rules.MethodRule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+
+/**
+ * <p>
+ * A JUnit rule that automatically cleans up resources when a test has finished. It provides a generic framework for cleaning stuff up, but it also
+ * has special cases for commonly used items.
+ * </p>
+ * <p>
+ * As a bonus, the {@code HouseKeeper} also automatically clears all non-{@code static} non-{@code final} fields of the test class that are of some
+ * type conforming to {@link EObject}, just to make sure that they can't cause memory leaks.
+ * </p>
+ * <p>
+ * To use the rule as a {@link ClassRule &#x40;ClassRule}, instantiate the {@linkplain Static} inner class.
+ * </p>
+ *
+ * @see Static
+ * @see Disposer
+ */
+public class HouseKeeper extends AbstractHouseKeeperRule implements MethodRule {
+
+ public HouseKeeper() {
+ super();
+ }
+
+ @Override
+ boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ Class<?> getTestClass() {
+ return test.getClass();
+ }
+
+ //
+ // Rule protocol
+ //
+
+ @Override
+ public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ test = target;
+ testName = method.getName();
+
+ try {
+ registerAutoCleanups();
+ base.evaluate();
+ } finally {
+ cleanUp();
+ }
+ }
+ };
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * A call-back interface for disposing of objects no longer needed by the test.
+ */
+ public static interface Disposer<T> {
+
+ void dispose(T object) throws Exception;
+ }
+
+ /**
+ * A variant of the {@link HouseKeeper} that is to be used for class rules. It cleans up static fields of the test class and runs disposers
+ * after the completion of the class's whole test suite.
+ */
+ public static class Static extends AbstractHouseKeeperRule implements TestRule {
+
+ @Override
+ boolean isStatic() {
+ return true;
+ }
+
+ @Override
+ Class<?> getTestClass() {
+ return (Class<?>) test;
+ }
+
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ // description.getTestClass() is null for static rules
+ test = findTestClass(description);
+ testName = description.getMethodName();
+
+ try {
+ registerAutoCleanups();
+ base.evaluate();
+ } finally {
+ cleanUp();
+ }
+ }
+
+ Class<?> findTestClass(Description description) {
+ Class<?> result = description.getTestClass();
+
+ if (result == null) {
+ for (Iterator<Description> iter = description.getChildren().iterator(); (result == null) && iter.hasNext();) {
+ result = findTestClass(iter.next());
+ }
+ }
+
+ return result;
+ }
+ };
+ }
+
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/JavaResource.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/JavaResource.java
new file mode 100644
index 00000000000..23d870281ba
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/JavaResource.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 CEA 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotation indicating the classpath-relative path to one or more resources from which to load the test model of an {@link AbstractModelFixture}.
+ *
+ * @see AbstractModelFixture
+ * @see PluginResource
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JavaResource {
+
+ String[] value();
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ModelSetFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ModelSetFixture.java
new file mode 100644
index 00000000000..65a8fbc9152
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ModelSetFixture.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2014, 2016 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bugs 399859, 485220
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.junit.Assert.fail;
+
+import java.util.Collections;
+import java.util.regex.Pattern;
+
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.papyrus.infra.core.resource.EditingDomainServiceFactory;
+import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.services.ServiceDescriptor;
+import org.eclipse.papyrus.infra.core.services.ServiceDescriptor.ServiceTypeKind;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServiceStartKind;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForResourceSet;
+import org.junit.runner.Description;
+
+
+/**
+ * This is the ModelSetFixture type. Enjoy.
+ */
+public class ModelSetFixture extends AbstractModelFixture<TransactionalEditingDomain> {
+
+ public ModelSetFixture() {
+ super();
+ }
+
+ @Override
+ public ModelSet getResourceSet() {
+ return (ModelSet) super.getResourceSet();
+ }
+
+ @Override
+ protected TransactionalEditingDomain createEditingDomain() {
+ try {
+ ServicesRegistry services = createServiceRegistry();
+ return services.getService(ModelSet.class).getTransactionalEditingDomain();
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to initialize service registry and/or editing domain: " + e.getLocalizedMessage());
+ return null; // unreachable
+ }
+ }
+
+ @Override
+ protected void finished(Description description) {
+ ResourceSet rset = getEditingDomain().getResourceSet();
+
+ try {
+ ServicesRegistry services = ServiceUtilsForResourceSet.getInstance().getServiceRegistry(rset);
+ services.disposeRegistry();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ super.finished(description);
+ }
+ }
+
+ protected ServicesRegistry createServiceRegistry() throws Exception {
+ ServicesRegistry result = new ServicesRegistry();
+
+ result.add(ModelSet.class, 10, new ModelSet());
+
+ ServiceDescriptor desc = new ServiceDescriptor(TransactionalEditingDomain.class, EditingDomainServiceFactory.class.getName(), ServiceStartKind.STARTUP, 10, Collections.singletonList(ModelSet.class.getName()));
+ desc.setServiceTypeKind(ServiceTypeKind.serviceFactory);
+ desc.setClassBundleID(org.eclipse.papyrus.infra.core.Activator.PLUGIN_ID);
+ result.add(desc);
+
+ result.startRegistry();
+
+ return result;
+ }
+
+ @Override
+ protected void didLoadResourceSet() {
+ try {
+ getResourceSet().loadModels(getModelResourceURI());
+ } catch (ModelMultiException e) {
+ // Is the problem only a missing model resource?
+ Pattern missingResource = Pattern.compile("ResourceException: Resource '.*' does not exist."); //$NON-NLS-1$
+ for (Throwable next : e.getExceptions()) {
+ if ((next.getMessage() == null) || !missingResource.matcher(next.getMessage()).find()) {
+ e.printStackTrace();
+
+ fail("Failed to initialize ModelSet fixture: " + e.getLocalizedMessage());
+ }
+ }
+ }
+ }
+
+ public final <S> S tryService(Class<S> serviceType) {
+ try {
+ ServicesRegistry services = ServiceUtilsForResourceSet.getInstance().getServiceRegistry(getResourceSet());
+ return services.getService(serviceType);
+ } catch (ServiceException e) {
+ // Okay, no such service
+ return null; // unreachable
+ }
+ }
+
+ public final <S> S requireService(Class<S> serviceType) {
+ try {
+ ServicesRegistry services = ServiceUtilsForResourceSet.getInstance().getServiceRegistry(getResourceSet());
+ return services.getService(serviceType);
+ } catch (ServiceException e) {
+ e.printStackTrace();
+ fail("Failed to initialize service registry and/or service: " + e.getLocalizedMessage());
+ return null; // unreachable
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionFixture.java
new file mode 100644
index 00000000000..dcada7fc60e
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionFixture.java
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.emf.common.command.AbstractCommand;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * This rule provides a ResourceSet with a TransactionalEditingDomain
+ *
+ * All test methods are executed within a Transaction (Which means test method
+ * do not need to care about transactions)
+ *
+ * This fixture is meant to be used through {@link NoTransactionRule}
+ *
+ * @author Camille Letavernier
+ *
+ * @see {@link NoTransactionRule}
+ */
+public class NoTransactionFixture implements TestRule {
+
+
+ private final ModelSetFixture modelSet;
+
+ public NoTransactionFixture(ModelSetFixture modelSet) {
+ this.modelSet = modelSet;
+ }
+
+ /**
+ * @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
+ *
+ * @param arg0
+ * @param arg1
+ * @return
+ */
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+
+ final AtomicReference<Throwable> throwable = new AtomicReference<Throwable>();
+ modelSet.getEditingDomain().getCommandStack().execute(new AbstractCommand() {
+
+ public void execute() {
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ throwable.set(t);
+ }
+ }
+
+ public void redo() {
+ // Nothing
+ }
+
+ /**
+ * @see org.eclipse.emf.common.command.AbstractCommand#prepare()
+ *
+ * @return
+ */
+ @Override
+ protected boolean prepare() {
+ return true;
+ }
+
+ });
+
+ if (throwable.get() != null) {
+ throw throwable.get();
+ }
+ }
+ };
+ }
+
+ public ResourceSet getResourceSet() {
+ return modelSet.getResourceSet();
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionRule.java
new file mode 100644
index 00000000000..b3dee015fda
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/NoTransactionRule.java
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * This rule provides a ResourceSet with a TransactionalEditingDomain
+ *
+ * All test methods are executed within a Transaction (Which means test method
+ * do not need to care about transactions)
+ *
+ * Usage:
+ *
+ * <pre>
+ * &#064;Rule
+ * public NoTransactionRule noTransaction = new NoTransactionRule();
+ * </pre>
+ *
+ * @author Camille Letavernier
+ */
+public class NoTransactionRule implements TestRule {
+
+ private final ModelSetFixture modelSet = new ModelSetFixture();
+
+ private final NoTransactionFixture noTransaction = new NoTransactionFixture(modelSet);
+
+ public RuleChain getRuleChain() {
+ return RuleChain.outerRule(modelSet).around(noTransaction);
+ }
+
+ public Statement apply(Statement base, Description description) {
+ return getRuleChain().apply(base, description);
+ }
+
+ public ResourceSet getResourceSet() {
+ return modelSet.getResourceSet();
+ }
+
+ public Resource getModelResource() {
+ return modelSet.getModelResource();
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java
new file mode 100644
index 00000000000..a061a4b3240
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PapyrusEditorFixture.java
@@ -0,0 +1,1415 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 2017 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bugs 433206, 465416, 434983, 483721, 469188, 485220, 491542, 497865
+ * Thanh Liem PHAN (ALL4TEC) thanhliem.phan@all4tec.net - Bug 521550
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.operations.IOperationHistory;
+import org.eclipse.core.commands.operations.IOperationHistoryListener;
+import org.eclipse.core.commands.operations.IUndoContext;
+import org.eclipse.core.commands.operations.IUndoableOperation;
+import org.eclipse.core.commands.operations.OperationHistoryEvent;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.draw2d.geometry.Dimension;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.e4.ui.model.application.ui.MUIElement;
+import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.e4.ui.model.application.ui.basic.MPartSashContainerElement;
+import org.eclipse.e4.ui.model.application.ui.basic.MPartStack;
+import org.eclipse.e4.ui.model.application.ui.basic.MStackElement;
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.provider.IItemLabelProvider;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.emf.workspace.IWorkspaceCommandStack;
+import org.eclipse.gef.EditPart;
+import org.eclipse.gef.GraphicalEditPart;
+import org.eclipse.gef.RootEditPart;
+import org.eclipse.gef.ui.palette.PaletteViewer;
+import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint;
+import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
+import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
+import org.eclipse.gmf.runtime.diagram.ui.editparts.IDiagramPreferenceSupport;
+import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor;
+import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditorWithFlyOutPalette;
+import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart;
+import org.eclipse.gmf.runtime.notation.Diagram;
+import org.eclipse.gmf.runtime.notation.View;
+import org.eclipse.papyrus.editor.PapyrusMultiDiagramEditor;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.resource.sasheditor.DiModel;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IComponentPage;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IEditorPage;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.IPageVisitor;
+import org.eclipse.papyrus.infra.core.sasheditor.editor.ISashWindowsContainer;
+import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
+import org.eclipse.papyrus.infra.gmfdiag.common.model.NotationModel;
+import org.eclipse.papyrus.infra.nattable.common.editor.AbstractEMFNattableEditor;
+import org.eclipse.papyrus.infra.nattable.common.modelresource.PapyrusNattableModel;
+import org.eclipse.papyrus.infra.nattable.manager.table.INattableModelManager;
+import org.eclipse.papyrus.infra.nattable.model.nattable.Table;
+import org.eclipse.papyrus.infra.tools.util.PlatformHelper;
+import org.eclipse.papyrus.infra.tools.util.TypeUtils;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.junit.utils.EditorUtils;
+import org.eclipse.papyrus.junit.utils.JUnitUtils;
+import org.eclipse.papyrus.junit.utils.tests.AbstractEditorTest;
+import org.eclipse.papyrus.uml.tools.model.UmlModel;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPage;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPageBookView;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerView;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.ISaveablePart;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.IPage;
+import org.eclipse.uml2.uml.NamedElement;
+import org.eclipse.uml2.uml.Package;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.junit.runner.Description;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+
+
+/**
+ * A fixture that presents editors on a model specified via an annotation as for {@link ProjectFixture}. The editor is closed automatically upon
+ * completion of the test.
+ */
+public class PapyrusEditorFixture extends AbstractModelFixture<TransactionalEditingDomain> {
+
+ private final Collection<IEditorPart> editorsToClose = Lists.newArrayList();
+
+ private final List<String> excludedTypeView = Arrays.asList(new String[] { "Note" });
+
+ private final boolean ensureOperationHistoryIntegrity;
+
+ @SuppressWarnings("restriction")
+ private org.eclipse.papyrus.infra.ui.internal.preferences.YesNo initialEditorLayoutStorageMigrationPreference;
+
+ private IMultiDiagramEditor editor;
+
+ private DiagramEditorWithFlyOutPalette activeDiagramEditor;
+
+ private AbstractEMFNattableEditor activeTableEditor;
+
+ private ModelExplorerView modelExplorer;
+
+ private Class<?> testClass;
+
+ private Description testDescription;
+
+ private Collection<IViewPart> viewsToClose;
+
+ private ListMultimap<Description, IFile> modelFiles;
+
+ private IOperationHistoryListener operationHistoryIntegrityListener;
+ private IOperationHistory operationHistory;
+
+ /**
+ * Initializes me with the assurance of undo/redo correspondence in the
+ * diagram and EMF views of the command history.
+ */
+ public PapyrusEditorFixture() {
+ this(true);
+ }
+
+ /**
+ * Initializes me with the option to ensure integrity of the operation history
+ * by listening for command execution in the diagram context and, if an operation
+ * executed in the diagram does not have the editing-domain context, adds that
+ * context. This ensures that the diagram editor and the model explorer, for
+ * example, see the same undo/redo history (which they would not if some
+ * diagram-only commands were only in the diagram's history). Some tests do
+ * need to suppress this convenience in order to accurately represent the
+ * normal Papyrus run-time environment.
+ *
+ * @param ensureOperationHistoryIntegrity
+ *
+ * @since 2.0
+ */
+ public PapyrusEditorFixture(boolean ensureOperationHistoryIntegrity) {
+ super();
+
+ this.ensureOperationHistoryIntegrity = ensureOperationHistoryIntegrity;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor getEditor() {
+ return editor;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor getEditor(String path) {
+ IMultiDiagramEditor result = null;
+
+ final String fileName = new Path(path).lastSegment();
+ for (IEditorReference next : getWorkbenchPage().getEditorReferences()) {
+ if (PapyrusMultiDiagramEditor.EDITOR_ID.equals(next.getId()) && fileName.equals(next.getName())) {
+ result = (IMultiDiagramEditor) next.getEditor(true);
+ }
+ }
+
+ return result;
+ }
+
+ @SuppressWarnings("restriction")
+ @Override
+ protected void starting(Description description) {
+ testClass = description.getTestClass();
+ testDescription = description;
+
+ // Ensure that we won't see a dialog prompting the user to migrate page layout
+ // storage from the DI file to the workspace-private sash file
+ initialEditorLayoutStorageMigrationPreference = org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().getConvertSharedPageLayoutToPrivate();
+ org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(org.eclipse.papyrus.infra.ui.internal.preferences.YesNo.NO);
+
+ if (hasRequiredViews()) {
+ openRequiredViews();
+ }
+
+ modelFiles = ArrayListMultimap.create();
+ openAll(description);
+
+ ActiveDiagram activeDiagram = JUnitUtils.getAnnotation(description, ActiveDiagram.class);
+ if (activeDiagram != null) {
+ String name = activeDiagram.value();
+ activateDiagram(name);
+ if ((activeDiagramEditor == null) || !name.equals(getActiveDiagramEditor().getDiagram().getName())) {
+ // OK, we need to open it, then
+ openDiagram(name);
+ }
+ } else {
+ ActiveTable activeTable = JUnitUtils.getAnnotation(description, ActiveTable.class);
+ if (activeTable != null) {
+ String name = activeTable.value();
+ activateTable(name);
+ if ((activeTableEditor == null) || !name.equals(getActiveTableEditor().getTable().getName())) {
+ openTable(name);
+ }
+ }
+ }
+
+ super.starting(description);
+
+ if (ensureOperationHistoryIntegrity) {
+ // Ensure consistency of undo/redo with EMF operations
+ final IWorkspaceCommandStack stack = (IWorkspaceCommandStack) getEditingDomain().getCommandStack();
+ final IUndoContext emfContext = stack.getDefaultUndoContext();
+ operationHistory = stack.getOperationHistory();
+ operationHistoryIntegrityListener = new IOperationHistoryListener() {
+ @Override
+ public void historyNotification(OperationHistoryEvent event) {
+ if ((event.getEventType() == OperationHistoryEvent.DONE) && (activeDiagramEditor != null)) {
+ IUndoContext diagramContext = activeDiagramEditor.getDiagramEditDomain().getDiagramCommandStack().getUndoContext();
+ if (diagramContext != null) {
+ IUndoableOperation undo = event.getOperation();
+ if ((undo != null) && !undo.hasContext(emfContext)) {
+ undo.addContext(emfContext);
+ }
+ }
+ }
+ // nothing to do for table
+ }
+ };
+ operationHistory.addOperationHistoryListener(operationHistoryIntegrityListener);
+ }
+ }
+
+ @SuppressWarnings("restriction")
+ @Override
+ protected void finished(Description description) {
+ try {
+ modelFiles = null;
+
+ Exception exception = null;
+
+ for (IEditorPart editor : ImmutableList.copyOf(editorsToClose)) {
+ try {
+ close(editor);
+ } catch (Exception e) {
+ if (exception == null) {
+ exception = e;
+ }
+ }
+ }
+
+ if (exception != null) {
+ exception.printStackTrace();
+ fail("Failed to close an editor: " + exception.getLocalizedMessage());
+ }
+ } finally {
+ try {
+ if (operationHistoryIntegrityListener != null) {
+ operationHistory.removeOperationHistoryListener(operationHistoryIntegrityListener);
+ operationHistoryIntegrityListener = null;
+ operationHistory = null;
+ }
+
+ } finally {
+ editorsToClose.clear();
+ editor = null;
+ activeDiagramEditor = null;
+
+ org.eclipse.papyrus.infra.ui.internal.preferences.EditorPreferences.getInstance().setConvertSharedPageLayoutToPrivate(initialEditorLayoutStorageMigrationPreference);
+
+ try {
+ if (hasRequiredViews()) {
+ closeRequiredViews();
+ }
+ } finally {
+ super.finished(description);
+ }
+ }
+ }
+ }
+
+ @Override
+ public TransactionalEditingDomain getEditingDomain() {
+ TransactionalEditingDomain result = null;
+
+ if (editor != null) {
+ result = getEditingDomain(editor);
+ }
+
+ return result;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public TransactionalEditingDomain getEditingDomain(IMultiDiagramEditor editor) {
+ TransactionalEditingDomain result = null;
+
+ try {
+ result = getServiceRegistry(editor).getService(TransactionalEditingDomain.class);
+ } catch (ServiceException e) {
+ e.printStackTrace();
+ fail("Failed to get editing domain from Papyrus editor: " + e.getLocalizedMessage());
+ }
+
+ return result;
+ }
+
+ @Override
+ protected TransactionalEditingDomain createEditingDomain() {
+ // We don't create the domain; the editor does
+ return null;
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected IMultiDiagramEditor open(final IFile modelFile) {
+ final boolean firstEditor = editorsToClose.isEmpty();
+
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ editor = EditorUtils.openPapyrusEditor(modelFile);
+ editorsToClose.add(editor);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to open Papyrus editor: " + e.getLocalizedMessage());
+ }
+ }
+ });
+
+ if (firstEditor && !editorsToClose.isEmpty()) {
+ final IWorkbenchPage page = editor.getSite().getPage();
+ page.addPartListener(new IPartListener() {
+
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ editorsToClose.remove(part);
+
+ if (part == editor) {
+ editor = null;
+ }
+
+ if (editorsToClose.isEmpty()) {
+ page.removePartListener(this);
+ }
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ // Pass
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPart part) {
+ // Pass
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // Pass
+ }
+
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+ // Pass
+ }
+ });
+ }
+
+ flushDisplayEvents();
+
+ return editor;
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected IMultiDiagramEditor openOne(Description description) {
+ IFile papyrusModel = getProject().getFile(Iterables.getOnlyElement(initModelResources(description)).getURI().trimFileExtension().appendFileExtension(DiModel.DI_FILE_EXTENSION));
+ modelFiles.put(description, papyrusModel);
+ return open(papyrusModel);
+ }
+
+ protected Iterable<IMultiDiagramEditor> openAll(Description description) {
+ List<IMultiDiagramEditor> result = Lists.newArrayList();
+
+ for (Resource resource : initModelResources(description)) {
+ if(resource.getURI().fileExtension().equals(UmlModel.UML_FILE_EXTENSION)){
+ IFile papyrusModel = getProject().getFile(resource.getURI().trimFileExtension().appendFileExtension(DiModel.DI_FILE_EXTENSION));
+ modelFiles.put(description, papyrusModel);
+ result.add(open(papyrusModel));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected IMultiDiagramEditor reopenOne(Description description) {
+ IFile papyrusModel = modelFiles.get(description).get(0);
+ return open(papyrusModel);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor open() {
+ return openOne(testDescription);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor open(String resourcePath) {
+ return open(new Path(resourcePath).removeFileExtension().lastSegment(), ResourceKind.BUNDLE, resourcePath);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor open(String targetPath, String resourcePath) {
+ return open(targetPath, ResourceKind.BUNDLE, resourcePath);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IMultiDiagramEditor open(String targetPath, ResourceKind resourceKind, String resourcePath) {
+ final IFile papyrusModel = getProject().getFile(initModelResource(targetPath, resourceKind, resourcePath).getURI().trimFileExtension().appendFileExtension(DiModel.DI_FILE_EXTENSION));
+ return open(papyrusModel);
+ }
+
+ /**
+ * Reopens the same test model that was previously {@link #open() opened} and the
+ * subsequently {@link #close() closed}. This is an important disction, as simply
+ * {@link #open() opening} the test model again would actually re-initialize it from
+ * the deployed test resources, potentially replacing any changes in the model files
+ * that may be significant to the test.
+ *
+ * @return the re-opened editor
+ * @since 2.0
+ */
+ public IMultiDiagramEditor reopen() {
+ return reopenOne(testDescription);
+ }
+
+ public void activate() {
+ if (editor != null) {
+ activate(editor);
+ }
+ }
+
+ public void activate(IWorkbenchPart part) {
+ IWorkbenchPage page = part.getSite().getPage();
+
+ if (page.getActivePart() != part) {
+ page.activate(part);
+ flushDisplayEvents();
+ }
+ }
+
+ public void close() {
+ if (editor != null) {
+ close(editor);
+ editor = null;
+ }
+ }
+
+ public void close(IEditorPart editor) {
+ editor.getSite().getPage().closeEditor(editor, false);
+ flushDisplayEvents();
+ }
+
+ public ModelExplorerView getModelExplorerView() {
+
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ ModelExplorerPageBookView view;
+ try {
+ view = (ModelExplorerPageBookView) getWorkbenchPage().showView(AbstractEditorTest.MODELEXPLORER_VIEW_ID);
+ } catch (PartInitException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ IPage currentPage = view.getCurrentPage();
+ ModelExplorerPage page = (ModelExplorerPage) currentPage;
+ IViewPart viewer = page.getViewer();
+ modelExplorer = (ModelExplorerView) viewer;
+ }
+ });
+
+ return modelExplorer;
+ }
+
+ protected final IWorkbenchPage getWorkbenchPage() {
+ IWorkbench bench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = bench.getActiveWorkbenchWindow();
+ if (window == null) {
+ window = bench.getWorkbenchWindows()[0];
+ }
+ return window.getActivePage();
+ }
+
+ public ServicesRegistry getServiceRegistry() {
+ return getServiceRegistry(editor);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public ServicesRegistry getServiceRegistry(IMultiDiagramEditor editor) {
+ return editor.getServicesRegistry();
+ }
+
+ public ModelSet getModelSet() {
+ return getModelSet(editor);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public ModelSet getModelSet(IMultiDiagramEditor editor) {
+ try {
+ return getServiceRegistry(editor).getService(ModelSet.class);
+ } catch (ServiceException e) {
+ e.printStackTrace();
+ fail("Failed to get model set from Papyrus editor: " + e.getLocalizedMessage());
+ return null; // Unreachable
+ }
+ }
+
+ @Override
+ public Package getModel() {
+ return getModel(editor);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public Package getModel(IMultiDiagramEditor editor) {
+ Package result = null;
+
+ ModelSet modelSet = getModelSet(editor);
+ UmlModel uml = (UmlModel) modelSet.getModel(UmlModel.MODEL_ID);
+ assertThat("No UML model present in resource set", uml.getResource(), notNullValue());
+
+ result = (Package) EcoreUtil.getObjectByType(uml.getResource().getContents(), UMLPackage.Literals.PACKAGE);
+ assertThat("Model resource contains no UML Package", result, notNullValue());
+
+ return result;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IPageManager getPageManager() {
+ return getPageManager(editor);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IPageManager getPageManager(IMultiDiagramEditor editor) {
+ try {
+ return getServiceRegistry(editor).getService(IPageManager.class);
+ } catch (ServiceException e) {
+ e.printStackTrace();
+ fail("Failed to get page manager from Papyrus editor: " + e.getLocalizedMessage());
+ return null; // Unreachable
+ }
+ }
+
+ public PapyrusEditorFixture activateDiagram(String name) {
+ return activateDiagram(editor, name);
+ }
+
+ public PapyrusEditorFixture activateTable(String name) {
+ return activateTable(editor, name);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture activateDiagram(IMultiDiagramEditor editor, final String name) {
+ activate(editor);
+
+ final ISashWindowsContainer sashContainer = PlatformHelper.getAdapter(editor, ISashWindowsContainer.class);
+ final org.eclipse.papyrus.infra.core.sasheditor.editor.IPage[] select = { null };
+
+ sashContainer.visit(new IPageVisitor() {
+
+ @Override
+ public void accept(IEditorPage page) {
+ if (name.equals(page.getPageTitle()) && (page.getIEditorPart() instanceof DiagramEditorWithFlyOutPalette)) {
+ select[0] = page;
+ setActiveDiagramEditor((DiagramEditorWithFlyOutPalette) page.getIEditorPart());
+ }
+ }
+
+ @Override
+ public void accept(IComponentPage page) {
+ // Pass
+ }
+ });
+
+ if (select[0] != null) {
+ sashContainer.selectPage(select[0]);
+ flushDisplayEvents();
+ }
+
+ return this;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture activateTable(IMultiDiagramEditor editor, final String name) {
+ activate(editor);
+
+ final ISashWindowsContainer sashContainer = PlatformHelper.getAdapter(editor, ISashWindowsContainer.class);
+ final org.eclipse.papyrus.infra.core.sasheditor.editor.IPage[] select = { null };
+
+ sashContainer.visit(new IPageVisitor() {
+
+ @Override
+ public void accept(IEditorPage page) {
+ if (name.equals(page.getPageTitle()) && (page.getIEditorPart() instanceof AbstractEMFNattableEditor)) {
+ select[0] = page;
+ setActiveTableEditor((AbstractEMFNattableEditor) page.getIEditorPart());
+ }
+ }
+
+ @Override
+ public void accept(IComponentPage page) {
+ // Pass
+ }
+ });
+
+ if (select[0] != null) {
+ sashContainer.selectPage(select[0]);
+ flushDisplayEvents();
+ }
+
+ return this;
+ }
+
+ private void setActiveDiagramEditor(DiagramEditorWithFlyOutPalette editor) {
+ activeDiagramEditor = editor;
+ activeTableEditor = null;
+ }
+
+ private void setActiveTableEditor(AbstractEMFNattableEditor editor) {
+ activeTableEditor = editor;
+ activeDiagramEditor = null;
+ }
+
+ public PapyrusEditorFixture activateDiagram(DiagramEditPart diagram) {
+ return activateDiagram(editor, diagram);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture activateDiagram(IMultiDiagramEditor editor, final DiagramEditPart diagram) {
+ activate(editor);
+
+ final ISashWindowsContainer sashContainer = PlatformHelper.getAdapter(editor, ISashWindowsContainer.class);
+ final org.eclipse.papyrus.infra.core.sasheditor.editor.IPage[] select = { null };
+
+ sashContainer.visit(new IPageVisitor() {
+
+ @Override
+ public void accept(IEditorPage page) {
+ DiagramEditorWithFlyOutPalette nested = TypeUtils.as(page.getIEditorPart(), DiagramEditorWithFlyOutPalette.class);
+ if ((nested != null) && (nested.getDiagramEditPart() == diagram)) {
+ select[0] = page;
+ setActiveDiagramEditor(nested);
+ }
+ }
+
+ @Override
+ public void accept(IComponentPage page) {
+ // Pass
+ }
+ });
+
+ if (select[0] != null) {
+ sashContainer.selectPage(select[0]);
+ flushDisplayEvents();
+ }
+
+ return this;
+ }
+
+ public PapyrusEditorFixture openDiagram(String name) {
+ return openDiagram(editor, name);
+ }
+
+ public PapyrusEditorFixture openTable(String name) {
+ return openTable(editor, name);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture openDiagram(IMultiDiagramEditor editor, final String name) {
+ activate(editor);
+
+ try {
+ ModelSet modelSet = ServiceUtils.getInstance().getModelSet(editor.getServicesRegistry());
+ NotationModel notation = (NotationModel) modelSet.getModel(NotationModel.MODEL_ID);
+ Diagram diagram = notation.getDiagram(name);
+ ServiceUtils.getInstance().getService(IPageManager.class, editor.getServicesRegistry()).openPage(diagram);
+ flushDisplayEvents();
+
+ activateDiagram(editor, name);
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot initialize test", e);
+ }
+
+ return this;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture openTable(IMultiDiagramEditor editor, final String name) {
+ activate(editor);
+
+ try {
+ ModelSet modelSet = ServiceUtils.getInstance().getModelSet(editor.getServicesRegistry());
+ PapyrusNattableModel notation = (PapyrusNattableModel) modelSet.getModel(PapyrusNattableModel.MODEL_ID);
+ Table table = notation.getTable(name);
+ ServiceUtils.getInstance().getService(IPageManager.class, editor.getServicesRegistry()).openPage(table);
+ flushDisplayEvents();
+
+ activateTable(editor, name);
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot initialize test", e); // NON-NLS-1
+ }
+
+ return this;
+ }
+
+ public String closeDiagram() {
+ String result = getActiveDiagramEditor().getDiagram().getName();
+ closeDiagram(editor, result);
+ return result;
+ }
+
+ public PapyrusEditorFixture closeDiagram(String name) {
+ return closeDiagram(editor, name);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PapyrusEditorFixture closeDiagram(IMultiDiagramEditor editor, final String name) {
+ try {
+ ModelSet modelSet = ServiceUtils.getInstance().getModelSet(editor.getServicesRegistry());
+ NotationModel notation = (NotationModel) modelSet.getModel(NotationModel.MODEL_ID);
+ Diagram diagram = notation.getDiagram(name);
+
+ // If the diagram was deleted, then so too was its page
+ if (diagram != null) {
+ ServiceUtils.getInstance().getService(IPageManager.class, editor.getServicesRegistry()).closePage(diagram);
+ flushDisplayEvents();
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot close diagram", e);
+ }
+
+ return this;
+ }
+
+ public DiagramEditorWithFlyOutPalette getActiveDiagramEditor() {
+ DiagramEditorWithFlyOutPalette result = activeDiagramEditor;
+
+ if (result == null) {
+ IEditorPart activeEditor = getWorkbenchPage().getActiveEditor();
+ if (activeEditor instanceof IMultiDiagramEditor) {
+ activeEditor = ((IMultiDiagramEditor) activeEditor).getActiveEditor();
+ if (activeEditor instanceof DiagramEditorWithFlyOutPalette) {
+ result = (DiagramEditorWithFlyOutPalette) activeEditor;
+ setActiveDiagramEditor(result);
+ }
+ }
+ }
+
+ assertThat("No diagram active", result, notNullValue());
+
+ return result;
+ }
+
+ public AbstractEMFNattableEditor getActiveTableEditor() {
+ AbstractEMFNattableEditor result = activeTableEditor;
+
+ if (result == null) {
+ IEditorPart activeEditor = getWorkbenchPage().getActiveEditor();
+ if (activeEditor instanceof IMultiDiagramEditor) {
+ activeEditor = ((IMultiDiagramEditor) activeEditor).getActiveEditor();
+ if (activeEditor instanceof AbstractEMFNattableEditor) {
+ result = (AbstractEMFNattableEditor) activeEditor;
+ setActiveTableEditor(result);
+ }
+ }
+ }
+
+ assertThat("No table active", result, notNullValue());
+
+ return result;
+ }
+
+ public DiagramEditPart getActiveDiagram() {
+ return getActiveDiagramEditor().getDiagramEditPart();
+ }
+
+ public INattableModelManager getActiveTableManager() {
+ return (INattableModelManager) getActiveTableEditor().getAdapter(INattableModelManager.class);
+ }
+
+ public DiagramEditPart getDiagram(String name) {
+ return getDiagram(editor, name);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public DiagramEditPart getDiagram(IMultiDiagramEditor editor, final String name) {
+ final ISashWindowsContainer sashContainer = PlatformHelper.getAdapter(editor, ISashWindowsContainer.class);
+ final org.eclipse.papyrus.infra.core.sasheditor.editor.IPage[] matchedPage = { null };
+
+ sashContainer.visit(new IPageVisitor() {
+
+ @Override
+ public void accept(IEditorPage page) {
+ if (name.equals(page.getPageTitle()) && (page.getIEditorPart() instanceof DiagramEditorWithFlyOutPalette)) {
+ matchedPage[0] = page;
+ }
+ }
+
+ @Override
+ public void accept(IComponentPage page) {
+ // Pass
+ }
+ });
+
+ IEditorPage editorPage = TypeUtils.as(matchedPage[0], IEditorPage.class);
+ IDiagramWorkbenchPart diagramPart = (editorPage == null) ? null : TypeUtils.as(editorPage.getIEditorPart(), IDiagramWorkbenchPart.class);
+
+ // The lazy initialization, used in the patch for bug 519107, does not initialize the diagram edit part of UmlGmfDiagramEditor
+ // So we call the setFocus method here to initialize it manually (see patch for bug 521353)
+ if (null != diagramPart) {
+ if (null == diagramPart.getDiagramEditPart()) {
+ diagramPart.setFocus();
+ }
+ return diagramPart.getDiagramEditPart();
+ }
+ return null;
+ }
+
+ public EditPart findEditPart(EObject modelElement) {
+ return findEditPart(getActiveDiagramEditor(), modelElement);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public EditPart findEditPart(IMultiDiagramEditor editor, EObject modelElement) {
+ IEditorPart activeEditor = editor.getActiveEditor();
+ assertThat("No diagram active", activeEditor, instanceOf(DiagramEditor.class));
+ return findEditPart((DiagramEditor) activeEditor, modelElement);
+ }
+
+ public EditPart findEditPart(IDiagramWorkbenchPart editor, EObject modelElement) {
+ DiagramEditPart diagram = editor.getDiagramEditPart();
+ return findEditPart(diagram, modelElement);
+ }
+
+
+ /**
+ * Find orphan edit part with a type.
+ *
+ * @param type
+ * the type
+ * @return the edits the part
+ */
+ public EditPart findOrphanEditPart(String type) {
+ IDiagramWorkbenchPart activeEditor = (IDiagramWorkbenchPart) editor.getActiveEditor();
+ EditPart result = null;
+ for (Iterator<View> views = Iterators.filter(activeEditor.getDiagram().eAllContents(), View.class); views.hasNext();) {
+ View next = views.next();
+ EObject element = next.getElement();
+ if (element == null && type.equals(next.getType())) {
+ result = (EditPart) activeEditor.getDiagramGraphicalViewer().getEditPartRegistry().get(next);
+ break;
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Find orphan edit part.
+ *
+ * @return the edits the part
+ */
+ public EditPart findOrphanEditPart() {
+ IDiagramWorkbenchPart activeEditor = (IDiagramWorkbenchPart) editor.getActiveEditor();
+ EditPart result = null;
+ for (Iterator<View> views = Iterators.filter(activeEditor.getDiagram().eAllContents(), View.class); views.hasNext();) {
+ View next = views.next();
+
+ String type = next.getType();
+ EObject element = next.getElement();
+
+ if (element == null && !excludedTypeView.contains(type)) {
+ result = (EditPart) activeEditor.getDiagramGraphicalViewer().getEditPartRegistry().get(next);
+ break;
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Find an edit-part for a model element in a particular {@code scope}.
+ *
+ * @param scope
+ * an edit part in which to search (its children) for an edit-part
+ * @param modelElement
+ * the model element visualized by the edit-part to search for
+ *
+ * @return the matching edit-part, or {@code null} if none is found in the {@code scope}
+ */
+ public EditPart findEditPart(EditPart scope, EObject modelElement) {
+ EditPart result = null;
+
+ View view = PlatformHelper.getAdapter(scope, View.class);
+ if ((view != null) && (view.getElement() == modelElement)) {
+ result = scope;
+ }
+
+ if (result == null) {
+ // Search children
+ for (Iterator<?> iter = scope.getChildren().iterator(); (result == null) && iter.hasNext();) {
+ result = findEditPart((EditPart) iter.next(), modelElement);
+ }
+ }
+
+ if ((result == null) && (scope instanceof GraphicalEditPart)) {
+ // Search edges
+ for (Iterator<?> iter = ((GraphicalEditPart) scope).getSourceConnections().iterator(); (result == null) && iter.hasNext();) {
+ result = findEditPart((EditPart) iter.next(), modelElement);
+ }
+ if (result == null) {
+ for (Iterator<?> iter = ((GraphicalEditPart) scope).getTargetConnections().iterator(); (result == null) && iter.hasNext();) {
+ result = findEditPart((EditPart) iter.next(), modelElement);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Require an edit-part for a model element in a particular {@code scope}.
+ *
+ * @param scope
+ * an edit part in which to search (its children) for an edit-part
+ * @param modelElement
+ * the model element visualized by the edit-part to search for
+ *
+ * @return the matching edit-part
+ *
+ * @throws AssertionError
+ * if the required edit-part is found in the {@code scope}
+ */
+ public EditPart requireEditPart(EditPart scope, EObject modelElement) {
+ EditPart result = findEditPart(scope, modelElement);
+ if (result == null) {
+ String label = getLabel(modelElement);
+ fail(String.format("No edit-part found for \"%s\" in %s", label, scope));
+ }
+ return result;
+ }
+
+ public String getLabel(EObject object) {
+ String result = null;
+
+ try {
+ EditingDomain domain = ServiceUtils.getInstance().getTransactionalEditingDomain(editor.getServicesRegistry());
+ if (domain instanceof AdapterFactoryEditingDomain) {
+ IItemLabelProvider labels = (IItemLabelProvider) ((AdapterFactoryEditingDomain) domain).getAdapterFactory().adapt(object, IItemLabelProvider.class);
+ if (labels != null) {
+ result = labels.getText(object);
+ }
+ }
+ } catch (ServiceException e) {
+ // Doesn't matter
+ }
+
+ if (result == null) {
+ result = String.valueOf(object);
+ }
+
+ return result;
+ }
+
+ public EditPart findEditPart(String name, Class<? extends NamedElement> type) {
+ return findEditPart(getActiveDiagramEditor(), name, type);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public EditPart findEditPart(IMultiDiagramEditor editor, String name, Class<? extends NamedElement> type) {
+ IEditorPart activeEditor = editor.getActiveEditor();
+ assertThat("No diagram active", activeEditor, instanceOf(DiagramEditor.class));
+ return findEditPart((DiagramEditor) activeEditor, name, type);
+ }
+
+ public EditPart findEditPart(IDiagramWorkbenchPart editor, String name, Class<? extends NamedElement> type) {
+ EditPart result = null;
+
+ for (Iterator<View> views = Iterators.filter(editor.getDiagram().eAllContents(), View.class); views.hasNext();) {
+ View next = views.next();
+ EObject element = next.getElement();
+ if (type.isInstance(element) && name.equals(type.cast(element).getName())) {
+ result = (EditPart) editor.getDiagramGraphicalViewer().getEditPartRegistry().get(next);
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public void select(EditPart editPart) {
+ editPart.getViewer().getSelectionManager().appendSelection(editPart);
+ }
+
+ public void deselect(EditPart editPart) {
+ editPart.getViewer().getSelectionManager().deselect(editPart);
+ }
+
+ public void move(EditPart editPart, Point newLocation) {
+ execute(new ICommandProxy(new SetBoundsCommand(getEditingDomain(), "Move Node", editPart, newLocation)));
+ }
+
+ public void resize(EditPart editPart, Dimension newSize) {
+ execute(new ICommandProxy(new SetBoundsCommand(getEditingDomain(), "Resize Node", editPart, newSize)));
+ }
+
+ public void execute(org.eclipse.gef.commands.Command command) {
+ assertThat("Command not executable", command.canExecute(), is(true));
+ getActiveDiagramEditor().getDiagramEditDomain().getDiagramCommandStack().execute(command);
+ flushDisplayEvents();
+ }
+
+ @Override
+ public void execute(Command command) {
+ super.execute(command);
+ flushDisplayEvents();
+ }
+
+ @Override
+ public IStatus execute(IUndoableOperation operation, IProgressMonitor monitor, IAdaptable info) {
+ IStatus result = super.execute(operation, monitor, info);
+ flushDisplayEvents();
+ return result;
+ }
+
+ @Override
+ public void undo() {
+ super.undo();
+ flushDisplayEvents();
+ }
+
+ @Override
+ public void redo() {
+ super.redo();
+ flushDisplayEvents();
+ }
+
+ public PaletteViewer getPalette() {
+ return getPalette(getActiveDiagramEditor());
+ }
+
+ /**
+ * @since 2.0
+ */
+ public PaletteViewer getPalette(IMultiDiagramEditor editor) {
+ IEditorPart activeEditor = editor.getActiveEditor();
+ assertThat("No diagram active", activeEditor, instanceOf(DiagramEditor.class));
+ return getPalette((DiagramEditor) activeEditor);
+ }
+
+ public PaletteViewer getPalette(IDiagramWorkbenchPart editor) {
+ return editor.getDiagramEditPart().getViewer().getEditDomain().getPaletteViewer();
+ }
+
+ public void flushDisplayEvents() {
+ for (;;) {
+ try {
+ if (Display.getCurrent()!=null && !Display.getCurrent().readAndDispatch()) {
+ break;
+ }
+ } catch (Exception e) {
+ Bundle testBundle = FrameworkUtil.getBundle((testClass == null) ? PapyrusEditorFixture.class : testClass);
+ Platform.getLog(testBundle).log(new Status(IStatus.ERROR, testBundle.getSymbolicName(), "Uncaught exception in display runnable.", e));
+ }
+ }
+ }
+
+ public IViewPart getView(String id, boolean open) {
+ IViewPart result = null;
+
+ IWorkbenchPage wbPage = getWorkbenchPage();
+
+ try {
+ result = wbPage.findView(id);
+ if ((result == null) && open) {
+ result = wbPage.showView(id);
+ }
+
+ if (result != null) {
+ result.getSite().getPage().activate(result);
+ flushDisplayEvents();
+ }
+ } catch (PartInitException e) {
+ e.printStackTrace();
+ fail("Failed to show a view: " + id);
+ } finally {
+ flushDisplayEvents();
+ }
+
+ return result;
+ }
+
+ public void save() {
+ save(getEditor());
+ }
+
+ public void save(ISaveablePart part) {
+ if (part.isDirty()) {
+ try {
+ part.doSave(new NullProgressMonitor());
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to save editor/view: " + e.getLocalizedMessage());
+ } finally {
+ // Must flush display events because some steps (e.g. dependent editor reload)
+ // are done asynchronously in a UI job
+ flushDisplayEvents();
+ }
+ }
+ }
+
+ public void saveAll() {
+ try {
+ IWorkbenchPage page = editor.getSite().getPage();
+ page.saveAllEditors(false);
+ } finally {
+ // Must flush display events because some steps (e.g. dependent editor reload)
+ // are done asynchronously in a UI job
+ flushDisplayEvents();
+ }
+ }
+
+ public void splitEditorArea(IEditorPart editorToMove, boolean splitHorizontally) {
+ MPart editorPart = editorToMove.getSite().getService(MPart.class);
+ EModelService modelService = editorPart.getContext().get(EModelService.class);
+ MPartStack oldStack = (MPartStack) modelService.getContainer(editorPart);
+ MPartStack newStack = modelService.createModelElement(MPartStack.class);
+ modelService.insert(newStack, oldStack, splitHorizontally ? EModelService.RIGHT_OF : EModelService.BELOW, 0.5f);
+ newStack.getChildren().add(editorPart);
+
+ activate(editorToMove);
+ }
+
+ public List<IWorkbenchPart> getPartStack(IWorkbenchPart part) {
+ List<IWorkbenchPart> result;
+
+ MPart mpart = part.getSite().getService(MPart.class);
+ EModelService modelService = mpart.getContext().get(EModelService.class);
+ MPartStack stack = (MPartStack) modelService.getContainer(mpart);
+
+ result = Lists.newArrayListWithCapacity(stack.getChildren().size());
+ for (MPart next : Iterables.filter(stack.getChildren(), MPart.class)) {
+ IWorkbenchPart wbPart = next.getContext().get(IWorkbenchPart.class);
+ if (wbPart != null) {
+ result.add(wbPart);
+ }
+ }
+
+ return result;
+ }
+
+ protected final boolean hasRequiredViews() {
+ return getRequiredViews() != null;
+ }
+
+ protected final ShowView getRequiredViews() {
+ ShowView result = testDescription.getAnnotation(ShowView.class);
+
+ if (result == null) {
+ for (Class<?> clazz = testClass; (result == null) && (clazz != null) && (clazz != Object.class); clazz = clazz.getSuperclass()) {
+ result = clazz.getAnnotation(ShowView.class);
+ }
+ }
+
+ return result;
+ }
+
+ protected void openRequiredViews() {
+ IWorkbenchPage page = getWorkbenchPage();
+
+ for (ShowViewDescriptor next : ShowViewDescriptor.getDescriptors(getRequiredViews())) {
+ IViewPart part = page.findView(next.viewID());
+ if (part == null) {
+ // Must open it
+ try {
+ part = page.showView(next.viewID());
+ movePartRelativeTo(part, next.relativeTo(), next.location());
+
+ if (viewsToClose == null) {
+ viewsToClose = Lists.newArrayListWithExpectedSize(1);
+ }
+ viewsToClose.add(part);
+ } catch (PartInitException e) {
+ e.printStackTrace();
+ fail("Failed to open required view: " + e.getLocalizedMessage());
+ }
+ }
+ }
+
+ flushDisplayEvents();
+ }
+
+ private void movePartRelativeTo(IWorkbenchPart part, String relativeTo, int where) {
+ MPart mPart = part.getSite().getService(MPart.class);
+ EModelService modelService = mPart.getContext().get(EModelService.class);
+ MUIElement relativePart = modelService.find(relativeTo, modelService.getTopLevelWindowFor(mPart));
+ if (relativePart instanceof MPartSashContainerElement) {
+ MStackElement toMove = mPart;
+ MPlaceholder placeHolder = mPart.getCurSharedRef();
+ if (placeHolder != null) {
+ toMove = placeHolder;
+ }
+
+ if (where < 0) {
+ // Add it to the relative part's containing stack
+ if (relativePart instanceof MPart) {
+ MPart relativeMPart = (MPart) relativePart;
+ if (relativeMPart.getCurSharedRef() != null) {
+ // This is where the part is stacked
+ relativePart = relativeMPart.getCurSharedRef();
+ }
+ }
+ relativePart.getParent().getChildren().add(toMove);
+ } else {
+ // Insert it next to the relative part
+ MPartStack newStack = modelService.createModelElement(MPartStack.class);
+ newStack.getChildren().add(toMove);
+ modelService.insert(newStack, (MPartSashContainerElement) relativePart, where, 0.3f);
+ }
+ }
+ }
+
+ protected void closeRequiredViews() {
+ // Only close the Palette view if we opened it
+ if (viewsToClose != null) {
+ for (IViewPart closeMe : viewsToClose) {
+ closeMe.getSite().getPage().hideView(closeMe);
+ }
+ viewsToClose = null;
+ flushDisplayEvents();
+ }
+ }
+
+ private static final class ShowViewDescriptor {
+
+ private static final String DEFAULT_RELATIVE_TO = "org.eclipse.ui.editorss"; //$NON-NLS-1$
+
+ private static final ShowView.Location DEFAULT_LOCATION_EDITORS = ShowView.Location.RIGHT;
+
+ private static final ShowView.Location DEFAULT_LOCATION_VIEW = ShowView.Location.STACKED;
+
+ private final String viewID;
+
+ private final String relativeTo;
+
+ private final ShowView.Location location;
+
+ private ShowViewDescriptor(ShowView annotation, int index) {
+ this.viewID = annotation.value()[index];
+
+ String[] relativeTo = annotation.relativeTo();
+ this.relativeTo = (relativeTo.length == 0) ? null : (relativeTo.length == 1) ? relativeTo[0] : relativeTo[index];
+
+ ShowView.Location[] location = annotation.location();
+ this.location = (location.length == 0) ? null : (location.length == 1) ? location[0] : location[index];
+ }
+
+ static Iterable<ShowViewDescriptor> getDescriptors(final ShowView annotation) {
+ ImmutableList.Builder<ShowViewDescriptor> result = ImmutableList.builder();
+
+ String[] ids = annotation.value();
+ for (int i = 0; i < ids.length; i++) {
+ result.add(new ShowViewDescriptor(annotation, i));
+ }
+
+ return result.build();
+ }
+
+ String viewID() {
+ return viewID;
+ }
+
+ String relativeTo() {
+ return (relativeTo != null) ? relativeTo : DEFAULT_RELATIVE_TO;
+ }
+
+ int location() {
+ return ((location != null) ? location : (relativeTo == null) ? DEFAULT_LOCATION_EDITORS : DEFAULT_LOCATION_VIEW).toModelServiceLocation();
+ }
+ }
+
+ public PreferencesHint getPreferencesHint() {
+ PreferencesHint result = PreferencesHint.USE_DEFAULTS;
+
+ if (activeDiagramEditor != null) {
+ RootEditPart rootEditPart = activeDiagramEditor.getDiagramGraphicalViewer().getRootEditPart();
+ if (rootEditPart instanceof IDiagramPreferenceSupport) {
+ result = ((IDiagramPreferenceSupport) rootEditPart).getPreferencesHint();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void ensurePapyrusPerspective() {
+ Display.getDefault().syncExec(new Runnable() {
+
+ @Override
+ public void run() {
+ final String papyrus = "org.eclipse.papyrus.infra.core.perspective"; //$NON-NLS-1$
+ final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IPerspectiveDescriptor perspective = activePage.getPerspective();
+ if (!papyrus.equals(perspective.getId())) {
+ perspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(papyrus);
+ if (perspective != null) {
+ activePage.setPerspective(perspective);
+ flushDisplayEvents();
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PluginResource.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PluginResource.java
new file mode 100644
index 00000000000..cd13354c59e
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/PluginResource.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2015 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 469188
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * Annotation indicating the bundle-relative path to one or more resources from which to load the test model of an {@link AbstractModelFixture}.
+ *
+ * @see AbstractModelFixture
+ * @see JavaResource
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PluginResource {
+
+ String[] value();
+
+ /** The bundle containing the referenced paths, if not the bundle containing the test. */
+ String bundle() default "";
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ProjectFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ProjectFixture.java
new file mode 100644
index 00000000000..dd726710dbc
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ProjectFixture.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2014, 2015 CEA, Christian W. Damus, 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ * Christian W. Damus - bug 451230
+ * Christian W. Damus - bug 468030
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.workspace.util.WorkspaceSynchronizer;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+
+/**
+ * A self-creating and self-destroying workspace project named according to the current test case.
+ */
+public class ProjectFixture implements TestRule {
+
+ private IProject project;
+
+ public ProjectFixture() {
+ super();
+ }
+
+ public final IProject getProject() {
+ return project;
+ }
+
+ public URI getURI(IPath path) {
+ return URI.createPlatformResourceURI(project.getFile(path).getFullPath().toString(), true);
+ }
+
+ public URI getURI(String path) {
+ return URI.createPlatformResourceURI(project.getFile(new Path(path)).getFullPath().toString(), true);
+ }
+
+ public IFile getFile(URI uri) {
+ return !uri.isPlatformResource() ? null : project.getWorkspace().getRoot().getFile(new Path(uri.toPlatformString(true)));
+ }
+
+ /**
+ * Creates a new file at the specified project-relative path with the contents of a bundle resource.
+ *
+ * @param relativeFilePath
+ * the project-relative path of the file to create
+ * @param classFromBundle
+ * the bundle in which its content is to be found
+ * @param resourcePath
+ * the path in the context bundle of the resource to copy
+ *
+ * @return the new file
+ *
+ * @throws IOException
+ * on any problem in creating the file
+ */
+ public IFile createFile(String relativeFilePath, Class<?> classFromBundle, String resourcePath) throws IOException {
+ IFile result;
+
+ Bundle bundle = FrameworkUtil.getBundle(classFromBundle);
+ URL resource = (bundle == null) ? null : bundle.getResource(resourcePath);
+ if (resource == null) {
+ throw new IOException("No such bundle resource: " + resourcePath);
+ }
+
+ IPath path = new Path(relativeFilePath);
+
+ try (InputStream input = resource.openStream()) {
+ createFolders(path.removeLastSegments(1));
+ result = project.getFile(path);
+ result.create(input, false, null);
+ } catch (CoreException e) {
+ if (e.getStatus().getException() instanceof IOException) {
+ throw (IOException) e.getStatus().getException();
+ } else if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ }
+ throw new IOException("Failed to create file", e);
+ }
+
+ return result;
+ }
+
+ private void createFolders(IPath folderPath) throws CoreException {
+ if ((folderPath.segmentCount() > 0) && !folderPath.lastSegment().isEmpty()) {
+ createFolders(folderPath.removeLastSegments(1));
+ IFolder folder = project.getFolder(folderPath);
+ if (!folder.isAccessible()) {
+ folder.create(false, true, null);
+ }
+ }
+ }
+
+ /**
+ * Creates a new file in my project with the contents of a bundle resource.
+ *
+ * @param classFromBundle
+ * the bundle in which its content is to be found
+ * @param resourcePath
+ * the path in the context bundle of the resource to copy
+ *
+ * @return the new file, which will have the same name as the bundle resource and will be at the top level of the project
+ *
+ * @throws IOException
+ * on any problem in creating the file
+ *
+ * @see #createFile(String, Class, String)
+ */
+ public IFile createFile(Class<?> classFromBundle, String resourcePath) throws IOException {
+ return createFile(new Path(resourcePath).lastSegment(), classFromBundle, resourcePath);
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ String name = description.getMethodName();
+ if (name == null) {
+ // We are used as a class rule, then
+ name = description.getClassName();
+ if (name != null) {
+ name = name.substring(name.lastIndexOf('.') + 1);
+ }
+ }
+
+ final String projectName = name;
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ createProject(projectName);
+
+ try {
+ base.evaluate();
+ } finally {
+ deleteProject();
+ }
+ }
+ };
+ }
+
+ protected void createProject(String name) throws CoreException {
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+
+ if (project.exists()) {
+ // Start clean, if we can
+ deleteProject();
+ }
+
+ if (!project.exists()) {
+ project.create(null);
+ }
+
+ if (!project.isOpen()) {
+ project.open(null);
+ }
+
+ project.refreshLocal(IResource.DEPTH_INFINITE, null);
+ }
+
+ protected void deleteProject() {
+ try {
+ project.refreshLocal(IResource.DEPTH_INFINITE, null);
+
+ // Make sure that we can delete everything
+ project.accept(new IResourceVisitor() {
+
+ @Override
+ public boolean visit(IResource resource) throws CoreException {
+ switch (resource.getType()) {
+ case IResource.FILE:
+ case IResource.FOLDER:
+ ensureWritable(resource);
+ break;
+ }
+
+ return true;
+ }
+ });
+
+ project.delete(true, null);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ // leave the project. We may end up re-using it, who knows?
+ }
+ }
+
+ protected void ensureWritable(IResource resource) throws CoreException {
+ ResourceAttributes attr = resource.getResourceAttributes();
+ if (attr.isReadOnly()) {
+ attr.setReadOnly(false);
+ resource.setResourceAttributes(attr);
+ }
+ }
+
+ public void setReadOnly(String projectRelativePath) {
+ setReadOnly(new Path(projectRelativePath));
+ }
+
+ public void setReadOnly(IPath projectRelativePath) {
+ setReadOnly(project.findMember(projectRelativePath));
+ }
+
+ public void setReadOnly(Resource resource) {
+ IFile file = WorkspaceSynchronizer.getFile(resource);
+ assertThat("Cannot set non-workspace resource read-only", file, notNullValue());
+ setReadOnly(file);
+ }
+
+ public void setReadOnly(IResource resource) {
+ setReadOnly(resource, true);
+ }
+
+ public void setReadOnly(IResource resource, boolean readOnly) {
+ ResourceAttributes attr = resource.getResourceAttributes();
+
+ if (attr.isReadOnly() != readOnly) {
+ attr.setReadOnly(readOnly);
+
+ try {
+ resource.setResourceAttributes(attr);
+ } catch (CoreException e) {
+ e.getLocalizedMessage();
+ fail(String.format("Failed to make workspace resource %s: %s", readOnly ? "read-only" : "writable", e.getLocalizedMessage()));
+ }
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ResourceSetFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ResourceSetFixture.java
new file mode 100644
index 00000000000..23b14c2a334
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ResourceSetFixture.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 CEA 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import org.eclipse.emf.common.command.BasicCommandStack;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
+
+
+/**
+ * A simple non-transactional {@link ResourceSet} fixture.
+ */
+public class ResourceSetFixture extends AbstractModelFixture<EditingDomain> {
+
+ public ResourceSetFixture() {
+ super();
+ }
+
+ protected EditingDomain createEditingDomain() {
+ return new AdapterFactoryEditingDomain(new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE), new BasicCommandStack());
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/RuleUtil.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/RuleUtil.java
new file mode 100644
index 00000000000..8ff16363b7c
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/RuleUtil.java
@@ -0,0 +1,132 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.junit.Assert.fail;
+
+import java.util.concurrent.Callable;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+
+/**
+ * Utilities for working with JUnit {@linkplain TestRule rules}.
+ */
+public class RuleUtil {
+
+ private RuleUtil() {
+ super();
+ }
+
+ /**
+ * Ad hoc invocation of an operation wrapped in the start/finish behaviour of a JUnit rule.
+ *
+ * @param run
+ * the operation to run
+ * @param rule
+ * the rule to wrap it in
+ *
+ * @throws Exception
+ * on any exception thrown by the runnable or the rule
+ */
+ public static void runWith(Runnable run, TestRule rule) throws Exception {
+ callWith(call(run), rule);
+ }
+
+ /**
+ * Safe ad hoc invocation of an operation wrapped in the start/finish behaviour of a JUnit rule.
+ * Fails JUnitishly on any exception in the callable or the rule.
+ *
+ * @param run
+ * the operation to run
+ * @param rule
+ * the rule to wrap it in
+ */
+ public static void safeRunWith(Runnable run, TestRule rule) throws Exception {
+ safeCallWith(call(run), rule);
+ }
+
+ static Callable<?> call(final Runnable run) {
+ return new Callable<Void>() {
+
+ public Void call() throws Exception {
+ run.run();
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Ad hoc invocation of a callable wrapped in the start/finish behaviour of a JUnit rule.
+ *
+ * @param callable
+ * the callable to execute
+ * @param rule
+ * the rule to wrap it in
+ *
+ * @throws Exception
+ * on any exception thrown by the callable or the rule
+ */
+ public static <V> V callWith(Callable<V> callable, TestRule rule) throws Exception {
+ class CallableStatement extends Statement {
+
+ final Callable<V> callable;
+
+ V result;
+
+ CallableStatement(Callable<V> callable) {
+ this.callable = callable;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ result = callable.call();
+ }
+ }
+
+ try {
+ CallableStatement statement = new CallableStatement(callable);
+ rule.apply(statement, Description.EMPTY).evaluate();
+ return statement.result;
+ } catch (Throwable t) {
+ if(t instanceof Exception) {
+ throw (Exception)t;
+ } else {
+ throw (Error)t;
+ }
+ }
+ }
+
+ /**
+ * Safe ad hoc invocation of a callable wrapped in the start/finish behaviour of a JUnit rule.
+ * Fails JUnitishly on any exception in the callable or the rule.
+ *
+ * @param callable
+ * the callable to execute
+ * @param rule
+ * the rule to wrap it in
+ */
+ public static <V> V safeCallWith(Callable<V> callable, TestRule rule) {
+ try {
+ return callWith(callable, rule);
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail("Failed to invoke callable with rule: " + e.getLocalizedMessage());
+ return null; // Unreachable
+ }
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ServiceRegistryModelSetFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ServiceRegistryModelSetFixture.java
new file mode 100644
index 00000000000..cf90e0274b2
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ServiceRegistryModelSetFixture.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import org.eclipse.papyrus.infra.core.services.ExtensionServicesRegistry;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+
+
+/**
+ * A model-set fixture that uses a fully configured service registry to provide the model set.
+ */
+public class ServiceRegistryModelSetFixture extends ModelSetFixture {
+
+ public ServiceRegistryModelSetFixture() {
+ super();
+ }
+
+ @Override
+ protected ServicesRegistry createServiceRegistry() throws Exception {
+ ServicesRegistry result = new ExtensionServicesRegistry(org.eclipse.papyrus.infra.core.Activator.PLUGIN_ID);
+
+ try {
+ result.startRegistry();
+ } catch (ServiceException e) {
+ // Try to continue with the test, anyways. This is expected in the test environment
+ }
+
+ return result;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowView.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowView.java
new file mode 100644
index 00000000000..103db0b1d37
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowView.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2014 CEA 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:
+ * Christian W. Damus (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.eclipse.e4.ui.workbench.modeling.EModelService;
+
+
+/**
+ * An annotation on tests using the {@link PapyrusEditorFixture} that need the palette view to be open before initializing the
+ * test (which generally entails opening one or more editors).
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ShowView {
+
+ /** The IDs of the views to show before the editor is opened. */
+ String[] value();
+
+ /**
+ * The IDs of workbench parts relative to which the views specified by the annotation are to be shown. If unspecified,
+ * the default is the workbench's editor area. If exactly part ID is specified, it is used for all views. Otherwise, a
+ * corresponding value is required for each view ID to be shown.
+ */
+ String[] relativeTo() default {};
+
+ /**
+ * The relative locations of the views to show. If unspecified, the default is {@link Location#STACKED} when the {@linkplain #relativeTo()
+ * relative part} is a view, otherwise {@link Location#RIGHT} when the relative part is the editor area.
+ * If exactly one value is specified, it is used for all views. Otherwise, a corresponding value is
+ * required for each view ID.
+ */
+ Location[] location() default {};
+
+ /**
+ * Enumeration of locations in which to open a view relative to some other workbench part.
+ */
+ enum Location {
+ /** Stacked with the relative part. */
+ STACKED(-1),
+ /** To the left of the relative part. */
+ LEFT(EModelService.LEFT_OF),
+ /** To the right of the relative part. */
+ RIGHT(EModelService.RIGHT_OF),
+ /** Above the relative part. */
+ ABOVE(EModelService.ABOVE),
+ /** Below the relative part. */
+ BELOW(EModelService.BELOW);
+
+ private final int modelServiceLocation;
+
+ private Location(int modelServiceLocation) {
+ this.modelServiceLocation = modelServiceLocation;
+ }
+
+ public int toModelServiceLocation() {
+ return modelServiceLocation;
+ }
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowViewRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowViewRule.java
new file mode 100644
index 00000000000..0459c73a971
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/ShowViewRule.java
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * Copyright (c) 2014 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.junit.Assert.fail;
+
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+
+/**
+ * A rule that ensures a view is showing for the duration of a test.
+ */
+public class ShowViewRule extends TestWatcher {
+
+ private final String viewID;
+
+ private IViewPart view;
+
+ public ShowViewRule(String viewID) {
+ super();
+
+ this.viewID = viewID;
+ }
+
+ @Override
+ protected void starting(Description description) {
+ IWorkbench bench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = bench.getActiveWorkbenchWindow();
+ if(window == null) {
+ window = bench.getWorkbenchWindows()[0];
+ }
+
+ IWorkbenchPage page = window.getActivePage();
+ view = page.findView(viewID);
+
+ if(view == null) {
+ try {
+ view = page.showView(viewID);
+ } catch (PartInitException e) {
+ fail(String.format("Failed to show view %s: %s", viewID, e.getLocalizedMessage()));
+ }
+ }
+ }
+
+ @Override
+ protected void finished(Description description) {
+ if(view != null) {
+ // Hide it again, because we showed it in the first place
+ view.getSite().getPage().hideView(view);
+ }
+
+ view = null;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/StandaloneResourceSetFixture.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/StandaloneResourceSetFixture.java
new file mode 100644
index 00000000000..2bc1745a2df
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/StandaloneResourceSetFixture.java
@@ -0,0 +1,179 @@
+/*****************************************************************************
+ * Copyright (c) 2015 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.net.URL;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.plugin.EcorePlugin;
+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.papyrus.junit.utils.JUnitUtils;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * A {@link ResourceSet} fixture rule that is compatible with stand-alone JUnit
+ * execution (not in an Eclipse instance). It uses the {@link JavaResource}
+ * annotation (only, not also {@link PluginResource}) to identify test resources
+ * to load into the resource set on set-up.
+ *
+ * @param <T>
+ * the kind of model fixture element to load
+ */
+public class StandaloneResourceSetFixture<T extends EObject> extends TestWatcher {
+
+ private final Class<? extends T> modelType;
+
+ private ResourceSet resourceSet;
+ private Resource resource;
+ private T model;
+
+ /**
+ * Initializes me with the kind of model fixture element to load.
+ *
+ * @param modelType
+ * the model fixture element's type
+ */
+ public StandaloneResourceSetFixture(Class<? extends T> modelType) {
+ super();
+
+ this.modelType = modelType;
+ }
+
+ /**
+ * Obtains the resource set in which the {@linkplain #getModel() model fixture} is loaded.
+ *
+ * @see #getModel()
+ */
+ public ResourceSet getResourceSet() {
+ return resourceSet;
+ }
+
+ /**
+ * Obtains the resource containing the {@linkplain #getModel() model fixture}.
+ *
+ * @see #getModel()
+ */
+ public Resource getResource() {
+ return resource;
+ }
+
+ public Resource getResource(URI uri, boolean loadOnDemand) {
+ return getResourceSet().getResource(uri, loadOnDemand);
+ }
+
+ /**
+ * Obtains the model fixture.
+ */
+ public T getModel() {
+ return model;
+ }
+
+ /**
+ * Obtains the root model element of the specified type from my resource set.
+ *
+ * @param modelType
+ * the type of model to retrieve, or {@code null} if none is found
+ */
+ public <E extends EObject> E getModel(Class<E> modelType) {
+ E result = null;
+
+ Resource resource;
+ for (Iterator<Resource> iter = resourceSet.getResources().iterator(); (result == null) && iter.hasNext();) {
+ resource = iter.next();
+ result = Iterables.getFirst(Iterables.filter(resource.getContents(), modelType), null);
+ }
+
+ return result;
+ }
+
+ public static <T extends EObject> StandaloneResourceSetFixture<T> create(Class<T> modelType) {
+ return new StandaloneResourceSetFixture<>(modelType);
+ }
+
+ @Override
+ protected void starting(Description description) {
+ JavaResource annotation = JUnitUtils.getAnnotation(description, JavaResource.class);
+ assertThat("No @JavaResource annotation found in test case.", annotation, notNullValue());
+
+ resourceSet = new ResourceSetImpl();
+
+ if (!EcorePlugin.IS_ECLIPSE_RUNNING) {
+ // EMF ensures that additional invocations have no effect
+ EcorePlugin.ExtensionProcessor.process(null);
+ }
+
+ final Class<?> testClass = JUnitUtils.getTestClass(description);
+ for (String next : annotation.value()) {
+ resourceSet.getResource(getTestResourceURI(next, testClass), true);
+ }
+
+ model = getModel(modelType);
+
+ assertThat("No model of type " + modelType.getSimpleName() + " loaded.", model, notNullValue());
+ resource = model.eResource();
+ }
+
+ @Override
+ protected void finished(Description description) {
+ for (Iterator<Resource> iter = resourceSet.getResources().iterator(); iter.hasNext();) {
+ Resource next = iter.next();
+
+ next.unload();
+ iter.remove();
+
+ next.eAdapters().clear();
+ }
+
+ resourceSet.eAdapters().clear();
+ resourceSet = null;
+ resource = null;
+ model = null;
+ }
+
+ /**
+ * Obtains the URI of a test resource.
+ *
+ * @param path
+ * the path of the test resource. Absolute paths are searched on the classpath
+ * and relative paths are searched relative to the given {@code context} class
+ * @param context
+ * the context class for resolution of relative resource paths
+ *
+ * @return the URI of the referenced test resource
+ */
+ protected URI getTestResourceURI(String path, Class<?> context) {
+ URL resultURL;
+
+ IPath resourcePath = new Path(path);
+ if (resourcePath.isAbsolute()) {
+ resultURL = context.getClassLoader().getResource(path);
+ } else {
+ resultURL = context.getResource(path);
+ }
+
+ return URI.createURI(resultURL.toString(), true);
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThread.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThread.java
new file mode 100644
index 00000000000..4f7a1bd9d25
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThread.java
@@ -0,0 +1,31 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used with the {@link UIThreadRule} to annotated a test that requires the UI
+ * thread, where perhaps others in the same scope do not.
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface UIThread {
+ // Empty annotation
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThreadRule.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThreadRule.java
new file mode 100644
index 00000000000..a22f6652263
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/rules/UIThreadRule.java
@@ -0,0 +1,94 @@
+/*****************************************************************************
+ * Copyright (c) 2016 Christian W. Damus 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:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.rules;
+
+import org.eclipse.papyrus.junit.utils.JUnitUtils;
+import org.eclipse.swt.widgets.Display;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit rule that runs its test synchronously on the UI thread.
+ * This should be used only when the test manipulates API that
+ * require the current thread to be the UI thread.
+ */
+public class UIThreadRule implements TestRule {
+
+ private final boolean requireAnnotation;
+
+ /**
+ * Initializes me without the requirement for any annotation: all tests
+ * in my scope will run on the UI thread.
+ */
+ public UIThreadRule() {
+ this(false);
+ }
+
+ /**
+ * Initializes me with the requirement that individual tests needin the UI
+ * thread be annotated with {@link UIThread @UIThread}.
+ *
+ * @param requireAnnotation
+ */
+ public UIThreadRule(boolean requireAnnotation) {
+ super();
+
+ this.requireAnnotation = requireAnnotation;
+ }
+
+ @Override
+ public Statement apply(final Statement base, Description description) {
+ if (!requiresUIThread(description)) {
+ return base; // Nothing to do for this test
+ } else {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ final Throwable[] caught = { null };
+
+ final Runnable runTest = new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ caught[0] = t;
+ }
+ }
+ };
+
+ if (Display.getCurrent() != null) {
+ // Just run it
+ runTest.run();
+ } else {
+ // Run it on the UI thread
+ Display.getDefault().syncExec(runTest);
+ }
+
+ if (caught[0] != null) {
+ throw caught[0];
+ }
+ }
+ };
+ }
+ }
+
+ private boolean requiresUIThread(Description description) {
+ return !requireAnnotation
+ || JUnitUtils.getAnnotation(description, UIThread.class) != null;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEMFResourceTest.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEMFResourceTest.java
new file mode 100644
index 00000000000..69066e9706d
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEMFResourceTest.java
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * Copyright (c) 2015 CEA LIST 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:
+ * CEA LIST - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.junit.utils.tests;
+
+import java.util.List;
+
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.emf.ecore.util.Diagnostician;
+import org.eclipse.papyrus.junit.framework.classification.tests.AbstractPapyrusTest;
+import org.junit.Assert;
+
+/**
+ * Abstract test class for all EMF-based configuration models (property view, palettes, etc.)
+ */
+public abstract class AbstractEMFResourceTest extends AbstractPapyrusTest {
+
+ /**
+ * URI of the EMF resource to test
+ *
+ * @return
+ */
+ public abstract String getFileUri();
+
+ /**
+ * do the validation checks on the EMF resource
+ */
+ protected void doValidateResource() {
+ URI createPlatformPluginURI = URI.createPlatformPluginURI(getFileUri(), true);
+ Resource resource = new ResourceSetImpl().getResource(createPlatformPluginURI, true);
+ Diagnostic diagnostic = Diagnostician.INSTANCE.validate(resource.getContents().get(0));
+ Assert.assertEquals("The constraint model is not valid: " + printDiagnostic(diagnostic), Diagnostic.OK, diagnostic.getSeverity());
+ }
+
+ // FIXME : Something should exist in API to do that
+ protected String printDiagnostic(Diagnostic diagnostic) {
+ String message = diagnostic.getMessage();
+ List<Diagnostic> children = diagnostic.getChildren();
+ for (Diagnostic diagnostic2 : children) {
+ message += "\n" + diagnostic2.getMessage();
+ }
+ return message;
+ }
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEditorTest.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEditorTest.java
new file mode 100644
index 00000000000..e143eaf99b7
--- /dev/null
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/utils/tests/AbstractEditorTest.java
@@ -0,0 +1,244 @@
+/*****************************************************************************
+ * Copyright (c) 2013 CEA LIST.
+ *
+ * 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:
+ * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
+ *****************************************************************************/
+package org.eclipse.papyrus.junit.utils.tests;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.transaction.TransactionalEditingDomain;
+import org.eclipse.papyrus.infra.core.resource.AbstractBaseModel;
+import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.sashwindows.di.service.IPageManager;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.ui.editor.IMultiDiagramEditor;
+import org.eclipse.papyrus.junit.framework.classification.tests.AbstractPapyrusTest;
+import org.eclipse.papyrus.junit.utils.Activator;
+import org.eclipse.papyrus.junit.utils.EditorUtils;
+import org.eclipse.papyrus.junit.utils.PapyrusProjectUtils;
+import org.eclipse.papyrus.junit.utils.ProjectUtils;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPage;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerPageBookView;
+import org.eclipse.papyrus.views.modelexplorer.ModelExplorerView;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.IPage;
+import org.junit.After;
+import org.junit.Assert;
+import org.osgi.framework.Bundle;
+
+
+public abstract class AbstractEditorTest extends AbstractPapyrusTest {
+
+ /** the id of the model explorer */
+ public static final String MODELEXPLORER_VIEW_ID = "org.eclipse.papyrus.views.modelexplorer.modelexplorer"; //$NON-NLS-1$
+
+
+ protected IMultiDiagramEditor editor;
+
+ protected IProject project;
+
+
+ protected ModelExplorerView modelExplorerView;
+
+ protected IFile diModelFile;
+ /**
+ *
+ * @return
+ * the current bundle
+ */
+ protected Bundle getBundle() {
+ return Activator.getDefault().getBundle();
+ }
+
+ /**
+ * Create a new test project
+ * @param projectName
+ */
+ protected IProject createProject(String projectName) throws Exception {
+ project = ProjectUtils.createProject(projectName);
+ return project;
+ }
+
+ /**
+ * Initializes this editor
+ * Fails or throws an exception if an error occurs
+ *
+ * @param projectName
+ * the project that will be created at runtime to execute the test
+ * @param modelName
+ * the model that will be copied and test executed on.
+ * @param bundle
+ * the source bundle where the model is stored
+ */
+ protected void initModel(String projectName, String modelName, Bundle bundle) throws Exception {
+ createProject(projectName);
+ initModel(modelName, bundle);
+ }
+
+ /**
+ * Initializes this editor. Should be called, after a previous invocation of createProject()
+ *
+ * @param modelName
+ * @param bundle
+ * @throws Exception
+ */
+ protected void initModel(String modelName, Bundle bundle) throws Exception {
+ this.diModelFile = PapyrusProjectUtils.copyPapyrusModel(project, bundle, getSourcePath(), modelName);
+ Display.getDefault().syncExec(new Runnable() {
+
+ public void run() {
+ try {
+ editor = EditorUtils.openPapyrusEditor(diModelFile);
+ } catch (Exception ex) {
+ Activator.log.error(ex);
+ Assert.fail(ex.getMessage());
+ }
+ }
+ });
+
+ Assert.assertNotNull(editor);
+ }
+
+
+ /**
+ * copy a model into the workspace, e.g. a profile that is required by the test model
+ *
+ * @param projectName
+ * the project that will be created at runtime to execute the test
+ * @param modelName
+ * the model that will be copied and test executed on.
+ * @param bundle
+ * the source bundle where the model is stored
+ */
+ protected void copyModel(String modelName, Bundle bundle) throws Exception {
+ PapyrusProjectUtils.copyPapyrusModel(project, bundle, getSourcePath(), modelName);
+ }
+
+ @After
+ public void dispose() throws Exception {
+ if(editor != null) {
+ Display.getDefault().syncExec(new Runnable() {
+
+ public void run() {
+ ((IEditorPart)editor).getSite().getPage().closeEditor(editor, false);
+ }
+ });
+
+ editor = null;
+ }
+
+ if(project != null) {
+ project.delete(true, new NullProgressMonitor());
+ project = null;
+ }
+ }
+
+
+ public ModelExplorerView getModelExplorerView() {
+
+ Display.getDefault().syncExec(new Runnable() {
+
+ public void run() {
+ IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+
+ // we look for the modelexplorer
+ IViewPart modelexplorer;
+ try {
+ modelexplorer = activeWorkbenchWindow.getActivePage().showView(MODELEXPLORER_VIEW_ID);
+ } catch (PartInitException ex) {
+ ex.printStackTrace(System.out);
+ return;
+ }
+ ModelExplorerPageBookView view = (ModelExplorerPageBookView)modelexplorer;
+ IPage currentPage = view.getCurrentPage();
+ ModelExplorerPage page = (ModelExplorerPage)currentPage;
+ IViewPart viewer = page.getViewer();
+ modelExplorerView = (ModelExplorerView)viewer;
+
+ }
+ });
+ return modelExplorerView;
+ }
+
+ /**
+ *
+ * @return the current UML model
+ */
+ protected org.eclipse.uml2.uml.Package getRootUMLModel() {
+
+ IModel umlIModel;
+ try {
+ umlIModel = getModelSet().getModel("org.eclipse.papyrus.infra.core.resource.uml.UmlModel"); //$NON-NLS-1$
+
+ AbstractBaseModel umlModel = null;
+ if(umlIModel instanceof AbstractBaseModel) {
+ umlModel = (AbstractBaseModel)umlIModel;
+ }
+
+ Assert.assertFalse("umlRessource contains nothing", umlModel.getResource().getContents().isEmpty());
+ Object root = umlModel.getResource().getContents().get(0);
+ Assert.assertFalse("the root of UML model is not a package", root instanceof Package);
+
+ return (org.eclipse.uml2.uml.Package)root;
+ } catch (ServiceException e) {
+ Assert.fail(e.getMessage());
+ }
+ // not reachable due to asserts above (no check by caller)
+ return null;
+
+ }
+
+ protected IPageManager getPageManager() throws ServiceException {
+ return getServicesRegistry().getService(IPageManager.class);
+ }
+
+ protected ServicesRegistry getServicesRegistry() throws ServiceException {
+ return editor.getServicesRegistry();
+ }
+
+ protected TransactionalEditingDomain getTransactionalEditingDomain() throws ServiceException {
+ return getServicesRegistry().getService(TransactionalEditingDomain.class);
+ }
+
+ protected ModelSet getModelSet() throws ServiceException {
+ return getServicesRegistry().getService(ModelSet.class);
+ }
+
+ /**
+ * The path to the source model folder
+ *
+ * @return
+ */
+ protected abstract String getSourcePath();
+
+ protected void flushDisplayEvents() {
+ for(;;) {
+ try {
+ if(!Display.getCurrent().readAndDispatch()) {
+ break;
+ }
+ } catch (Exception e) {
+ Bundle testBundle = getBundle();
+ Platform.getLog(testBundle).log(new Status(IStatus.ERROR, testBundle.getSymbolicName(), "Uncaught exception in display runnable.", e));
+ }
+ }
+ }
+}

Back to the top