Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests')
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/Activator.java66
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/exceptions/impl/TestExceptionsObject.java30
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UML.xtend263
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLComponent.java98
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLModule.java47
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/AppearanceTest.xtend46
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CanonicalTests.xtend59
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteChildLabelNodesTest.xtend80
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteTest.xtend268
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DeleteTest.xtend45
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DirectEditTest.xtend45
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DropTest.xtend45
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPComponent.java154
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPModule.xtend179
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/SynchronizationTest.xtend322
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TestExceptionManager.xtend90
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TransformationUtilities.xtend765
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/DefaultingList.xtend57
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/Metamodels.xtend108
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorComponent.java93
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorModule.java87
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AbstractTestTemplate.xtend163
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AllPackageTestsTemplate.xtend45
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AppearanceTestTemplate.xtend35
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/CodegenContext.xtend72
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DeleteTestTemplate.xtend33
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DirectEditTestTemplate.xtend35
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DropTestTemplate.xtend55
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Importator.xtend87
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/PapyrusDiagramCanonicalTests.xtend93
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Queries.java83
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/SynchronizationTestTemplate.xtend72
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TemplateQueries.xtend174
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestChildLabelNodeTemplate.xtend24
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestLinkTemplate.xtend23
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestNodeTemplate.xtend40
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/GenerateTestsWorkflow.xtend298
-rw-r--r--tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/TestExceptionsBuilder.xtend335
38 files changed, 4614 insertions, 0 deletions
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/Activator.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/Activator.java
new file mode 100644
index 00000000000..26e0c280b3c
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/Activator.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2011 Obeo.
+ * 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:
+ * Obeo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.papyrus.tests.framework;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle.
+ */
+public class Activator extends Plugin {
+
+ /**
+ * The plug-in ID.
+ */
+ public static final String PLUGIN_ID = "org.eclipse.papyrus.tests.framework";
+
+ /**
+ * The shared instance.
+ */
+ private static Activator plugin;
+
+ /**
+ * The constructor.
+ */
+ public Activator() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
+ */
+ 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;
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/exceptions/impl/TestExceptionsObject.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/exceptions/impl/TestExceptionsObject.java
new file mode 100644
index 00000000000..d5666f8874b
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/exceptions/impl/TestExceptionsObject.java
@@ -0,0 +1,30 @@
+/*****************************************************************************
+ * 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.tests.framework.exceptions.impl;
+
+import org.eclipse.emf.ecore.impl.MinimalEObjectImpl.Container;
+
+/**
+ * Base implementation of test-exceptions model objects.
+ */
+public class TestExceptionsObject extends Container {
+
+ /** Bit-field for compact boolean and enumeration field encoding. */
+ protected int eFlags;
+
+ public TestExceptionsObject() {
+ super();
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UML.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UML.xtend
new file mode 100644
index 00000000000..73e96e2f5dc
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UML.xtend
@@ -0,0 +1,263 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgen2uml
+
+import javax.inject.Inject
+import org.eclipse.emf.ecore.EObject
+import org.eclipse.emf.ecore.util.EcoreUtil
+import org.eclipse.gmf.codegen.gmfgen.ElementType
+import org.eclipse.gmf.codegen.gmfgen.GenCommonBase
+import org.eclipse.gmf.codegen.gmfgen.GenCompartment
+import org.eclipse.gmf.codegen.gmfgen.GenDiagram
+import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator
+import org.eclipse.gmf.codegen.gmfgen.GenLink
+import org.eclipse.gmf.codegen.gmfgen.GenLinkEnd
+import org.eclipse.gmf.codegen.gmfgen.GenNode
+import org.eclipse.gmf.codegen.gmfgen.TypeModelFacet
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.InstanceValue
+import org.eclipse.uml2.uml.UMLFactory
+import org.eclipse.uml2.uml.ValueSpecification
+import java.util.List
+import org.eclipse.gmf.codegen.gmfgen.TypeLinkModelFacet
+import org.eclipse.emf.ecore.EStructuralFeature
+import org.eclipse.gmf.codegen.gmfgen.FeatureLinkModelFacet
+
+/**
+ * Mapping of GMFGen model elements to UML instance-specifications.
+ */
+class GMFGen2UML {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject extension Metamodels
+
+ def create createModel toUMLModel(GenEditorGenerator genEditor) {
+ it.name = genEditor.modelID
+ it.packagedElements += #[genEditor.toUML, genEditor.diagram.toUML] +
+ genEditor.diagram.nodesAndLinks.map[toUML] +
+ genEditor.diagram.nodesAndLinks.map[elementType?.toUML].filterNull +
+ genEditor.diagram.nodesAndLinks.map[modelFacet?.toUML].filterNull +
+ genEditor.diagram.compartments.map[toUML]
+ }
+
+ def nodesAndLinks(GenDiagram genDiagram) {
+ genDiagram.topLevelNodes +
+ genDiagram.childNodes +
+ genDiagram.links.filter[modelFacet instanceof TypeModelFacet]
+ }
+
+ def getModelFacet(GenLinkEnd genLinkEnd) {
+ switch (genLinkEnd) {
+ GenNode : genLinkEnd.modelFacet
+ GenLink : genLinkEnd.modelFacet
+ default : null
+ }
+ }
+
+ private def instanceName(EObject object, String name) {
+ object.metaclassName + '_' + (name ?: '')
+ }
+
+ def dispatch create createInstanceSpecification toUML(GenEditorGenerator genEditor) {
+ it.name = genEditor.instanceName(genEditor.packageNamePrefix)
+
+ val metaclass = genEditor.gmfgenMetaclass
+ it.classifiers += metaclass
+ it.slots += #[
+ genEditor.packageNamePrefix.toSlot('packageNamePrefix', metaclass),
+ genEditor.diagram.toUML.toSlot('diagram', metaclass),
+ genEditor.modelID.toSlot('modelID', metaclass),
+ genEditor.domainFileExtension.toSlot('domainFileExtension', metaclass)
+ ]
+ }
+
+ private def toSlot(Object value, String propertyName, Class ofMetaclass) {
+ createSlot => [
+ it.definingFeature = ofMetaclass.getInheritedAttribute(propertyName)
+ it.values += switch (value) {
+ String : createLiteralString => [it.value = value]
+ Integer : createLiteralInteger => [it.value = value]
+ InstanceSpecification : createInstanceValue => [it.instance = value]
+ EStructuralFeature : createLiteralString => [it.value = value.umlMetamodelProperty.qualifiedName]
+ case null : createLiteralNull as ValueSpecification
+ }
+ ]
+ }
+
+ private def toEditPartListSlot(List<? extends GenCommonBase> editParts, String propertyName, Class ofMetaclass) {
+ createSlot => [
+ it.definingFeature = ofMetaclass.getInheritedAttribute(propertyName)
+ it.values += editParts.map[editPartClassName].filterNull.map[editPart |
+ createLiteralString => [it.value = editPart]
+ ]
+ ]
+ }
+
+ /**
+ * Queries an attribute, possibly inherited in the Java sense, of a class.
+ * In this case, inheritance includes properties defined by realized interfaces
+ * (which are not, strictly UMLishly speaking, actually inherited).
+ */
+ def getInheritedAttribute(Class class_, String name) {
+ class_.getAllAttributes().findFirst[it.name == name] ?:
+ class_.allImplementedInterfaces.map[getAllAttributes().findFirst[it.name == name]].filterNull.head
+ }
+
+ private def commonBase(InstanceSpecification is, GenCommonBase genBase) {
+ is.name = genBase.instanceName(genBase.editPartClassName)
+
+ val metaclass = genBase.gmfgenMetaclass
+ is.classifiers += metaclass
+ is.slots += #[
+ genBase.visualID.toSlot('visualID', metaclass),
+ genBase.editPartClassName.toSlot('editPartClassName', metaclass),
+ genBase.itemSemanticEditPolicyClassName.toSlot('itemSemanticEditPolicyClassName', metaclass)
+ ]
+
+ return metaclass
+ }
+
+ def dispatch create createInstanceSpecification toUML(GenDiagram genDiagram) {
+ val metaclass = it.commonBase(genDiagram)
+
+ it.slots += #[
+ genDiagram.canonicalEditPolicyClassName.toSlot('canonicalEditPolicyClassName', metaclass),
+ createSlot => [
+ it.definingFeature = metaclass.getInheritedAttribute('topLevelNodes')
+ it.values += (genDiagram.nodesAndLinks + genDiagram.compartments)
+ .map[toUML].map[is|createInstanceValue => [instance = is]]
+ ]
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(GenNode genNode) {
+ val metaclass = it.commonBase(genNode)
+
+ it.slots += #[
+ genNode.elementType?.toUML.toSlot('elementType', metaclass),
+ genNode.modelFacet?.toUML.toSlot('modelFacet', metaclass)
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(GenLink genLink) {
+ val metaclass = it.commonBase(genLink)
+
+ it.slots += #[
+ genLink.elementType?.toUML.toSlot('elementType', metaclass),
+ genLink.modelFacet?.toUML.toSlot('modelFacet', metaclass),
+
+ // These are derived properties in the GMFGen that will be awkward to compute from the UML,
+ // so just cache the derived values in the intermediate model
+ genLink.sources.toEditPartListSlot('sources', metaclass),
+ genLink.targets.toEditPartListSlot('targets', metaclass)
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(GenCompartment genCompartment) {
+ val metaclass = it.commonBase(genCompartment)
+
+ it.slots += #[
+ genCompartment.node.toUML.toSlot('node', metaclass),
+ createSlot => [
+ it.definingFeature = metaclass.getInheritedAttribute('childNodes')
+ it.values += genCompartment.childNodes.map[toUML].map[is|createInstanceValue => [instance = is]]
+ ]
+ ]
+
+ it.setOppositeSlots('node', 'compartments')
+ it.setOppositeSlots('childNodes', 'containers')
+ }
+
+ private def setOppositeSlots(InstanceSpecification is, String slotName, String oppositeName) {
+ is.getSlot(slotName).values.filter(InstanceValue).forEach[ref|
+ var opposite = ref.instance.getSlot(oppositeName)
+ if (opposite == null) {
+ // Create the opposite slot
+ ref.instance.slots += is.toSlot(oppositeName, ref.instance.classifiers.head as Class)
+ } else {
+ // Add to the opposite slot
+ opposite.values += createInstanceValue => [instance = is]
+ }
+ ]
+ }
+
+ def getSlot(InstanceSpecification is, String name) {
+ is.slots.findFirst[definingFeature.name == name]
+ }
+
+ def dispatch create createInstanceSpecification toUML(ElementType elementType) {
+ it.name = elementType.instanceName(elementType.displayName)
+
+ val metaclass = elementType.gmfgenMetaclass
+ it.classifiers += metaclass
+ it.slots += #[
+ elementType.displayName.toSlot('displayName', metaclass)
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(TypeModelFacet modelFacet) {
+ it.name = modelFacet.instanceName(modelFacet.modelFacetName)
+
+ val metaclass = modelFacet.gmfgenMetaclass
+ it.classifiers += metaclass
+ it.slots += #[
+ modelFacet.modelFacetName.toSlot('metaClass', metaclass)
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(TypeLinkModelFacet modelFacet) {
+ it.name = modelFacet.instanceName(modelFacet.modelFacetName)
+
+ val metaclass = modelFacet.gmfgenMetaclass
+ it.classifiers += metaclass
+ it.slots += #[
+ modelFacet.modelFacetName.toSlot('metaClass', metaclass),
+ modelFacet.containmentMetaFeature?.ecoreFeature.toSlot('containmentMetaFeature', metaclass),
+ modelFacet.sourceMetaFeature?.ecoreFeature.toSlot('sourceMetaFeature', metaclass),
+ modelFacet.targetMetaFeature?.ecoreFeature.toSlot('targetMetaFeature', metaclass)
+ ]
+ }
+
+ def dispatch create createInstanceSpecification toUML(FeatureLinkModelFacet modelFacet) {
+ it.name = modelFacet.instanceName(modelFacet.metaFeature.name)
+
+ val metaclass = modelFacet.gmfgenMetaclass
+ it.classifiers += metaclass
+ it.slots += #[
+ modelFacet.metaFeature.ecoreFeature.toSlot('metaFeature', metaclass)
+ ]
+ }
+
+ /**
+ * A TypeModelFacet may be unresolved, which is usually the case for the Diagram shortcut.
+ * In such cases, we try to infer a name from the last segment of the URI fragment.
+ */
+ private def modelFacetName(TypeModelFacet modelFacet) {
+ var result = modelFacet.metaClass.ecoreClass?.name
+
+ result ?: {
+ // Proxy case
+ val uriFragment = EcoreUtil.getURI(modelFacet.metaClass).fragment
+ uriFragment.substring(uriFragment.lastIndexOf('/') + 1)
+ }
+ }
+
+ private def umlMetamodelProperty(EStructuralFeature eFeature) {
+ eFeature.EContainingClass.name.umlMetaclass.getAttribute(eFeature.name, null)
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLComponent.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLComponent.java
new file mode 100644
index 00000000000..f47a2f85606
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLComponent.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.gmfgen2uml;
+
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.emf.mwe.core.WorkflowContext;
+import org.eclipse.emf.mwe.core.issues.Issues;
+import org.eclipse.emf.mwe.core.lib.WorkflowComponentWithModelSlot;
+import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
+import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator;
+import org.eclipse.papyrus.tests.framework.gmfgenuml2utp.GMFGen2UTPComponent;
+import org.eclipse.uml2.uml.Model;
+
+import com.google.common.collect.Iterables;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * A workflow component that configures and runs a transformation of a GMFGen model to its UML representation.
+ * The output is intended as an input to the UTP model generation.
+ *
+ * @see GMFGen2UTPComponent
+ */
+public class GMFGen2UMLComponent extends WorkflowComponentWithModelSlot {
+
+ private Log log = LogFactory.getLog(getClass());
+
+ private String metamodelSlot;
+
+ private String outputSlot;
+
+ public GMFGen2UMLComponent() {
+ super();
+ }
+
+ public String getMetamodelSlot() {
+ return metamodelSlot;
+ }
+
+ public void setMetamodelSlot(String metamodelSlot) {
+ this.metamodelSlot = metamodelSlot;
+ }
+
+ public String getOutputSlot() {
+ return outputSlot;
+ }
+
+ public void setOutputSlot(String outputSlot) {
+ this.outputSlot = outputSlot;
+ }
+
+ protected GMFGen2UMLModule createGMFGen2UMLModule(WorkflowContext ctx) {
+ return new GMFGen2UMLModule((Model) ctx.get(getMetamodelSlot()));
+ }
+
+ @Override
+ protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor,
+ Issues issues) {
+
+ log.info("Transforming GMFGen to UML ...");
+ Object modelSlotContent = ctx.get(getModelSlot());
+ GenEditorGenerator model = null;
+ if (modelSlotContent instanceof GenEditorGenerator) {
+ model = (GenEditorGenerator) modelSlotContent;
+ } else if (modelSlotContent instanceof List) {
+ List<?> slotContentList = (List<?>) modelSlotContent;
+ model = Iterables.getFirst(Iterables.filter(slotContentList, GenEditorGenerator.class), null);
+ }
+ if ((model == null) || !(model instanceof GenEditorGenerator)) {
+ log.error("The input model for the transformation was not loaded!");
+ return;
+ }
+
+ Injector injector = Guice.createInjector(createGMFGen2UMLModule(ctx));
+
+ GMFGen2UML transformation = injector.getInstance(GMFGen2UML.class);
+
+ Model uml = transformation.toUMLModel(model);
+
+ ctx.set(getOutputSlot(), uml);
+ log.info("The transformation successfully created Model " + uml.getLabel());
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLModule.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLModule.java
new file mode 100644
index 00000000000..0ea8ce3d6b2
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgen2uml/GMFGen2UMLModule.java
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * 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.tests.framework.gmfgen2uml;
+
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels;
+import org.eclipse.uml2.uml.Model;
+
+import com.google.inject.AbstractModule;
+
+/**
+ * Guice module for the GMFGen-to-UML transformation.
+ */
+public class GMFGen2UMLModule extends AbstractModule {
+ private final Model gmfgenMetamodel;
+
+ public GMFGen2UMLModule(Model gmfgenMetamodel) {
+ super();
+
+ this.gmfgenMetamodel = gmfgenMetamodel;
+ }
+
+ @Override
+ protected void configure() {
+ bindMetamodels();
+ bindGMFGen2UML();
+ }
+
+ protected void bindMetamodels() {
+ bind(Metamodels.class).toInstance(new Metamodels(gmfgenMetamodel, null, null));
+ }
+
+ private void bindGMFGen2UML() {
+ // Pass
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/AppearanceTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/AppearanceTest.xtend
new file mode 100644
index 00000000000..d1fef65f460
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/AppearanceTest.xtend
@@ -0,0 +1,46 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.papyrus.tests.framework.gmfgenuml2utp.TransformationUtilities.TestPackageBuilder
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to appearance tests in the UTP test model.
+ */
+class AppearanceTest {
+ @Inject extension TransformationUtilities
+
+ @FrameworkConfig Collection<String> elementTypesAppearanceTests = Collections.emptyList
+
+ @TestPackageRule val appearancePackage = [
+ name = 'appearance'
+
+ testContextRules += mapNone -> topNodeAppearance
+ ]
+
+ @TestContextRule val topNodeAppearance = [
+ simple('AbstractAppearanceNodeTest', 'AppearanceTest')
+ topEditParts += gmfgen.getTopNodes(elementTypesAppearanceTests)
+ testBehaviors += mapTests(topEditParts) [toCallTestNodeOperationActivity('AbstractAppearanceNodeTest', 'AppearanceNodeTest', false)]
+ testCaseRule = testCaseRule('testAppearanceNode')
+ ]
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CanonicalTests.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CanonicalTests.xtend
new file mode 100644
index 00000000000..4f70a41b626
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CanonicalTests.xtend
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import javax.inject.Inject
+import javax.inject.Singleton
+import org.eclipse.emf.ecore.resource.Resource
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.UMLFactory
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to UTP test model.
+ */
+@Singleton
+class CanonicalTests {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+
+ @Inject extension AppearanceTest
+ @Inject extension CreateFromPaletteTest
+ @Inject extension DirectEditTest
+ @Inject extension DropTest
+ @Inject extension DeleteTest
+ @Inject extension SynchronizationTest
+
+ @FrameworkConfig String diagramTestPackageName
+
+ def create createModel toUTPModel(Model gmfgenModel, Resource extent) {
+ name = diagramTestPackageName
+
+ // Add the output model to a resource-set context now to support the static UTP profile
+ extent.contents.add(it)
+ applyProfile(utp)
+
+ createTestPackage(gmfgenModel, appearancePackage)
+ createTestPackage(gmfgenModel, createFromPalettePackage)
+ createTestPackage(gmfgenModel, directEditPackage)
+ createTestPackage(gmfgenModel, dropPackage)
+ createTestPackage(gmfgenModel, deletePackage)
+ createTestPackage(gmfgenModel, synchronizationPackage)
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteChildLabelNodesTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteChildLabelNodesTest.xtend
new file mode 100644
index 00000000000..f25621cdcf3
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteChildLabelNodesTest.xtend
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.UMLFactory
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to child label palette tests in the UTP test model.
+ */
+class CreateFromPaletteChildLabelNodesTest {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+
+ protected def getContainerCompartmentNames(InstanceSpecification node) {
+ node.containerCompartments.map[editPart]
+ }
+
+ protected def getParentNodeNames(InstanceSpecification node) {
+ node.parentNodes.map[editPart]
+ }
+
+ protected def toCallTestChildLabelNodeOperationActivity(InstanceSpecification labelEditPart,
+ InstanceSpecification compartmentEditPart, InstanceSpecification parentNodeEditPart) {
+
+ createActivity => [
+ name = labelEditPart.editPart.replace('EditPart', '').toFirstLower
+
+ ownedNodes += createCallOperationAction => [
+ name = 'ChildLabelTestNodeActivity_' + parentNodeEditPart.name + '_' + labelEditPart.name + '_' + compartmentEditPart.name
+ operation = frameworkClass('AbstractCreateChildLabelNodeFromPaletteTest').allOperations.head
+ arguments += #[
+ parentNodeEditPart.toValuePin('parentNode'),
+ labelEditPart.toValuePin('childNode'),
+ compartmentEditPart.toIntegerValuePin('compartment'),
+ true.toValuePin('mustPass')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestChildLabelNodeOperationActivity(InstanceSpecification labelEditPart,
+ InstanceSpecification compartmentEditPart, InstanceSpecification nestedNodeEditPart,
+ InstanceSpecification topNodeCompartmentEditPart, InstanceSpecification topNodeEditPart) {
+
+ createActivity => [
+ name = labelEditPart.editPart.replace('EditPart', '').toFirstLower
+
+ ownedNodes += createCallOperationAction => [
+ name = 'ChildLabelTestNodeActivity_' + nestedNodeEditPart.name + '_' + labelEditPart.name + '_' + compartmentEditPart.name
+ operation = frameworkClass('AbstractCreateChildLabelNodeFromPaletteTest').allOperations.head
+ arguments += #[
+ topNodeEditPart.toValuePin('topNode'),
+ topNodeCompartmentEditPart.toIntegerValuePin('topNodeCompartment'),
+ nestedNodeEditPart.toValuePin('parentNode'),
+ labelEditPart.toValuePin('childNode'),
+ compartmentEditPart.toIntegerValuePin('compartment'),
+ true.toValuePin('mustPass')
+ ]
+ ]
+ ]
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteTest.xtend
new file mode 100644
index 00000000000..21cdd38d9f5
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/CreateFromPaletteTest.xtend
@@ -0,0 +1,268 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.UMLFactory
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+import org.apache.log4j.Logger
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to palette tests in the UTP test model.
+ */
+class CreateFromPaletteTest {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject(optional=true) Logger log = Logger.getLogger(CreateFromPaletteTest)
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+ @Inject extension CreateFromPaletteChildLabelNodesTest
+
+ @FrameworkConfig String topContainerEditPart = ''
+ @FrameworkConfig Collection<String> topNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> childNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> childLabelNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> linksToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> linksOwnedBySourceToTest = Collections.emptyList
+
+ @TestPackageRule val createFromPalettePackage = [
+ name = 'createFromPalette'
+
+ testContextRules += #[
+ mapNone -> topNodeCreation,
+ childLabelNodesToTest.mapChildLabelNode -> childLabelNodeCreation,
+ topNodesToTest.mapTopNode -> childNodeCreation,
+ linksToTest.mapLink -> linkCreation,
+ linksOwnedBySourceToTest.mapLink -> linkOwnedBySourceCreation
+ ]
+ ]
+
+ @TestContextRule val topNodeCreation = [
+ common('AbstractCreateNodeFromPaletteTest', 'TopNodesTest')
+ topEditParts += gmfgen.getTopNodes(topNodesToTest)
+ testBehaviors += mapTests(topEditParts) [
+ toCallTestNodeOperationActivity('AbstractCreateNodeFromPaletteTest', 'TestTopNode')
+ ]
+ testCaseRule = testCaseRule('testTopNode')
+ ]
+
+ @TestContextRule val childLabelNodeCreation = [
+ common('AbstractCreateChildLabelNodeFromPaletteTest', 'Label' + selfInstance.editPart + 'Test')
+ childLabelEditParts += selfInstance
+
+ val validLabels = childLabelEditParts.filter[getSlot('containers') != null]
+ validLabels.forEach[label |
+ label.containerCompartments.forEach[compartment |
+ val allParentNodes = compartment.parentNodes
+ val nestedParentNodes = allParentNodes.filter[isChildNode && containerCompartments.exists[parentNodes.exists[isTopNode]]]
+ val topParentNodes = allParentNodes.filter[isTopNode]
+
+ testBehaviors += mapTestsByInstance(#[label], #[compartment], topParentNodes) [
+ labelEditPart, compartmentEditPart, parentNodeEditPart |
+ labelEditPart.toCallTestChildLabelNodeOperationActivity(compartmentEditPart, parentNodeEditPart)
+ ]
+
+ for (nested : nestedParentNodes) {
+ // Compute a representative top node and compartment in which to create the nested node (in which to create the label)
+ val topNodeCompartment = nested.containerCompartments.filter[parentNodes.exists[isTopNode]].head
+ val topNode = topNodeCompartment.parentNodes.filter[isTopNode].head
+
+ testBehaviors += mapTestsByInstance(#[label], #[compartment], #[nested]) [
+ labelEditPart, compartmentEditPart, parentNodeEditPart |
+ labelEditPart.toCallTestChildLabelNodeOperationActivity(compartmentEditPart, parentNodeEditPart, topNodeCompartment, topNode)
+ ]
+ }
+ ]
+ ]
+ testCaseRule = testCaseRule('testChildLabel')
+ ]
+
+ @TestContextRule val childNodeCreation = [
+ common('AbstractCreateNodeFromPaletteTest', 'ChildNodeIn' + selfInstance.editPart + 'Test')
+ containerEditPart = selfInstance
+
+ selfInstance.compartments.forEach[compartment |
+ val contents = compartment.childNodes.filter[childNodesToTest.contains(editPart)]
+ childEditParts += contents
+ testBehaviors += mapTestsByInstance(#[containerEditPart], contents) [parent, child |
+ child.toCallTestChildNodeOperationActivity(parent, 'AbstractCreateNodeFromPaletteTest', 'TestChildNode')
+ ]
+ ]
+ testCaseRule = testCaseRule('testChildNode')
+ ]
+
+ @TestContextRule val linkCreation = [
+ common('AbstractCreateLinkFromPaletteTest', selfInstance.editPart + 'Test')
+ linkEditParts += selfInstance
+
+ if (!selfInstance.canCreateTests) {
+ log.warn('Cannot create test cases for ' + selfInstance.editPart + ' because it is missing either source or target edit-parts.')
+ } else {
+ val ctx = it
+
+ testBehaviors += mapTests(linkEditParts, topNodesToTest.filter[linksTo(ctx.selfInstance)], topNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target)
+ ]
+
+ testBehaviors += mapTests(linkEditParts, childNodesToTest.filter[linksTo(ctx.selfInstance)], childNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, gmfgen.getNode(topContainerEditPart))
+ ]
+
+ testCaseRule = testCaseRule('testLink')
+ }
+ ]
+
+ @TestContextRule val linkOwnedBySourceCreation = [
+ common('AbstractCreateLinkOwnedBySourceFromPaletteTest', selfInstance.editPart + 'Test')
+ linkOwnedBySourceEditParts += selfInstance
+
+ if (!selfInstance.canCreateTests) {
+ log.warn('Cannot create test cases for ' + selfInstance.editPart + ' because it is missing either source or target edit-parts.')
+ } else {
+ val ctx = it
+
+ testBehaviors += mapTests(linkOwnedBySourceEditParts, topNodesToTest.filter[linksTo(ctx.selfInstance)], topNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target)
+ ]
+
+ testBehaviors += mapTests(linkOwnedBySourceEditParts, childNodesToTest.filter[linksTo(ctx.selfInstance)], childNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, gmfgen.getNode(topContainerEditPart))
+ ]
+
+ testCaseRule = testCaseRule('testLinkOwnedBySource')
+ }
+ ]
+
+ protected def toCallTestChildNodeOperationActivity(InstanceSpecification childEditPart, InstanceSpecification parentEditPart, String abstractTestClassName, String nodeType) {
+ createActivity => [
+ name = childEditPart.testBehaviorName
+ ownedNodes += createCallOperationAction => [
+ operation = frameworkClass(abstractTestClassName).allOperations.head
+ arguments += #[
+ childEditPart.toValuePin('node'),
+ parentEditPart.toValuePin('container'),
+ true.toValuePin('mustSucceed')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLinkOperationActivity(InstanceSpecification linkEditPart, InstanceSpecification sourceEditPart, InstanceSpecification targetEditPart) {
+ createActivity => [
+ name = String.format('%s_%s_%s',
+ linkEditPart.editPart.replace('EditPart', '').toFirstLower,
+ sourceEditPart.editPart.replace('EditPart', '').toFirstLower,
+ targetEditPart.editPart.replace('EditPart', '').toFirstLower)
+
+ ownedNodes += createCallOperationAction => [
+ operation = frameworkClass('AbstractCreateLinkFromPaletteTest').allOperations.head
+ arguments += #[
+ linkEditPart.toValuePin(sourceEditPart.editPart, 'source'),
+ linkEditPart.toValuePin(targetEditPart.editPart, 'target'),
+ linkEditPart.toValuePin('link'),
+ true.toValuePin('mustSucceed')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLinkOperationActivity(InstanceSpecification linkEditPart, InstanceSpecification sourceEditPart, InstanceSpecification targetEditPart, InstanceSpecification containerEditPart) {
+ createActivity => [
+ name = String.format('%s_%s_%s',
+ linkEditPart.editPart.replace('EditPart', '').toFirstLower,
+ sourceEditPart.editPart.replace('EditPart', '').toFirstLower,
+ targetEditPart.editPart.replace('EditPart', '').toFirstLower)
+
+ ownedNodes += createCallOperationAction => [
+ operation = frameworkClass('AbstractCreateLinkFromPaletteTest').allOperations.head
+ arguments += #[
+ linkEditPart.toValuePin(sourceEditPart.editPart, 'source'),
+ linkEditPart.toValuePin(targetEditPart.editPart, 'target'),
+ linkEditPart.toValuePin('link'),
+ linkEditPart.toValuePin(containerEditPart.editPart, 'container'),
+ true.toValuePin('mustSucceed')
+ ]
+ ]
+ ]
+ }
+
+ /**
+ * Queries whether a node edit-part may be the source of a link edit-part, according to the GMFGen model.
+ */
+ public def linksTo(String sourceNodeEditPart, InstanceSpecification linkEditPart) {
+ linkEditPart.getSlotStringValues('sources').contains(sourceNodeEditPart)
+ }
+
+ /**
+ * Queries whether a node edit-part may be the target of a link edit-part, according to the GMFGen model.
+ */
+ public def linksFrom(String targetNodeEditPart, InstanceSpecification linkEditPart) {
+ linkEditPart.getSlotStringValues('targets').contains(targetNodeEditPart)
+ }
+
+ protected def linksToOrFrom(String nodeEditPart, InstanceSpecification linkEditPart) {
+ nodeEditPart.linksTo(linkEditPart) || nodeEditPart.linksFrom(linkEditPart)
+ }
+
+ /**
+ * Queries whether we can create any tests for the specified link edit-part. This is generally only
+ * {@code false} when the link edit part either has no viable source edit-parts or no viable target
+ * edit-parts.
+ */
+ public def canCreateTests(InstanceSpecification linkEditPart) {
+ !linkEditPart.nodesRequiredForTest.empty
+ }
+
+ protected def nodesRequiredForTest(InstanceSpecification linkEditPart) {
+ val sources = linkEditPart.getSlotStringValues('sources')
+ val targets = linkEditPart.getSlotStringValues('targets')
+
+ val sourceTopNodes = sources.toSet => [retainAll(topNodesToTest)]
+ val sourceChildNodes = sources.toSet => [retainAll(childNodesToTest)]
+ val targetTopNodes = targets.toSet => [retainAll(topNodesToTest)]
+ val targetChildNodes = targets.toSet => [retainAll(childNodesToTest)]
+
+ newLinkedHashSet => [
+ // Handle top nodes together and child nodes together
+ if (!(sourceTopNodes.empty || targetTopNodes.empty)) {
+ it.addAll(sourceTopNodes)
+ it.addAll(targetTopNodes)
+ }
+ if (!(sourceChildNodes.empty || targetChildNodes.empty)) {
+ it.addAll(sourceChildNodes)
+ it.addAll(targetChildNodes)
+ it.add(topContainerEditPart)
+ }
+ ]
+ }
+
+ private def toValuePin(InstanceSpecification linkEditPart, String editPartName, String role) {
+ createValuePin => [
+ val otherInstance = linkEditPart.model.getNode(editPartName)
+ name = role
+ value = createInstanceValue => [
+ name = otherInstance.name
+ instance = otherInstance
+ ]
+ ]
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DeleteTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DeleteTest.xtend
new file mode 100644
index 00000000000..45c4e5b6a8f
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DeleteTest.xtend
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to deletion tests in the UTP test model.
+ */
+class DeleteTest {
+ @Inject extension TransformationUtilities
+
+ @FrameworkConfig Collection<String> elementTypesDeleteTests = Collections.emptyList
+
+ @TestPackageRule val deletePackage = [
+ name = 'delete'
+
+ testContextRules += mapNone -> topNodeDeletion
+ ]
+
+ @TestContextRule val topNodeDeletion = [
+ common('AbstractDeleteNodeTest', 'DeleteTest')
+ topEditParts += gmfgen.getTopNodes(elementTypesDeleteTests)
+ testBehaviors += mapTests(topEditParts) [toCallTestNodeOperationActivity('AbstractDeleteNodeTest', 'DeleteNodeTest', false)]
+ testCaseRule = testCaseRule('testDeleteNode')
+ ]
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DirectEditTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DirectEditTest.xtend
new file mode 100644
index 00000000000..460d4bf99af
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DirectEditTest.xtend
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to direct-edit tests in the UTP test model.
+ */
+class DirectEditTest {
+ @Inject extension TransformationUtilities
+
+ @FrameworkConfig Collection<String> elementTypesEditTests = Collections.emptyList
+
+ @TestPackageRule val directEditPackage = [
+ name = 'directedit'
+
+ testContextRules += mapNone -> topNodeDirectEdit
+ ]
+
+ @TestContextRule val topNodeDirectEdit = [
+ simple('AbstractEditableNodeTest', 'DirectEditTest')
+ topEditParts += gmfgen.getTopNodes(elementTypesEditTests)
+ testBehaviors += mapTests(topEditParts) [toCallTestNodeOperationActivity('AbstractEditableNodeTest', 'DirectEditTest', false)]
+ testCaseRule = testCaseRule('testDirectEdit')
+ ]
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DropTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DropTest.xtend
new file mode 100644
index 00000000000..d2da252c35a
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/DropTest.xtend
@@ -0,0 +1,45 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to drag-and-drop tests in the UTP test model.
+ */
+class DropTest {
+ @Inject extension TransformationUtilities
+
+ @FrameworkConfig Collection<String> elementTypesDropTests = Collections.emptyList
+
+ @TestPackageRule val dropPackage = [
+ name = 'drop'
+
+ testContextRules += mapNone -> topNodeDrop
+ ]
+
+ @TestContextRule val topNodeDrop = [
+ simple('AbstractDropNodeTest', 'DropTest')
+ topEditParts += gmfgen.getTopNodes(elementTypesDropTests)
+ testBehaviors += mapTests(topEditParts) [toCallTestNodeOperationActivity('AbstractDropNodeTest', 'DropNodeTest')]
+ testCaseRule = testCaseRule('testDropNode')
+ ]
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPComponent.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPComponent.java
new file mode 100644
index 00000000000..f24b05e7ae6
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPComponent.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.mwe.core.WorkflowContext;
+import org.eclipse.emf.mwe.core.issues.Issues;
+import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
+import org.eclipse.emf.mwe.utils.AbstractEMFWorkflowComponent;
+import org.eclipse.papyrus.tests.framework.exceptions.TestExceptions;
+import org.eclipse.papyrus.tests.framework.gmfgen2uml.GMFGen2UMLComponent;
+import org.eclipse.uml2.uml.Model;
+import org.eclipse.uml2.uml.Profile;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.eclipse.uml2.uml.resource.UMLResource;
+import org.eclipse.xtext.xbase.lib.Functions.Function3;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * A workflow component that configures and runs a transformation of the UML representation of a
+ * GMFGen model to a UML-UTP model describing the tests to be generated for that diagram.
+ *
+ * @see GMFGen2UMLComponent
+ */
+public class GMFGen2UTPComponent extends AbstractEMFWorkflowComponent {
+
+ private Log log = LogFactory.getLog(getClass());
+
+ private String metamodelSlot;
+
+ private String frameworkBaseSlot;
+
+ private String utpSlot;
+
+ private String outputSlot;
+
+ private Function3<Model, Model, Profile, GMFGen2UTPModule> utpModule;
+
+ public GMFGen2UTPComponent() {
+ super();
+ }
+
+ public String getMetamodelSlot() {
+ return metamodelSlot;
+ }
+
+ public void setMetamodelSlot(String metamodelSlot) {
+ this.metamodelSlot = metamodelSlot;
+ }
+
+ public String getFrameworkBaseSlot() {
+ return frameworkBaseSlot;
+ }
+
+ public void setFrameworkBaseSlot(String frameworkBaseSlot) {
+ this.frameworkBaseSlot = frameworkBaseSlot;
+ }
+
+ public String getUtpSlot() {
+ return utpSlot;
+ }
+
+ public void setUtpSlot(String utpSlot) {
+ this.utpSlot = utpSlot;
+ }
+
+ public String getOutputSlot() {
+ return outputSlot;
+ }
+
+ public void setOutputSlot(String outputSlot) {
+ this.outputSlot = outputSlot;
+ }
+
+ public Function3<Model, Model, Profile, GMFGen2UTPModule> getUtpModule() {
+ return utpModule;
+ }
+
+ public void setUtpModule(Function3<Model, Model, Profile, GMFGen2UTPModule> utpModule) {
+ this.utpModule = utpModule;
+ }
+
+ protected GMFGen2UTPModule createGMFGen2UTPModule(WorkflowContext ctx, Collection<TestExceptions> testExceptions) {
+ TestExceptionManager excmgr = new TestExceptionManager(testExceptions);
+
+ GMFGen2UTPModule result = getUtpModule().apply(
+ (Model) ctx.get(getMetamodelSlot()),
+ (Model) ctx.get(getFrameworkBaseSlot()),
+ (Profile) ctx.get(getUtpSlot()));
+ result.setTestExceptionManager(excmgr);
+
+ return result;
+ }
+
+ @Override
+ protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor,
+ Issues issues) {
+
+ log.info("Transforming GMFGen UML model to UTP test model ...");
+ Object modelSlotContent = ctx.get(getModelSlot());
+ Model model = null;
+ Collection<TestExceptions> testExceptions = Collections.emptyList();
+ if (modelSlotContent instanceof Model) {
+ model = (Model) modelSlotContent;
+ } else if (modelSlotContent instanceof List) {
+ List<?> slotContentList = (List<?>) modelSlotContent;
+ model = Iterables.getFirst(Iterables.filter(slotContentList, Model.class), null);
+ testExceptions = ImmutableList.copyOf(Iterables.filter(slotContentList, TestExceptions.class));
+ }
+ if ((model == null) || !(model instanceof Model)) {
+ log.error("The input model for the transformation was not loaded!");
+ return;
+ }
+
+ GMFGen2UTPModule module = createGMFGen2UTPModule(ctx, testExceptions);
+ module.initEditPartDefaults(model, new TransformationUtilities());
+ Injector injector = Guice.createInjector(module);
+
+ CanonicalTests transformation = injector.getInstance(CanonicalTests.class);
+
+ // Need a resource set context for working with static profiles
+ ResourceSet rset = getResourceSet();
+ rset.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(UMLPackage.eCONTENT_TYPE, UMLResource.Factory.INSTANCE);
+ Resource resource = rset.createResource(URI.createURI("tmp:uml"), UMLPackage.eCONTENT_TYPE);
+
+ Model uml = transformation.toUTPModel(model, resource);
+ ctx.set(getOutputSlot(), uml);
+ log.info("The transformation successfully created Model " + uml.getLabel());
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPModule.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPModule.xtend
new file mode 100644
index 00000000000..0e2c3c78884
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/GMFGen2UTPModule.xtend
@@ -0,0 +1,179 @@
+/*****************************************************************************
+ * Copyright (c) 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import static extension org.eclipse.papyrus.tests.framework.m2m.DefaultingList.*
+
+import com.google.common.collect.ImmutableList
+import com.google.inject.AbstractModule
+import com.google.inject.TypeLiteral
+import com.google.inject.name.Names
+import java.util.Collection
+import java.util.List
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.papyrus.tests.framework.xtend.annotations.Cached
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.Profile
+import org.eclipse.uml2.uml.Property
+import org.eclipse.uml2.uml.util.UMLUtil
+import org.eclipse.xtend.lib.annotations.Accessors
+
+/**
+ * Guice module for the GMFGen(UML)-to-UTP transformation.
+ */
+public class GMFGen2UTPModule extends AbstractModule {
+ @Accessors final Model gmfgenMetamodel
+ @Accessors final Model frameworkBase
+ @Accessors final Profile utp
+
+ @Accessors TestExceptionManager testExceptionManager
+
+ @Accessors String diagramTestPackageName;
+ @Accessors String diagramUpdater;
+ @Accessors String diagramCreationCommand;
+ @Accessors String testConstantsInterface;
+
+ @Accessors String topContainerEditPart;
+ @Accessors final List<String> topNodesToTest = newDefaultingList
+ @Accessors final List<String> childNodesToTest = newDefaultingList
+ @Accessors final List<String> childLabelNodesToTest = newDefaultingList
+ @Accessors final List<String> linksToTest = newDefaultingList
+ @Accessors final List<String> linksOwnedBySourceToTest = newDefaultingList
+ @Accessors final List<String> elementTypesAppearanceTests = newDefaultingList
+ @Accessors final List<String> elementTypesDeleteTests = newDefaultingList
+ @Accessors final List<String> elementTypesDropTests = newDefaultingList
+ @Accessors final List<String> elementTypesEditTests = newDefaultingList
+
+ @Accessors final List<String> testExceptionURIs = newArrayList()
+
+ new(Model gmfgenMetamodel, Model frameworkBase, Profile utp) {
+ super()
+
+ this.gmfgenMetamodel = gmfgenMetamodel
+ this.frameworkBase = frameworkBase
+ this.utp = utp
+ }
+
+ protected override configure() {
+ bindLogger()
+
+ bindMetamodels()
+ bindGMFGen2UTP()
+
+ bindTestRules()
+
+ bindTestExceptionManager()
+
+ bindTestParameters()
+ }
+
+ protected def void bindLogger() {
+ // May be overridden if transformation rules are not to use their own loggers
+ }
+
+ protected def void bindMetamodels() {
+ bind(Metamodels).toInstance(new Metamodels(gmfgenMetamodel, frameworkBase, utp))
+ }
+
+ protected def void bindTestRules() {
+ // May be overridden to inject custom transformation rules
+ }
+
+ protected def void bindTestExceptionManager() {
+ bind(TestExceptionManager).toInstance(testExceptionManager ?: new TestExceptionManager)
+ }
+
+ protected def void bindGMFGen2UTP() {
+ // Pass
+ }
+
+ protected def void bindTestParameters() {
+ if(diagramTestPackageName != null) bind(String).annotatedWith(Names.named('diagramTestPackageName')).toInstance(
+ diagramTestPackageName)
+ if(diagramUpdater != null) bind(String).annotatedWith(Names.named('diagramUpdater')).toInstance(diagramUpdater)
+ if(diagramCreationCommand != null) bind(String).annotatedWith(Names.named('diagramCreationCommand')).toInstance(
+ diagramCreationCommand)
+ if(testConstantsInterface != null) bind(String).annotatedWith(Names.named('testConstantsInterface')).toInstance(
+ testConstantsInterface)
+
+ if(topContainerEditPart != null) bind(String).annotatedWith(Names.named('topContainerEditPart')).toInstance(
+ topContainerEditPart)
+
+ val TypeLiteral<Collection<String>> stringsKey = new TypeLiteral<Collection<String>> {
+ }
+ bind(stringsKey).annotatedWith(Names.named('topNodesToTest')).toInstance(ImmutableList.copyOf(topNodesToTest))
+ bind(stringsKey).annotatedWith(Names.named('childNodesToTest')).toInstance(
+ ImmutableList.copyOf(childNodesToTest))
+ bind(stringsKey).annotatedWith(Names.named('childLabelNodesToTest')).toInstance(
+ ImmutableList.copyOf(childLabelNodesToTest))
+ bind(stringsKey).annotatedWith(Names.named('linksToTest')).toInstance(ImmutableList.copyOf(linksToTest))
+ bind(stringsKey).annotatedWith(Names.named('linksOwnedBySourceToTest')).toInstance(
+ ImmutableList.copyOf(linksOwnedBySourceToTest))
+ bind(stringsKey).annotatedWith(Names.named('elementTypesAppearanceTests')).toInstance(
+ ImmutableList.copyOf(elementTypesAppearanceTests))
+ bind(stringsKey).annotatedWith(Names.named('elementTypesDeleteTests')).toInstance(
+ ImmutableList.copyOf(elementTypesDeleteTests))
+ bind(stringsKey).annotatedWith(Names.named('elementTypesDropTests')).toInstance(
+ ImmutableList.copyOf(elementTypesDropTests))
+ bind(stringsKey).annotatedWith(Names.named('elementTypesEditTests')).toInstance(
+ ImmutableList.copyOf(elementTypesEditTests))
+ }
+
+ def void initEditPartDefaults(Model gmfgen, extension TransformationUtilities utilities) {
+ if (topNodesToTest.isDefault) {
+ topNodesToTest += gmfgen.getInstances[isTopNode].map[editPart]
+ }
+ if (childNodesToTest.isDefault) {
+ childNodesToTest += gmfgen.getInstances[isChildNode].map[editPart]
+ }
+ if (childLabelNodesToTest.isDefault) {
+ childLabelNodesToTest += gmfgen.getInstances[isLabelNode].map[editPart]
+ }
+ if (linksToTest.isDefault) {
+ linksToTest += gmfgen.getInstances[isLink && !isOwnedBySource(utilities)].map[editPart]
+ }
+ if (linksOwnedBySourceToTest.isDefault) {
+ linksOwnedBySourceToTest += gmfgen.getInstances[isLink && isOwnedBySource(utilities)].map[editPart]
+ }
+ if (elementTypesAppearanceTests.isDefault) {
+ elementTypesAppearanceTests += topNodesToTest
+ }
+ if (elementTypesDeleteTests.isDefault) {
+ elementTypesDeleteTests += topNodesToTest
+ }
+ if (elementTypesDropTests.isDefault) {
+ elementTypesDropTests += topNodesToTest
+ }
+ if (elementTypesEditTests.isDefault) {
+ elementTypesEditTests += topNodesToTest
+ }
+ }
+
+ private def isOwnedBySource(InstanceSpecification linkEditPart, extension TransformationUtilities utilities) {
+ val modelFacet = linkEditPart.getSlotInstances('modelFacet').head
+ val containment = modelFacet?.getSlotStringValue('containmentMetaFeature')?.umlMetaattribute
+ val source = modelFacet?.getSlotStringValue('sourceMetaFeature')?.umlMetaattribute
+
+ // A feature-link trivially is owned by the source end. Otherwise, if the source is null we assume
+ // ownership by the source end (otherwise, the link can't reference its source)
+ modelFacet.isA('FeatureLinkModelFacet') ||
+ ((containment != null) && ((source == null) || (source.otherEnd == containment)))
+ }
+
+ @Cached def Property umlMetaattribute(String qualifiedName) {
+ if (qualifiedName == null) null else UMLUtil.findNamedElements(utp.eResource.resourceSet, qualifiedName).filter(Property).head
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/SynchronizationTest.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/SynchronizationTest.xtend
new file mode 100644
index 00000000000..f835719decf
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/SynchronizationTest.xtend
@@ -0,0 +1,322 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.inject.Inject
+import java.util.Collection
+import java.util.Collections
+import org.apache.log4j.Logger
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestContextRule
+import org.eclipse.papyrus.tests.framework.xtend.annotations.TestPackageRule
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.Operation
+import org.eclipse.uml2.uml.UMLFactory
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+
+/**
+ * Mapping of UML instance specifications for GMFGen model elements to view synchronization tests in the UTP test model.
+ */
+class SynchronizationTest {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject(optional=true) Logger log = Logger.getLogger(SynchronizationTest)
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+ @Inject extension CreateFromPaletteTest
+
+ @FrameworkConfig String topContainerEditPart = ''
+ @FrameworkConfig Collection<String> topNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> childNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> childLabelNodesToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> linksToTest = Collections.emptyList
+ @FrameworkConfig Collection<String> linksOwnedBySourceToTest = Collections.emptyList
+
+ @TestPackageRule val synchronizationPackage = [
+ name = 'synchronization'
+
+ // Top node synchronization
+ testContextRules += #[
+ topContainerEditPart.mapTopNode -> topNodeSynchronization,
+ childLabelNodesToTest.mapChildLabelNode -> childLabelNodeSynchronization,
+ topNodesToTest.mapTopNode -> childNodeSynchronization,
+ linksToTest.mapLink -> linkSynchronization,
+ linksOwnedBySourceToTest.mapLink -> linkOwnedBySourceSynchronization
+ ]
+ ]
+
+ @TestContextRule val topNodeSynchronization = [
+ simple('AbstractCSSSynchronizationTest', 'SynchTest')
+ makeSynchSUTProperties('topnode')
+ val abstractTestOperation = superclass.findOperation('testSynchronizeTopNode')
+
+ containerEditPart = selfInstance
+ topEditParts += gmfgen.getTopNodes(topNodesToTest)
+ testBehaviors += mapTests(topEditParts)[toCallTestTopNodeSynchronizationActivity(abstractTestOperation)]
+ testCaseRule = testScenarioRule(abstractTestOperation, 'testSynchronize')
+ ]
+
+ @TestContextRule val childLabelNodeSynchronization = [
+ simple('AbstractCSSSynchronizationTest', 'Label' + selfInstance.editPart + 'SynchTest')
+ makeSynchSUTProperties('labelnode')
+ val abstractTestOperation = superclass.findOperation('testSynchronizeLabelNode')
+
+ childLabelEditParts += selfInstance
+
+ val validLabels = childLabelEditParts.filter[getSlot('containers') != null]
+ validLabels.forEach[label |
+ label.containerCompartments.forEach[compartment |
+ val allParentNodes = compartment.parentNodes
+ val nestedParentNodes = allParentNodes.filter[isChildNode && containerCompartments.exists[parentNodes.exists[isTopNode]]]
+ val topParentNodes = allParentNodes.filter[isTopNode]
+
+ testBehaviors += mapTestsByInstance(#[label], #[compartment], topParentNodes) [
+ labelEditPart, compartmentEditPart, parentNodeEditPart |
+ labelEditPart.toCallTestLabelNodeSynchronizationActivity(compartmentEditPart, parentNodeEditPart, abstractTestOperation)
+ ]
+
+ for (nested : nestedParentNodes) {
+ // Compute a representative top node and compartment in which to create the nested node (in which to create the label)
+ val topNodeCompartment = nested.containerCompartments.filter[parentNodes.exists[isTopNode]].head
+ val topNode = topNodeCompartment.parentNodes.filter[isTopNode].head
+
+ testBehaviors += mapTestsByInstance(#[label], #[compartment], #[nested]) [
+ labelEditPart, compartmentEditPart, parentNodeEditPart |
+ labelEditPart.toCallTestLabelNodeSynchronizationActivity(compartmentEditPart, parentNodeEditPart, topNodeCompartment, topNode, abstractTestOperation)
+ ]
+ }
+ ]
+ ]
+ testCaseRule = testScenarioRule(abstractTestOperation, 'testChildLabel')
+ ]
+
+ @TestContextRule val childNodeSynchronization = [
+ simple('AbstractCSSSynchronizationTest', 'ChildNodeIn' + selfInstance.editPart + 'SynchTest')
+ makeSynchSUTProperties('childnode')
+ val abstractTestOperation = superclass.findOperation('testSynchronizeChildNode')
+ val parentEditPart = selfInstance
+
+ containerEditPart = parentEditPart
+ childEditParts += gmfgen.getChildNodes(childNodesToTest).filter[parentEditPart.canContain(it)]
+ testBehaviors += mapTestsByInstance(#[containerEditPart], childEditParts) [parent, child |
+ child.toCallTestChildNodeSynchronizationActivity(parent, abstractTestOperation)
+ ]
+ testCaseRule = testScenarioRule(abstractTestOperation, 'testSynchronizeChild')
+ ]
+
+ @TestContextRule val linkSynchronization = [
+ common('AbstractCSSSynchronizationTest', selfInstance.editPart + 'SynchTest')
+ makeSynchSUTProperties('link')
+ val linkTestOperation = superclass.findOperation('testSynchronizeLink')
+ val linkTestInContainerOperation = superclass.findOperation('testSynchronizeLinkInContainer')
+
+ linkEditParts += selfInstance
+
+ if (!selfInstance.canCreateTests) {
+ log.warn('Cannot create test cases for ' + selfInstance.editPart +
+ ' because it is missing either source or target edit-parts.')
+ } else {
+ val ctx = it
+
+ testBehaviors += mapTests(linkEditParts, topNodesToTest.filter[linksTo(ctx.selfInstance)], topNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, linkTestOperation)
+ ]
+
+ testBehaviors += mapTests(linkEditParts, childNodesToTest.filter[linksTo(ctx.selfInstance)], childNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, gmfgen.getNode(topContainerEditPart), linkTestInContainerOperation)
+ ]
+
+ testCaseRule = testLinkScenarioRule(linkTestOperation, 'testLink')
+ }
+ ]
+
+ @TestContextRule val linkOwnedBySourceSynchronization = [
+ common('AbstractCSSSynchronizationTest', selfInstance.editPart + 'SynchTest')
+ makeSynchSUTProperties('link')
+ val linkTestOperation = superclass.findOperation('testSynchronizeLink')
+ val linkTestInContainerOperation = superclass.findOperation('testSynchronizeLinkInContainer')
+
+ linkOwnedBySourceEditParts += selfInstance
+
+ if (!selfInstance.canCreateTests) {
+ log.warn('Cannot create test cases for ' + selfInstance.editPart +
+ ' because it is missing either source or target edit-parts.')
+ } else {
+ val ctx = it
+
+ testBehaviors += mapTests(linkOwnedBySourceEditParts, topNodesToTest.filter[linksTo(ctx.selfInstance)], topNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, linkTestOperation)
+ ]
+
+ testBehaviors += mapTests(linkOwnedBySourceEditParts, childNodesToTest.filter[linksTo(ctx.selfInstance)], childNodesToTest.filter[linksFrom(ctx.selfInstance)])[
+ link, source, target | link.toCallTestLinkOperationActivity(source, target, gmfgen.getNode(topContainerEditPart), linkTestInContainerOperation)
+ ]
+
+ testCaseRule = testLinkScenarioRule(linkTestOperation, 'testLinkOwnedBySource')
+ }
+ ]
+
+ def (Object, Activity)=>Operation testScenarioRule(Operation abstractTestOperation, String testName) {
+ [ tuple, test |
+ tuple.toTestCaseOperation(testName, test)
+ ]
+ }
+
+ def (Object, Activity)=>Operation testLinkScenarioRule(Operation abstractTestOperation, String testName) {
+ [ tuple, test |
+ tuple.toTestCaseOperation(testName, test)
+ ]
+ }
+
+ protected def toCallTestTopNodeSynchronizationActivity(InstanceSpecification editPart, Operation abstractTestOperation) {
+ createActivity => [
+ name = editPart.testBehaviorName
+ ownedNodes += createCallOperationAction => [
+ operation = abstractTestOperation
+ arguments += #[
+ editPart.toValuePin('node'),
+ editPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLabelNodeSynchronizationActivity(InstanceSpecification labelEditPart,
+ InstanceSpecification compartmentEditPart, InstanceSpecification topEditPart, Operation abstractTestOperation) {
+
+ createActivity => [
+ name = labelEditPart.editPart.replace('EditPart', '').toFirstLower
+
+ // Find the compartments of the topEditPart that contain the labelEditParts
+ ownedNodes += createCallOperationAction =>[
+ name = 'ChildLabelTestNodeActivity_' + topEditPart.editPart + '_' + labelEditPart.editPart +
+ '_' + compartmentEditPart.editPart
+ operation = abstractTestOperation
+ arguments += #[
+ topEditPart.toValuePin('topNode'),
+ labelEditPart.toValuePin('childNode'),
+ compartmentEditPart.toIntegerValuePin('expectedCompartmentType'),
+ labelEditPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLabelNodeSynchronizationActivity(InstanceSpecification labelEditPart,
+ InstanceSpecification compartmentEditPart, InstanceSpecification nestedEditPart,
+ InstanceSpecification topNodeCompartmentEditPart, InstanceSpecification topNodeEditPart,
+ Operation abstractTestOperation) {
+
+ createActivity => [
+ name = labelEditPart.editPart.replace('EditPart', '').toFirstLower
+
+ // Find the compartments of the topEditPart that contain the labelEditParts
+ ownedNodes += createCallOperationAction => [
+ name = 'ChildLabelTestNodeActivity_' + nestedEditPart.editPart + '_' + labelEditPart.editPart +
+ '_' + compartmentEditPart.editPart
+ operation = abstractTestOperation
+ arguments += #[
+ topNodeEditPart.toValuePin('topNode'),
+ nestedEditPart.toValuePin('nestedNode'),
+ labelEditPart.toValuePin('childNode'),
+ compartmentEditPart.toIntegerValuePin('expectedCompartmentType'),
+ labelEditPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestChildNodeSynchronizationActivity(InstanceSpecification childEditPart,
+ InstanceSpecification parentEditPart, Operation abstractTestOperation) {
+ createActivity => [
+ name = childEditPart.testBehaviorName
+ ownedNodes += createCallOperationAction => [
+ operation = abstractTestOperation
+ arguments += #[
+ parentEditPart.toValuePin('parent'),
+ childEditPart.toValuePin('child'),
+ childEditPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLinkOperationActivity(InstanceSpecification linkEditPart, InstanceSpecification sourceEditPart,
+ InstanceSpecification targetEditPart, Operation linkTestOperation) {
+
+ createActivity => [
+ name = linkEditPart.testBehaviorName
+
+ ownedNodes += createCallOperationAction => [
+ operation = linkTestOperation
+ arguments += #[
+ linkEditPart.toValuePin(sourceEditPart.editPart, 'source'),
+ linkEditPart.toValuePin(targetEditPart.editPart, 'target'),
+ linkEditPart.toValuePin('link'),
+ linkEditPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ protected def toCallTestLinkOperationActivity(InstanceSpecification linkEditPart, InstanceSpecification sourceEditPart,
+ InstanceSpecification targetEditPart, InstanceSpecification containerEditPart, Operation linkTestOperation) {
+
+ createActivity => [
+ name = linkEditPart.testBehaviorName
+
+ ownedNodes += createCallOperationAction => [
+ operation = linkTestOperation
+ arguments += #[
+ linkEditPart.toValuePin(containerEditPart.editPart, 'container'),
+ linkEditPart.toValuePin(sourceEditPart.editPart, 'source'),
+ linkEditPart.toValuePin(targetEditPart.editPart, 'target'),
+ linkEditPart.toValuePin('link'),
+ linkEditPart.toIntegerValuePin('expectedEditPartType')
+ ]
+ ]
+ ]
+ }
+
+ private def toValuePin(InstanceSpecification editPart, String otherEditPartName, String role) {
+ createValuePin => [
+ val otherInstance = editPart.model.getNode(otherEditPartName)
+ name = role
+ value = createInstanceValue => [
+ name = otherInstance.name
+ instance = otherInstance
+ ]
+ ]
+ }
+
+ private def testKindConfigurator(String kind) {
+ [org.eclipse.uml2.uml.Property sutProperty |
+ sutProperty.name = 'syncTestKind'
+
+ val syncTestKindEnum = 'SynchronizationTestKind'.frameworkEnum
+ sutProperty.type = syncTestKindEnum
+ sutProperty.defaultValue = createInstanceValue => [
+ instance = syncTestKindEnum.getOwnedLiteral(kind)
+ ]
+ ]
+ }
+
+ protected def makeSynchSUTProperties(TransformationUtilities.TestContextBuilder tcBuilder, String testKind) {
+ tcBuilder.makeSUTProperty(testKindConfigurator(testKind))
+ tcBuilder.makeSUTProperty('css', stringType, createLiteralString => [value = '* { canonical: true }'])
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TestExceptionManager.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TestExceptionManager.xtend
new file mode 100644
index 00000000000..b59a4fe3da4
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TestExceptionManager.xtend
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * 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.tests.framework.gmfgenuml2utp
+
+import com.google.common.collect.Sets
+import com.google.inject.Singleton
+import java.util.Collection
+import java.util.Collections
+import org.eclipse.emf.common.util.BasicDiagnostic
+import org.eclipse.emf.common.util.BasicEList
+import org.eclipse.papyrus.tests.framework.exceptions.TestExceptions
+import org.eclipse.uml2.uml.Behavior
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.UMLFactory
+import org.eclipse.emf.common.util.Diagnostic
+import org.eclipse.papyrus.tests.framework.exceptions.ForbiddenEditPart
+import org.eclipse.papyrus.tests.framework.xtend.annotations.Cached
+
+/**
+ * An injectable component that determines whether a particular permutation of edit-parts may generate a
+ * specific kind of test case.
+ */
+@Singleton
+class TestExceptionManager {
+ Collection<TestExceptions> testExceptions;
+
+ BasicDiagnostic diagnostics = new BasicDiagnostic
+
+ new() {
+ this(Collections.emptyList)
+ }
+
+ new(Collection<TestExceptions> exceptions) {
+ testExceptions = Sets.newHashSet(exceptions);
+ }
+
+ def addTestExceptions(TestExceptions exceptions) {
+ testExceptions.add(exceptions);
+ return this
+ }
+
+ def shouldGenerate(Class abstractTestClass, InstanceSpecification... editPart) {
+ testExceptions.empty || {
+ val editPartsEList = new BasicEList(editPart)
+ testExceptions.forall[validate(editPartsEList, abstractTestClass, diagnostics)]
+ }
+ }
+
+ /** Queries whether an edit-part is absolutely excluded from all tests. */
+ @Cached def boolean isExcluded(InstanceSpecification editPart) {
+ !testExceptions.empty && testExceptions.exists[
+ constraints.filter(ForbiddenEditPart).exists[it.editPart.matches(editPart)]
+ ]
+ }
+
+ def boolean processExclusions(Class abstractTestClass, Behavior testMethod, InstanceSpecification... editPart) {
+ var result = true
+
+ val current = diagnostics.children.size
+ if (!shouldGenerate(abstractTestClass, editPart)) {
+ // Can only reasonably append one annotation in the generated code
+ val newProblem = diagnostics.children.get(current)
+ result = newProblem.severity < Diagnostic.ERROR
+
+ // Don't bother with annotations if we'll be omitting the test case
+ if (result) {
+ testMethod.preconditions += UMLFactory.eINSTANCE.createConstraint => [
+ name = newProblem.data.get(0).toString
+ specification = UMLFactory.eINSTANCE.createLiteralString => [
+ value = newProblem.message
+ ]
+ ]
+ }
+ }
+
+ result
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TransformationUtilities.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TransformationUtilities.xtend
new file mode 100644
index 00000000000..1b792cbf4f3
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/gmfgenuml2utp/TransformationUtilities.xtend
@@ -0,0 +1,765 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.gmfgenuml2utp
+
+import com.google.common.collect.ImmutableList
+import com.google.inject.Inject
+import com.google.inject.Injector
+import com.google.inject.Singleton
+import java.util.Collection
+import java.util.Collections
+import java.util.List
+import java.util.Set
+import org.eclipse.papyrus.tests.framework.m2m.Metamodels
+import org.eclipse.papyrus.tests.framework.xtend.annotations.Cached
+import org.eclipse.papyrus.tests.framework.xtend.annotations.FrameworkConfig
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.Behavior
+import org.eclipse.uml2.uml.BehavioredClassifier
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Classifier
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.InstanceValue
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.OpaqueExpression
+import org.eclipse.uml2.uml.Operation
+import org.eclipse.uml2.uml.Package
+import org.eclipse.uml2.uml.Property
+import org.eclipse.uml2.uml.Type
+import org.eclipse.uml2.uml.UMLFactory
+import org.eclipse.uml2.uml.ValueSpecification
+import org.eclipse.xtend.lib.annotations.Accessors
+import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor
+import org.eclipse.xtext.util.Triple
+import org.eclipse.xtext.util.Tuples
+
+/**
+ * Commmon helpers and utilities for the GMFGen (as UML) to UTP transformation.
+ */
+@Singleton
+class TransformationUtilities {
+ static extension UMLFactory = UMLFactory.eINSTANCE
+
+ @Inject extension Metamodels
+ @Inject extension TestExceptionManager
+
+ @Inject Injector guice
+
+ @FrameworkConfig String diagramCreationCommand
+ @FrameworkConfig String testConstantsInterface
+ @FrameworkConfig String diagramUpdater = 'UMLDiagramUpdater'
+
+ def getDiagramName(Model gmfgen) {
+ gmfgen.getInstance('GenEditorGenerator').getSlotStringValue('modelID').replace('PapyrusUML', '')
+ }
+
+ def getInstance(Model gmfgen, String classifierName) {
+ gmfgen.getInstances[isA(classifierName)].head
+ }
+
+ @Cached def InstanceSpecification getInstanceNamed(Model gmfgen, String instanceName) {
+ gmfgen.getInstances[name == instanceName].head
+ }
+
+ def getTopNode(Model gmfgen, String editPart) {
+ gmfgen.getInstanceNamed('GenTopLevelNode_' + editPart)
+ }
+
+ def getChildNode(Model gmfgen, String editPart) {
+ gmfgen.getInstanceNamed('GenChildNode_' + editPart)
+ }
+
+ def getNode(Model gmfgen, String editPart) {
+ gmfgen.getTopNode(editPart) ?: gmfgen.getChildNode(editPart)
+ }
+
+ def getCompartment(Model gmfgen, String editPart) {
+ gmfgen.getInstanceNamed('GenCompartment_' + editPart)
+ }
+
+ def getLink(Model gmfgen, String editPart) {
+ gmfgen.getInstanceNamed('GenLink_' + editPart)
+ }
+
+ def getInstances(Model gmfgen, (InstanceSpecification)=>Boolean predicate) {
+ gmfgen.packagedElements.filter(InstanceSpecification).filter(predicate)
+ }
+
+ def getInstances(Model gmfgen, String classifierName, Collection<String> testedEditParts) {
+ gmfgen.getInstances[is | is.isA(classifierName) && testedEditParts.contains(is.editPart)]
+ }
+
+ def getTopNodes(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | is.isTopNode && editParts.contains(is.editPart)]
+ }
+
+ def isTopNode(InstanceSpecification editPart) {
+ editPart.isA('GenTopLevelNode')
+ }
+
+ def getChildNodes(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | is.isChildNode && editParts.contains(is.editPart)]
+ }
+
+ def isChildNode(InstanceSpecification editPart) {
+ editPart.isA('GenChildNode')
+ }
+
+ def getChildLabels(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | is.isLabelNode && editParts.contains(is.editPart)]
+ }
+
+ def isLabelNode(InstanceSpecification editPart) {
+ editPart.isA('GenChildLabelNode')
+ }
+
+ def getNodes(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | (is.isTopNode || is.isChildNode) && editParts.contains(is.editPart)]
+ }
+
+ def getCompartments(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | is.isCompartment && editParts.contains(is.editPart)]
+ }
+
+ def isCompartment(InstanceSpecification editPart) {
+ editPart.isA('GenCompartment')
+ }
+
+ def getContainerCompartments(InstanceSpecification node) {
+ node.getSlotInstances('containers')
+ }
+
+ def getParentNodes(InstanceSpecification compartment) {
+ compartment.getSlotInstances('node')
+ }
+
+ def getCompartments(InstanceSpecification node) {
+ node.getSlotInstances('compartments')
+ }
+
+ def getChildNodes(InstanceSpecification compartment) {
+ compartment.getSlotInstances('childNodes')
+ }
+
+ def canContain(InstanceSpecification node, InstanceSpecification child) {
+ node.compartments.exists[childNodes.contains(child)]
+ }
+
+ def getLinks(Model gmfgen, Collection<String> editParts) {
+ gmfgen.getInstances[is | is.isLink && editParts.contains(is.editPart)]
+ }
+
+ def isLink(InstanceSpecification editPart) {
+ editPart.isA('GenLink')
+ }
+
+ def getSlot(InstanceSpecification instance, String slotName) {
+ instance.slots.findFirst[definingFeature?.name == slotName]
+ }
+
+ def getSlotStringValue(InstanceSpecification instance, String slotName) {
+ instance.getSlot(slotName)?.values?.head.stringValue
+ }
+
+ def List<String> getSlotStringValues(InstanceSpecification instance, String slotName) {
+ val slot = instance.getSlot(slotName)
+ if (slot != null) {
+ slot.values.map[stringValue]
+ } else {
+ #[]
+ }
+ }
+
+ def List<InstanceSpecification> getSlotInstances(InstanceSpecification instance, String slotName) {
+ val slot = instance.getSlot(slotName)
+ if (slot != null) {
+ slot.values.filter(InstanceValue).map[it.instance].filterNull.toList
+ } else {
+ #[]
+ }
+ }
+
+ def isKindOfEditPart(InstanceSpecification instance) {
+ !instance.classifiers.empty && instance.classifiers.head.conformsToEx('GenCommonBase'.gmfgenMetaclass)
+ }
+
+ def dispatch boolean conformsToEx(Classifier subtype, Classifier supertype) {
+ // No implemented interfaces to worry about
+ (subtype == supertype) || subtype.generals.exists[conformsToEx(supertype)]
+ }
+
+ def dispatch boolean conformsToEx(BehavioredClassifier subtype, Classifier supertype) {
+ (subtype == supertype) || subtype.generals.exists[conformsToEx(supertype)]
+ || subtype.implementedInterfaces.exists[conformsToEx(supertype)]
+ }
+
+ def isA(InstanceSpecification instance, String classifierName) {
+ instance.classifiers.exists[name == classifierName]
+ }
+
+ def computeImports(Model gmfgen, Iterable<? extends String> classNames) {
+ classNames.map[name|createElementImport => [alias = name]]
+ }
+
+ def createSelfProperty(Class class_) {
+ createProperty => [
+ name = 'self'
+ type = class_
+ ]
+ }
+
+ def toDiagramUpdaterProperty(Model gmfgen) {
+ createProperty => [
+ name = 'diagramUpdater'
+ defaultValue = createStringExpression => [
+ symbol = diagramUpdater
+ ]
+ ]
+ }
+
+ def upTo(String string, String substring) {
+ string.substring(0, string.indexOf(substring))
+ }
+
+ def following(String string, String substring) {
+ string.substring(string.indexOf(substring) + substring.length)
+ }
+
+ def toNodeEditPartProperty(InstanceSpecification node, String nodeType) {
+ createProperty => [
+ name = node.name.following('_')
+ type = node.classifiers.head.name.gmfgenMetaclass
+ defaultValue = createInstanceValue => [ instance = node ]
+ ]
+ }
+
+ def toDiagramProperty(InstanceSpecification diagram) {
+ createProperty => [
+ name = 'diagram'
+ type = diagram.name.upTo('_').gmfgenMetaclass;
+ defaultValue = createInstanceValue => [ instance = diagram ]
+ ]
+ }
+
+ def toEditorGeneratorProperty(InstanceSpecification editorGenerator) {
+ createProperty => [
+ name = 'generator'
+ type = 'GenEditorGenerator'.gmfgenMetaclass;
+ defaultValue = createInstanceValue => [ instance = editorGenerator ]
+ ]
+ }
+
+ private def getDiagramCreationCommand(InstanceSpecification editorGenerator) {
+ if (diagramCreationCommand == null) {
+ var commandClassName = editorGenerator.getSlotStringValue('modelID')
+
+ // Strip off 'PapyrusUML' prefix, if any, and ensure that it ends with 'Diagram'
+ commandClassName = commandClassName.replaceFirst('^PapyrusUML', '')
+ commandClassName = commandClassName.replaceFirst('(?<!Diagram)$', 'Diagram')
+
+ diagramCreationCommand = 'Create' + commandClassName + 'Command'
+ }
+ diagramCreationCommand
+ }
+
+ def toCreationCommandProperty(InstanceSpecification editorGenerator) {
+ createProperty => [
+ name = 'diagramCreationCommand'
+ type = stringType
+ defaultValue = createLiteralString => [ value = editorGenerator.diagramCreationCommand]
+ ]
+ }
+
+ private def getTestConstantsInterface(InstanceSpecification editorGenerator) {
+ if (testConstantsInterface == null) {
+ var interfaceName = editorGenerator.getSlotStringValue('modelID')
+
+ // Strip off 'PapyrusUML' prefix, if any, and ensure that it ends with 'Diagram'
+ interfaceName = interfaceName.replaceFirst('^PapyrusUML', '')
+ interfaceName = interfaceName.replaceFirst('(?<!Diagram)$', 'Diagram')
+
+ testConstantsInterface = 'I' + interfaceName + 'TestsConstants'
+ }
+ testConstantsInterface
+ }
+
+ def toTestConstantsInterfaceProperty(InstanceSpecification editorGenerator) {
+ createProperty => [
+ name = 'testConstantsInterface'
+ type = stringType
+ defaultValue = createLiteralString => [ value = editorGenerator.testConstantsInterface]
+ ]
+ }
+
+ def toTestConfigurationProperties(InstanceSpecification editorGenerator) {
+ #[
+ editorGenerator.toEditorGeneratorProperty,
+ editorGenerator.toCreationCommandProperty,
+ editorGenerator.toTestConstantsInterfaceProperty
+ ]
+ }
+
+ def dispatch toTestCaseOperation(Void editPart, String testName, Behavior testBehavior) {
+ createOperation => [
+ name = testName.toFirstLower
+ methods += testBehavior
+ ]
+ }
+
+ def dispatch toTestCaseOperation(InstanceSpecification editPart, String testName, Behavior testBehavior) {
+ editPart.toTestCaseOperationWithBlock([String coreName | testName + coreName], testBehavior)
+ }
+
+ def dispatch toTestCaseOperation(Pair<InstanceSpecification, InstanceSpecification> editParts, String testName, Behavior testBehavior) {
+ val childEditPart = editParts.value.editPart
+ editParts.key.toTestCaseOperationWithBlock([String coreName | String.format("%s%s_%s", testName, coreName, childEditPart)], testBehavior)
+ }
+
+ def dispatch toTestCaseOperation(Triple<InstanceSpecification, InstanceSpecification, InstanceSpecification> editParts, String testName, Behavior testBehavior) {
+ val sourceEditPart = editParts.second.editPart
+ val targetEditPart = editParts.third.editPart
+ editParts.first.toTestCaseOperationWithBlock([String coreName | String.format("%s%s_%s_%s", testName, coreName, sourceEditPart, targetEditPart)], testBehavior)
+ }
+
+ private def toTestCaseOperationWithBlock(InstanceSpecification editPart, (String)=>String testName, Behavior testBehavior) {
+ val isLink = editPart.isA('GenLink')
+ val operationName = testName.apply(editPart.name.following('_'))
+
+ createOperation => [
+ name = if (isLink) operationName.replace('EditPart', '') else operationName
+ ownedParameters += createParameter => [
+ name = if (isLink) 'link' else 'node'
+ defaultValue = createInstanceValue => [ instance = editPart ]
+ ]
+ methods += testBehavior
+ ]
+ }
+
+ def (Object, Activity)=>Operation testCaseRule(String name) {
+ [tuple, test | tuple.toTestCaseOperation(name, test) ]
+ }
+
+ def getEditPart(InstanceSpecification instance) {
+ instance.getSlotStringValue('editPartClassName')
+ }
+
+ def getTestBehaviorName(InstanceSpecification editPartToTest) {
+ editPartToTest.editPart + 'TestCase'
+ }
+
+ def dispatch findOperation(Class testClass, Void operationName) {
+ testClass.allOperations.head
+ }
+
+ def dispatch findOperation(Class testClass, String operationName) {
+ testClass.allOperations.findFirst[name == operationName]
+ }
+
+ def toCallTestNodeOperationActivity(InstanceSpecification nodeEditPart, String abstractTestClassName, String nodeType) {
+ toCallTestNodeOperationActivity(nodeEditPart, abstractTestClassName, nodeType, true)
+ }
+
+ def toCallTestNodeOperationActivity(InstanceSpecification nodeEditPart, String abstractTestClassName, String nodeType, boolean includeMustSucceed) {
+ createActivity => [
+ name = nodeEditPart.testBehaviorName
+ ownedNodes += createCallOperationAction => [
+ operation = frameworkClass(abstractTestClassName).allOperations.head
+ arguments += createValuePin => [
+ name = 'node'
+ value = createInstanceValue => [
+ name = nodeEditPart.name
+ instance = nodeEditPart
+ ]
+ ]
+
+ if (includeMustSucceed) {
+ arguments += true.toValuePin('mustSucceed')
+ }
+ ]
+ ]
+ }
+
+ def toValuePin(boolean value, String role) {
+ createValuePin => [
+ name = role
+ value = createLiteralBoolean => [
+ it.value = value
+ type = booleanType
+ ]
+ ]
+ }
+
+ def toValuePin(String value, String role) {
+ createValuePin => [
+ name = role
+ value = createLiteralString => [
+ it.value = value
+ type = stringType
+ ]
+ ]
+ }
+
+ def toValuePin(InstanceSpecification editPart, String role) {
+ createValuePin => [
+ name = role
+ value = createInstanceValue => [
+ name = editPart.name
+ instance = editPart
+ type = editPart.classifiers.get(0)
+ ]
+ ]
+ }
+
+ /**
+ * Creates a value pin that casts an edit-part to its integer visual ID.
+ */
+ def toIntegerValuePin(InstanceSpecification editPart, String role) {
+ createValuePin => [
+ name = role
+ value = createOpaqueExpression => [
+ languages += 'Java'
+ bodies += editPart.editPart + '.VISUAL_ID'
+ type = integerType
+ ]
+ ]
+ }
+
+ def toContainerDerivedProperty(InstanceSpecification editPart) {
+ createProperty => [
+ name = 'containerEditPart'
+ isDerived = true
+ type = redefinedProperties.head?.type // TODO: What is supposed to be redefined?
+ defaultValue = createInstanceValue => [ instance = editPart ]
+ ]
+ }
+
+ def createTestPackage(Model testsModel, Model gmfgen, (TestPackageBuilder)=>void script) {
+ val builder = new TestPackageBuilder(createPackage, gmfgen, testsModel) => [
+ guice.injectMembers(it)
+
+ testsModel.packagedElements += testPackage // Get the profile context now
+ ]
+ script.apply(builder)
+ builder.build
+ }
+
+ def isEligible(InstanceSpecification editPart) {
+ !editPart.isExcluded
+ }
+
+ //
+ // Nested types: test builder DSL
+ //
+
+ @FinalFieldsConstructor
+ static class TestPackageBuilder {
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+
+ final Package testPackage
+ final Model gmfgen
+ final Model testsModel
+
+ @Accessors(PUBLIC_GETTER)
+ List<Pair<? extends Pair<GenType, ? extends Collection<String>>, (TestContextBuilder)=>void>> testContextRules = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<Class> testContexts = newArrayList
+
+ def void setName(String name) {
+ testPackage.name = testsModel.name + '.' + name
+ }
+
+ def Class mapNone((TestContextBuilder)=>void nodeRule) {
+ (null as InstanceSpecification).createTestContext(gmfgen, nodeRule)
+ }
+
+ def Class mapNode(String editPart, (TestContextBuilder)=>void nodeRule) {
+ gmfgen.getNode(editPart).createTestContext(nodeRule)
+ }
+
+ def Iterable<Class> mapTopNodes(Collection<String> editParts, (TestContextBuilder)=>void topNodeRule) {
+ gmfgen.getTopNodes(editParts).map[createTestContext(topNodeRule)]
+ }
+
+ def Iterable<Class> mapChildNodes(Collection<String> editParts, (TestContextBuilder)=>void childNodeRule) {
+ gmfgen.getChildNodes(editParts).map[createTestContext(childNodeRule)]
+ }
+
+ def Iterable<Class> mapChildLabelNodes(Collection<String> editParts, (TestContextBuilder)=>void childLabelNodeRule) {
+ gmfgen.getChildLabels(editParts).map[createTestContext(childLabelNodeRule)]
+ }
+
+ def Iterable<Class> mapLinks(Collection<String> editParts, (TestContextBuilder)=>void linkRule) {
+ gmfgen.getLinks(editParts).map[createTestContext(linkRule)]
+ }
+
+ def build() {
+ // First, process rules
+ testContextRules.forEach[editParts2block |
+ val genType = editParts2block.key.key
+ val editParts = editParts2block.key.value
+ val block = editParts2block.value
+
+ testContexts += switch (genType) {
+ case TOP_NODE : editParts.mapTopNodes(block)
+ case CHILD_NODE : editParts.mapChildNodes(block)
+ case CHILD_LABEL_NODE : editParts.mapChildLabelNodes(block)
+ case LINK : editParts.mapLinks(block)
+ case NIL : #[mapNone(block)]
+ }
+ ]
+
+ // Now add all of the contexts (including those generated by rules)
+ testPackage.packagedElements += testContexts
+
+ testContexts.forEach[
+ applyTestContext
+ ownedAttributes.forEach[applySUT]
+ ownedOperations.forEach[applyTestCase]
+ ]
+
+ testPackage
+ }
+ }
+
+ def mapTopNode(Collection<String> editParts) { GenType.TOP_NODE -> editParts }
+ def mapTopNode(String editPart) { #[editPart].mapTopNode }
+ def mapChildNode(Collection<String> editParts) { GenType.CHILD_NODE -> editParts }
+ def mapChildNode(String editPart) { #[editPart].mapChildNode }
+ def mapChildLabelNode(Collection<String> editParts) { GenType.CHILD_LABEL_NODE -> editParts }
+ def mapChildLabelNode(String editPart) { #[editPart].mapChildLabelNode }
+ def mapLink(Collection<String> editParts) { GenType.LINK -> editParts }
+ def mapLink(String editPart) { #[editPart].mapLink }
+ def mapNone() { GenType.NIL -> Collections.<String>emptyList }
+ enum GenType { TOP_NODE, CHILD_NODE, CHILD_LABEL_NODE, LINK, NIL }
+
+ private def createTestContext(InstanceSpecification selfInstance, (TestContextBuilder)=>void script) {
+ createTestContext(selfInstance, selfInstance.model, script)
+ }
+
+ private def createTestContext(InstanceSpecification selfInstance, Model gmfgen, (TestContextBuilder)=>void script) {
+ val builder = new TestContextBuilder(selfInstance, gmfgen, createClass) => [
+ guice.injectMembers(it)
+ ]
+ script.apply(builder)
+ builder.build
+ }
+
+ @FinalFieldsConstructor
+ static class TestContextBuilder {
+ @Inject extension Metamodels
+ @Inject extension TransformationUtilities
+ @Inject extension TestExceptionManager
+
+ @Accessors final InstanceSpecification selfInstance
+ @Accessors final Model gmfgen
+ final Class testContext
+
+ @Accessors(PUBLIC_GETTER)
+ Set<String> imports = newHashSet
+
+ @Accessors
+ InstanceSpecification containerEditPart
+
+ @Accessors(PUBLIC_GETTER)
+ List<InstanceSpecification> topEditParts = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<InstanceSpecification> childEditParts = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<InstanceSpecification> childLabelEditParts = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<InstanceSpecification> linkEditParts = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<InstanceSpecification> linkOwnedBySourceEditParts = newArrayList
+
+ @Accessors(PUBLIC_GETTER)
+ List<Pair<?, Activity>> testBehaviors = newArrayList
+
+ @Accessors(PUBLIC_SETTER)
+ (Object, Activity)=>Operation testCaseRule
+
+ def void common(String superclassName, String nameSuffix) {
+ simple(superclassName, nameSuffix)
+ makeDiagramUpdater
+ }
+
+ def void simple(String superclassName, String nameSuffix) {
+ superclass = superclassName
+ name = [it + nameSuffix]
+ makeSelf
+ makeSimpleProperties
+ }
+
+ def void setSuperclass(String abstractTestClass) {
+ testContext.generals += abstractTestClass.frameworkClass
+ }
+
+ def getSuperclass() {
+ testContext.generals.head as Class
+ }
+
+ def void setName((String)=>String name) {
+ testContext.name = name.apply(gmfgen.diagramName)
+ }
+
+ def void makeSelf() {
+ testContext.ownedAttributes += testContext.createSelfProperty
+ }
+
+ def void makeDiagram() {
+ testContext.ownedAttributes += gmfgen.getInstance('GenDiagram').toDiagramProperty
+ }
+
+ def void makeEditor() {
+ testContext.ownedAttributes += gmfgen.getInstance('GenEditorGenerator').toEditorGeneratorProperty
+ }
+
+ def void makeCreationCommand() {
+ testContext.ownedAttributes += gmfgen.getInstance('GenEditorGenerator').toCreationCommandProperty
+ }
+
+ def void makeTestConstants() {
+ testContext.ownedAttributes += gmfgen.getInstance('GenEditorGenerator').toTestConstantsInterfaceProperty
+ }
+
+ def void makeDiagramUpdater() {
+ testContext.ownedAttributes += gmfgen.toDiagramUpdaterProperty
+ }
+
+ private def void makeSimpleProperties() {
+ makeDiagram
+ makeEditor
+ makeCreationCommand
+ makeTestConstants
+ }
+
+ def void makeSUTProperty(String name, Type type, ValueSpecification defaultValue) {
+ testContext.ownedAttributes += UMLFactory.eINSTANCE.createProperty => [
+ it.name = name
+ it.type = type
+ it.defaultValue = defaultValue
+ ]
+ }
+
+ def void makeSUTProperty((Property)=>void propertyConfigurator) {
+ testContext.ownedAttributes += UMLFactory.eINSTANCE.createProperty => [
+ propertyConfigurator.apply(it)
+ ]
+ }
+
+ def <T extends Behavior> Iterable<Pair<InstanceSpecification, T>> mapTests(Iterable<? extends InstanceSpecification> editParts,
+ (InstanceSpecification)=>T testRule) {
+
+ editParts.map[ (it -> testRule.apply(it)) ].filter[
+ processExclusions(testContext, value, key)
+ ].toList // Filter exactly once because of the side-effects
+ }
+
+ def <T extends Behavior> Iterable<Pair<Pair<InstanceSpecification, InstanceSpecification>, T>> mapTests(
+ Iterable<? extends InstanceSpecification> editParts,
+ Iterable<String> children,
+ (InstanceSpecification, InstanceSpecification)=>T testRule) {
+
+ val childEditParts = children.map[gmfgen.getNode(it)].filterNull
+
+ mapTestsByInstance(editParts, childEditParts, testRule)
+ }
+
+ def <T extends Behavior> Iterable<Pair<Pair<InstanceSpecification, InstanceSpecification>, T>> mapTestsByInstance(
+ Iterable<? extends InstanceSpecification> editParts,
+ Iterable<InstanceSpecification> children,
+ (InstanceSpecification, InstanceSpecification)=>T testRule) {
+
+ editParts.map[top |
+ children.map[ ((top -> it) -> testRule.apply(top, it)) ]
+ ].flatten.filter[
+ processExclusions(testContext, value, key.key, key.value)
+ ].toList // Filter exactly once because of the side-effects
+ }
+
+ def <T extends Behavior> Iterable<Pair<Triple<InstanceSpecification, InstanceSpecification, InstanceSpecification>, T>> mapTests(
+ Iterable<? extends InstanceSpecification> linkEditParts,
+ Iterable<String> sources,
+ Iterable<String> targets,
+ (InstanceSpecification, InstanceSpecification, InstanceSpecification)=>T testRule) {
+
+ val sourceEditParts = sources.map[gmfgen.getNode(it)].filterNull
+ val targetEditParts = targets.map[gmfgen.getNode(it)].filterNull
+
+ mapTestsByInstance(linkEditParts, sourceEditParts, targetEditParts, testRule)
+ }
+
+ def <T extends Behavior> Iterable<Pair<Triple<InstanceSpecification, InstanceSpecification, InstanceSpecification>, T>> mapTestsByInstance(
+ Iterable<? extends InstanceSpecification> linkEditParts,
+ Iterable<? extends InstanceSpecification> sources,
+ Iterable<? extends InstanceSpecification> targets,
+ (InstanceSpecification, InstanceSpecification, InstanceSpecification)=>T testRule) {
+
+ linkEditParts.map[link |
+ sources.map[source |
+ targets.map[ (Tuples.create(link, source, it) -> testRule.apply(link, source, it)) ]
+ ].flatten
+ ].flatten.filter[
+ processExclusions(testContext, value, key.first, key.second, key.third)
+ ].toList // Filter exactly once because of the side-effects
+ }
+
+ def build() {
+ if (containerEditPart != null) {
+ testContext.ownedAttributes += containerEditPart.toContainerDerivedProperty
+ }
+ testContext.ownedAttributes += topEditParts.map[toNodeEditPartProperty('GenTopLevelNode')]
+ testContext.ownedAttributes += childEditParts.map[toNodeEditPartProperty('GenChildNode')]
+ testContext.ownedAttributes += childLabelEditParts.map[toNodeEditPartProperty('GenChildLabelNode')]
+ testContext.ownedAttributes += linkEditParts.map[toNodeEditPartProperty('GenLink')]
+ testContext.ownedAttributes += linkOwnedBySourceEditParts.map[toNodeEditPartProperty('GenLinkOwnedBySource')]
+
+ importTypesRequiredByTestBehaviors
+ testContext.elementImports += gmfgen.computeImports(imports)
+
+ testContext.ownedBehaviors += testBehaviors.map[value]
+ if (testCaseRule != null) {
+ testContext.ownedOperations += testBehaviors.map[pair |
+ testCaseRule.apply(pair.key, pair.value) => [
+ if (!pair.value.preconditions.empty) {
+ // Transfer the pre-condition constraints to the operation
+ it.preconditions += ImmutableList.copyOf(pair.value.preconditions)
+ }
+ ]
+ ]
+ }
+
+ testContext
+ }
+
+ protected def void importTypesRequiredByTestBehaviors() {
+ testBehaviors.map[value].forEach[
+ // References to actual edit-part instances
+ allOwnedElements.filter(InstanceValue).filter[instance.isKindOfEditPart].forEach[imports += instance.editPart]
+
+ // References to edit-part types by visual ID
+ allOwnedElements.filter(OpaqueExpression).filter[languages.contains('Java') && bodies.exists[endsWith('.VISUAL_ID')]].forEach[
+ imports += bodies.get(languages.indexOf('Java')).upTo('.VISUAL_ID')
+ ]
+ ]
+ }
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/DefaultingList.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/DefaultingList.xtend
new file mode 100644
index 00000000000..b60e4325268
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/DefaultingList.xtend
@@ -0,0 +1,57 @@
+/*****************************************************************************
+ * 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.tests.framework.m2m
+
+import org.eclipse.emf.common.util.BasicEList
+import org.eclipse.xtend.lib.annotations.Accessors
+
+/**
+ * A custom list implementation that has an initial unset state that can be queried to determine whether
+ * the list was ever modified.
+ */
+class DefaultingList<E> extends BasicEList<E> {
+ @Accessors(PUBLIC_GETTER) boolean set
+
+ static def <E> DefaultingList<E> newDefaultingList() {
+ new DefaultingList<E>
+ }
+
+ static def dispatch isDefault(Iterable<?> collection) {
+ false
+ }
+
+ static def dispatch isDefault(DefaultingList<?> collection) {
+ !collection.isSet
+ }
+
+ /**
+ * Iterable alternation: the result is {@code collection} if it is not {@link #isDefault()}, otherwise the value of
+ * the {@code defaultBlock}.
+ */
+ def static <E> Iterable<? extends E> operator_or(Iterable<? extends E> collection, ()=>Iterable<? extends E> defaultBlock) {
+ if (collection.isDefault) defaultBlock.apply else collection
+ }
+
+ /**
+ * Iterable alternation: the result is {@code collection} if it is not {@link #isDefault()}, otherwise the value of
+ * the {@code defaultBlock}.
+ */
+ def static <E> Iterable<? extends E> operator_or(Iterable<? extends E> collection, Iterable<? extends E> defaultValue) {
+ if (collection.isDefault) defaultValue else collection
+ }
+
+ override protected didChange() {
+ set = true
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/Metamodels.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/Metamodels.xtend
new file mode 100644
index 00000000000..be505269f52
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2m/Metamodels.xtend
@@ -0,0 +1,108 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - adapted from QVTo
+ * Christian W. Damus - bug 464647
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.tests.framework.m2m
+
+import javax.inject.Singleton
+import org.eclipse.emf.ecore.EObject
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.Property
+import org.eclipse.uml2.uml.UMLPackage
+import org.eclipse.xtend.lib.annotations.Data
+import org.eclipse.uml2.uml.Profile
+import org.eclipse.uml2.uml.Element
+import org.eclipse.uml2.uml.Operation
+import org.eclipse.uml2.uml.PrimitiveType
+import org.eclipse.papyrus.tests.framework.xtend.annotations.Cached
+import org.eclipse.uml2.uml.Enumeration
+import org.eclipse.emf.common.util.URI
+import org.eclipse.uml2.uml.resource.UMLResource
+
+/**
+ * Reference metamodels for GMFGen-to-UML transformations.
+ */
+@Data
+@Singleton
+class Metamodels {
+ Model gmfgenMetamodel
+ Model frameworkBase
+ Profile utp
+
+ def metaclassName(EObject element) {
+ element.eClass.name
+ }
+
+ @Cached def org.eclipse.uml2.uml.Class gmfgenMetaclass(String name) {
+ gmfgenMetamodel.getMember(name, false, UMLPackage.Literals.CLASS) as org.eclipse.uml2.uml.Class
+ }
+
+ def gmfgenMetaclass(EObject gmfgenElement) {
+ gmfgenElement.metaclassName.gmfgenMetaclass
+ }
+
+ @Cached def frameworkClass(String name) {
+ frameworkBase.eAllContents.filter(org.eclipse.uml2.uml.Class).findFirst[it.name == name]
+ }
+
+ @Cached def frameworkEnum(String name) {
+ frameworkBase.eAllContents.filter(Enumeration).findFirst[it.name == name]
+ }
+
+ def utpStereotype(String name) {
+ utp.getOwnedStereotype(name)
+ }
+
+ private def applyUTP(Element element, String name) {
+ utpStereotype(name) => [
+ element.applyStereotype(it)
+ ]
+ }
+
+ def applyTestContext(org.eclipse.uml2.uml.Class class_) {
+ class_.applyUTP('TestContext')
+ }
+
+ def applySUT(Property property) {
+ property.applyUTP('SUT')
+ }
+
+ def applyTestCase(Operation operation) {
+ operation.applyUTP('TestCase')
+ }
+
+ def primitiveType(String name) {
+ frameworkBase.members.filter(PrimitiveType).findFirst[it.name == name]
+ }
+
+ def stringType() {
+ 'String'.primitiveType
+ }
+
+ def booleanType() {
+ 'Boolean'.primitiveType
+ }
+
+ def integerType() {
+ 'Integer'.primitiveType
+ }
+
+ def umlMetaclass(String name) {
+ umlMetamodel.getOwnedType(name) as org.eclipse.uml2.uml.Class
+ }
+
+ @Cached def Model umlMetamodel() {
+ gmfgenMetamodel.eResource.resourceSet.getResource(URI.createURI(UMLResource.UML_METAMODEL_URI), true).contents.filter(Model).head
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorComponent.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorComponent.java
new file mode 100644
index 00000000000..938b01b10d5
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorComponent.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend;
+
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.mwe.core.WorkflowContext;
+import org.eclipse.emf.mwe.core.issues.Issues;
+import org.eclipse.emf.mwe.core.lib.WorkflowComponentWithModelSlot;
+import org.eclipse.emf.mwe.core.monitor.ProgressMonitor;
+import org.eclipse.emf.mwe.core.resources.ResourceLoaderFactory;
+import org.eclipse.papyrus.tests.framework.m2t.xtend.templates.CodegenContext;
+import org.eclipse.papyrus.tests.framework.m2t.xtend.templates.PapyrusDiagramCanonicalTests;
+import org.eclipse.uml2.uml.Model;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * The step in the test framework workflow that generates the test code from the UML-UTP test model.
+ */
+public class CodeGeneratorComponent extends WorkflowComponentWithModelSlot {
+
+ private String tempSrcPath = null;
+ private Log log = LogFactory.getLog(getClass());
+
+ private final Injector injector;
+
+ @Inject
+ private PapyrusDiagramCanonicalTests canonicalTests;
+
+ @Inject
+ private CodegenContext codegenContext;
+
+ public CodeGeneratorComponent() {
+ injector = Guice.createInjector(createCodeGeneratorModule());
+ injector.injectMembers(this);
+ }
+
+ protected CodeGeneratorModule createCodeGeneratorModule() {
+ return new CodeGeneratorModule();
+ }
+
+ public String getTempSrcPath() {
+ return tempSrcPath;
+ }
+
+
+ public void setTempSrcPath(String tempSrcPath) {
+ this.tempSrcPath = tempSrcPath;
+ }
+
+ @Override
+ protected void invokeInternal(WorkflowContext ctx, ProgressMonitor monitor,
+ Issues issues) {
+ log.info("Generating code...");
+ Object modelSlotContent = ctx.get(getModelSlot());
+ Model model = null;
+ if (modelSlotContent instanceof Model) {
+ model = (Model) modelSlotContent;
+ } else if (modelSlotContent instanceof List) {
+ List<?> slotContentList = (List<?>) modelSlotContent;
+ model = (Model) slotContentList.get(0);
+ }
+ if (model == null || !(model instanceof Model)) {
+ log.error("The input model for the generation was not loaded!");
+ }
+ EcoreUtil.resolveAll(model);
+
+ codegenContext.setOutputFolderPath(tempSrcPath);
+ codegenContext.setResourceLoader(ResourceLoaderFactory.createResourceLoader());
+
+ canonicalTests.generate(model);
+ log.info("The code was succesfully generated in " + tempSrcPath);
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorModule.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorModule.java
new file mode 100644
index 00000000000..23d7218d360
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/CodeGeneratorModule.java
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * 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.tests.framework.m2t.xtend;
+
+import com.google.inject.AbstractModule;
+
+/**
+ * Guice module for the Xtend code generator.
+ */
+public class CodeGeneratorModule extends AbstractModule {
+
+ public CodeGeneratorModule() {
+ super();
+ }
+
+ @Override
+ protected void configure() {
+ bindImportator();
+ bindPapyrusDiagramCanonicalTests();
+
+ bindTemplateQueries();
+ bindAllPackageTestsTemplate();
+ bindAppearanceTestTemplate();
+ bindDeleteTestTemplate();
+ bindDropTestTemplate();
+ bindEditionTestTemplate();
+ bindTestChildLabelNodeTestTemplate();
+ bindTestLinkTemplate();
+ bindTestNodeTemplate();
+ }
+
+ protected void bindImportator() {
+ // Pass
+ }
+
+ private void bindTemplateQueries() {
+ // Pass
+ }
+
+ protected void bindPapyrusDiagramCanonicalTests() {
+ // Pass
+ }
+
+ private void bindAllPackageTestsTemplate() {
+ // Pass
+ }
+
+ private void bindAppearanceTestTemplate() {
+ // Pass
+ }
+
+ private void bindDeleteTestTemplate() {
+ // Pass
+ }
+
+ private void bindDropTestTemplate() {
+ // Pass
+ }
+
+ private void bindEditionTestTemplate() {
+ // Pass
+ }
+
+ private void bindTestChildLabelNodeTestTemplate() {
+ // Pass
+ }
+
+ private void bindTestLinkTemplate() {
+ // Pass
+ }
+
+ private void bindTestNodeTemplate() {
+ // Pass
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AbstractTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AbstractTestTemplate.xtend
new file mode 100644
index 00000000000..d3b945946b7
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AbstractTestTemplate.xtend
@@ -0,0 +1,163 @@
+/*
+ * 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.tests.framework.m2t.xtend.templates
+
+import static extension com.google.common.base.Strings.nullToEmpty
+
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceValue
+import org.eclipse.uml2.uml.Operation
+import javax.inject.Inject
+import org.eclipse.uml2.uml.ValuePin
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.ValueSpecification
+import org.eclipse.uml2.uml.OpaqueExpression
+import org.eclipse.uml2.uml.CallOperationAction
+import org.eclipse.uml2.uml.LiteralString
+
+/**
+ * Common structure for all generated test classes. Provides several abstract or default-blank snippets
+ * that subclasses should override to plug in specific code fragments.
+ */
+abstract class AbstractTestTemplate {
+ @Inject protected extension Queries
+ @Inject protected extension TemplateQueries
+ @Inject protected extension Importator
+
+ def generate(Class clazz) '''
+ «javaHeader»
+ package «clazz.package.name»;
+
+ «markImports»
+ «clazz.elementImports.map[alias].forEach[imported(packageRootName(clazz)+'.edit.parts.'+it)]»
+
+ /**
+ * The Class «clazz.name».
+ */
+ «clazz.generatedTest»
+ «clazz.runWith»
+ «clazz.additionalAnnotations»
+ public class «clazz.name» «clazz.extendsDeclaration» {
+
+ «clazz.diagramCreationMethods»
+
+ «FOR Operation testCaseOperation : clazz.testCases SEPARATOR '\n'»
+ «val instanceSpec = (testCaseOperation.ownedParameters.head?.defaultValue as InstanceValue)?.instance»
+ /**
+ * «testCaseOperation.purpose(instanceSpec?.componentName)»
+ */
+ «testCaseOperation.testAnnotations»
+ «testCaseOperation.exclusionAnnotation»
+ public void «testCaseOperation.name»() {
+ «safeTestCaseBody(testCaseOperation.methods.head as Activity, clazz)»
+ }
+ «ENDFOR»
+
+ «clazz.getDiagramUpdaterMethod»
+
+ «clazz.additionalMethods»
+ }
+ '''
+
+ def extendsDeclaration(Class class_) '''extends «imported(class_.generals.head.qualifiedJavaName)»'''
+
+ def diagramCreationMethods(Class class_) '''
+ @Override
+ protected «imported('org.eclipse.papyrus.commands.ICreationCommand')» getDiagramCommandCreation() {
+ return new «class_.diagramCreationCommand»();
+ }
+
+ @Override
+ protected String getProjectName() {
+ return «class_.constantsInterface».PROJECT_NAME;
+ }
+
+ @Override
+ protected String getFileName() {
+ return «class_.constantsInterface».FILE_NAME;
+ }
+ '''
+
+ def getDiagramUpdaterMethod(Class class_) '''
+ @Override
+ public «imported('org.eclipse.gmf.tooling.runtime.update.DiagramUpdater')» getDiagramUpdater() {
+ return «class_.diagramUpdater».«class_.diagramUpdaterInstanceField»;
+ }
+ '''
+
+ def additionalMethods(Class class_) ''''''
+
+ def runWith(Class testContext) ''''''
+
+ def generatedTest(Class testContext) '''@«imported('org.eclipse.papyrus.junit.framework.classification.GeneratedTest')»'''
+
+ protected def additionalAnnotations(Class testContext) ''''''
+
+ abstract def CharSequence purpose(Operation testCase, String componentName)
+
+ def junitTest(Operation testCase) '''@«imported('org.junit.Test')»'''
+ def generatedTest(Operation testCase) ''''''
+ def testAnnotations(Operation testCase) '''
+ «testCase.junitTest»
+ «testCase.generatedTest»
+ '''
+
+ def exclusionAnnotation(Operation testCase) '''
+ «val pre = testCase.exclusionPrecondition»
+ «IF pre != null»
+ @«pre.name.imported»("«pre.specification.stringValue»")
+ «ENDIF»
+ '''
+
+ protected def getExclusionPrecondition(Operation testCase) {
+ testCase.preconditions.findFirst[
+ (name != null) && (specification instanceof LiteralString)
+ ]
+ }
+
+ private def safeTestCaseBody(Activity method, Class class_) {
+ try {
+ testCaseBody(method, class_);
+ } catch (Exception e) {
+ imported('org.junit.Assert') + '.fail("Test case specification problem: ' + (e.message ?: e.class.simpleName) +'");'
+ }
+ }
+
+ def testCaseBody(Activity method, Class class_) '''
+ «FOR action : method.ownedNodes.filter(CallOperationAction)»
+ «action.operation.name»(«FOR pin : action.arguments.filter(ValuePin) SEPARATOR ', '»«pin.toCallArgument(class_)»«ENDFOR»);
+ «ENDFOR»
+ '''
+
+ protected def dispatch CharSequence toCallArgument(ValuePin pin, Class testContext)
+ '''«pin.value.toCallArgument(testContext)»'''
+
+ protected def dispatch toCallArgument(ValueSpecification value, Class testContext)
+ '''«value.stringValue»'''
+
+ protected def dispatch toCallArgument(LiteralString value, Class testContext)
+ '''"«value.stringValue.nullToEmpty.replace('"', '\\"')»"'''
+
+ protected def dispatch toCallArgument(InstanceValue value, Class testContext) {
+ var node = value.instance as InstanceSpecification
+ val umlElementTypes = imported(packageRootName(testContext) + '.providers.UMLElementTypes')
+ '''«umlElementTypes».getElementType(«node.getStringSlotValue('editPartClassName')».VISUAL_ID)'''
+ }
+
+ protected def dispatch toCallArgument(OpaqueExpression value, Class testContext) {
+ val index = value.languages.indexOf('Java')
+ '''«value.bodies.get(index)»'''
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AllPackageTestsTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AllPackageTestsTemplate.xtend
new file mode 100644
index 00000000000..660ab79cb30
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AllPackageTestsTemplate.xtend
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import java.util.List
+import javax.inject.Inject
+
+/**
+ * Code generation template for the all-tests suite of a test package. Also used for the top-level all-tests
+ * suite that aggregates the suite for each package.
+ */
+class AllPackageTestsTemplate {
+ @Inject extension TemplateQueries
+ @Inject extension Importator
+
+ def generate(String className, String packageName, List<String> classes ) '''
+ «javaHeader»
+ package «packageName»;
+
+ «markImports()»
+
+ /**
+ * All test in canonical package
+ */
+ @«imported('org.junit.runner.RunWith')»(«imported('org.eclipse.papyrus.junit.framework.classification.ClassificationSuite')».class)
+ @«imported('org.junit.runners.Suite.SuiteClasses')»({
+ «FOR String clazz : classes»
+ «imported(clazz)».class,
+ «ENDFOR»
+ })
+ @«imported('org.eclipse.papyrus.junit.framework.classification.GeneratedTest')»
+ public class «className» {
+ }
+ '''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AppearanceTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AppearanceTestTemplate.xtend
new file mode 100644
index 00000000000..6d7cdf29f2c
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/AppearanceTestTemplate.xtend
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Code generation template for the appearance tests class for an edit-part.
+ */
+class AppearanceTestTemplate extends AbstractTestTemplate {
+
+ override diagramCreationMethods(Class class_) '''
+ @Override
+ public «imported('org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart')» getContainerEditPart() {
+ return getDiagramEditPart();
+ }
+
+ «super.diagramCreationMethods(class_)»
+ '''
+
+ override getDiagramUpdaterMethod(Class class_) ''''''
+
+ override purpose(Operation testCase, String componentName) '''Test the appearance properties of «componentName».'''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/CodegenContext.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/CodegenContext.xtend
new file mode 100644
index 00000000000..348c191c451
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/CodegenContext.xtend
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * 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.tests.framework.m2t.xtend.templates
+
+import org.eclipse.xtend.lib.annotations.Accessors
+import javax.inject.Inject
+import java.io.File
+import java.io.FileWriter
+import com.google.common.io.Closeables
+import javax.inject.Singleton
+import org.eclipse.emf.mwe.core.resources.ResourceLoader
+
+/**
+ * A code generation context for the Xtend templates.
+ */
+ @Singleton
+class CodegenContext {
+ @Inject extension TemplateQueries
+ @Inject extension Importator
+
+ @Accessors String outputFolderPath
+
+ @Accessors ResourceLoader resourceLoader
+
+ def create new File(outputFolderPath) outputFolder() {
+ // Pass
+ }
+
+ def create new File(outputFolder, package_.computePackagePath) outputFolder(org.eclipse.uml2.uml.Package package_) {
+ if (!it.exists) it.mkdirs
+ }
+
+ def outputFile(org.eclipse.uml2.uml.Class class_) {
+ class_.nearestPackage.outputFile(class_.name)
+ }
+
+ def create new File(package_.outputFolder, class_ + '.java') outputFile(org.eclipse.uml2.uml.Package package_, String class_) {
+ // Pass
+ }
+
+ def createClass(org.eclipse.uml2.uml.Package package_, String className, () => CharSequence template) {
+ createFile(package_.outputFile(className), template)
+ }
+
+ def createFile(org.eclipse.uml2.uml.Package package_, String fileName, () => CharSequence template) {
+ createFile(new File(package_.outputFolder, fileName), template)
+ }
+
+ def createFile(org.eclipse.uml2.uml.Class class_, () => CharSequence template) {
+ createFile(class_.outputFile, template)
+ }
+
+ def createFile(File file, () => CharSequence template) {
+ val writer = new FileWriter(file)
+ try {
+ writer.write(managingImports(file, template).toString)
+ } finally {
+ Closeables.close(writer, true)
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DeleteTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DeleteTestTemplate.xtend
new file mode 100644
index 00000000000..3b801c37abf
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DeleteTestTemplate.xtend
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Template for the edit-part deletion tests class for an edit-part.
+ */
+class DeleteTestTemplate extends AbstractTestTemplate {
+
+ override diagramCreationMethods(Class class_) '''
+ @Override
+ public «imported('org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart')» getContainerEditPart() {
+ return getDiagramEditPart();
+ }
+
+ «super.diagramCreationMethods(class_)»
+ '''
+
+ override purpose(Operation testCase, String componentName) '''Test the deletion of «componentName».'''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DirectEditTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DirectEditTestTemplate.xtend
new file mode 100644
index 00000000000..0cabc9d3d1f
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DirectEditTestTemplate.xtend
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Template for the direct-edit tests for an edit-part.
+ */
+class DirectEditTestTemplate extends AbstractTestTemplate {
+
+ override diagramCreationMethods(Class class_) '''
+ @Override
+ public «imported('org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart')» getContainerEditPart() {
+ return getDiagramEditPart();
+ }
+
+ «super.diagramCreationMethods(class_)»
+ '''
+
+ override getDiagramUpdaterMethod(Class class_) ''''''
+
+ override purpose(Operation testCase, String componentName) '''Test the direct-edit of «componentName».'''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DropTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DropTestTemplate.xtend
new file mode 100644
index 00000000000..ef9eea05465
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/DropTestTemplate.xtend
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.CallOperationAction
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceValue
+import org.eclipse.uml2.uml.Operation
+import org.eclipse.uml2.uml.ValuePin
+
+/**
+ * Template for the drag-and-drop tests class for an edit-part.
+ */
+class DropTestTemplate extends AbstractTestTemplate {
+
+ override diagramCreationMethods(Class class_) '''
+ @Override
+ public «imported('org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart')» getContainerEditPart() {
+ return getDiagramEditPart();
+ }
+
+ «super.diagramCreationMethods(class_)»
+ '''
+
+ override getDiagramUpdaterMethod(Class class_) ''''''
+
+ override purpose(Operation testCase, String componentName) '''Test to drop «componentName».'''
+
+ override testCaseBody(Activity method, Class class_) '''
+ «FOR action : method.ownedNodes.filter(CallOperationAction)»
+ «val pin = action.arguments.head as ValuePin»
+ «val node = (pin.value as InstanceValue).instance»
+ «action.operation.name»(«pin.toCallArgument(class_)», «node.umlPackageElementType», true);
+ «ENDFOR»
+ '''
+
+ override additionalMethods(Class class_) '''
+ @Override
+ protected «imported('org.eclipse.gmf.runtime.notation.View')» getRootView() {
+ return («imported('org.eclipse.gmf.runtime.notation.View')»)getContainerEditPart().getModel();
+ }
+ '''
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Importator.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Importator.xtend
new file mode 100644
index 00000000000..bb720f81ee2
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Importator.xtend
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * 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.tests.framework.m2t.xtend.templates
+
+import java.util.Map
+import com.google.common.collect.Maps
+import com.google.common.base.Joiner
+import com.google.common.collect.Iterables
+import com.google.common.collect.Ordering
+import javax.inject.Singleton
+import java.io.File
+
+/**
+ * Extensions for managing imports in generated Java files.
+ */
+ @Singleton
+class Importator {
+ Map<File, Map<String, String>> importsByFile = Maps.newHashMap
+ ThreadLocal<File> tlFile = new ThreadLocal;
+
+ def reset() {
+ imports.clear
+ }
+
+ def CharSequence managingImports(File file, () => CharSequence template) {
+ var CharSequence result = null
+
+ tlFile.set(file)
+ try {
+ result = importify(template.apply)
+ } finally {
+ tlFile.remove
+ importsByFile.remove(file)
+ }
+
+ result
+ }
+
+ private def file() {
+ tlFile.get
+ }
+
+ def imports() {
+ if (importsByFile.containsKey(file))
+ importsByFile.get(file)
+ else
+ Maps.newHashMap => [
+ importsByFile.put(file, it)
+ ]
+ }
+
+ def String imported(String qualifiedClassName) {
+ val simpleName = qualifiedClassName.substring(qualifiedClassName.lastIndexOf('.') + 1)
+ val existing = imports.get(simpleName)
+
+ if ((simpleName == qualifiedClassName) || ((existing != null) && (existing != qualifiedClassName))) {
+ // Cannot import the same name again
+ qualifiedClassName
+ } else {
+ imports.put(simpleName, qualifiedClassName)
+ simpleName
+ }
+ }
+
+ def String markImports() {
+ "$$$imports$$$"
+ }
+
+ private def CharSequence importify(CharSequence text) {
+ val importsText = Joiner.on(System.getProperty("line.separator")).join(
+ Iterables.transform(Ordering.natural.sortedCopy(imports.values), [f|'import ' + f + ';'])
+ )
+
+ text.toString.replaceFirst("\\$\\$\\$imports\\$\\$\\$", importsText)
+ }
+} \ No newline at end of file
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/PapyrusDiagramCanonicalTests.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/PapyrusDiagramCanonicalTests.xtend
new file mode 100644
index 00000000000..e62293e97c7
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/PapyrusDiagramCanonicalTests.xtend
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import java.util.ArrayList
+import java.util.List
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.Package
+import javax.inject.Inject
+
+/**
+ * The main entry-point rule for the model-to-text transformation that generates the test code
+ * from a UML-UTP model describing the diagram test cases.
+ */
+class PapyrusDiagramCanonicalTests {
+ @Inject extension TemplateQueries
+ @Inject extension CodegenContext
+
+ @Inject AllPackageTestsTemplate allPackageTests
+ @Inject AppearanceTestTemplate appearanceTests
+ @Inject DirectEditTestTemplate directEditTests
+ @Inject DeleteTestTemplate deleteTests
+ @Inject DropTestTemplate dropTests
+ @Inject TestChildLabelNodeTemplate childLabelNodeTests
+ @Inject TestLinkTemplate linkTests
+ @Inject TestNodeTemplate nodeTests
+ @Inject SynchronizationTestTemplate synchronizationTests
+
+ def generate(Model model) {
+ var List<String> suiteClasses = new ArrayList<String>
+ for (Package subpackage : model.ownedElements.filter(Package)){
+ val allTestClasses = subpackage.allOwnedElements.filter(Class)
+ .filter[getAppliedStereotype("utp::TestContext") != null]
+ .filter[!ownedAttributes.empty]
+ .filter[hasTestCases]
+
+ if (!allTestClasses.empty) {
+ for (Class clazz : allTestClasses) {
+
+ // Invoke the proper template for the class
+ val superClass = clazz.generals.head as Class
+
+ createFile(clazz) [
+ switch superClass.name {
+ case "AbstractCreateNodeFromPaletteTest": nodeTests.generate(clazz)
+ case "AbstractCreateLinkFromPaletteTest": linkTests.generate(clazz)
+ case "AbstractCreateChildLabelNodeFromPaletteTest": childLabelNodeTests.generate(clazz)
+ case "AbstractCreateLinkOwnedBySourceFromPaletteTest": linkTests.generate(clazz)
+ case "AbstractAppearanceNodeTest": appearanceTests.generate(clazz)
+ case "AbstractDropNodeTest": dropTests.generate(clazz)
+ case "AbstractDeleteNodeTest": deleteTests.generate(clazz)
+ case "AbstractEditableNodeTest": directEditTests.generate(clazz)
+ case "AbstractSynchronizationTest",
+ case "AbstractCSSSynchronizationTest" : synchronizationTests.generate(clazz)
+ default: throw new RuntimeException("Type of test not recognized: " + superClass.name)
+ }
+ ]
+ }
+ var className = generatePackageTestSuiteClass(subpackage, allTestClasses)
+ suiteClasses.add(className);
+ }
+ }
+ generateAllTestSuiteClass(model, suiteClasses)
+ }
+
+ def generateAllTestSuiteClass(Package model, List<String> testSuiteNames) {
+ val className = "AllGenTests"
+ createClass(model, className) [
+ allPackageTests.generate(className, model.name, testSuiteNames);
+ ]
+ }
+
+ def generatePackageTestSuiteClass(Package package_, Iterable<Class> allTestClasses) {
+ val packageName = package_.name
+ val className = "All" + packageName.substring(packageName.lastIndexOf('.') + 1).toFirstUpper + "Tests"
+ createClass(package_, className) [
+ allPackageTests.generate(className, packageName, allTestClasses.toList.map[name]);
+ ]
+
+ packageName + '.' + className
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Queries.java b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Queries.java
new file mode 100644
index 00000000000..3c0769c130d
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/Queries.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.uml2.uml.InstanceSpecification;
+import org.eclipse.uml2.uml.InstanceValue;
+import org.eclipse.uml2.uml.LiteralString;
+import org.eclipse.uml2.uml.Property;
+import org.eclipse.uml2.uml.Slot;
+import org.eclipse.uml2.uml.StructuralFeature;
+import org.eclipse.uml2.uml.ValueSpecification;
+
+/**
+ * Helper query operations for introspection of the UML-UTP model describing diagram test cases,
+ * for use in the Xtend transformation pipeline.
+ */
+public class Queries {
+
+ public InstanceSpecification getDefaultValueInstanceSpecification(org.eclipse.uml2.uml.Class clazz, String propertyName) {
+ EList<Property> attributes = clazz.getAttributes();
+ for (Property attribute : attributes) {
+ if (attribute.getName().equalsIgnoreCase(propertyName)) {
+ return ((InstanceValue) attribute.getDefaultValue()).getInstance();
+ }
+ }
+ throw new RuntimeException("Attribute '" + propertyName + "' not found in class " + clazz.getName());
+ }
+
+ public String getStringSlotValue(InstanceSpecification instanceSpecification, String slotName) {
+ if (instanceSpecification == null)
+ throw new RuntimeException("instance argument is null!");
+ EList<Slot> slots = instanceSpecification.getSlots();
+ for (Slot slot : slots) {
+ StructuralFeature definingFeature = slot.getDefiningFeature();
+ String name = definingFeature.getName();
+ if (name.equalsIgnoreCase(slotName)) {
+ EList<ValueSpecification> values = slot.getValues();
+ ValueSpecification value = values.get(0);
+ return ((LiteralString) value).getValue();
+ }
+ }
+ throw new RuntimeException("Slot _" + slotName + "_ not found in instance " + instanceSpecification);
+ }
+
+ public InstanceSpecification getInstanceSlotValue(InstanceSpecification instance, String slotName) {
+ if (instance == null)
+ throw new RuntimeException("instance argument is null!");
+ if (instance.eIsProxy()) {
+ EcoreUtil.resolve(instance, instance.eResource());
+ }
+ EList<Slot> slots = instance.getSlots();
+ for (Slot slot : slots) {
+ if (slot.getDefiningFeature().getName().equalsIgnoreCase(slotName)) {
+ if (slot.getValues().isEmpty()) {
+ throw new RuntimeException(slotName + " in " + instance + " has no values");
+ }
+ ValueSpecification value = slot.getValues().get(0);
+ InstanceSpecification instanceSpec = ((InstanceValue) value).getInstance();
+ if (instanceSpec.eIsProxy()) {
+ EcoreUtil.resolve(instanceSpec, instance.eResource());
+ }
+ return instanceSpec;
+ }
+ }
+ throw new RuntimeException("Slot _" + slotName + "_ not found in instance " + instance);
+ }
+
+ public String getSlotValueDefaultInstanceSpecification(org.eclipse.uml2.uml.Class clazz, String propertyName, String slotName) {
+ return getStringSlotValue(getDefaultValueInstanceSpecification(clazz, propertyName), slotName);
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/SynchronizationTestTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/SynchronizationTestTemplate.xtend
new file mode 100644
index 00000000000..40173bac565
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/SynchronizationTestTemplate.xtend
@@ -0,0 +1,72 @@
+/*
+ * 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.tests.framework.m2t.xtend.templates
+
+import javax.inject.Inject
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceValue
+import org.eclipse.uml2.uml.InvocationAction
+import org.eclipse.uml2.uml.ValuePin
+import org.eclipse.uml2.uml.InputPin
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Common structure of the model-view synchronization tests for an edit-part. Provides several
+ * abstract or default-blank snippets that subclasses should override to plug in specific code fragments.
+ */
+class SynchronizationTestTemplate extends AbstractTestTemplate {
+ @Inject protected extension TemplateQueries
+ @Inject protected extension Importator
+
+ protected def syncTestKind(Class testContext) {
+ testContext.getOwnedAttribute('syncTestKind', null)?.defaultValue?.stringValue
+ }
+
+ override additionalAnnotations(Class testContext) {
+ val org.eclipse.uml2.uml.Property css = testContext.getAttribute('css', null);
+ if (css != null) {
+ '''@«'org.eclipse.papyrus.uml.diagram.tests.synchronization.AbstractCSSSynchronizationTest.CSS'.imported»("«css.defaultValue.stringValue»")'''
+ } else {
+ super.additionalAnnotations(testContext)
+ }
+ }
+
+ override purpose(Operation testCase, String componentName) {
+ switch (testCase.class_.syncTestKind) {
+ case 'labelnode' : '''Test to synchronize child label node «componentName».'''
+ case 'childnode' : '''Test to synchronize child node «componentName».'''
+ case 'link' : '''Test to synchronize link «componentName».'''
+ default: '''Test to synchronize node «componentName».'''
+ }
+ }
+
+ override getDiagramUpdaterMethod(Class class_) ''''''
+
+ protected def instanceValue(Activity method, String pinName) {
+ (method.ownedNodes.head as InvocationAction).instanceValue(pinName)
+ }
+
+ protected def instanceValue(InvocationAction action, String pinName) {
+ action.arguments.findFirst[name == pinName].instanceValue
+ }
+
+ protected def instanceValue(InputPin pin) {
+ ((pin as ValuePin).value as InstanceValue).instance
+ }
+
+ protected def umlElementTypes(Class class_) {
+ (class_.packageRootName + '.providers.UMLElementTypes').imported
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TemplateQueries.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TemplateQueries.xtend
new file mode 100644
index 00000000000..a795d285eb8
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TemplateQueries.xtend
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.InstanceSpecification
+import org.eclipse.uml2.uml.Package
+import org.eclipse.uml2.uml.Property
+import javax.inject.Singleton
+import javax.inject.Inject
+import org.eclipse.uml2.uml.Expression
+import java.util.Calendar
+import org.eclipse.uml2.uml.NamedElement
+import com.google.common.io.Resources
+import com.google.common.base.Charsets
+import org.eclipse.papyrus.tests.framework.xtend.annotations.Cached
+
+/**
+ * Helper query operations and template snippets for the model-to-text transformations
+ * that generate test class code from the UML-UTP model describing a diagram's test cases.
+ */
+@Singleton
+class TemplateQueries {
+ @Inject extension CodegenContext
+ @Inject extension Importator
+ @Inject extension Queries
+
+ def javaHeader() '''
+ /*****************************************************************************
+ * Copyright (c) «thisYear» 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
+ *
+ * This file has been generated automatically in the Papyrus Test Framework.
+ *
+ *****************************************************************************/
+ '''
+
+ private def create Calendar.instance.get(Calendar.YEAR) thisYear() {
+ // Pass
+ }
+
+ def getQualifiedJavaName(NamedElement element) {
+ element.qualifiedName.replace('::', '.')
+ }
+
+ def getTestCases(Class class_) {
+ class_.ownedOperations.filter[getAppliedStereotype("utp::TestCase") != null]
+ }
+
+ def hasTestCases(Class class_) {
+ !class_.testCases.nullOrEmpty
+ }
+
+ def umlPackageElementType(InstanceSpecification node) {
+ var componentName = getComponentName(node) as String
+ if (componentName == 'Class') componentName = componentName + '_'
+
+ '''«imported('org.eclipse.uml2.uml.UMLPackage')».eINSTANCE.get«componentName»()'''
+ }
+
+ def getComponentName(InstanceSpecification node) {
+ node.getInstanceSlotValue('modelFacet').getStringSlotValue('metaClass')
+ }
+
+ def getDiagramName(Class clazz) {
+ clazz.getDefaultValueInstanceSpecification('generator').getStringSlotValue('modelID').replace('PapyrusUML', '')
+ }
+
+ private def getDiagramCreationCommandName(Class clazz) {
+ clazz.getAttribute('diagramCreationCommand', null)?.defaultStringValue
+ }
+
+ def diagramCreationCommand(Class class_) {
+ resolveImportedClassName(packageRootName(class_), class_.diagramCreationCommandName, #['custom', ''])
+ }
+
+ def packageRootName(Class clazz) {
+ clazz.getDefaultValueInstanceSpecification('generator').getStringSlotValue('packageNamePrefix')
+ }
+
+ def computePackagePath(Package model) {
+ model.name.replace(".", System.getProperty("file.separator")) +
+ //System.getProperty("file.separator") + "canonical" +
+ System.getProperty("file.separator")
+ }
+
+ private def constantsInterfaceName(Class class_) {
+ class_.getAttribute('testConstantsInterface', null)?.defaultStringValue
+ }
+
+ def constantsInterface(Class class_) {
+ resolveImportedClassName(class_.packageRootName, class_.constantsInterfaceName, #['test', 'tests'])
+ }
+
+ private def diagramUpdaterName(Class class_) {
+ class_.getAttribute('diagramUpdater', null)?.defaultStringValue
+ }
+
+ def defaultStringValue(Property property) {
+ val value = property.defaultValue
+
+ switch (value) {
+ case null: ''
+ Expression: value.symbol // XXX Why do we use an expression symbol to store the updater name?
+ default: value.stringValue
+ }
+ }
+
+ def diagramUpdater(Class class_) {
+ class_.resolveDiagramUpdater.imported
+ }
+
+ private def resolveDiagramUpdater(Class class_) {
+ resolveClassName(class_.packageRootName, class_.diagramUpdaterName,
+ #['custom.edit.part', 'custom.edit.parts', 'custom.part', 'edit.part', 'edit.parts', 'part']
+ )
+ }
+
+ @Cached
+ def diagramUpdaterInstanceField(Class class_) {
+ val resourceName = class_.resolveDiagramUpdater.replace('.', '/') + '.class'
+ val resourceURL = resourceLoader.getResource(resourceName)
+ if (resourceURL == null) {
+ 'INSTANCE'
+ } else {
+ // Quick hack to look for legacy field name
+ try {
+ val contents = Resources.toString(resourceURL, Charsets.UTF_8)
+ if (contents.contains('TYPED_INSTANCE')) 'TYPED_INSTANCE' else 'INSTANCE'
+ } catch (Exception e) {
+ 'INSTANCE'
+ }
+ }
+ }
+
+ def String resolveImportedClassName(String rootPackage, String name, Iterable<String> searchPath) {
+ rootPackage.resolveClassName(name, searchPath).imported
+ }
+
+ @Cached
+ def String resolveClassName(String rootPackage, String name, Iterable<String> searchPath) {
+ val search = searchPath.map [
+ val prefix = if (it.startsWith(rootPackage+'.')) it else rootPackage + '.' + it
+
+ // Account for a '' search path, which results in the prefix ending with a '.'
+ if (prefix.endsWith('.')) prefix + name else prefix + '.' + name
+ ]
+
+ val result = search.findFirst [
+ try {
+ // The diagram plug-in is on our classpath, so look for the class
+ resourceLoader.getResource(it.replace('.', '/') + '.class') != null
+ } catch (Exception e) {
+ false
+ }
+ ]
+
+ result ?: search.last
+ }
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestChildLabelNodeTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestChildLabelNodeTemplate.xtend
new file mode 100644
index 00000000000..6ef2c91ff1e
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestChildLabelNodeTemplate.xtend
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Template for the tests class for creation of label-node edit parts.
+ */
+class TestChildLabelNodeTemplate extends AbstractTestTemplate {
+
+ override purpose(Operation testCase, String componentName) '''Test to create child label nodes.'''
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestLinkTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestLinkTemplate.xtend
new file mode 100644
index 00000000000..92bef38f3c3
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestLinkTemplate.xtend
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Operation
+
+/**
+ * Template for the tests class for creation of link edit-parts.
+ */
+class TestLinkTemplate extends AbstractTestTemplate {
+
+ override purpose(Operation testCase, String componentName) '''Test to create link «componentName».'''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestNodeTemplate.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestNodeTemplate.xtend
new file mode 100644
index 00000000000..2ddb6c0c45b
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/m2t/xtend/templates/TestNodeTemplate.xtend
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.m2t.xtend.templates
+
+import org.eclipse.uml2.uml.Activity
+import org.eclipse.uml2.uml.CallOperationAction
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Operation
+import org.eclipse.uml2.uml.ValuePin
+
+/**
+ * Template for the test class for creation of a top-node edit-part.
+ */
+class TestNodeTemplate extends AbstractTestTemplate {
+
+ override purpose(Operation testCase, String componentName) '''Test to create node «componentName».'''
+
+ override testCaseBody(Activity method, Class class_) '''
+ «FOR action : method.ownedNodes.filter(CallOperationAction)»
+ testCreateNodeFromPalette(«FOR pin : action.arguments.filter(ValuePin) SEPARATOR ', '»«pin.toCallArgument(class_)»«ENDFOR»);
+ «ENDFOR»
+ '''
+
+ override additionalMethods(Class class_) '''«IF (class_.getAttribute('containerEditPart', null) != null)»
+ @Override
+ protected «imported('org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest')» createViewRequestShapeContainer() {
+ return «imported('org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequestFactory')».getCreateShapeRequest(«imported(class_.packageRootName + '.providers.UMLElementTypes')».getElementType(«class_.getDefaultValueInstanceSpecification('containerEditPart').getStringSlotValue('editPartClassName')».VISUAL_ID), getDiagramEditPart().getDiagramPreferencesHint());
+ }
+ «ENDIF»'''
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/GenerateTestsWorkflow.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/GenerateTestsWorkflow.xtend
new file mode 100644
index 00000000000..e177eec2ce6
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/GenerateTestsWorkflow.xtend
@@ -0,0 +1,298 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 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:
+ * CEA LIST - Initial API and implementation
+ * Christian W. Damus - bug 464647
+ *
+ ******************************************************************************/
+package org.eclipse.papyrus.tests.framework.mwe
+
+import org.eclipse.emf.ecore.plugin.EcorePlugin
+import org.eclipse.emf.mwe.utils.DirectoryCleaner
+import org.eclipse.emf.mwe.utils.Mapping
+import org.eclipse.emf.mwe.utils.Reader
+import org.eclipse.emf.mwe.utils.StandaloneSetup
+import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext
+import org.eclipse.papyrus.mwe2.utils.XtendWorkflow
+import org.eclipse.papyrus.mwe2.utils.components.RegisterUmlProfile
+import org.eclipse.papyrus.mwe2.utils.components.UMLWriter
+import org.eclipse.papyrus.tests.framework.gmfgen2uml.GMFGen2UMLComponent
+import org.eclipse.papyrus.tests.framework.gmfgenuml2utp.GMFGen2UTPComponent
+import org.eclipse.papyrus.tests.framework.gmfgenuml2utp.GMFGen2UTPModule
+import org.eclipse.papyrus.tests.framework.m2t.xtend.CodeGeneratorComponent
+import org.eclipse.uml2.uml.Model
+import org.eclipse.uml2.uml.Profile
+import org.eclipse.uml2.uml.resources.util.UMLResourcesUtil
+import org.eclipse.xtend.lib.annotations.Accessors
+import org.eclipse.emf.common.util.URI
+import java.util.List
+import org.eclipse.papyrus.mwe2.utils.components.MultiReader
+import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator
+import java.util.Collection
+import org.eclipse.emf.mwe.core.lib.AbstractWorkflowComponent2
+import org.eclipse.emf.mwe.core.monitor.ProgressMonitor
+import org.eclipse.emf.mwe.core.issues.Issues
+import org.eclipse.emf.mwe.core.WorkflowContext
+
+/**
+ * A workflow orchestrating a transformation pipeline for generation of JUnit tests for the edit-parts of a diagram.
+ * <p>
+ * Inputs: a GMFGen model and references to the UML representation of the GMFGen metamodel, the test framework's base
+ * model describing the abstract test class library, and the UML Testing (UTP) Profile. Also a Guice model that
+ * injects various transformation options such as which edit-parts are to have tests generated.
+ * </p>
+ * Steps:
+ * <ol>
+ * <li>Generate a UML representation of the diagram's GMFGen model, as a UML model collecting instance-specifications
+ * of the classes in the UML representation of the GMFGen metamodel</li>
+ * <li>Transform the UML representation of the GMFGen model to a UML-UTP model describing the test cases</li>
+ * <li>Transform the UML-UTP model of the test cases to JUnit test sources</li>
+ * </ol>
+ */
+class GenerateTestsWorkflow extends XtendWorkflow {
+
+ @Accessors String testProjectName
+ @Accessors String gmfgenUri
+ @Accessors String testSrcGenLocation
+ @Accessors String testModel
+ @Accessors List<String> testExceptions = newArrayList()
+ @Accessors String gitRoot = "../../../../../../"
+
+ (Model, Model, Profile)=>GMFGen2UTPModule utpModuleFunction
+
+ final TestExceptionsBuilder testExceptionsBuilder = new TestExceptionsBuilder
+
+ def setUtpModuleFunction((Model, Model, Profile)=>GMFGen2UTPModule utpModuleFunction) {
+ this.utpModuleFunction = utpModuleFunction
+ }
+
+ def (Model, Model, Profile)=>GMFGen2UTPModule getUtpModuleFunction() {
+ utpModuleFunction
+ }
+
+ public override run(IWorkflowContext context) {
+ beans += new StandaloneSetup => [
+ platformUri = gitRoot
+
+ // Ensure that we can resolve references to the UML metamodel
+ addUriMap(new Mapping => [
+ from = 'pathmap://UML_METAMODELS/'
+ // Most likely to be in the right plug-in
+ to = classpathURI('metamodels/UML.metamodel.properties').trimSegments(1).appendSegment('').toString()
+ ])
+
+ // Ensure that we can resolve references to the Ecore profile
+ addUriMap(new Mapping => [
+ from = 'pathmap://UML_PROFILES/'
+ // Most likely to be in the right plug-in
+ to = classpathURI('profiles/Ecore.profile.properties').trimSegments(1).appendSegment('').toString()
+ ])
+
+ // Ensure that we can resolve references to the UML genmodel and ecore
+ addUriMap(new Mapping => [
+ from = 'platform:/plugin/org.eclipse.uml2.uml/model/'
+ // Most likely to be in the right plug-in
+ to = classpathURI('model/CMOF_2_UML.ecore2xml').trimSegments(1).appendSegment('').toString()
+ ])
+
+ // Ensure that we can resolve references to the GMFGen genmodel and ecore
+ addUriMap(new Mapping => [
+ from = 'platform:/plugin/org.eclipse.gmf.codegen/models/'
+ to = classpathURI('models/gmfgen.ecore').trimSegments(1).appendSegment('').toString()
+ ])
+
+ // Ensure that we can resolve references to the Notation genmodel and ecore
+ addUriMap(new Mapping => [
+ from = 'platform:/plugin/org.eclipse.gmf.runtime.notation/model/'
+ to = classpathURI('model/notation.genmodel').trimSegments(1).appendSegment('').toString()
+ ])
+
+ scanClassPath = true
+
+ // Ensure UML's registrations for standard profiles and libraries, legacy metamodel migrations, etc.
+ UMLResourcesUtil.init(null)
+
+ addRegisterGeneratedEPackage('org.eclipse.uml2.codegen.ecore.genmodel.GenModelPackage')
+ addRegisterGeneratedEPackage('org.eclipse.papyrus.papyrusgmfgenextension.PapyrusgmfgenextensionPackage')
+ addRegisterGeneratedEPackage('org.eclipse.upr.utp.UTPPackage')
+ addRegisterGeneratedEPackage('org.eclipse.papyrus.tests.framework.exceptions.ExceptionsPackage')
+ ]
+
+ // 1. Transform GMFGen file to UML
+ context.transformGmfgenToUmlPhase
+
+ // 2. Generate Test specification (UML-UTP) model
+ context.generateTestModelPhase
+
+ // 3. Generate Test code
+ context.generateTestCodePhase
+
+ super.run(context)
+ }
+
+ def resourceURI(String path) {
+ ('platform:/resource/' + path).replace('//', '/')
+ }
+
+ def classpathURI(String path) {
+ URI.createURI(getClass().classLoader.getResource(path).toExternalForm(), true)
+ }
+
+ def projectFile(String path) {
+ EcorePlugin.resolvePlatformResourcePath(("/" + testProjectName + '/' + path).replace('//', '/')).toFileString
+ }
+
+ def projectResourceURI(String path) {
+ resourceURI((testProjectName + '/' + path).replace('//', '/'))
+ }
+
+ def frameworkFile(String path) {
+ EcorePlugin.resolvePlatformResourcePath(('/org.eclipse.papyrus.tests.framework/' + path).replace('//', '/')).
+ toFileString
+ }
+
+ def frameworkResourceURI(String path) {
+ resourceURI(('org.eclipse.papyrus.tests.framework/' + path).replace('//', '/'))
+ }
+
+ protected def transformGmfgenToUmlPhase(IWorkflowContext context) {
+ components += new Reader => [
+ uri = gmfgenUri
+ modelSlot = 'inputGmfgen'
+ firstElementOnly = true
+ useSingleGlobalResourceSet = true
+ ]
+
+ components += new Reader => [
+ uri = 'pathmap://UML_PROFILES/Ecore.profile.uml'
+ modelSlot = 'ecoreprofile'
+ useSingleGlobalResourceSet = true
+ ]
+
+ components += new RegisterUmlProfile => [
+ profileSlot = 'ecoreprofile'
+ ]
+
+ components += new Reader => [
+ uri = resourceURI('/org.eclipse.papyrus.tests.framework/model/gmfgen.uml')
+ modelSlot = 'gmfgenUmlMetamodel'
+ useSingleGlobalResourceSet = true
+ ]
+
+ components += new GMFGen2UMLComponent => [
+ modelSlot = 'inputGmfgen'
+ metamodelSlot = 'gmfgenUmlMetamodel'
+ outputSlot = 'inputGmfgenUml'
+ ]
+
+ components += new UMLWriter => [
+ modelSlot = 'inputGmfgenUml'
+ uri = gmfgenUri + '.uml'
+ useSingleGlobalResourceSet = true
+ addXmiIdentifierFilters('^org\\.eclipse\\.papyrus\\.uml\\.diagram\\.' -> '')
+ ]
+ }
+
+ protected def generateTestModelPhase(IWorkflowContext context) {
+ components += new Reader => [
+ uri = gmfgenUri + '.uml'
+ modelSlot = 'inputModels'
+ useSingleGlobalResourceSet = true
+ ]
+ components += new Reader => [
+ uri = resourceURI('/org.eclipse.papyrus.tests.framework/model/testingFrameworkBase.uml')
+ modelSlot = 'frameworkBase'
+ useSingleGlobalResourceSet = true
+ ]
+
+ components += new Reader => [
+ uri = resourceURI('/org.eclipse.upr.utp/model/utp.profile.uml')
+ modelSlot = 'utp'
+ useSingleGlobalResourceSet = true
+ ]
+ components += new RegisterUmlProfile => [
+ profileSlot = 'utp'
+ generatedPackageInterfaceName = 'org.eclipse.upr.utp.UTPPackage'
+ ]
+
+ testExceptions.forEach[next |
+ var modelURI = URI.createURI(next, true)
+ if (modelURI.relative) {
+ modelURI = URI.createURI(resourceURI('/' + testProjectName + '/' + next), true)
+ }
+ val excURI = modelURI.toString
+
+ components += new MultiReader => [
+ uri = excURI
+ modelSlot = 'inputModels'
+ useSingleGlobalResourceSet = true
+ ]
+ ]
+
+ // And build ad hoc test exceptions
+ components += new AbstractWorkflowComponent2 {
+ override def invokeInternal(WorkflowContext context, ProgressMonitor monitor, Issues issues) {
+ val frameworkBase = context.get('frameworkBase') as Model
+ val gmfgen = context.get('inputGmfgen') as GenEditorGenerator
+
+ val adHocExceptions = testExceptionsBuilder.build(frameworkBase, gmfgen)
+
+ // If we have ad hoc test exceptions, add them to the input models
+ val inputModels = context.get('inputModels')
+ if ((adHocExceptions != null) && !adHocExceptions.constraints.empty) {
+ val newInputModels = switch inputModels {
+ Collection<Object> : inputModels
+ default : newArrayList(inputModels)
+ }
+ newInputModels += adHocExceptions
+ context.set('inputModels', newInputModels)
+ }
+ }
+ }
+
+ components += new GMFGen2UTPComponent => [
+ modelSlot = 'inputModels'
+ metamodelSlot = 'gmfgenUmlMetamodel'
+ frameworkBaseSlot = 'frameworkBase'
+ utpSlot = 'utp'
+ utpModule = utpModuleFunction
+ outputSlot = 'papyrusTestModel'
+ ]
+
+ components += new UMLWriter => [
+ modelSlot = 'papyrusTestModel'
+ uri = projectResourceURI(testModel)
+ useSameResource = true
+// addXmiIdentifierFilters('^org\\.eclipse\\.papyrus\\.uml\\.diagram\\.' -> '')
+ ]
+ }
+
+ protected def generateTestCodePhase(IWorkflowContext context) {
+ components += new Reader => [
+ uri = projectResourceURI(testModel).toString
+ modelSlot = 'papyrusTestModel'
+ ]
+
+ components += new DirectoryCleaner => [
+ directory = projectFile(testSrcGenLocation)
+ ]
+ components += new CodeGeneratorComponent => [
+ tempSrcPath = projectFile(testSrcGenLocation)
+ modelSlot = 'papyrusTestModel'
+ ]
+ }
+
+ //
+ // Test Exceptions DSL
+ //
+
+ def except((TestExceptionsBuilder)=>void buildScript) {
+ buildScript.apply(testExceptionsBuilder)
+ }
+
+}
diff --git a/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/TestExceptionsBuilder.xtend b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/TestExceptionsBuilder.xtend
new file mode 100644
index 00000000000..4c54e4e89ed
--- /dev/null
+++ b/tests/framework/org.eclipse.papyrus.tests.framework/src/org/eclipse/papyrus/tests/framework/mwe/TestExceptionsBuilder.xtend
@@ -0,0 +1,335 @@
+/*****************************************************************************
+ * 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.tests.framework.mwe
+
+import java.util.Collection
+import java.util.List
+import org.eclipse.gmf.codegen.gmfgen.GenCommonBase
+import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator
+import org.eclipse.papyrus.tests.framework.exceptions.EditPartSpec
+import org.eclipse.papyrus.tests.framework.exceptions.ExceptionsFactory
+import org.eclipse.papyrus.tests.framework.exceptions.ForbiddenEditPart
+import org.eclipse.papyrus.tests.framework.exceptions.ForbiddenEditPartPermutation
+import org.eclipse.papyrus.tests.framework.exceptions.ForbiddenReasonKind
+import org.eclipse.papyrus.tests.framework.exceptions.OperatorKind
+import org.eclipse.papyrus.tests.framework.exceptions.TestConstraint
+import org.eclipse.papyrus.tests.framework.exceptions.TestExceptions
+import org.eclipse.papyrus.tests.framework.xtend.annotations.LiteralConstants
+import org.eclipse.uml2.uml.Class
+import org.eclipse.uml2.uml.Model
+import org.eclipse.xtend.lib.annotations.Accessors
+
+import static extension org.eclipse.papyrus.tests.framework.m2m.DefaultingList.*
+
+/**
+ * A builder DSL for an ad hoc test exclusions model in the transformation workflow's Guice module.
+ */
+class TestExceptionsBuilder {
+ List<TestConstraintBuilder> constraints = newArrayList
+
+ def editPart((ForbiddenEditPartBuilder)=>void constraintBuilder) {
+ val forbidden = new ForbiddenEditPartBuilder
+ constraintBuilder.apply(forbidden)
+ constraints += forbidden
+ }
+
+ def permutation((ForbiddenEditPartPermutationBuilder)=>void permutationBuilder) {
+ val forbidden = new ForbiddenEditPartPermutationBuilder
+ permutationBuilder.apply(forbidden)
+ constraints += forbidden
+ }
+
+ def TestExceptions build(Model frameworkBase, GenEditorGenerator gmfgen) {
+ ExceptionsFactory.eINSTANCE.createTestExceptions => [model |
+ // Process critical constraints first because if any non-critical constraints also match,
+ // then we'll get test cases annotated out that shouldn't even be generated
+ this.constraints.sortBy[if (isCritical) -1 else 1].forEach[build(frameworkBase, gmfgen, model.constraints)]
+ ]
+ }
+
+ //
+ // Nested builders
+ //
+
+ abstract static class AbstractBuilder {
+ def EditPartSpecBuilder named(String editPartClassName) {
+ new EditPartRefBuilder(editPartClassName)
+ }
+
+ def EditPartSpecBuilder any() {
+ AnyEditPartBuilder.INSTANCE
+ }
+ }
+
+ abstract static class TestConstraintBuilder extends AbstractBuilder {
+ boolean isCritical
+
+ abstract package def void build(Model frameworkBase, GenEditorGenerator gmfgen, Collection<? super TestConstraint> products)
+
+ def void critical() {
+ isCritical = true
+ }
+
+ protected def configure(TestConstraint constraint) {
+ constraint.omitOnFailure = isCritical
+ }
+ }
+
+ abstract static class AbstractForbiddenEditPartsBuilder extends TestConstraintBuilder {
+ @Accessors @LiteralConstants(isStatic = false) ForbiddenReasonKind kind = ForbiddenReasonKind.INVALID
+ @Accessors String reason = "Capability not implemented."
+
+ protected def dispatch configure(ForbiddenEditPart constraint) {
+ super.configure(constraint)
+
+ constraint.reasonKind = kind
+ constraint.reason = reason
+ }
+
+ protected def dispatch configure(ForbiddenEditPartPermutation constraint) {
+ super.configure(constraint)
+
+ constraint.reasonKind = kind
+ constraint.reason = reason
+ }
+ }
+
+ static class ForbiddenEditPartBuilder extends AbstractForbiddenEditPartsBuilder {
+ @Accessors(PUBLIC_SETTER) EditPartSpecBuilder editPart
+
+ override package def build(Model frameworkBase, GenEditorGenerator gmfgen, Collection<? super TestConstraint> products) {
+ products += ExceptionsFactory.eINSTANCE.createForbiddenEditPart => [
+ configure
+
+ it.editPart = editPart.build(frameworkBase, gmfgen)
+ ]
+ }
+ }
+
+ static class ForbiddenEditPartPermutationBuilder extends AbstractForbiddenEditPartsBuilder {
+ static final List<String> DEFAULT_CONTEXTS = #['AbstractPapyrusTestCase', 'AbstractSynchronizationTest']
+
+ @Accessors Collection<String> testContexts = newDefaultingList
+ List<EditPartsBuilder> editParts = newArrayList
+
+ def editParts((EditPartsBuilder)=>void editPartsBuilder) {
+ val spec = new EditPartsBuilder
+ editPartsBuilder.apply(spec)
+ editParts += spec
+ }
+
+ override package def build(Model frameworkBase, GenEditorGenerator gmfgen, Collection<? super TestConstraint> products) {
+ products += editParts.map[leaves].flatten.map[builder |
+ ExceptionsFactory.eINSTANCE.createForbiddenEditPartPermutation => [
+ configure
+
+ it.editParts += builder.build(frameworkBase, gmfgen)
+
+ testClasses += (testContexts || DEFAULT_CONTEXTS).map[testContext |
+ frameworkBase.allOwnedElements.filter(Class).findFirst[name == testContext] => [
+ if (it == null) {
+ throw new IllegalArgumentException('No such abtract test context in the framework: ' + testContext)
+ }
+ ]
+ ].filterNull
+ ]
+ ]
+ }
+
+ public final def nodeAppearance() { 'AbstractAppearanceNodeTest'}
+ public final def createNode() { 'AbstractCreateNodeFromPaletteTest'}
+ public final def createLabel() { 'AbstractCreateChildLabelNodeFromPaletteTest'}
+ public final def createLink() { 'AbstractCreateLinkFromPaletteTest'}
+ public final def deleteNode() { 'AbstractDeleteNodeTest'}
+ public final def directEditNode() { 'AbstractEditableNodeTest'}
+ public final def dropNode() { 'AbstractDropNodeTest'}
+ public final def synchronization() { 'AbstractSynchronizationTest'}
+ }
+
+ static class EditPartsBuilder extends AbstractBuilder {
+ final EditPartRole roleToken = new EditPartRole(this)
+
+ List<EditPartSpecBuilder> editParts = newArrayList
+
+ EditPartsBuilder parent
+ List<EditPartsBuilder> children = newArrayList
+
+ new() {
+ this(null)
+ }
+
+ new(EditPartsBuilder parent) {
+ this.parent = parent
+ }
+
+ final def node() { roleToken }
+ final def topNode() { roleToken }
+ final def child() { roleToken }
+ final def label() { roleToken }
+ final def link() { roleToken }
+ final def source() { roleToken }
+ final def target() { roleToken }
+
+ /**
+ * Nested edit-part filters. Each nested group is combined with the edit-parts of the parent
+ * groups, in order from the root down.
+ */
+ def editParts((EditPartsBuilder)=>void editPartsBuilder) {
+ val spec = new EditPartsBuilder(this)
+ editPartsBuilder.apply(spec)
+ children += spec
+ }
+
+ /**
+ * Nested edit-part filters for link ends. Specify two filters (for source and targets ends), which generate
+ * for the {@link #source() source} role a pair with an {@link TestExceptionsBuilder.AbstractBuilder#any() any} {@link #target() target}
+ * and vice versa for the {@link #target() target} role.
+ */
+ def linkEnds((EditPartsBuilder)=>void editPartsBuilder) {
+ val spec = new EditPartsBuilder(this)
+ editPartsBuilder.apply(spec)
+ children += spec.forSource
+ children += spec.forTarget
+ }
+
+ package def Iterable<EditPartsBuilder> leaves() {
+ if (children.empty) #[this] else children.map[leaves].flatten.toList
+ }
+
+ package def build(Model frameworkBase, GenEditorGenerator gmfgen) {
+ allEditParts.map[build(frameworkBase, gmfgen)]
+ }
+
+ private def Iterable<EditPartsBuilder> parentChain() {
+ if (parent == null) #[this] else parent.parentChain + #[this]
+ }
+
+ private def allEditParts() {
+ if (parent == null) editParts else parentChain.map[editParts].flatten.toList
+ }
+
+ private def forSource() {
+ new EditPartsBuilder(parent) => [
+ it.editParts += this.editParts.get(0)
+ it.editParts += any
+ ]
+ }
+
+ private def forTarget() {
+ new EditPartsBuilder(parent) => [
+ it.editParts += any
+ it.editParts += this.editParts.get(1)
+ ]
+ }
+ }
+
+ static final class EditPartRole {
+ final EditPartsBuilder builder
+
+ package new(EditPartsBuilder builder) {
+ this.builder = builder
+ }
+
+ /** The {@code role} is only informative to the reader. */
+ def operator_add(EditPartSpecBuilder builder) {
+ this.builder.editParts += builder
+ this
+ }
+ }
+
+ static abstract class EditPartSpecBuilder {
+ abstract package def EditPartSpec build(Model frameworkBase, GenEditorGenerator gmfgen)
+
+ def EditPartSpecBuilder operator_and(EditPartSpecBuilder builder) {
+ composite(OperatorKind.AND, this, builder)
+ }
+
+ def EditPartSpecBuilder operator_or(EditPartSpecBuilder builder) {
+ composite(OperatorKind.OR, this, builder)
+ }
+
+ def EditPartSpecBuilder operator_not() {
+ composite(OperatorKind.NOT, this)
+ }
+
+ def EditPartSpecBuilder operator_and(String editPartClassName) {
+ operator_and(new EditPartRefBuilder(editPartClassName))
+ }
+
+ def EditPartSpecBuilder operator_or(String editPartClassName) {
+ operator_or(new EditPartRefBuilder(editPartClassName))
+ }
+
+ private def composite(OperatorKind operator, EditPartSpecBuilder... builders) {
+ new CompositeEditPartSpecBuilder(operator, builders)
+ }
+ }
+
+ private static class AnyEditPartBuilder extends EditPartSpecBuilder {
+ static final AnyEditPartBuilder INSTANCE = new AnyEditPartBuilder
+
+ override package EditPartSpec build(Model frameworkBase, GenEditorGenerator gmfgen) {
+ ExceptionsFactory.eINSTANCE.createAnyEditPart
+ }
+ }
+
+ private static class CompositeEditPartSpecBuilder extends EditPartSpecBuilder {
+ final OperatorKind operator
+ final List<EditPartSpecBuilder> editParts
+
+ private new(OperatorKind operator, EditPartSpecBuilder... builders) {
+ this.operator = operator
+ this.editParts = newArrayList(builders)
+ }
+
+ override operator_and(EditPartSpecBuilder builder) {
+ switch operator {
+ case OperatorKind.AND : this => [editParts += builder]
+ default: super.operator_and(builder)
+ }
+ }
+
+ override operator_or(EditPartSpecBuilder builder) {
+ switch operator {
+ case OperatorKind.OR : this => [editParts += builder]
+ default: super.operator_and(builder)
+ }
+ }
+
+ override package EditPartSpec build(Model frameworkBase, GenEditorGenerator gmfgen) {
+ ExceptionsFactory.eINSTANCE.createCompositeEditPartSpec => [
+ it.operator = operator
+ it.operands += editParts.map[build(frameworkBase, gmfgen)]
+ ]
+ }
+ }
+
+ private static class EditPartRefBuilder extends EditPartSpecBuilder {
+ final String editPartClassName
+
+ new(String editPartClassName) {
+ this.editPartClassName = editPartClassName
+ }
+
+ override package EditPartSpec build(Model frameworkBase, GenEditorGenerator gmfgen) {
+ ExceptionsFactory.eINSTANCE.createEditPartRef => [
+ it.editPart = gmfgen.eAllContents.filter(GenCommonBase).findFirst[it.editPartClassName == editPartClassName]
+
+ if (it.editPart == null) {
+ throw new IllegalArgumentException('No such edit part in the GMFGen model: ' + editPartClassName)
+ }
+ ]
+ }
+ }
+}

Back to the top