blob: 6b5e8c4cd040a09bb27f3fa734aabf8d1ba7105a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 IBM Corporation, Zeligsoft Inc., 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:
* IBM - Initial API and implementation
* Zeligsoft - Bug 248869
*******************************************************************************/
package org.eclipse.ocl.uml.tests;
import java.util.Collections;
import java.util.Iterator;
import junit.framework.AssertionFailedError;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Type;
/**
* Tests support for operation constraints (pre/post/body).
*
* @author Christian W. Damus (cdamus)
*/
@SuppressWarnings("nls")
public class OperationConstraintsTest extends AbstractTestSuite {
/**
* Tests a very simple precondition, with a package and operation context.
*/
public void test_simplePrecondition() {
parseConstraint(
"package ocltest context Fruit::ripen(color : Color) : Boolean " +
"pre: color <> Color::black " +
"endpackage");
}
/**
* Tests a very simple postcondition, with a package and operation context
* and using the "self" variable to disambiguate the name "color".
*/
public void test_simplePostcondition() {
parseConstraint(
"package ocltest context Fruit::ripen(color : Color) : Boolean " +
"post: self.color = color " +
"endpackage");
}
/**
* Tests a postcondition containing the "@ pre" construct.
*/
public void test_postcondition_atPre() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"post: color <> color@ pre implies color = c " +
"endpackage");
}
/**
* Tests a postcondition containing the "@pre" construct on an operation call.
*/
public void test_postcondition_atPre_operation() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"post: preferredColor() <> preferredColor@pre() implies color = c " +
"endpackage");
}
/**
* Tests availability of the special "result" variable in postconditions.
*/
public void test_postcondition_result() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"post: result implies color <> color@pre " +
"endpackage");
}
/**
* Tests that in case of multiple constraints, each defines its own namespace
* for local variables (we don't get errors on multiple declarations).
*/
public void test_namespaces() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"pre notBlack: let ok : Boolean = c <> Color::black in " +
" ok " +
"pre different: let ok : Boolean = c <> color in " +
" ok " +
"body: let b : Boolean = c <> color in " +
" result = b implies color <> Color::black " +
"post worked: result implies color <> color@pre " +
"endpackage");
}
/**
* Checks that we correctly reject illegal use of "@pre" in precondition
* constraints.
*/
public void test_illegalUseOfAtPre_precondition() {
AssertionFailedError err = null;
try {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"pre: c <> color@pre " +
"endpackage");
} catch (AssertionFailedError e) {
// success
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have succeeded in validating illegal @pre", err);
}
/**
* Checks that we correctly reject illegal use of "@pre" in invariant
* constraints.
*/
public void test_illegalUseOfAtPre_invariant() {
AssertionFailedError err = null;
try {
parseConstraint(
"package ocltest context Fruit " +
"inv: color@pre <> Color::black " +
"endpackage");
} catch (AssertionFailedError e) {
// success
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have succeeded in validating illegal @pre", err);
}
/**
* Checks that we correctly reject illegal use of "@pre" in postcondition
* constraints (where the "@pre" is not used on a model property expression).
*/
public void test_illegalUseOfAtPre_postcondition() {
AssertionFailedError err = null;
try {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"post: let oldColor : Color = color in c <> oldColor@pre " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have succeeded in validating illegal @pre", err);
}
/**
* Tests that local variables correctly hide attributes of the context
* classifier.
*/
public void test_variablesHidingAttributes() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"pre: let color : Boolean = (c = self.color) in " +
" color implies c <> Color::black " +
"endpackage");
}
/**
* Tests that parameters are in the same scope as local variables (cannot
* use same names).
*/
public void test_parametersAreLocalVariables() {
AssertionFailedError err = null;
try {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"pre: color : Boolean = (c = self.color) in " +
" color implies c <> Color::black " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull(err);
}
/**
* Tests that body conditions are correctly checked for conformance to the
* operation type.
*/
public void test_bodyConditionConformance() {
AssertionFailedError err = null;
try {
// try a scenario with no common supertype
parseConstraint(
"package ocltest context Fruit::preferredColor() : Color " +
"body: result = (if true then 'red' else 'brown' endif) " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull(err);
err = null;
try {
// try a scenario with a common supertype
parseConstraint(
"package ocltest context Apple::newApple() : Apple " +
"body: result = self.newFruit() " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull(err);
err = null;
try {
// this scenario is OK
parseConstraint(
"package ocltest context Apple::newFruit() : Fruit " +
"body: result = self.newApple() " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.err.println("Got unexpected error: " + e.getLocalizedMessage());
}
assertNull(err);
}
/**
* Tests that body conditions are not allowed for void operations.
*/
public void test_bodyConditionVoidOperation() {
AssertionFailedError err = null;
try {
parseConstraint(
"package ocltest context Apple::label(newLabel : String) : " +
"body: result = (if true then 'Spy' else 'Spartan' endif) " +
"endpackage");
} catch (AssertionFailedError e) {
err = e;
System.out.println("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull(err);
}
/**
* Tests that when a parameter name (or any other local variable, for that
* matter) coincides with an attribute name, we correctly distinguish
* references to the variable from references to the attribute.
*/
public void test_parameterNameCoincidesWithAttributeName_140008() {
expectModified = true;
Operation foo = apple.createOwnedOperation("foo",
new BasicEList<String>(Collections.singleton("str")),
new BasicEList<Type>(Collections.singleton((Type) getUMLString())),
getUMLString());
Property myStr = apple.createOwnedAttribute("str", getUMLString());
try {
OCLExpression<Classifier> expr = parseConstraint(
"package ocltest context Apple::foo(str : String) : String " +
"body: result = (if str = self.str then '' else str endif) " +
"endpackage");
int propertyCalls = 0;
int variableCalls = 0;
for (Iterator<?> iter = EcoreUtil.getAllContents(Collections.singleton(expr)); iter.hasNext();) {
Object next = iter.next();
if (next instanceof PropertyCallExp<?, ?>) {
@SuppressWarnings("unchecked")
PropertyCallExp<Classifier, Property> pc =
(PropertyCallExp<Classifier, Property>) next;
if ("str".equals(pc.getReferredProperty().getName())) {
propertyCalls++;
}
} else if (next instanceof VariableExp<?, ?>) {
@SuppressWarnings("unchecked")
VariableExp<Classifier, Parameter> v =
(VariableExp<Classifier, Parameter>) next;
if ("str".equals(v.getReferredVariable().getName())) {
variableCalls++;
}
}
}
assertEquals("property calls", 1, propertyCalls);
assertEquals("variable calls", 2, variableCalls);
} finally {
apple.getOwnedOperations().remove(foo);
apple.getOwnedAttributes().remove(myStr);
}
}
/**
* Tests that an expression resolves the implicit source of a property call
* correctly when the environment has additional variable names such as
* parameters that define the same property. This test parses raw OCL.
*/
@SuppressWarnings("unchecked")
public void test_implicitPropertySourceLookup_raw_151234() {
OCLExpression<Classifier> expr = parseConstraint(
"package ocltest context Apple::setColor(fruit : Fruit, newColor : Color) : " +
"pre: color <> newColor " +
"endpackage");
assertTrue(expr instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> notEquals =
(OperationCallExp<Classifier, Operation>) expr;
assertTrue(notEquals.getSource() instanceof PropertyCallExp<?, ?>);
PropertyCallExp<Classifier, Property> propertyCall =
(PropertyCallExp<Classifier, Property>) notEquals.getSource();
assertTrue(propertyCall.getSource() instanceof VariableExp<?, ?>);
VariableExp<Classifier, Parameter> var =
(VariableExp<Classifier, Parameter>) propertyCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertEquals("self", var.getReferredVariable().getName());
// now check the resolution of implicit iterator variables as sources
expr = parseConstraint(
"package ocltest context Apple::setColor(fruit : Fruit, newColor : Color) : " +
"pre: Fruit.allInstances()->forAll(color <> newColor) " +
"endpackage");
assertTrue(expr instanceof IteratorExp<?, ?>);
IteratorExp<Classifier, Parameter> forAll =
(IteratorExp<Classifier, Parameter>) expr;
assertTrue(forAll.getBody() instanceof OperationCallExp<?, ?>);
notEquals = (OperationCallExp<Classifier, Operation>) forAll.getBody();
assertTrue(notEquals.getSource() instanceof PropertyCallExp<?, ?>);
propertyCall = (PropertyCallExp<Classifier, Property>) notEquals.getSource();
assertTrue(propertyCall.getSource() instanceof VariableExp<?, ?>);
var = (VariableExp<Classifier, Parameter>) propertyCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertTrue(var.getReferredVariable().getName().startsWith("temp"));
}
/**
* Tests that an expression resolves the implicit source of a property call
* correctly when the environment has additional variable names such as
* parameters that define the same property. This test uses a helper.
*/
@SuppressWarnings("unchecked")
public void test_implicitPropertySourceLookup_helper_151234() {
try {
helper.setOperationContext(fruit, fruit_setColor);
OCLExpression<Classifier> expr =
getBodyExpression(helper.createPrecondition("color <> newColor"));
assertTrue(expr instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> notEquals =
(OperationCallExp<Classifier, Operation>) expr;
assertTrue(notEquals.getSource() instanceof PropertyCallExp<?, ?>);
PropertyCallExp<Classifier, Property> propertyCall =
(PropertyCallExp<Classifier, Property>) notEquals.getSource();
assertTrue(propertyCall.getSource() instanceof VariableExp<?, ?>);
VariableExp<Classifier, Parameter> var =
(VariableExp<Classifier, Parameter>) propertyCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertEquals("self", var.getReferredVariable().getName());
// now check the resolution of implicit iterator variables as sources
expr = getBodyExpression(helper.createPrecondition(
"Fruit.allInstances()->forAll(color <> newColor)"));
assertTrue(expr instanceof IteratorExp<?, ?>);
IteratorExp<Classifier, Parameter> forAll = (IteratorExp<Classifier, Parameter>) expr;
assertTrue(forAll.getBody() instanceof OperationCallExp<?, ?>);
notEquals = (OperationCallExp<Classifier, Operation>) forAll.getBody();
assertTrue(notEquals.getSource() instanceof PropertyCallExp<?, ?>);
propertyCall = (PropertyCallExp<Classifier, Property>) notEquals.getSource();
assertTrue(propertyCall.getSource() instanceof VariableExp<?, ?>);
var = (VariableExp<Classifier, Parameter>) propertyCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertTrue(var.getReferredVariable().getName().startsWith("temp"));
} catch (ParserException e) {
fail("Parse failed: " + e.getLocalizedMessage());
}
}
/**
* Tests that an expression resolves the implicit source of an operation call
* correctly when the environment has additional variable names such as
* parameters that define the same property. This test parses raw OCL.
*/
@SuppressWarnings("unchecked")
public void test_implicitOperationSourceLookup_raw_151234() {
OCLExpression<Classifier> expr = parseConstraint(
"package ocltest context Apple::setColor(fruit : Fruit, newColor : Color) : " +
"pre: preferredColor() <> newColor " +
"endpackage");
assertTrue(expr instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> notEquals =
(OperationCallExp<Classifier, Operation>) expr;
assertTrue(notEquals.getSource() instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> operationCall =
(OperationCallExp<Classifier, Operation>) notEquals.getSource();
assertTrue(operationCall.getSource() instanceof VariableExp<?, ?>);
VariableExp<Classifier, Parameter> var = (VariableExp<Classifier, Parameter>) operationCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertEquals("self", var.getReferredVariable().getName());
// now check the resolution of implicit iterator variables as sources
expr = parseConstraint(
"package ocltest context Apple::setColor(fruit : Fruit, newColor : Color) : " +
"pre: Fruit.allInstances()->forAll(preferredColor() <> newColor) " +
"endpackage");
assertTrue(expr instanceof IteratorExp<?, ?>);
IteratorExp<Classifier, Parameter> forAll = (IteratorExp<Classifier, Parameter>) expr;
assertTrue(forAll.getBody() instanceof OperationCallExp<?, ?>);
notEquals = (OperationCallExp<Classifier, Operation>) forAll.getBody();
assertTrue(notEquals.getSource() instanceof OperationCallExp<?, ?>);
operationCall = (OperationCallExp<Classifier, Operation>) notEquals.getSource();
assertTrue(operationCall.getSource() instanceof VariableExp<?, ?>);
var = (VariableExp<Classifier, Parameter>) operationCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertTrue(var.getReferredVariable().getName().startsWith("temp"));
}
/**
* Tests that an expression resolves the implicit source of an operation call
* correctly when the environment has additional variable names such as
* parameters that define the same property. This test uses a helper.
*/
@SuppressWarnings("unchecked")
public void test_implicitOperationSourceLookup_helper_151234() {
try {
helper.setOperationContext(fruit, fruit_setColor);
OCLExpression<Classifier> expr = getBodyExpression(
helper.createPrecondition("preferredColor() <> newColor"));
assertTrue(expr instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> notEquals =
(OperationCallExp<Classifier, Operation>) expr;
assertTrue(notEquals.getSource() instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> operationCall =
(OperationCallExp<Classifier, Operation>) notEquals.getSource();
assertTrue(operationCall.getSource() instanceof VariableExp<?, ?>);
VariableExp<Classifier, Parameter> var =
(VariableExp<Classifier, Parameter>) operationCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertEquals("self", var.getReferredVariable().getName());
// now check the resolution of implicit iterator variables as sources
expr = getBodyExpression(helper.createPrecondition(
"Fruit.allInstances()->forAll(preferredColor() <> newColor)"));
assertTrue(expr instanceof IteratorExp<?, ?>);
IteratorExp<Classifier, Parameter> forAll = (IteratorExp<Classifier, Parameter>) expr;
assertTrue(forAll.getBody() instanceof OperationCallExp<?, ?>);
notEquals = (OperationCallExp<Classifier, Operation>) forAll.getBody();
assertTrue(notEquals.getSource() instanceof OperationCallExp<?, ?>);
operationCall = (OperationCallExp<Classifier, Operation>) notEquals.getSource();
assertTrue(operationCall.getSource() instanceof VariableExp<?, ?>);
var = (VariableExp<Classifier, Parameter>) operationCall.getSource();
// we did not resolve against "fruit", which also has a color property
assertTrue(var.getReferredVariable().getName().startsWith("temp"));
} catch (ParserException e) {
fail("Parse failed: " + e.getLocalizedMessage());
}
}
}