blob: c7dbf8ff2a92dc1fc9a4cc3ce490f72656cd7b17 [file] [log] [blame]
/*
* Copyright (c) 2013 CEA and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christian W. Damus (CEA) - initial API and implementation
*
*/
package org.eclipse.uml2.uml.bug.tests;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.AttributeOwner;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.InstanceValue;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.OperationOwner;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.TemplateableElement;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.tests.util.StandaloneSupport;
import org.eclipse.uml2.uml.util.UMLUtil;
import org.eclipse.uml2.uml.util.UMLValidator;
import org.eclipse.uml2.uml.util.UMLUtil.PackageMerger;
import org.eclipse.uml2.uml.util.UMLUtil.TemplateExpander;
/**
* Tests the template expander utility.
*
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=180744
*/
public class Bug180744Test
extends TestCase {
private ResourceSet rset;
private Package fixture;
public Bug180744Test() {
super();
}
public Bug180744Test(String name) {
super(name);
}
public static Test suite() {
return new TestSuite(Bug180744Test.class, "Bug 180744 tests"); //$NON-NLS-1$
}
/**
* Verify a classifier with a single template binding having all parameters
* explicitly substituted.
*/
public void testClassifierSingleBindingExplicit() {
Class listOfBars = expand(getClass("ListOfBars"));
assertCollectionKind(listOfBars, "sequence");
assertOperation(listOfBars, "add", "element", getClass("Bar"));
}
/**
* Verify a classifier with a single template binding having a defaulted
* parameter.
*/
public void testClassifierSingleBindingWithDefault() {
Class bagOfFoos = expand(getClass("BagOfFoos"));
assertCollectionKind(bagOfFoos, "bag"); // default enum literal sub
assertOperation(bagOfFoos, "remove", "element", getClass("Foo"));
}
/**
* Verify a classifier that has multiple bindings (to the same template, no
* less!).
*/
public void testClassifierMultipleBindings() {
Class bagListOfFooBars = expand(getClass("BagListOfFooBars"));
// multiple bindings generate an anonymous superclass each, so these
// features are not owned but inherited
assertTrue(bagListOfFooBars.getOwnedAttributes().isEmpty());
assertTrue(bagListOfFooBars.getOwnedOperations().isEmpty());
assertEquals(2, bagListOfFooBars.getGeneralizations().size());
Class general1 = (Class) bagListOfFooBars.getGeneralizations().get(0)
.getGeneral();
Class general2 = (Class) bagListOfFooBars.getGeneralizations().get(1)
.getGeneral();
assertCollectionKind(general1, "sequence");
assertOperation(general1, "add", "element", getClass("Bar"));
assertCollectionKind(general2, "bag"); // default enum literal sub
assertOperation(general2, "remove", "element", getClass("Foo"));
}
/**
* Verify a package that has a single binding with actual parameters that
* are owned classes and defaulted name-expression parameters.
*/
public void testPackageSingleBindingWithOwnedActualParametersAndDefaultNameParts() {
Package rentalSystem = expand(getPackage("rental system"));
Class car = getClass(rentalSystem, "Car");
Class carSpec = getClass(rentalSystem, "CarSpec");
assertProperty(car, "kind", carSpec);
assertProperty(car, "mileage", "Integer"); // not from the template
assertProperty(carSpec, "resource", car);
assertProperty(carSpec, "model", "String"); // not from the template
// the default name-expression part is an empty string replacing
// the '${ResourceKind}' part in '${ResourceKind}Allocation' and
// '${ResourceKind}Request'
Class allocation = getClass(rentalSystem, "Allocation");
Class request = getClass(rentalSystem, "Request");
assertProperty(allocation, "resource", car);
assertProperty(request, "kind", carSpec);
// the anonymous associations are not all merged together
assertAssociation(rentalSystem, car, carSpec);
assertAssociation(rentalSystem, allocation, car);
assertAssociation(rentalSystem, request, carSpec);
}
/**
* Verify a package that has multiple bindings to the same template, using
* name expressions to ensure that classes substituting for template
* parameters are expanded with distinct names so that they are not merged
* together.
*/
public void testPackageMultipleBindingWithNameExpressionsHavingDistinctSubstitutions() {
Package deploymentSystem = expand(getPackage("deployment system"));
Class softwarePackage = getClass(deploymentSystem, "SoftwarePackage");
Class softwareSpec = getClass(deploymentSystem, "SoftwareSpec");
assertProperty(softwarePackage, "kind", softwareSpec);
assertProperty(softwarePackage, "id", "Integer"); // not from the
// template
assertProperty(softwareSpec, "resource", softwarePackage);
assertProperty(softwareSpec, "version", "String"); // not from the
// template
// the actual name-expression part is 'Software' replacing
// the '${ResourceKind}' part in '${ResourceKind}Allocation' and
// '${ResourceKind}Request'
Class softwareAllocation = getClass(deploymentSystem,
"SoftwareAllocation");
Class softwareRequest = getClass(deploymentSystem, "SoftwareRequest");
assertProperty(softwareAllocation, "resource", softwarePackage);
assertProperty(softwareRequest, "kind", softwareSpec);
// the anonymous associations are not all merged together
assertAssociation(deploymentSystem, softwarePackage, softwareSpec);
assertAssociation(deploymentSystem, softwareAllocation, softwarePackage);
assertAssociation(deploymentSystem, softwareRequest, softwareSpec);
Class hardwareComponent = getClass(deploymentSystem,
"HardwareComponent");
Class hardwareSpec = getClass(deploymentSystem, "HardwareSpec");
assertProperty(hardwareComponent, "kind", hardwareSpec);
assertProperty(hardwareComponent, "id", "Integer"); // not from the
// template
assertProperty(hardwareSpec, "resource", hardwareComponent);
assertProperty(hardwareSpec, "deviceName", "String"); // not from the
// template
// the actual name-expression part is 'Hardware' replacing
// the '${ResourceKind}' part in '${ResourceKind}Allocation' and
// '${ResourceKind}Request'
Class hardwareAllocation = getClass(deploymentSystem,
"HardwareAllocation");
Class hardwareRequest = getClass(deploymentSystem, "HardwareRequest");
assertProperty(hardwareAllocation, "resource", hardwareComponent);
assertProperty(hardwareRequest, "kind", hardwareSpec);
// the anonymous associations are not all merged together
assertAssociation(deploymentSystem, hardwareComponent, hardwareSpec);
assertAssociation(deploymentSystem, hardwareAllocation,
hardwareComponent);
assertAssociation(deploymentSystem, hardwareRequest, hardwareSpec);
}
/**
* Verifies that stereotype applications are copied from templates to the
* bound elements.
*/
public void testStereotypesOnTemplates() {
Package rentalSystem = getPackage("rental system");
Class originalCar = getClass(rentalSystem, "Car");
assertEquals(0, originalCar.getAppliedStereotypes().size());
expand(rentalSystem);
Class car = getClass(rentalSystem, "Car");
assertSame(originalCar, car); // the class hasn't been replaced
assertEquals(1, car.getAppliedStereotypes().size());
assertEquals("Limited", car.getAppliedStereotypes().get(0).getName());
}
public void testMissingParameterSubstitutions() {
BasicDiagnostic diagnostic = new BasicDiagnostic();
Class badCollection = expand(getClass("BadCollection"), diagnostic);
// default enum literal substitution
assertCollectionKind(badCollection, "bag");
// contents are copied, but with the unsubstituted template parameter
Class template = getClass("Collection");
Classifier parameterE = (Classifier) template
.getOwnedTemplateSignature().getParameters().get(0)
.getOwnedParameteredElement();
assertOperation(badCollection, "remove", "element", parameterE);
// but there was no substitution for E
assertDiagnostic(diagnostic,
TemplateExpander.MISSING_PARAMETER_SUBSTITUTION);
}
public void testUsualMergeCapabilityTracesPackage() {
Package rentalSystem = expand(getPackage("rental system"));
Package system = getPackage("system");
// a parametered element
Class car = getClass(rentalSystem, "Car");
assertCapabilityTrace(rentalSystem, system, car);
// not a parametered element
Class request = getClass(rentalSystem, "Request");
assertCapabilityTrace(rentalSystem, system, request);
}
public void testUsualMergeCapabilityTracesClassifier() {
Class listOfBars = expand(getClass("ListOfBars"));
Class collection = getClass("Collection");
Operation remove = getOperation(listOfBars, "remove");
assertCapabilityTrace(listOfBars, collection, remove);
}
public void testMergeCapabilityTracesClassifierMultipleBindings() {
Class bagListOfFooBars = expand(getClass("BagListOfFooBars"));
Class general1 = (Class) bagListOfFooBars.getNestedClassifiers().get(0);
Class general2 = (Class) bagListOfFooBars.getNestedClassifiers().get(1);
Class collection = getClass("Collection");
// the traces are in the expanded template that owns the nested
// anonymous superclasses
Operation remove = getOperation(general1, "remove");
assertCapabilityTrace(bagListOfFooBars, collection, remove);
Property kind = getProperty(general2, "kind");
assertCapabilityTrace(bagListOfFooBars, collection, kind);
}
public void testMergeCapabilityTracesPackageMultipleBindings() {
Package deploymentSystem = expand(getPackage("deployment system"));
Package system = getPackage("system");
// a class defined in the bound template that substitutes for a
// parameter in the template
Class softwareSpec = (Class) deploymentSystem
.getOwnedType("SoftwareSpec");
// a class defined solely in the template and merged into the bound
// template
Class hardwareRequest = (Class) deploymentSystem
.getOwnedType("HardwareRequest");
assertCapabilityTrace(deploymentSystem, system, softwareSpec);
assertCapabilityTrace(deploymentSystem, system, hardwareRequest);
}
//
// Test framework
//
@Override
protected void setUp()
throws Exception {
rset = new ResourceSetImpl();
if (StandaloneSupport.isStandalone()) {
StandaloneSupport.init(rset);
}
fixture = getTestModel();
}
@Override
protected void tearDown()
throws Exception {
fixture = null;
// clean up the CacheAdapter as well as we can
for (Resource next : rset.getResources()) {
next.unload();
next.eAdapters().clear();
}
rset.getResources().clear();
rset.eAdapters().clear();
}
Class getClass(String name) {
return getClass(fixture, name);
}
Class getClass(Package package_, String name) {
return (Class) package_.getOwnedType(name);
}
Package getPackage(String name) {
return fixture.getNestedPackage(name);
}
Package getTestModel() {
URL url = getClass().getResource("Bug180744.uml"); //$NON-NLS-1$
return (Package) UML2Util.load(rset,
URI.createURI(url.toExternalForm()), UMLPackage.Literals.PACKAGE);
}
Property getProperty(AttributeOwner owner, String name) {
return owner.getOwnedAttribute(name, null);
}
Operation getOperation(OperationOwner owner, String name) {
return owner.getOwnedOperation(name, null, null);
}
<T extends TemplateableElement> T expand(T boundElement) {
assertFalse(boundElement.getTemplateBindings().isEmpty());
Map<String, String> options = new HashMap<String, String>();
options
.put(PackageMerger.OPTION__CAPABILITIES, UMLUtil.OPTION__PROCESS);
UMLUtil.expand(boundElement, options);
assertExpanded(boundElement);
return boundElement;
}
<T extends TemplateableElement> T expand(T boundElement,
DiagnosticChain diagnostics) {
assertFalse(boundElement.getTemplateBindings().isEmpty());
Map<String, String> options = new HashMap<String, String>();
options
.put(PackageMerger.OPTION__CAPABILITIES, UMLUtil.OPTION__PROCESS);
options.put(TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS,
UMLUtil.OPTION__REPORT);
UMLUtil.expand(boundElement, options, diagnostics, null);
assertExpanded(boundElement);
return boundElement;
}
void assertExpanded(TemplateableElement element) {
assertEquals(0, element.getTemplateBindings().size());
}
void assertCollectionKind(Class collectionType, String kindLiteral) {
Enumeration kindEnum = (Enumeration) fixture
.getOwnedType("CollectionKind");
EnumerationLiteral literal = kindEnum.getOwnedLiteral(kindLiteral);
assertNotNull(literal);
Property kind = collectionType.getOwnedAttribute("kind", kindEnum);
assertNotNull(kind);
assertTrue(kind.isStatic());
assertTrue(kind.getDefaultValue() instanceof InstanceValue);
assertSame(literal,
((InstanceValue) kind.getDefaultValue()).getInstance());
}
void assertProperty(Class class_, String name, Type type) {
Property property = class_.getOwnedAttribute(name, type);
assertNotNull(property);
}
void assertProperty(Class class_, String name, String type) {
Property property = class_.getOwnedAttribute(name, null);
assertNotNull(property);
assertNotNull(property.getType());
assertEquals(type, property.getType().getName());
}
void assertOperation(Class class_, String name, String param1Name,
Type param1Type) {
Operation operation = class_.getOwnedOperation(name,
ECollections.singletonEList(param1Name),
ECollections.singletonEList(param1Type));
assertNotNull(operation);
}
void assertAssociation(Package package_, Type endType1, Type endType2) {
Association found = null;
for (Type next : package_.getOwnedTypes()) {
if (next instanceof Association) {
Association association = (Association) next;
if (association.isBinary()
&& association.getEndTypes().contains(endType1)
&& association.getEndTypes().contains(endType2)) {
found = association;
break;
}
}
}
assertNotNull(found);
}
void assertCapabilityTrace(Element receivingElement,
NamedElement capability,
Element capabilityElement) {
EAnnotation annotation = receivingElement
.getEAnnotation(UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI);
assertNotNull(annotation);
annotation = annotation.getEAnnotation(capability.getQualifiedName());
assertNotNull(annotation);
if (capabilityElement == null) {
assertTrue(annotation.getReferences().contains(capabilityElement));
}
}
void assertDiagnostic(Diagnostic diagnostics, int code) {
for (Diagnostic child : diagnostics.getChildren()) {
if (UMLValidator.DIAGNOSTIC_SOURCE.equals(child.getSource())
&& (child.getCode() == code)) {
// found it
return;
}
}
fail("Did not find diagnostic with code " + code);
}
}