blob: 4ddfe207ed0e5a26ef1bb9277cd83ba0c1814559 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* SAP AG - initial API and implementation
******************************************************************************/
package org.eclipse.ocl.examples.impactanalyzer.tests.instanceScope;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.examples.impactanalyzer.ImpactAnalyzer;
import org.eclipse.ocl.examples.impactanalyzer.ImpactAnalyzerFactory;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.model.ModelCloner;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.notifications.NotificationResourceLoader;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.notifications.RawNotification;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.ocl.OCLExpressionFromClassTcsPicker;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.ocl.OCLExpressionWithContext;
import org.eclipse.ocl.examples.impactanalyzer.configuration.OptimizationActivation;
import org.eclipse.ocl.examples.impactanalyzer.instanceScope.traceback.AbstractTracebackStep;
import org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory;
import org.eclipse.ocl.examples.testutils.BaseTest;
import org.junit.Test;
import data.classes.ClassTypeDefinition;
import data.classes.ClassesFactory;
import data.classes.ClassesPackage;
import data.classes.MethodSignature;
import data.classes.SapClass;
import dataaccess.expressions.ExpressionsPackage;
import dataaccess.expressions.MethodCallExpression;
public class NgpmModelBasedOclIaTest extends BaseTest {
Resource ngpmModel = null;
List<RawNotification> modifyElementaryTypesTrace = null;
@Override
public void setUp(){
ngpmModel = NotificationResourceLoader.loadModel("NgpmModel.xmi");
modifyElementaryTypesTrace = NotificationResourceLoader.loadTrace("modifyElementaryTypesEventTrace.trace");
}
@Test
public void testRenameStringAppendToAppend2() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context dataaccess::expressions::MethodCallExpression inv: "
+ "self.object.getType().getInnermost().oclAsType(classes::ClassTypeDefinition).clazz.allSignatures()->select(s : MethodSignature | s.name='.'.concat('xxx'))",
ClassesPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final MethodSignature append = (MethodSignature) ngpmModel.getEObject("E01F04667A9220905D0911DFA13BFF380A1CE22F");
assertEquals("append", append.getName());
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(
exp, /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
append.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
if (OptimizationActivation.getOption().isOperationCallSelectionActive()) {
result[0] = impact.size() > 5 && impact.size() < 10;
if (!result[0]) {
System.err
.println("Expected between 5 and 10 impacted MethodCallExpressions only for calls on string but found "
+ impact.size());
}
} else {
result[0] = impact.size() > 50 && impact.size() < 100;
if (!result[0]) {
System.err.println("Expected between 50 and 100 impacted MethodCallExpressions but found only "
+ impact.size());
}
}
}
});
append.setName("append2");
assertTrue(result[0]);
}
@Test
public void testVariableExpressionWithCollectionType() {
OCLExpression exp = (OCLExpression) OclIaTest.parse("context NestedTypeDefinition inv: self.getNamedValuesInScope()", ClassesPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
RawNotification rawNotification = modifyElementaryTypesTrace.get(1);
Notification notification = rawNotification.convertToNotification(ModelCloner.cloneResource(ngpmModel,"1"));
ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp,
data.classes.ClassesPackage.eINSTANCE.getNestedTypeDefinition(),
/* notifyOnNewContextElements */ false, OCLFactory.getInstance());
Collection<EObject> impact = ia.getContextObjects(notification);
assertEquals(impact.size(), 0);
}
public void testExpressionWithLargeNavigationStepGraph(){
OCLExpressionWithContext expr = new OCLExpressionFromClassTcsPicker().pickUpExpression(44);
Notification notification = getNotification(10, ngpmModel);
ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(expr.getExpression(), expr.getContext(),/* notifyOnNewContextElements */ false, OCLFactory.getInstance());
Collection<EObject> impact = ia.getContextObjects(notification);
assertNotNull(impact);
}
@Test
public void testSuperExpensiveChange() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context dataaccess::expressions::MethodCallExpression inv: "
+ "self.object.getType().getInnermost().oclAsType(classes::ClassTypeDefinition).clazz.allSignatures()->select(s : MethodSignature | s.name='.'.concat('xxx'))",
ClassesPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final SapClass string = (SapClass) ngpmModel.getEObject("E0B91841F0303550560511DECC310019D29902CC");
final MethodSignature append = (MethodSignature) ngpmModel.getEObject("E01F04667A9220905D0911DFA13BFF380A1CE22F");
final ClassTypeDefinition appendParamCTD = (ClassTypeDefinition) append.getInput().get(0).getOwnedTypeDefinition();
assertEquals(string, appendParamCTD.getClazz());
assertEquals("append", append.getName());
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp, /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
appendParamCTD.setClazz(null);
appendParamCTD.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
result[0] = impact.size() == 0;
if (!result[0]) {
System.err.println("Expected unused check to find out that change has no impact but IA said " + impact.size()
+ " objects were impacted");
}
}
});
appendParamCTD.setClazz(null);
assertTrue(result[0]);
}
@Test
public void testAllSignaturesDependencyOnChangingSomethingOnStringClass() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context SapClass inv: " + "self.allSignatures()",
ClassesPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final SapClass string = (SapClass) ngpmModel.getEObject("E0B91841F0303550560511DECC310019D29902CC");
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp, data.classes.ClassesPackage.eINSTANCE.getSapClass(), /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
string.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
result[0] = impact.size() == 2 && impact.contains(string);
}
});
string.getOwnedSignatures().add(ClassesFactory.eINSTANCE.createMethodSignature());
assertTrue(result[0]);
}
@Test
public void testExpensiveAllSignaturesExpressionAddingMethodSignatureToString() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context MethodCallExpression inv: " +
"self.object.getType().getInnermost().oclAsType(data::classes::ClassTypeDefinition).clazz.allSignatures()",
ExpressionsPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final SapClass string = (SapClass) ngpmModel.getEObject("E0B91841F0303550560511DECC310019D29902CC");
final MethodCallExpression callOnStringTypedExpression = (MethodCallExpression) ngpmModel.getEObject("E02C978BFD3F74805D0811DF8A6AFF380A1CE22F");
final EList<MethodSignature> oldValue = ((ClassTypeDefinition) callOnStringTypedExpression.getObject().getType().getInnermost()).getClazz().allSignatures();
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp, /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
string.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
result[0] = impact.contains(callOnStringTypedExpression);
if (!result[0]) {
System.err
.println("Expecting a method call on a string-typed expression to be impacted by adding a signature to String");
} else {
if (OptimizationActivation.getOption().isOperationCallSelectionActive()) {
result[0] = impact.size() > 5 && impact.size() < 10;
if (!result[0]) {
System.err
.println("Expected between 5 and 10 impacted MethodCallExpressions only for calls on string but found "
+ impact.size());
}
} else {
result[0] = impact.size() > 50 && impact.size() < 100;
if (!result[0]) {
System.err.println("Expected between 50 and 100 impacted MethodCallExpressions but found only "
+ impact.size());
}
}
Iterator<EObject> ii = impact.iterator();
while (result[0] && ii.hasNext()) {
EObject n = ii.next();
result[0] = result[0] && (n instanceof MethodCallExpression);
if (!result[0]) {
System.err
.println("Found an impacted object that was not, as expected, a MethodCallExpression: " + n);
}
if (OptimizationActivation.getOption().isOperationCallSelectionActive()) {
// can enforce result type only if selective operation call traceback is active
// because traceback visits OperationCallExp of getType() that are
// not actually used and therefore returns a true superset; activate the following check if crisp
// operation call
// checking is performed
result[0] = result[0]
&& (((ClassTypeDefinition) (((MethodCallExpression) n).getObject().getType().getInnermost()))
.getClazz().conformsTo(string));
if (!result[0]) {
System.err
.println("Found an impacted MethodCallExpression that is not called on an object of type String but on "
+ (((ClassTypeDefinition) (((MethodCallExpression) n).getObject().getType()
.getInnermost())).getClazz().getName())
+ ": "
+ ((MethodCallExpression) n).getMethodSignature().getName());
}
}
}
}
}
});
string.getOwnedSignatures().add(ClassesFactory.eINSTANCE.createMethodSignature());
EList<MethodSignature> newValue = ((ClassTypeDefinition) callOnStringTypedExpression.getObject().getType().getInnermost()).getClazz().allSignatures();
assertFalse("Expecting a method call on a string-typed expression to be impacted by adding a signature to String",
newValue.equals(oldValue));
assertTrue(result[0]);
}
@Test
public void testUnusedForAdaptedSignaturesExpressionNullingClazzReferenceOnCTD() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context SapClass inv: " +
"self.adaptedSignatures()",
ClassesPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final SapClass string = (SapClass) ngpmModel.getEObject("E0B91841F0303550560511DECC310019D29902CC");
final MethodSignature append = (MethodSignature) ngpmModel.getEObject("E01F04667A9220905D0911DFA13BFF380A1CE22F");
final ClassTypeDefinition appendOutputCTD = (ClassTypeDefinition) append.getOutput();
assertEquals(string, appendOutputCTD.getClazz());
assertEquals("append", append.getName());
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp, data.classes.ClassesPackage.eINSTANCE.getSapClass(), /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
appendOutputCTD.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
if (OptimizationActivation.getOption().isTracebackStepISAActive() && OptimizationActivation.getOption().isUnusedDetectionActive()) {
result[0] = impact.size() == 0;
if (!result[0]) {
System.err.println("Expected unused check to find out that change has no impact but IA said "+impact.size()+
" objects were impacted");
}
} else {
result[0] = impact.size() == 2; // String and the formal type parameter "T extends String" in the generics test
if (!result[0]) {
System.err.println("Expected two impacted classes (String and T) but found "+impact.size());
}
}
}
});
appendOutputCTD.setClazz(null);
assertTrue(result[0]);
}
@Test
public void testUnusedForAllSignaturesExpressionNullingClazzReferenceOnCTD() {
final OCLExpression exp = (OCLExpression) OclIaTest
.parse("context MethodCallExpression inv: " +
"self.object.getType().getInnermost().oclAsType(data::classes::ClassTypeDefinition).clazz.allSignatures()",
ExpressionsPackage.eINSTANCE).iterator().next().getSpecification().getBodyExpression();
final SapClass string = (SapClass) ngpmModel.getEObject("E0B91841F0303550560511DECC310019D29902CC");
// would have to be a call on the output of append()
final MethodCallExpression callOnAppendCallResult = (MethodCallExpression) ngpmModel.getEObject("E012BF1E3D01FCF05D0E11DF9483DFF10A1CE22F");
callOnAppendCallResult.getObject().setOwnedTypeDefinition(null); // force append's output type definition to be used in call's getType()
final Object oldValue = org.eclipse.ocl.examples.impactanalyzer.util.OCL.newInstance().evaluate(callOnAppendCallResult, exp);
final MethodSignature append = (MethodSignature) ngpmModel.getEObject("E01F04667A9220905D0911DFA13BFF380A1CE22F");
final ClassTypeDefinition appendOutputCTD = (ClassTypeDefinition) append.getOutput();
assertEquals(string, appendOutputCTD.getClazz());
assertEquals("append", append.getName());
final ImpactAnalyzer ia = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(exp, /* notifyOnNewContextElements */ false, OCLFactory.getInstance());
final boolean[] result = new boolean[1];
appendOutputCTD.eAdapters().add(new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
Collection<EObject> impact = ia.getContextObjects(msg);
if (!impact.contains(callOnAppendCallResult)) {
System.err.println("Expecting a method call on a string-typed expression to be impacted by adding a signature to String");
result[0] = false;
} else {
if (OptimizationActivation.getOption().isTracebackStepISAActive()) {
result[0] = impact.size() > 5 && impact.size() < 10;
if (!result[0]) {
System.err.println("Expected unused check to find between 5 and 10 changed but IA said "
+ impact.size() + " objects were impacted");
}
} else {
result[0] = impact.size() > 50;
if (!result[0]) {
System.err
.println("Expected more than 50 method calls to be impacted without unused check but found only "
+ impact.size());
}
}
}
}
});
appendOutputCTD.setClazz(null);
assertFalse(oldValue instanceof DynamicEObjectImpl);
assertNull("Expecting a method call on the result of String.append() with no ownedTypeDefinition on that call to me impacted "+
"by setting String.append()'s output type definition's clazz to null",
((ClassTypeDefinition) callOnAppendCallResult.getObject().getType().getInnermost()).getClazz());
Object invalid = OCL.newInstance().evaluate(callOnAppendCallResult, exp);
assertFalse(invalid.equals(oldValue));
assertTrue(result[0]);
}
@Test
public void testPrintNumberOfTracebackExecutions() {
debugPrintln("Number of traceback evaluations: "+AbstractTracebackStep.tracebackExecutions);
debugPrintln("Number of unused proven: "+AbstractTracebackStep.provenUnused);
}
private Notification getNotification(int id, Resource model){
Resource clonedModel = ModelCloner.cloneResource(model, String.valueOf(Math.random()));
for(int i = 0; i < (id - 1); i++) {
changeModel(i, clonedModel);
}
return changeModel(id - 1, clonedModel);
}
private Notification changeModel(int id, Resource clonedModel) {
RawNotification raw = modifyElementaryTypesTrace.get(id);
debugPrintln(raw.getEventType());
if(raw.isSplitCandidate()){
debugPrintln("candidate");
raw.split().get(0).convertToNotification(clonedModel);
return raw.split().get(1).convertToNotification(clonedModel);
} else
return raw.convertToNotification(clonedModel);
}
}