blob: f716bb1abec4743e7705f18f03f1e5098b332ad3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2011, 2018 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 v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* IBM - Initial API and implementation
* Zeligsoft - Bug 248869
* Axel Uhl (SAP AG) - Bug 342644
*******************************************************************************/
package org.eclipse.ocl.uml.tests;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.uml.BagType;
import org.eclipse.ocl.uml.CollectionType;
import org.eclipse.ocl.uml.OCL;
import org.eclipse.ocl.uml.OrderedSetType;
import org.eclipse.ocl.uml.SequenceType;
import org.eclipse.ocl.uml.SetType;
import org.eclipse.ocl.uml.TupleType;
import org.eclipse.ocl.uml.UMLEnvironmentFactory;
import org.eclipse.ocl.uml.UMLFactory;
import org.eclipse.ocl.util.Tuple;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.resource.UMLResource;
import junit.framework.AssertionFailedError;
/**
* Regression tests for specific RATLC defects.
*
* @author Christian W. Damus (cdamus)
*/
@SuppressWarnings("nls")
public class RegressionTest
extends AbstractTestSuite {
/**
* Tests the "..." escape syntax for reserved words. Regression test for
* RATLC00527506.
*/
public void test_quoteReservedWords_RATLC00527506() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class eclass = epackage.createOwnedClass("MyType", false);
// "context" is an OCL reserved word
eclass.createOwnedAttribute("context", getUMLString());
try {
parseConstraint(
"package MyPackage context MyType " +
"inv: self.\"context\"->notEmpty() " +
"endpackage");
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests the "..." escape syntax for whitespace. Regression test for
* RATLC00527509.
*/
public void test_quoteWhitespace_RATLC00527509() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class eclass = epackage.createOwnedClass("MyType", false);
// "context" is an OCL reserved word
eclass.createOwnedAttribute("an attribute", getUMLString());
try {
parseConstraint(
"package MyPackage context MyType " +
"inv: self.\"an attribute\"->notEmpty() " +
"endpackage");
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests the \" escape syntax for double-quotes. Regression test for
* RATLC00527509.
*/
public void test_quoteQuote_RATLC00527509() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class eclass = epackage.createOwnedClass("MyType", false);
// "context" is an OCL reserved word
eclass.createOwnedAttribute("an\"attribute", getUMLString());
try {
// try first to parse within surrounding double-quotes
parseConstraint(
"package MyPackage context MyType " +
"inv: self.\"an\\\"attribute\"->notEmpty() " +
"endpackage");
AssertionFailedError err = null;
try {
// also try to parse without the surrounding double-quotes.
// This is not allowed
parseConstraint(
"package MyPackage context MyType " +
"inv: self.an\\\"attribute->notEmpty() " +
"endpackage");
} catch (AssertionFailedError e) {
// success
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have parsed.", err);
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests the support for international characters. Regression test for
* RATLC01080816.
*/
public void test_internationalCharacters_RATLC01080816() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class eclass = epackage.createOwnedClass("MyType", false);
// "context" is an OCL reserved word
eclass.createOwnedAttribute("\u0160\u01d6\u0429\u0639", getUMLString());
try {
// try these characters in the attribute name and string literal
parseConstraint(
"package MyPackage context MyType " +
"inv: self.\u0160\u01d6\u0429\u0639 <> '\u0160\u01d6\u0429\u0639' " +
"endpackage");
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests support for oclIsKindOf() and oclAsType() to cast between
* classifiers that are not related, but where their subtypes may be
* conformant.
*/
public void test_oclIsKindOf_RATLC01087664() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
// create three classes. A and B are unrelated, but C extends
// both. Therefore, it is possible to cast a variable of type
// A to type B where the run-time type is C
Class a = epackage.createOwnedClass("A", false);
Class b = epackage.createOwnedClass("B", false);
Class c = epackage.createOwnedClass("C", false);
c.createGeneralization(a);
c.createGeneralization(b);
Property attrA = a.createOwnedAttribute("a", getUMLBoolean());
Property attrB = b.createOwnedAttribute("b", getUMLBoolean());
try {
OCLExpression<Classifier> constraint = parseConstraint(
"package MyPackage context A " +
"inv: self.oclIsKindOf(B) implies (self.oclAsType(B).b <> self.a) " +
"endpackage");
InstanceSpecification eobj = instantiate(epackage, c);
setValue(eobj, attrA, Boolean.TRUE);
setValue(eobj, attrB, Boolean.TRUE);
assertFalse("Should have failed the check", check(constraint, eobj));
setValue(eobj, attrB, Boolean.FALSE);
assertTrue("Should not have failed the check", check(constraint, eobj));
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests support for short-circuiting AND operator.
*/
public void test_shortcircuitAnd_RATLC00536528() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class a = epackage.createOwnedClass("A", false);
Class b = epackage.createOwnedClass("B", false);
b.createGeneralization(a);
Property attrA = a.createOwnedAttribute("a", getUMLBoolean());
Property attrB = b.createOwnedAttribute("b", getUMLBoolean());
try {
OCLExpression<Classifier> constraint = parseConstraint(
"package MyPackage context A " +
"inv: self.oclIsKindOf(B) and self.oclAsType(B).b " +
"endpackage");
// create an A
InstanceSpecification eobj = instantiate(epackage, a);
setValue(eobj, attrA, Boolean.TRUE);
// this would fail with an NPE on the access to '.b' when
// 'self.oclAsType(B)' evaluates to null, if we didn't short-circuit
assertFalse("Should have failed the check", check(constraint, eobj));
// create a B this time
eobj = instantiate(epackage, b);
setValue(eobj, attrA, Boolean.TRUE);
setValue(eobj, attrB, Boolean.TRUE);
assertTrue("Should not have failed the check", check(constraint, eobj));
} catch (Exception e) {
fail("Failed to parse or evaluate: " + e.getLocalizedMessage());
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests support for short-circuiting OR operator.
*/
public void test_shortcircuitOr_RATLC00536528() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class a = epackage.createOwnedClass("A", false);
Class b = epackage.createOwnedClass("B", false);
b.createGeneralization(a);
Property attrA = a.createOwnedAttribute("a", getUMLBoolean());
Property attrB = b.createOwnedAttribute("b", getUMLBoolean());
try {
OCLExpression<Classifier> constraint = parseConstraint(
"package MyPackage context A " +
"inv: (not self.oclIsKindOf(B)) or self.oclAsType(B).b " +
"endpackage");
// create an A
InstanceSpecification eobj = instantiate(epackage, a);
setValue(eobj, attrA, Boolean.TRUE);
// this would fail with an NPE on the access to '.b' when
// 'self.oclAsType(B)' evaluates to null, if we didn't short-circuit
assertTrue("Should not have failed the check", check(constraint, eobj));
// create a B this time
eobj = instantiate(epackage, b);
setValue(eobj, attrA, Boolean.TRUE);
setValue(eobj, attrB, Boolean.TRUE);
assertTrue("Should not have failed the check", check(constraint, eobj));
} catch (Exception e) {
fail("Failed to parse or evaluate: " + e.getLocalizedMessage());
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests support for short-circuiting IMPLIES operator.
*/
public void test_shortcircuitImplies_RATLC00536528() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
Class a = epackage.createOwnedClass("A", false);
Class b = epackage.createOwnedClass("B", false);
b.createGeneralization(a);
Property attrA = a.createOwnedAttribute("a", getUMLBoolean());
Property attrB = b.createOwnedAttribute("b", getUMLBoolean());
try {
OCLExpression<Classifier> constraint = parseConstraint(
"package MyPackage context A " +
"inv: self.oclIsKindOf(B) implies self.oclAsType(B).b " +
"endpackage ");
// create an A
InstanceSpecification eobj = instantiate(epackage, a);
setValue(eobj, attrA, Boolean.TRUE);
// this would fail with an NPE on the access to '.b' when
// 'self.oclAsType(B)' evaluates to null, if we didn't short-circuit
assertTrue("Should not have failed the check", check(constraint, eobj));
// create a B this time
eobj = instantiate(epackage, b);
setValue(eobj, attrA, Boolean.TRUE);
setValue(eobj, attrB, Boolean.FALSE);
assertFalse("Should have failed the check", check(constraint, eobj));
} catch (Exception e) {
fail("Failed to parse or evaluate: " + e.getLocalizedMessage());
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests that we correctly parse the <tt>oclIsNew</tt> operation in
* invariant constraints, but that validation reports a suitable error.
*/
public void test_oclIsNew_invariant_RATLC00529981() {
OCLExpression<Classifier> constraint = parseConstraintUnvalidated(
"package ocltest context Fruit " +
"inv: color.oclIsNew() " +
"endpackage");
AssertionFailedError err = null;
try {
validate(constraint);
} catch (AssertionFailedError e) {
// success
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have succeeded in validating illegal oclIsNew", err);
}
/**
* Tests that we correctly parse the <tt>oclIsNew</tt> operation in
* precondition constraints, but that validation reports a suitable error.
*/
public void test_oclIsNew_precondition_RATLC00529981() {
OCLExpression<Classifier> constraint = parseConstraintUnvalidated(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"pre: c.oclIsNew() implies c <> Color::black " +
"endpackage");
AssertionFailedError err = null;
try {
validate(constraint);
} catch (AssertionFailedError e) {
// success
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Should not have succeeded in validating illegal oclIsNew", err);
}
/**
* Tests that we correctly parse the <tt>oclIsNew</tt> operation in
* postcondition constraints, and that validation reports no errors.
*/
public void test_oclIsNew_postcondition_RATLC00529981() {
parseConstraint(
"package ocltest context Fruit::ripen(c : Color) : Boolean " +
"post: color.oclIsNew() implies color <> Color::black " +
"endpackage");
}
/**
* Tests that we correctly parse and evaluate the <tt>toLower</tt> operation
* on OCL string values.
*/
public void test_toLower_RATLC00529981() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: 'AlPHaBet'.toLower() " +
"endpackage");
Object value = evaluate(expr);
assertEquals("alphabet", value);
}
/**
* Tests that we correctly parse and evaluate the <tt>toUpper</tt> operation
* on OCL string values.
*/
public void test_toUpper_RATLC00529981() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: 'AlPHaBet'.toUpper() " +
"endpackage");
Object value = evaluate(expr);
assertEquals("ALPHABET", value);
}
/**
* Tests that references that have multiplicity, are unique, and
* are ordered are rendered as OCL sets. Incidentally tests access to
* static properties.
*/
public void test_referenceMultiplicity_orderedSet_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.orderedSet" +
" endpackage");
// check that the result type is an ordered set type
assertTrue(
"Not an ordered set type",
expr.getType() instanceof OrderedSetType);
}
/**
* Tests that references that have multiplicity, are unique, and
* are unordered are rendered as OCL sets. Incidentally tests access to
* static properties.
*/
public void test_referenceMultiplicity_set_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.set" +
" endpackage");
// check that the result type is a set type
assertTrue(
"Not a set type",
(expr.getType() instanceof SetType)
&& !(expr.getType() instanceof OrderedSetType));
}
/**
* Tests that references that have multiplicity, are non-unique, and
* are ordered are rendered as OCL sequences. Incidentally tests access to
* static properties.
*/
public void test_referenceMultiplicity_sequence_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.sequence" +
" endpackage");
// check that the result type is a sequence set type
assertTrue(
"Not a sequence type",
expr.getType() instanceof SequenceType);
}
/**
* Tests that references that have multiplicity, are non-unique, and
* are unordered are rendered as OCL bags. Incidentally tests access to
* static properties.
*/
public void test_referenceMultiplicity_bag_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.bag" +
" endpackage");
// check that the result type is a bag set type
assertTrue(
"Not a bag type",
expr.getType() instanceof BagType);
}
/**
* Tests that operation parameters that have multiplicity, are unique, and
* are ordered are rendered as OCL sets. Incidentally tests access to
* static operations.
*/
public void test_parameterMultiplicity_orderedSet_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processOrderedSet(FruitUtil.orderedSet)" +
" endpackage");
// now also check that the result type is an ordered set type
assertTrue(
"Not an ordered set type",
expr.getType() instanceof OrderedSetType);
}
/**
* Tests that operation parameters that have multiplicity, are unique, and
* are unordered are rendered as OCL sets. Incidentally tests access to
* static operations.
*/
public void test_parameterMultiplicity_set_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processSet(FruitUtil.set)" +
" endpackage");
// now also check that the result type is a set type
assertTrue(
"Not a set type",
(expr.getType() instanceof SetType)
&& !(expr.getType() instanceof OrderedSetType));
}
/**
* Tests that operation parameters that have multiplicity, are non-unique, and
* are ordered are rendered as OCL sequences. Incidentally tests access to
* static operations.
*/
public void test_parameterMultiplicity_sequence_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processSequence(FruitUtil.sequence)" +
" endpackage");
// now also check that the result type is a sequence type
assertTrue(
"Not a sequence type",
expr.getType() instanceof SequenceType);
}
/**
* Tests that operation parameters that have multiplicity, are non-unique, and
* are unordered are rendered as OCL bags. Incidentally tests access to
* static operations.
*/
public void test_parameterMultiplicity_bag_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processBag(FruitUtil.bag)" +
" endpackage");
// now also check that the result type is a bag type
assertTrue(
"Not a bag type",
expr.getType() instanceof BagType);
}
/**
* Tests that operations that have multiplicity, are unique, and
* are ordered are rendered as OCL sets.
*/
public void test_operationMultiplicity_orderedSet_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processOrderedSet(FruitUtil.processOrderedSet(FruitUtil.orderedSet))" +
" endpackage");
// now also check that the result type is an ordered set type
assertTrue(
"Not an ordered set type",
expr.getType() instanceof OrderedSetType);
}
/**
* Tests that operations that have multiplicity, are unique, and
* are unordered are rendered as OCL sets.
*/
public void test_operationMultiplicity_set_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processSet(FruitUtil.processSet(FruitUtil.set))" +
" endpackage");
// now also check that the result type is a set type
assertTrue(
"Not a set type",
(expr.getType() instanceof SetType)
&& !(expr.getType() instanceof OrderedSetType));
}
/**
* Tests that operations that have multiplicity, are non-unique, and
* are ordered are rendered as OCL sequences.
*/
public void test_operationMultiplicity_sequence_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processSequence(FruitUtil.processSequence(FruitUtil.sequence))" +
" endpackage");
// now also check that the result type is a sequence type
assertTrue(
"Not a sequence type",
expr.getType() instanceof SequenceType);
}
/**
* Tests that operations that have multiplicity, are non-unique, and
* are unordered are rendered as OCL bags.
*/
public void test_operationMultiplicity_bag_RATLC00538035() {
OCLExpression<Classifier> expr = parse(
"package ocltest context Fruit " +
"inv: FruitUtil.processBag(FruitUtil.processBag(FruitUtil.bag))" +
" endpackage");
// now also check that the result type is a bag type
assertTrue(
"Not a bag type",
expr.getType() instanceof BagType);
}
/**
* Tests that the operation context parsing matches ordered set types
* correctly in the parameters, result type, and body expression type.
*/
public void test_operationContext_orderedSet_RATLC00538035() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context " +
"FruitUtil::processOrderedSet(x : Fruit) : Fruit " +
"body: result = x" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
// this should work
parse(
"package ocltest context " +
"FruitUtil::processOrderedSet(x : OrderedSet(Fruit)) : OrderedSet(Fruit) " +
"body: result = x" +
" endpackage");
}
/**
* Tests that the operation context parsing matches set types
* correctly in the parameters, result type, and body expression type.
*/
public void test_operationContext_set_RATLC00538035() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context " +
"FruitUtil::processSet(x : Fruit) : Fruit " +
"body: result = x" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
// this should work
parse(
"package ocltest context " +
"FruitUtil::processSet(x : Set(Fruit)) : Set(Fruit) " +
"body: result = x" +
" endpackage");
}
/**
* Tests that the operation context parsing matches sequence types
* correctly in the parameters, result type, and body expression type.
*/
public void test_operationContext_sequence_RATLC00538035() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context " +
"FruitUtil::processSequence(x : Fruit) : Fruit " +
"body: result = x" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
// this should work
parse(
"package ocltest context " +
"FruitUtil::processSequence(x : Sequence(Fruit)) : Sequence(Fruit) " +
"body: result = x" +
" endpackage");
}
/**
* Tests that the operation context parsing matches bag types
* correctly in the parameters, result type, and body expression type.
*/
public void test_operationContext_bag_RATLC00538035() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context " +
"FruitUtil::processBag(x : Fruit) : Fruit " +
"body: result = x" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
// this should work
parse(
"package ocltest context " +
"FruitUtil::processBag(x : Bag(Fruit)) : Bag(Fruit) " +
"body: result = x" +
" endpackage");
}
/**
* Regression test to check that <code>allInstances()</code> works as
* expected on enumerations.
*/
public void test_allInstances_enumeration_RATLC00538079() {
Object result = evaluate(parse(
"package ocltest context Fruit " +
"inv: Color.allInstances() " +
" endpackage"));
Set<EnumerationLiteral> expected = new java.util.HashSet<EnumerationLiteral>(
color.getOwnedLiterals());
assertEquals(expected, result);
}
/**
* Regression test to check that <code>allInstances()</code> works as
* expected on the <code>OclVoid</code> type.
*/
public void test_allInstances_voidType_RATLC00538079() {
Object result = evaluate(parse(
"package ocltest context Fruit " +
"inv: OclVoid.allInstances() " +
" endpackage"));
Set<Object> expected = new java.util.HashSet<Object>();
expected.add(null);
assertEquals(expected, result);
}
/**
* Regression test to check that <code>allInstances()</code> works as
* expected on primitive types.
*/
public void test_allInstances_primitive_RATLC00538079() {
Object result = evaluate(parse(
"package ocltest context Fruit " +
"inv: PrimitiveTypes::\"String\".allInstances() " +
" endpackage"));
assertEquals(Collections.EMPTY_SET, result);
}
/**
* Regression test for the problem of extra closing parentheses allowing
* garbage expressions to appear to parse correctly. This test tests
* the core parser.
*/
public void test_closingParentheses_core() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context Fruit " +
"inv: self)garbage " +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
}
/**
* Regression test for the problem of extra closing parentheses allowing
* garbage expressions to appear to parse correctly. This test tests
* the OCL helper.
*/
public void test_closingParentheses_helper() {
helper.setContext(fruit);
try {
// this should not work
helper.createInvariant("self)garbage");
fail("Parse should have failed");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
}
/**
* Regression test for the problem of extra closing parentheses allowing
* garbage expressions to appear to parse correctly. This test tests
* the OCL helper with a precondition instead of an invariant.
*/
public void test_closingParentheses_helper_precondition() {
helper.setOperationContext(fruit, fruit_ripen);
try {
// this should not work
helper.createPrecondition("self)garbage");
fail("Parse should have failed");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
}
public void test_operationBodyBoolean_116251() {
AssertionFailedError err = null;
// this should work
parseConstraint(
"package ocltest context " +
"FruitUtil::processBag(x : Bag(Fruit)) : Bag(Fruit) " +
"body: result = x->asSet()->asBag()" +
" endpackage");
// as should this
parseConstraint(
"package ocltest context " +
"FruitUtil::processBag(x : Bag(Fruit)) : Bag(Fruit) " +
"body: x->asSet()->asBag() = result" +
" endpackage");
// and this (allow any number of lets to wrap the expression)
parseConstraint(
"package ocltest context " +
"FruitUtil::processBag(x : Bag(Fruit)) : Bag(Fruit) " +
"body: let set : Set(Fruit) = x->asSet() in" +
" let bag : Bag(Fruit) = set->asBag() in" +
" result = bag" +
" endpackage");
// this should not work, however, because it has the result in the
// body expression part of the constraint
err = null;
try {
parseConstraint(
"package ocltest context " +
"FruitUtil::processBag(x : Bag(Fruit)) : Bag(Fruit) " +
"body: result = result->asSet()->union(x)->asBag()" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
}
/**
* When resolving unqualified property calls in an inner scope (such as in a loop
* expression), the OCL language specification requires that the lookup of the
* implicit target of the property call start with the innermost iterator variable
* (whether explicitly or implicitly defined) and work outwards until it finds a
* match).
*/
public void test_innerScopeFeatureResolution_bugzilla113355() {
expectModified = true;
Package epackage = umlf.createPackage();
epackage.setName("MyPackage");
fruitPackage.eResource().getContents().add(epackage);
// Library1
// - Library2
// - Writer1
// - Writer2
Class libraryClass = epackage.createOwnedClass("Library", false);
Class writerClass = epackage.createOwnedClass("Writer", false);
Property branchesRef = libraryClass.createOwnedAttribute("branches", libraryClass);
branchesRef.setUpper(LiteralUnlimitedNatural.UNLIMITED);
branchesRef.setIsOrdered(true);
Property writersRef = libraryClass.createOwnedAttribute("writers", writerClass);
writersRef.setUpper(LiteralUnlimitedNatural.UNLIMITED);
writersRef.setIsOrdered(true);
Property writerName = writerClass.createOwnedAttribute("name", getUMLString());
// create our test instance
InstanceSpecification library1 = instantiate(epackage, libraryClass);
InstanceSpecification library2 = instantiate(epackage, libraryClass);
InstanceSpecification writer1 = instantiate(epackage, writerClass);
InstanceSpecification writer2 = instantiate(epackage, writerClass);
setValue(writer1, writerName, "Joe");
setValue(writer2, writerName, "Jane");
addValue(library1, branchesRef, library2);
addValue(library2, writersRef, writer1);
addValue(library2, writersRef, writer2);
// parse expression
try {
OCLExpression<Classifier> expr = parse(
"package MyPackage context Library " +
"inv: branches->collect(writers->collect(w : Writer | w))->flatten()" +
"endpackage");
@SuppressWarnings("unchecked")
List<InstanceSpecification> result =
(List<InstanceSpecification>) evaluate(expr, library1);
assertTrue(result.size() == 2);
assertTrue(getValue(result.get(0), writerName).equals("Joe"));
assertTrue(getValue(result.get(1), writerName).equals("Jane"));
} catch (Exception e) {
fail("Failed to parse or evaluate: " + e.getLocalizedMessage());
} finally {
EcoreUtil.remove(epackage);
}
}
/**
* Tests the collection product() operation.
*/
public void test_product_126336() {
helper.setContext(getUMLString());
Set<Tuple<Operation, Property>> product = null;
try {
OCLExpression<Classifier> expr = helper.createQuery(
"Set{'foo', 'bar'}->product(Sequence{1, 2, 3})");
Classifier resultType = expr.getType();
assertTrue(resultType instanceof CollectionType);
Classifier elementType = ((CollectionType) resultType).getElementType();
assertTrue(elementType instanceof TupleType);
TupleType tupleType = (TupleType) elementType;
assertEquals(2, tupleType.oclProperties().size());
@SuppressWarnings("unchecked")
Set<Tuple<Operation, Property>> resultValue =
(Set<Tuple<Operation, Property>>) ocl.evaluate("", expr);
product = resultValue;
} catch (Exception e) {
fail("Failed to parse or evaluate: " + e.getLocalizedMessage());
}
assertNotNull(product);
// got as many product tuples as required (2 x 3)
assertEquals(6, product.size());
Map<String, Set<Integer>> expectedTuples =
new java.util.HashMap<String, Set<Integer>>();
Set<Integer> values = new java.util.HashSet<Integer>();
values.add(1);
values.add(2);
values.add(3);
expectedTuples.put("foo", new java.util.HashSet<Integer>(values));
expectedTuples.put("bar", new java.util.HashSet<Integer>(values));
for (Tuple<Operation, Property> tuple : product) {
values = expectedTuples.get(tuple.getValue("first"));
// every "first" value must hit
assertNotNull(values);
// every "second" must remove a different mapping
assertTrue(values.remove(tuple.getValue("second")));
}
}
/**
* Test that the conversion of an expression to string and re-parsing works
* as expected. Use the particular iteration expression described in the
* referenced bugzilla.
*/
public void test_iterationToString_126454() {
Package fakePkg = umlf.createPackage();
fakePkg.setName("fake");
Class fake = fakePkg.createOwnedClass("Fake", false);
Property eattr = fake.createOwnedAttribute("e", getUMLInteger());
eattr.setUpper(1); // not a collection
InstanceSpecification aFake = instantiate(fakePkg, fake);
setValue(aFake, eattr, Integer.valueOf(7));
helper.setContext(fake);
try {
OCLExpression<Classifier> expr = helper.createQuery("self.e->sum()");
// convert to string and re-parse
String toStringResult = expr.toString();
expr = helper.createQuery(toStringResult);
assertEquals(getValue(aFake, eattr), ocl.evaluate(aFake, expr));
} catch (Exception exc) {
fail("Failed to parse or evaluate: " + exc.getLocalizedMessage());
}
}
/**
* Test the OclVoid literal 'null'.
*/
public void test_null() {
Object result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclIsTypeOf(OclVoid) " +
" endpackage"));
assertEquals(Boolean.TRUE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclIsUndefined() " +
" endpackage"));
assertEquals(Boolean.TRUE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclIsInvalid() " +
" endpackage"));
assertEquals(Boolean.FALSE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclAsType(Integer) " +
" endpackage"));
assertNull(result);
}
/**
* Test the OclInvalid type and its literal 'invalid'.
*/
public void test_oclInvalid() {
Object result = evaluate(parse(
"package ocltest context Fruit " +
"inv: invalid.oclIsTypeOf(OclInvalid) " +
" endpackage"));
assertEquals(Boolean.TRUE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: invalid.oclIsUndefined() " +
" endpackage"));
assertEquals(Boolean.TRUE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: invalid.oclIsInvalid() " +
" endpackage"));
assertEquals(Boolean.TRUE, result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: invalid.oclAsType(Integer) " +
" endpackage"));
assertInvalid(result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclAsType(Apple).color " +
" endpackage"));
// feature calls on null result in invalid
assertInvalid(result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclAsType(Apple).tree " +
" endpackage"));
// feature calls on null result in invalid
assertInvalid(result);
result = evaluate(parse(
"package ocltest context Fruit " +
"inv: null.oclAsType(Apple).preferredLabel('foo') " +
" endpackage"));
// feature calls on null result in invalid
assertInvalid(result);
}
/**
* Tests that we report an error on failing to find an operation matching
* a call. Moreover, the error is in parsing, not in validating.
*/
public void test_operationNotFound() {
AssertionFailedError err = null;
// this should not work (failure in parse, not validation)
try {
parseUnvalidated(
"package ocltest context Fruit " +
"inv: FruitUtil.processOrderedSet(1) = 0 " +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
}
/**
* Tests that matching operations finds the first match, but that
* casting via oclAsType() can direct the parser to the best match.
*/
@SuppressWarnings("unchecked")
public void test_operationSignatureMatching() {
Package pkg = umlf.createPackage();
Class a = pkg.createOwnedClass("A", false);
Class b = pkg.createOwnedClass("B", false);
Class c = pkg.createOwnedClass("C", false);
c.createGeneralization(b);
a.createOwnedAttribute("b", b);
a.createOwnedAttribute("c", c);
Operation foo1 = a.createOwnedOperation("foo",
new BasicEList<String>(Collections.singleton("c")),
new BasicEList<Type>(Collections.singleton((Type) c)),
getUMLBoolean());
foo1.setIsQuery(true);
Operation foo2 = a.createOwnedOperation("foo",
new BasicEList<String>(Collections.singleton("b")),
new BasicEList<Type>(Collections.singleton((Type) b)),
getUMLBoolean());
foo2.setIsQuery(true);
helper.setContext(a);
try {
OCLExpression<Classifier> expr = helper.createQuery("self.foo(c)");
assertTrue(expr instanceof OperationCallExp<?, ?>);
OperationCallExp<Classifier, Operation> oc =
(OperationCallExp<Classifier, Operation>) expr;
// foo1's parameter type is c
assertSame(foo1, oc.getReferredOperation());
expr = helper.createQuery("self.foo(b)");
assertTrue(expr instanceof OperationCallExp<?, ?>);
oc = (OperationCallExp<Classifier, Operation>) expr;
// we matched foo1 because it was the first operation matching b
// (we skipped the foo having parameter type c)
assertSame(foo2, oc.getReferredOperation());
expr = helper.createQuery("self.foo(b.oclAsType(C))");
assertTrue(expr instanceof OperationCallExp<?, ?>);
oc = (OperationCallExp<Classifier, Operation>) expr;
// coerced the arg to type C to find the correct foo
assertSame(foo1, oc.getReferredOperation());
} catch (Exception e) {
fail("Failed to parse: " + e.getLocalizedMessage());
}
}
/**
* Tests that looking up an operation call on an implicit source works
* and fails gracefully when it doesn't. Test the case where the implicit
* source is not self.
*/
public void test_operationImplicitSource() {
AssertionFailedError err = null;
// this should not work
try {
parse(
"package ocltest context Fruit " +
"inv: Apple.allInstances()->collect(preferredLabel())" +
" endpackage");
} catch (AssertionFailedError e) {
// this is expected (success case)
err = e;
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
assertNotNull("Parse should have failed", err);
// this should work
parse(
"package ocltest context Fruit " +
"inv: Apple.allInstances()->collect(preferredLabel('foo'))" +
" endpackage");
// and this
parse(
"package ocltest context Apple " +
"inv: preferredLabel('foo')" +
" endpackage");
}
/**
* Tests referencing an element in an enclosing package using an unqualified name.
*
* <ul>
* <li>EPackage level1<ul>
* <li>EEnum Enum1<ul>
* <li>EEnumLiteral Enum1Literal1</li></ul></li>
* <li>EPackage level2<ul>
* <li>EClass Class1<ul>
* <li>EAttribute attr1 : Enum1</li></ul></li></ul></li></ul></li>
* </ul>
* <pre>
* context: Class1
* expression: attr1 = Enum1::Enum1Literal1
* </pre>
*/
public void test_enclosingPackage_unqualifiedName() {
Package level1 = umlf.createPackage();
level1.setName("level1");
Package level2 = level1.createNestedPackage("level2");
Enumeration enum1 = level1.createOwnedEnumeration("Enum1");
enum1.createOwnedLiteral("Enum1Literal1");
Class class1 = level2.createOwnedClass("Class1", false);
class1.createOwnedAttribute("attr1", enum1);
helper.setContext(class1);
try {
helper.createInvariant("attr1 = Enum1::Enum1Literal1");
} catch (Exception e) {
fail("Failed to parse: " + e.getLocalizedMessage());
}
}
/**
* The Hebrew (Israel) locale on SuSe Linux likes to use character 0xB4
* (acute accent) as a single quotation mark. Should also, then, support
* the back-quote (grave accent), as well.
*/
public void test_hebrew_singleQuote_135321() {
// checkForUTF8Encoding();
parse(
// English locale style
"package ocltest context Apple " +
"inv: preferredLabel('foo')" +
" endpackage");
/* parse( FIXME Bug 291310 rewrite for OCL 2.3 and reintroduce when build is UTF-8.
// SuSe Linux in Hebrew Local
"package ocltest context Apple " +
"inv: preferredLabel(´foo´)" +
" endpackage");
parse(
// I've seen this before (esp. in text export from MS Word)
"package ocltest context Apple " +
"inv: preferredLabel(`foo´)" +
" endpackage"); */
}
/**
* Test that we don't get a <code>null</code> string from an OCL expression
* that contains a reference to a variable that has no name.
*/
public void test_nullVariableName_143386() {
org.eclipse.ocl.uml.Variable var =
UMLFactory.eINSTANCE.createVariable();
assertEquals("\"<null>\"", var.toString());
VariableExp<Classifier, Parameter> exp =
UMLFactory.eINSTANCE.createVariableExp();
exp.setReferredVariable(var);
assertEquals("\"<null>\"", exp.toString());
var.setName("foo");
assertEquals("foo", var.toString());
assertEquals("foo", exp.toString());
// recreate to avoid caching of names
var = UMLFactory.eINSTANCE.createVariable();
var.setName("foo");
var.setType(getUMLString());
exp = UMLFactory.eINSTANCE.createVariableExp();
exp.setReferredVariable(var);
assertEquals("foo : String", var.toString());
assertEquals("foo", exp.toString());
}
/**
* Tests that we don't get NPEs from getting the names of collection types
* that do not (yet) have element types.
*/
public void test_nameOfCollectionTypeWithoutElementType() {
org.eclipse.ocl.types.CollectionType<?, ?> collType =
ocl.getEnvironment().getOCLFactory().createCollectionType(
CollectionKind.BAG_LITERAL, null);
collType.getName();
}
/**
* Tests that disposing an OCL instance correctly disposes the resource
* created by the type resolver. This is crucial for cleaning up the
* CacheAdapter in UML.
*/
public void test_dispose_resourceOwnedByOCL_213045() {
helper.setContext(getMetaclass("Action"));
try {
Constraint constraint = helper.createInvariant("self.owner <> null");
Resource res = constraint.eResource();
assertSame(ocl.getEnvironment().getTypeResolver().getResource(), res);
Adapter adapter = new AdapterImpl();
constraint.getSpecification().eAdapters().add(adapter);
assertSame(constraint.getSpecification(), adapter.getTarget());
ocl.dispose();
assertFalse(res.isLoaded());
assertNull(constraint.eResource());
assertNull(adapter.getTarget());
} catch (ParserException e) {
fail("Should not have failed to parse: " + e.getLocalizedMessage());
}
}
/**
* Tests that disposing an OCL instance does not dispose the resource
* created by a user. This is crucial for cleaning up the
* CacheAdapter in UML.
*/
public void test_dispose_resourceOwnedByClient_213045() {
UMLEnvironmentFactory factory = (UMLEnvironmentFactory) ocl.getEnvironment().getFactory();
ocl.dispose();
Resource res = UMLResource.Factory.INSTANCE.createResource(
URI.createURI("foo://foo"));
ocl = OCL.newInstance(factory.loadEnvironment(res));
helper = ocl.createOCLHelper();
helper.setContext(getMetaclass("Action"));
try {
Constraint constraint = helper.createInvariant("self.owner <> null");
assertSame(res, constraint.eResource());
Adapter adapter = new AdapterImpl();
constraint.getSpecification().eAdapters().add(adapter);
assertSame(constraint.getSpecification(), adapter.getTarget());
ocl.dispose();
assertTrue(res.isLoaded());
assertSame(res, constraint.eResource());
assertSame(constraint.getSpecification(), adapter.getTarget());
} catch (ParserException e) {
fail("Should not have failed to parse: " + e.getLocalizedMessage());
}
}
}