blob: 93299b586472f2378f9998a4924f2f1322aaf605 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 Borland Software 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:
* Radek Dvorak - initial API and implementation
* Axel Uhl (SAP AG) - Bug 342644
*******************************************************************************/
package org.eclipse.ocl.uml.tests;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.EnvironmentFactory;
import org.eclipse.ocl.EvaluationEnvironment;
import org.eclipse.ocl.EvaluationHaltedException;
import org.eclipse.ocl.EvaluationVisitor;
import org.eclipse.ocl.EvaluationVisitorDecorator;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.uml.OCL;
import org.eclipse.ocl.uml.OCL.Query;
import org.eclipse.ocl.uml.UMLEnvironment;
import org.eclipse.ocl.uml.UMLEnvironmentFactory;
import org.eclipse.ocl.uml.UMLEvaluationEnvironment;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.uml2.uml.CallOperationAction;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Constraint;
import org.eclipse.uml2.uml.EnumerationLiteral;
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.SendSignalAction;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.UMLFactory;
/**
* Tests usage of EvaluationHaltedException.
*
* @author radvorak
*/
@SuppressWarnings("nls")
public class EvaluationHaltedTest
extends AbstractTestSuite {
org.eclipse.ocl.expressions.OCLExpression<Classifier> queryExp;
org.eclipse.ocl.uml.OCL.Query query;
InterruptibleEnvFactory envFactory = new InterruptibleEnvFactory();
@Override
public OCL createOCL() {
return OCL.newInstance(envFactory);
}
@Override
protected void setUp() {
envFactory = new InterruptibleEnvFactory();
super.setUp();
helper.setContext(ocl.getEnvironment().getOCLStandardLibrary()
.getString());
try {
// testing on iteration to check exception propagation through
// nested #visitExpression() and operation call
this.queryExp = helper
.createQuery("Sequence { self }->collect(i | i.halt(self))");
assertNull(helper.getProblems());
} catch (Exception e) {
fail("Failed to parse: " + e.getLocalizedMessage());
}
query = (Query) ocl.createQuery(queryExp);
}
public void testBasicEvaluationHalted() {
assertNull(ocl.getEvaluationProblems());
Object result = ocl.evaluate(HALT_KIND_BASIC, queryExp);
assertInvalid(result);
Diagnostic evaluationProblems = ocl.getEvaluationProblems();
assertNotNull(evaluationProblems);
assertNull(evaluationProblems.getException());
assertEquals(evaluationProblems.getMessage(), HALT_KIND_BASIC);
// clear problems from previous run
ocl.evaluate(HALT_KIND_NONE, queryExp);
assertNull(ocl.getProblems());
}
public void testEvaluationHaltedOnError() {
assertNull(ocl.getEvaluationProblems());
Object result = ocl.evaluate(HALT_KIND_ON_ERROR, queryExp);
assertInvalid(result);
Diagnostic evaluationProblems = ocl.getEvaluationProblems();
assertNotNull(evaluationProblems);
Throwable exception = evaluationProblems.getException();
assertTrue(exception instanceof TestCauseException);
assertEquals(evaluationProblems.getMessage(), HALT_KIND_ON_ERROR);
// clear problems from previous run
ocl.evaluate(HALT_KIND_NONE, queryExp);
assertNull(ocl.getProblems());
}
public void testEvaluationHaltedWithCustomDiagnostic() {
assertNull(ocl.getEvaluationProblems());
Object result = ocl.evaluate(HALT_KIND_CUSTOM, queryExp);
assertInvalid(result);
Diagnostic evaluationProblems = ocl.getEvaluationProblems();
assertSame(CUSTOM_DIAGNOSTIC_INSTANCE, evaluationProblems);
assertNull(evaluationProblems.getException());
assertEquals(evaluationProblems.getMessage(),
CUSTOM_DIAGNOSTIC_INSTANCE.getMessage());
// clear problems from previous run
ocl.evaluate(HALT_KIND_NONE, queryExp);
assertNull(ocl.getProblems());
}
public void testOCLCaughtExcConvertedToInvalid() {
assertNull(ocl.getEvaluationProblems());
assertInvalid(ocl.evaluate(HALT_KIND_BASIC, queryExp));
assertNotNull(ocl.getEvaluationProblems());
// no diagnostic should be available
assertFalse(this.exceptionThrown);
Object result = ocl.evaluate(OCL_CATCHED_EXC, queryExp);
assertInvalid(result);
// check we it was thrown
assertTrue(this.exceptionThrown);
// no evaluation halt due to evaluation problems
assertNull(ocl.getEvaluationProblems());
}
public void testHaltedQuery() {
assertNull(OCLUtil.getEvaluationProblems(query));
assertListResult(query.evaluate(HALT_KIND_NONE), Collections.singletonList(HALT_KIND_NONE));
assertNull(OCLUtil.getEvaluationProblems(query));
envFactory.haltOnContextLessExecution = true;
assertInvalid(query.evaluate(HALT_KIND_NONE));
assertNotNull(OCLUtil.getEvaluationProblems(query));
assertNotNull(OCLUtil.getEvaluationProblems(query).getMessage().equals(
"Halt"));
envFactory.haltOnContextLessExecution = false;
// check we clear the problems on next evaluate
query.evaluate(HALT_KIND_NONE);
assertNull(OCLUtil.getEvaluationProblems(query));
}
public void testEvaluateListHaltedQuery() {
assertNull(OCLUtil.getEvaluationProblems(query));
List<?> listResult = query.evaluate(Arrays.asList(HALT_KIND_BASIC,
HALT_KIND_NONE, HALT_KIND_CUSTOM));
// check result
assertEquals(3, listResult.size());
assertInvalid(listResult.get(0));
assertListResult(listResult.get(1), Collections
.singletonList(HALT_KIND_NONE));
assertInvalid(listResult.get(2));
// check for problems
Diagnostic batchProblems = OCLUtil.getEvaluationProblems(query);
assertNotNull(batchProblems);
// expect only 2 problems, HALT_KIND_NONE does not stop evaluation
assertEquals(2, batchProblems.getChildren().size());
assertEquals(HALT_KIND_BASIC, batchProblems.getChildren().get(0)
.getMessage());
assertEquals(HALT_KIND_CUSTOM, batchProblems.getChildren().get(1)
.getMessage());
// test problems cleanup on next run
query.evaluate(Arrays.asList(HALT_KIND_NONE));
assertNull(OCLUtil.getEvaluationProblems(query));
}
public void testSelectListHaltedQuery() {
query = createQuery("Sequence { self }->exists(i | i.halt(self) = 'none')");
assertNull(OCLUtil.getEvaluationProblems(query));
List<?> listResult = query.select(Arrays.asList(HALT_KIND_BASIC,
HALT_KIND_NONE, HALT_KIND_CUSTOM));
// check result
assertEquals(1, listResult.size());
assertEquals(listResult.get(0), HALT_KIND_NONE);
// check for problems
Diagnostic batchProblems = OCLUtil.getEvaluationProblems(query);
assertNotNull(batchProblems);
// expect only 2 problems, HALT_KIND_NONE does not stop evaluation
assertEquals(2, batchProblems.getChildren().size());
assertEquals(HALT_KIND_BASIC, batchProblems.getChildren().get(0)
.getMessage());
assertEquals(HALT_KIND_CUSTOM, batchProblems.getChildren().get(1)
.getMessage());
// test problems cleanup on next run
assertEquals(Arrays.asList(HALT_KIND_NONE), query.select(Arrays
.asList(HALT_KIND_NONE)));
assertNull(OCLUtil.getEvaluationProblems(query));
}
public void testRejectListHaltedQuery() {
query = createQuery("Sequence { self }->one(i | i.halt(self) = 'none')");
assertNull(OCLUtil.getEvaluationProblems(query));
List<?> listResult = query.reject(Arrays.asList(HALT_KIND_BASIC,
HALT_KIND_NONE, HALT_KIND_CUSTOM));
// check result
assertEquals(2, listResult.size());
assertListResult(listResult, Arrays.asList(HALT_KIND_BASIC,
HALT_KIND_CUSTOM));
// check for problems
Diagnostic batchProblems = OCLUtil.getEvaluationProblems(query);
assertNotNull(batchProblems);
// expect only 2 problems, HALT_KIND_NONE does not stop evaluation
assertEquals(2, batchProblems.getChildren().size());
assertEquals(HALT_KIND_BASIC, batchProblems.getChildren().get(0)
.getMessage());
assertEquals(HALT_KIND_CUSTOM, batchProblems.getChildren().get(1)
.getMessage());
// test problems cleanup on next run
assertEquals(Collections.emptyList(), query.reject(Arrays
.asList(HALT_KIND_NONE)));
assertNull(OCLUtil.getEvaluationProblems(query));
}
public void testVariablesCleanup() {
// let expression
assertCleanupAfterHaltedEvaluation("let i : OclAny = true in halt('basic')");
assertCleanupAfterHaltedEvaluation("let i : OclAny = halt('basic') in true");
// any
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->any(i | i.halt('basic').size() > 0)");
// collect
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->collect(i | i.halt('basic'))");
// collectNested
assertCleanupAfterHaltedEvaluation("Bag { Bag{ 'foo'} }->collectNested(i | i.halt('basic'))");
// exists
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->exists(i | i.halt('basic').size() > 0)");
// forAll
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->forAll(i1, i2 | i1.halt('basic').size() > 0)");
// isUnique
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->isUnique(i | i.halt('basic'))");
// one
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->one(i | i.halt('basic').size() > 0)");
// reject
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->reject(i | i.halt('basic').size() > 0)");
// select
assertCleanupAfterHaltedEvaluation("Bag { 'foo' }->select(i | i.halt('basic').size() > 0)");
// sortedBy
assertCleanupAfterHaltedEvaluation("Bag { 'a', 'b' }->sortedBy(i | i.halt('basic'))");
}
//
// Framework part
//
static final String HALT_OPERATION_NAME = "halt";
static final String HALT_KIND_BASIC = "basic";
static final String HALT_KIND_ON_ERROR = "error";
static final String HALT_KIND_CUSTOM = "custom";
static final String HALT_KIND_NONE = "none";
static final String OCL_CATCHED_EXC = "ocl.catch";
static final Diagnostic CUSTOM_DIAGNOSTIC_INSTANCE = new BasicDiagnostic(
HALT_KIND_CUSTOM, 0, HALT_KIND_CUSTOM, null);
class InterruptibleEnv
extends UMLEnvironment {
Operation haltOperation;
// this constructor is used to initialize the root environment
InterruptibleEnv(EPackage.Registry registry, ResourceSet rset) {
super(registry, rset);
defineHaltOperation();
}
// this constructor is used to initialize child environments
InterruptibleEnv(InterruptibleEnv parent) {
super(parent);
// get the parent's custom operations
haltOperation = parent.haltOperation;
}
@Override
protected void setFactory(
EnvironmentFactory<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> factory) {
super.setFactory(factory);
}
private void defineHaltOperation() {
haltOperation = UMLFactory.eINSTANCE.createOperation();
haltOperation.setName(HALT_OPERATION_NAME);
haltOperation.setType(getOCLStandardLibrary().getString());
Parameter parm = UMLFactory.eINSTANCE.createParameter();
parm.setName("kind");
parm.setType(getOCLStandardLibrary().getString());
haltOperation.getOwnedParameters().add(parm);
haltOperation.setIsQuery(true);
addHelperOperation(getOCLStandardLibrary().getString(),
haltOperation);
}
}
class InterruptibleEvalEnv
extends UMLEvaluationEnvironment {
private Set<String> names = new HashSet<String>();
InterruptibleEvalEnv(UMLEnvironmentFactory factory) {
super(factory);
}
InterruptibleEvalEnv(
EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> parent) {
super(parent);
}
@Override
public boolean overrides(Operation operation, int opcode) {
return operation.getName().equals(HALT_OPERATION_NAME);
}
@Override
public Object callOperation(Operation operation, int opcode,
Object source, Object[] args) {
String kind = (String) args[0];
if (HALT_KIND_BASIC.equals(kind)) {
throw new EvaluationHaltedException(HALT_KIND_BASIC);
} else if (HALT_KIND_ON_ERROR.equals(kind)) {
TestCauseException error = new TestCauseException();
error.fillInStackTrace();
throw new EvaluationHaltedException(HALT_KIND_ON_ERROR, error);
} else if (HALT_KIND_CUSTOM.equals(kind)) {
throw new EvaluationHaltedException(CUSTOM_DIAGNOSTIC_INSTANCE);
} else if (OCL_CATCHED_EXC.equals(kind)) {
throw new TestCauseException();
}
assertEquals(HALT_KIND_NONE, kind);
return kind;
}
@Override
public void add(String name, Object value) {
names.add(name);
super.add(name, value);
}
@Override
public Object remove(String name) {
names.remove(name);
return super.remove(name);
}
@Override
public void clear() {
names.clear();
}
boolean isEmpty() {
return names.isEmpty();
}
}
class InterruptibleEnvFactory
extends UMLEnvironmentFactory {
public boolean haltOnContextLessExecution = false;
@Override
public InterruptibleEnv createEnvironment() {
InterruptibleEnv result = new InterruptibleEnv(
getEPackageRegistry(), getResourceSet());
result.setFactory(this);
return result;
}
@Override
public Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> createEnvironment(
Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> parent) {
if (!(parent instanceof InterruptibleEnv)) {
throw new IllegalArgumentException(
"Parent environment must be my environment: " + parent);
}
InterruptibleEnv result = new InterruptibleEnv(
(InterruptibleEnv) parent);
result.setFactory(this);
return result;
}
@Override
public EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> createEvaluationEnvironment() {
return new InterruptibleEvalEnv(this);
}
@Override
public EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> createEvaluationEnvironment(
EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> parent) {
return new InterruptibleEvalEnv(parent);
}
@Override
public EvaluationVisitor<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> createEvaluationVisitor(
Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> env,
EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> evalEnv,
Map<? extends Class, ? extends Set<? extends EObject>> extentMap) {
EvaluationVisitor<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> baseVisitor = super
.createEvaluationVisitor(env, evalEnv, extentMap);
if (haltOnContextLessExecution) {
return new EvaluationVisitorDecorator<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject>(
baseVisitor) {
@Override
public Object visitExpression(
org.eclipse.ocl.expressions.OCLExpression<Classifier> expression) {
throw new EvaluationHaltedException("Halt");
}
};
}
return baseVisitor;
}
}
private boolean exceptionThrown = false;
private class TestCauseException
extends RuntimeException {
private static final long serialVersionUID = -2513266245112148087L;
public TestCauseException() {
exceptionThrown = true;
}
}
private org.eclipse.ocl.uml.OCL.Query createQuery(String expression) {
try {
return (Query) ocl.createQuery(helper.createQuery(expression));
} catch (ParserException e) {
// processed bellow
fail(e.getLocalizedMessage());
}
return null;
}
private void assertCleanupAfterHaltedEvaluation(String testExpression) {
query = createQuery(testExpression);
InterruptibleEvalEnv evalEnv = (InterruptibleEvalEnv) query
.getEvaluationEnvironment();
Object result = query.evaluate();
assertInvalid(result);
// expect the evaluation environment cleared
assertTrue(evalEnv.isEmpty());
Diagnostic evaluationProblems = OCLUtil.getEvaluationProblems(query);
assertNotNull(evaluationProblems);
assertTrue(evaluationProblems.getChildren().isEmpty());
assertEquals(HALT_KIND_BASIC, evaluationProblems.getMessage());
}
private static void assertListResult(Object result, List<?> expectedElements) {
assertTrue("result must be a List", result instanceof List<?>);
assertNotNull("expectedElements must be a List", result instanceof List<?>);
List<?> resultCollection = (List<?>) result;
assertEquals(expectedElements, resultCollection);
}
}