blob: cdd5eccc4439b55d2656a42474d53ce206bcbed8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2018 IBM Corporation 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
*******************************************************************************/
package org.eclipse.ocl.ecore.tests;
import java.util.Arrays;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironment;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.OCL;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.internal.UMLReflectionImpl;
import org.eclipse.ocl.helper.Choice;
import org.eclipse.ocl.helper.ChoiceKind;
import org.eclipse.ocl.helper.ConstraintKind;
import org.eclipse.ocl.utilities.UMLReflection;
/**
* Tests for state expressions.
*
* @author Christian W. Damus (cdamus)
*/
@SuppressWarnings("nls")
public class StatesTest
extends AbstractTestSuite {
/**
* Tests the parsing of the oclIsInState() expression.
*/
public void test_isInState() {
helper.setContext(fruit);
try {
// test implicit and explicit source
helper.createInvariant(
"oclIsInState(Ripe) implies not self.oclIsInState(Bad)");
// test source of different type than context (and also implicit)
helper.createInvariant(
"Apple.allInstances()->forAll(not oclIsInState(Bad::Rotten))");
} catch (Exception e) {
fail("Failed to parse: " + e.getLocalizedMessage());
}
try {
// just to make sure that the second test, above, wasn't a fluke.
// Fruit doesn't have this state
helper.createInvariant(
"Fruit.allInstances()->forAll(not oclIsInState(Bad::Rotten))");
fail("Should have failed to parse non-existent state");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
}
/**
* Tests some validation of the oclIsInState() expression.
*/
public void test_isInState_validation() {
helper.setContext(fruit);
try {
// not enough arguments
helper.createInvariant(
"self.oclIsInState()");
fail("Should have failed to parse empty arglist");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
try {
// too many arguments
helper.createInvariant(
"Apple.allInstances()->forAll(oclIsInState(Bad, Rotten))");
fail("Should have failed to parse overabundant arglist");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
try {
// wrong kind of argument
helper.createInvariant(
"self.oclIsInState(color <> Color::black)");
fail("Should have failed to parse arg of wrong type");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
try {
// another kind of wrong kind of argument
helper.createInvariant(
"self.oclIsInState(OclTest::Fruit)");
fail("Should have failed to parse arg of wrong type");
} catch (Exception e) {
// success
debugPrintln("Got expected error: " + e.getLocalizedMessage());
}
}
/**
* Tests the content-assist for states.
*/
public void test_stateContentAssist() {
helper.setContext(fruit);
try {
// simplest case of first path name part completion
List<Choice> choices = helper.getSyntaxHelp(
ConstraintKind.INVARIANT, "self.oclIsInState(");
assertNotNull(choices);
assertChoice(choices, ChoiceKind.STATE, "Ripe");
assertChoice(choices, ChoiceKind.STATE, "Bad");
// case of no more completions (path is already complete)
choices = helper.getSyntaxHelp(
ConstraintKind.INVARIANT, "self.oclIsInState(Ripe::");
assertNotNull(choices);
assertTrue(choices.isEmpty());
// explicit source of non-self type
choices = helper.getSyntaxHelp(
ConstraintKind.INVARIANT,
"Apple.allInstances()->forAll(a : Apple | a.oclIsInState(");
assertNotNull(choices);
assertChoice(choices, ChoiceKind.STATE, "Ripe");
assertChoice(choices, ChoiceKind.STATE, "Bad");
// implicit source of non-self type
// choices = helper.getSyntaxHelp(
// ConstraintType.INVARIANT,
// "Apple.allInstances()->forAll(oclIsInState(");
// assertNotNull(choices);
// assertChoice(choices, ChoiceType.STRUCTURAL_FEATURE, "Ripe");
// assertChoice(choices, ChoiceType.STRUCTURAL_FEATURE, "Bad");
// available second-level completions
choices = helper.getSyntaxHelp(
ConstraintKind.INVARIANT,
"Apple.allInstances()->forAll(a : Apple | a.oclIsInState(Bad::");
assertNotNull(choices);
assertChoice(choices, ChoiceKind.STATE, "Bruised");
assertChoice(choices, ChoiceKind.STATE, "Rotten");
} catch (Exception e) {
fail("Parse failed: " + e.getLocalizedMessage());
}
}
//
// Test framework
//
@Override
protected OCL createOCL() {
return OCL.newInstance(new StatefulFruitEnvironmentFactory(this));
}
private static final List<String> FRUIT_BAD = Arrays.asList(new String[] {"Bad"});
public static class StatefulFruitEnvironmentFactory extends EcoreEnvironmentFactory {
protected final AbstractTestSuite suite;
public StatefulFruitEnvironmentFactory(AbstractTestSuite suite) {
this.suite = suite;
}
@Override
public EcoreEnvironment createEnvironment() {
return new StatefulFruitEnvironment(this, suite);
}
@Override
public EcoreEnvironment createEnvironment(
Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> parent) {
return new StatefulFruitEnvironment(this, parent, suite);
}
}
private static class StatefulFruitEnvironment extends EcoreEnvironment {
protected final AbstractTestSuite suite;
private EObject fruitRipe;
private EObject fruitBad;
private EObject appleBruised;
private EObject appleRotten;
public StatefulFruitEnvironment(StatefulFruitEnvironmentFactory factory, AbstractTestSuite suite) {
super(factory, null);
this.suite = suite;
setContextPackage(suite.fruitPackage);
initStates();
}
public StatefulFruitEnvironment(
StatefulFruitEnvironmentFactory factory,
Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> parent, AbstractTestSuite suite) {
super(parent);
this.suite = suite;
initStates();
}
private void initStates() {
fruitRipe = EcoreFactory.eINSTANCE.createEObject();
((InternalEObject) fruitRipe).eSetClass(
(EClass) getOCLStandardLibrary().getState());
fruitBad = EcoreFactory.eINSTANCE.createEObject();
((InternalEObject) fruitBad).eSetClass(
(EClass) getOCLStandardLibrary().getState());
appleBruised = EcoreFactory.eINSTANCE.createEObject();
((InternalEObject) appleBruised).eSetClass(
(EClass) getOCLStandardLibrary().getState());
appleRotten = EcoreFactory.eINSTANCE.createEObject();
((InternalEObject) appleRotten).eSetClass(
(EClass) getOCLStandardLibrary().getState());
}
@Override
protected void collectStates(EClassifier owner, List<String> pathPrefix, List<EObject> states) {
if (owner == suite.fruit) {
if (pathPrefix.isEmpty()) {
states.add(fruitRipe);
states.add(fruitBad);
}
} else if (owner == suite.apple) {
if (pathPrefix.isEmpty()) {
states.add(fruitBad);
} else if (pathPrefix.equals(FRUIT_BAD)) {
states.add(appleBruised);
states.add(appleRotten);
}
}
}
@Override
public UMLReflection<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> getUMLReflection() {
return new UMLReflectionImpl() {
@Override
public String getName(Object namedElement) {
if (namedElement == fruitRipe) {
return "Ripe";
} else if (namedElement == fruitBad) {
return "Bad";
} else if (namedElement == appleBruised) {
return "Bruised";
} else if (namedElement == appleRotten) {
return "Rotten";
} else {
return super.getName(namedElement);
}
}};
}
}
}