package com.sap.ap.metamodel.test;
import java.util.Collection;
import junit.framework.TestCase;
import modelmanagement.Package;
import behavioral.BehavioralPackage;
import behavioral.actions.Block;
import com.sap.ap.metamodel.utils.MetamodelUtils;
import com.sap.tc.moin.repository.Connection;
import com.sap.tc.moin.repository.Moin;
import com.sap.tc.moin.repository.events.PreChangeListener;
import com.sap.tc.moin.repository.events.filter.AssociationFilter;
import com.sap.tc.moin.repository.events.type.ChangeEvent;
import com.sap.tc.moin.repository.events.type.LinkRemoveEvent;
import com.sap.tc.moin.repository.ide.MoinFactory;
import com.sap.tc.moin.repository.mmi.model.MofClass;
import com.sap.tc.moin.repository.mmi.reflect.ConstraintViolationException;
import com.sap.tc.moin.repository.mmi.reflect.JmiException;
import data.DataPackage;
import data.classes.Association;
import data.classes.AssociationEnd;
import data.classes.ClassesPackage;
import data.classes.LinkSetting;
import data.classes.LinkTraversal;
import data.classes.MethodSignature;
import data.classes.Parameter;
import data.classes.SapClass;
import data.classes.TypeAdapter;
public class MetamodelTests extends TestCase {
private Moin moin;
private Connection conn;
public void setUp() {
moin = MoinFactory.getMoinInstance();
conn = createConnection();
}
public void tearDown() {
createConnection().close();
}
public void testMetamodelAvailability() {
MofClass typeAdapter = getClassesPackage().getTypeAdapter().refMetaObject();
assertEquals("TypeAdapter", typeAdapter.getName());
}
public void testLinkRemoveEventUponElementDelete() {
final boolean[] ok = new boolean[1];
final SapClass clazz = (SapClass) conn.getClass(SapClass.CLASS_DESCRIPTOR).refCreateInstance();
final TypeAdapter ta = (TypeAdapter) conn.getClass(TypeAdapter.CLASS_DESCRIPTOR).refCreateInstance();
conn.getSession().getEventRegistry().registerPreChangeListener(
new PreChangeListener() {
@Override
public void prepare(ChangeEvent event) {
if (event instanceof LinkRemoveEvent) {
LinkRemoveEvent e = (LinkRemoveEvent) event;
ok[0] = ok[0] || e.getFirstLinkEnd(conn).equals(clazz) &&
e.getSecondLinkEnd(conn).equals(ta);
}
}
}, new AssociationFilter(
getClassesPackage().getAdaptedTo().refMetaObject()));
ta.setTo(clazz);
ta.refDelete();
// If this test fails, check if in the target IDE there is an editor or the Runlet console open;
// this may turn off events during creating a deep copy / snapshot of the object under edit
// which temporarily disables the event handling required / tested here
assertTrue(ok[0]);
}
/**
* Asserts that two types don't just conform based on their signature structures
*/
public void testNoStructuralConformance() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
assertFalse("Two classes that don't have an adapter between them must not conform",
c1.conformsTo(c2));
}
/**
* Asserts that two types conform if there is one adapter between them
*/
public void testConformanceWithOneAdapter() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setAdapted(c1);
adapter.setTo(c2);
assertTrue("Classes with no signatures and an adapter between them should conform",
c1.conformsTo(c2));
}
/**
* Asserts that two types conform if there is one adapter between them
*/
public void testConformanceWithSignaturesAndOneAdapter() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
MethodSignature s1 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s1.setName("someSignature");
c1.getOwnedSignatures().add(s1);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
MethodSignature s2 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("someSignature");
c2.getOwnedSignatures().add(s2);
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setName("FromC1ToC2");
adapter.setAdapted(c1);
adapter.setTo(c2);
Collection<JmiException> exceptions = adapter.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be valid but has "+
((exceptions==null)?0:exceptions.size())+" violated constraints",
exceptions == null || exceptions.size() == 0);
assertTrue("Classes with no signatures and an adapter between them should conform",
c1.conformsTo(c2));
}
/**
* Check that adapter invariants are really enforced
*/
public void testAdapterInvariantsEnforcement() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
MethodSignature s1 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s1.setName("s1");
c1.getOwnedSignatures().add(s1);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
MethodSignature s2 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("s2");
c2.getOwnedSignatures().add(s2);
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setName("FromC1ToC2");
adapter.setAdapted(c1);
adapter.setTo(c2);
Collection<JmiException> exceptions = adapter.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be considered invalid and should violate the IsFullAdaptationToTo constraint",
exceptions.size() == 1 && ((ConstraintViolationException) exceptions.iterator().next()).getMessage().equals(
"[data, classes, TypeAdapter, IsFullAdaptationToTo]"));
}
/**
* Check that the constraint works that requires an association between two value classes to
* have at least one non-equality-relevant end
*/
public void testDoubleEqualityRelevantValueClassAssociation() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
c1.setValueType(true);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
c2.setValueType(true);
Association a = MetamodelUtils.createAssociation(conn, c1, "c1", 0, 1, true, false, false, false, c2, "c2", 0, 1, true, false, false, false);
a.setName("a");
modelmanagement.Package p = (Package) conn.getClass(modelmanagement.Package.CLASS_DESCRIPTOR).refCreateInstance();
p.setName("p");
c1.setPackage(p);
c2.setPackage(p);
p.getAssociations().add(a);
for (AssociationEnd ae:a.getEnds()) {
ae.setContributesToEquality(true);
}
Collection<JmiException> exceptions = a.refVerifyConstraints(/*deepVerify*/false);
assertEquals("Expected exactly one constraint violation", 1, exceptions.size());
assertEquals("Associations between two value types that affect equality of both types are currently not supported. It would lead to a recursive equality definition and therefore to recursive value constructors.",
((ConstraintViolationException) exceptions.iterator().next()).getMessage());
}
/**
* Check that no modifying feature of an association end can be exposed if that
* would modify a value
*/
public void testValueImmutability() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1Value");
c1.setValueType(true);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2Entity");
c2.setValueType(false);
Association a = MetamodelUtils.createAssociation(conn, c1, "c1", 0, 1, true, false, false, false, c2, "c2", 0, 1, true, false, false, false);
a.setName("a");
modelmanagement.Package p = (Package) conn.getClass(modelmanagement.Package.CLASS_DESCRIPTOR).refCreateInstance();
p.setName("p");
c1.setPackage(p);
c2.setPackage(p);
p.getAssociations().add(a);
assertEquals("c1", a.getEnds().get(0).getName());
a.getEnds().get(0).setContributesToEquality(true);
// expose eqality-relevant association as getter (required!)
MethodSignature getter = (MethodSignature) conn.getClass(MethodSignature.CLASS_DESCRIPTOR).refCreateInstance();
getter.setName("getC2");
getter.setOutput(a.getEnds().get(1).getType());
LinkTraversal linkTraversal = (LinkTraversal) conn.getClass(LinkTraversal.CLASS_DESCRIPTOR).refCreateInstance();
linkTraversal.setEnd(a.getEnds().get(1));
linkTraversal.setImplements(getter);
c1.getOwnedSignatures().add(getter);
Collection<JmiException> exceptions = a.refVerifyConstraints(/*deepVerify*/true);
assertNull(exceptions);
Collection<JmiException> exceptions2 = c1.refVerifyConstraints(/*deepVerify*/true);
assertNull(exceptions2);
Collection<JmiException> exceptions3 = c2.refVerifyConstraints(/*deepVerify*/true);
assertNull(exceptions3);
// expose equality-relevant association as setter (illegal!)
MethodSignature setter = (MethodSignature) conn.getClass(MethodSignature.CLASS_DESCRIPTOR).refCreateInstance();
setter.setName("setC2");
LinkSetting linkSetting = (LinkSetting) conn.getClass(LinkSetting.CLASS_DESCRIPTOR).refCreateInstance();
linkSetting.setEnd(a.getEnds().get(1));
linkSetting.setImplements(setter);
Parameter setterParam = (Parameter) conn.getClass(Parameter.CLASS_DESCRIPTOR).refCreateInstance();
setterParam.setName("c2Param");
setter.getInput().add(setterParam);
c1.getOwnedSignatures().add(setter);
Collection<JmiException> exceptions4 = c1.refVerifyConstraints(/*deepVerify*/true);
assertEquals("Expected exactly one constraint violation", 1, exceptions4.size());
assertEquals("[data, classes, ExtentModifyingAssociationEndSignatureImplementation, MustNotModifyExtentIfEqualityRelevantForValueClass]",
((ConstraintViolationException) exceptions4.iterator().next()).getMessage());
}
/**
* Check that adapter invariants are really enforced
*/
public void testEnforcementNoAbstractSignaturesInAdapter() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
MethodSignature s1 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s1.setName("s1");
c1.getOwnedSignatures().add(s1);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
MethodSignature s2 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("s2");
c2.getOwnedSignatures().add(s2);
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setName("FromC1ToC2");
adapter.setAdapted(c1);
adapter.setTo(c2);
MethodSignature adapterSig = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
adapterSig.setName("s2");
adapter.getOwnedSignatures().add(adapterSig);
Collection<JmiException> exceptions = adapter.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be considered invalid and should violate the SignatureCannotBeAbstract constraint",
exceptions.size() == 1 && ((ConstraintViolationException) exceptions.iterator().next()).getMessage().equals(
"Signatures can not be abstract"));
}
/**
* Check that adapter invariants are really enforced
*/
public void testConformanceWithoutSignatureAdaptation() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
MethodSignature s1 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s1.setName("s1");
c1.getOwnedSignatures().add(s1);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
MethodSignature s2 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("s2");
c2.getOwnedSignatures().add(s2);
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setName("FromC1ToC2");
adapter.setAdapted(c1);
adapter.setTo(c2);
MethodSignature adapterSig = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
adapterSig.setName("s2");
Block impl = (Block) ((BehavioralPackage) conn.getPackage(/*containerName*/ null, "behavioral")).getActions().
getBlock().refCreateInstance();
impl.setImplements(adapterSig);
adapter.getOwnedSignatures().add(adapterSig);
Collection<JmiException> exceptions = adapter.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be valid but has "+
((exceptions==null)?0:exceptions.size())+" violated constraints",
exceptions == null || exceptions.size() == 0);
assertTrue("Classes with no signatures and an adapter between them should conform",
c1.conformsTo(c2));
}
/**
* Check that adapter invariants are really enforced
*/
public void testConformanceAlongAdapterChain() {
ClassesPackage classesPackage = getClassesPackage();
SapClass c1 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c1.setName("C1");
MethodSignature s1 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s1.setName("s1");
c1.getOwnedSignatures().add(s1);
SapClass c2 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C2");
MethodSignature s2 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("s2");
c2.getOwnedSignatures().add(s2);
TypeAdapter adapter = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter.setName("FromC1ToC2");
adapter.setAdapted(c1);
adapter.setTo(c2);
MethodSignature adapterSig = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
adapterSig.setName("s2");
Block impl = (Block) ((BehavioralPackage) conn.getPackage(/*containerName*/ null, "behavioral")).getActions().
getBlock().refCreateInstance();
impl.setImplements(adapterSig);
adapter.getOwnedSignatures().add(adapterSig);
SapClass c3 = (SapClass) classesPackage.getSapClass().refCreateInstance();
c2.setName("C3");
MethodSignature s3 = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
s2.setName("s3");
c3.getOwnedSignatures().add(s3);
TypeAdapter adapter2 = (TypeAdapter) classesPackage.getTypeAdapter().refCreateInstance();
adapter2.setName("FromC2ToC3");
adapter2.setAdapted(c2);
adapter2.setTo(c3);
MethodSignature adapter2Sig = (MethodSignature) classesPackage.getMethodSignature().refCreateInstance();
adapterSig.setName("s3");
Block impl2 = (Block) ((BehavioralPackage) conn.getPackage(/*containerName*/ null, "behavioral")).getActions().
getBlock().refCreateInstance();
impl2.setImplements(adapter2Sig);
adapter2.getOwnedSignatures().add(adapter2Sig);
Collection<JmiException> exceptions = adapter.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be valid but has "+
((exceptions==null)?0:exceptions.size())+" violated constraints",
exceptions == null || exceptions.size() == 0);
Collection<JmiException> exceptions2 = adapter2.refVerifyConstraints(/*deepVerify*/false);
assertTrue("Adapter should be valid but has "+
((exceptions2==null)?0:exceptions2.size())+" violated constraints",
exceptions2 == null || exceptions2.size() == 0);
assertTrue("Classes with no signatures and an adapter between them should conform",
c1.conformsTo(c3));
}
/**
* Obtains a connection that is good only for reading from this facility and
* in particular from all metamodels. It is obtained by setting up a new session
* with a compound client spec containing only one client spec for a "transient"
* data area for this facility and then obtaining a connection to that session.
*/
private Connection createConnection() {
return getMoin().createSession(getMoin().getCompoundDataAreaManager().
getCompoundClientSpecTransientOnly()).createConnection();
}
private Moin getMoin() {
return moin;
}
private ClassesPackage getClassesPackage() {
return conn.getPackage(DataPackage.PACKAGE_DESCRIPTOR).getClasses();
}
}