diff options
Diffstat (limited to 'tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit')
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 @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> + * @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)); + } + } + } +} |