Added tests for short-circuiting EVL and ensured condition is revalidated after an unsatisfied constraint is found.
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/Constraint.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/Constraint.java
index d384888..fd85270 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/Constraint.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/Constraint.java
@@ -181,6 +181,9 @@
fixInstance.setSelf(self);
unsatisfiedConstraintFixes.add(fixInstance);
}
+
+ // Update the short-circuiting logic on fail
+ context.shouldShortCircuit(this);
}
// Don't dispose the frame we leave if unsatisfied because it may be needed for fix parts,
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/ConstraintContext.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/ConstraintContext.java
index 1c19c2b..c11fd36 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/ConstraintContext.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/dom/ConstraintContext.java
@@ -65,7 +65,7 @@
* @since 1.6
*/
public boolean shouldBeChecked(Object modelElement, IEvlContext context) throws EolRuntimeException {
- return !context.shouldShortCircuit(this) && !isLazy(context) && appliesTo(modelElement, context, false);
+ return !isLazy(context) && appliesTo(modelElement, context, false);
}
public boolean appliesTo(Object object, IEvlContext context) throws EolRuntimeException {
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/EvlContext.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/EvlContext.java
index 7b2ec1d..fa1ad65 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/EvlContext.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/EvlContext.java
@@ -12,10 +12,10 @@
import java.util.HashSet;
import java.util.Set;
import org.eclipse.epsilon.common.module.IModule;
-import org.eclipse.epsilon.eol.dom.AnnotatableModuleElement;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.erl.execute.context.ErlContext;
import org.eclipse.epsilon.evl.IEvlModule;
+import org.eclipse.epsilon.evl.dom.Constraint;
import org.eclipse.epsilon.evl.execute.UnsatisfiedConstraint;
import org.eclipse.epsilon.evl.trace.ConstraintTrace;
@@ -70,9 +70,9 @@
}
@Override
- public boolean shouldShortCircuit(AnnotatableModuleElement rule) throws EolRuntimeException {
+ public boolean shouldShortCircuit(Constraint constraint) throws EolRuntimeException {
if (!terminate) {
- terminate = IEvlContext.super.shouldShortCircuit(rule);
+ terminate = IEvlContext.super.shouldShortCircuit(constraint);
}
return terminate;
}
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/IEvlContext.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/IEvlContext.java
index 6971c8f..560d2b1 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/IEvlContext.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/IEvlContext.java
@@ -12,13 +12,11 @@
import java.util.*;
import java.util.stream.Collectors;
-import org.eclipse.epsilon.eol.dom.AnnotatableModuleElement;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.erl.execute.context.IErlContext;
import org.eclipse.epsilon.evl.IEvlModule;
import org.eclipse.epsilon.evl.dom.Constraint;
-import org.eclipse.epsilon.evl.dom.ConstraintContext;
import org.eclipse.epsilon.evl.execute.UnsatisfiedConstraint;
import org.eclipse.epsilon.evl.trace.ConstraintTrace;
@@ -76,31 +74,21 @@
* {@link #isShortCircuiting()} flag is enabled, or if the specified module element has been annotated
* with a termination criteria and an unsatisfied constraint containing the type is already present.
*
- * @param rule The rule with the termination annotation.
+ * @param Constraint The rule with the termination annotation.
* @return Whether termination should be suspended.
* @throws EolRuntimeException
* @throws IllegalArgumentException If the rule parameter is not an appropriate type.
* @since 1.6
*/
- default boolean shouldShortCircuit(AnnotatableModuleElement rule) throws EolRuntimeException {
+ default boolean shouldShortCircuit(Constraint constraint) throws EolRuntimeException {
if (isShortCircuiting() && !getUnsatisfiedConstraints().isEmpty()) {
return true;
}
- if (rule.getBooleanAnnotationValue("terminate", this)) {
- Collection<Constraint> constraints;
- if (rule instanceof ConstraintContext) {
- constraints = ((ConstraintContext) rule).getConstraints();
- }
- else if (rule instanceof Constraint) {
- constraints = Collections.singleton((Constraint) rule);
- }
- else {
- throw new IllegalArgumentException("Unexpected module element: "+rule);
- }
+ if (constraint.getBooleanAnnotationValue("terminate", this)) {
return getUnsatisfiedConstraints()
.stream()
.map(UnsatisfiedConstraint::getConstraint)
- .filter(constraints::contains)
+ .filter(constraint::equals)
.findAny()
.isPresent();
}
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/concurrent/EvlContextParallel.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/concurrent/EvlContextParallel.java
index e5d5202..0c97a14 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/concurrent/EvlContextParallel.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/execute/context/concurrent/EvlContextParallel.java
@@ -12,12 +12,12 @@
import java.util.Set;
import org.eclipse.epsilon.common.concurrent.ConcurrencyUtils;
import org.eclipse.epsilon.common.module.IModule;
-import org.eclipse.epsilon.eol.dom.AnnotatableModuleElement;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.concurrent.EolNestedParallelismException;
import org.eclipse.epsilon.eol.execute.context.concurrent.IEolContextParallel;
import org.eclipse.epsilon.erl.execute.context.concurrent.ErlContextParallel;
import org.eclipse.epsilon.evl.concurrent.EvlModuleParallel;
+import org.eclipse.epsilon.evl.dom.Constraint;
import org.eclipse.epsilon.evl.execute.UnsatisfiedConstraint;
import org.eclipse.epsilon.evl.execute.context.IEvlContext;
import org.eclipse.epsilon.evl.trace.ConstraintTrace;
@@ -111,9 +111,9 @@
}
@Override
- public boolean shouldShortCircuit(AnnotatableModuleElement rule) throws EolRuntimeException {
+ public boolean shouldShortCircuit(Constraint constraint) throws EolRuntimeException {
if (!terminate) {
- terminate = IEvlContextParallel.super.shouldShortCircuit(rule);
+ terminate = IEvlContextParallel.super.shouldShortCircuit(constraint);
}
return terminate;
}
diff --git a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/launch/EvlRunConfiguration.java b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/launch/EvlRunConfiguration.java
index 7b76516..baeb26e 100644
--- a/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/launch/EvlRunConfiguration.java
+++ b/plugins/org.eclipse.epsilon.evl.engine/src/org/eclipse/epsilon/evl/launch/EvlRunConfiguration.java
@@ -24,16 +24,70 @@
*/
public class EvlRunConfiguration extends IErlRunConfiguration {
+ @SuppressWarnings("unchecked")
+ public static class Builder<R extends EvlRunConfiguration, B extends Builder<R, B>> extends IErlRunConfiguration.Builder<R, B> {
+
+ public boolean optimizeTrace, optimizeConstraints, shortCircuit;
+
+ public B withShortCircuiting() {
+ return withShortCircuiting(true);
+ }
+ public B withShortCircuiting(boolean sc) {
+ this.shortCircuit = sc;
+ return (B) this;
+ }
+
+ public B withOptimizeConstraints() {
+ return withOptimizeConstraints(true);
+ }
+ public B withOptimizeConstraints(boolean optimize) {
+ this.optimizeConstraints = optimize;
+ return (B) this;
+ }
+
+ public B withOptimizeConstraintTrace() {
+ return withOptimizeConstraintTrace(true);
+ }
+ public B withOptimizeConstraintTrace(boolean optimize) {
+ this.optimizeTrace = optimize;
+ return (B) this;
+ }
+
+ protected Builder() {
+ super();
+ }
+ protected Builder(Class<R> runConfigClass) {
+ super(runConfigClass);
+ }
+ }
+
public static Builder<? extends EvlRunConfiguration, ?> Builder() {
- return Builder(EvlRunConfiguration.class);
+ return new Builder<>();
+ }
+
+ public EvlRunConfiguration(IErlRunConfiguration.Builder<? extends EvlRunConfiguration, ?> builder) {
+ super(builder);
}
public EvlRunConfiguration(Builder<? extends EvlRunConfiguration, ?> builder) {
super(builder);
+ IEvlModule module = getModule();
+ if (module instanceof EvlModule) {
+ ((EvlModule) module).setOptimizeConstraints(builder.optimizeConstraints);
+ }
+ module.getContext().setShortCircuit(builder.shortCircuit);
+ module.getContext().setOptimizeConstraintTrace(builder.optimizeTrace);
}
public EvlRunConfiguration(EvlRunConfiguration other) {
super(other);
+ IEvlModule module = getModule(), otherModule = other.getModule();
+ if (module instanceof EvlModule && otherModule instanceof EvlModule) {
+ ((EvlModule) module).setOptimizeConstraints(((EvlModule) otherModule).isOptimizeConstraints());
+ }
+ IEvlContext context = module.getContext(), otherContext = otherModule.getContext();
+ context.setShortCircuit(otherContext.isShortCircuiting());
+ context.setOptimizeConstraintTrace(otherContext.isOptimizeConstraintTrace());
}
@Override
diff --git a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlAcceptanceTestUtil.java b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlAcceptanceTestUtil.java
index 4558469..b82d8d0 100644
--- a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlAcceptanceTestUtil.java
+++ b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlAcceptanceTestUtil.java
@@ -19,7 +19,6 @@
import java.util.function.Supplier;
import org.eclipse.epsilon.common.util.CollectionUtil;
import org.eclipse.epsilon.eol.engine.test.acceptance.util.EolAcceptanceTestUtil;
-import org.eclipse.epsilon.eol.launch.IEolRunConfiguration;
import org.eclipse.epsilon.evl.*;
import org.eclipse.epsilon.evl.concurrent.*;
import org.eclipse.epsilon.evl.concurrent.experimental.*;
@@ -116,7 +115,7 @@
IEvlModule evlStd = moduleGetter.get();
scenarios.add(
- IEolRunConfiguration.Builder(EvlRunConfiguration.class)
+ EvlRunConfiguration.Builder()
.withScript(EvlTests.getTestScript(evlStd).toPath())
.withModel(EvlTests.getTestModel(false))
.withModule(evlStd)
diff --git a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlTests.java b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlTests.java
index 81a9fb6..6675947 100644
--- a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlTests.java
+++ b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/EvlTests.java
@@ -10,6 +10,7 @@
package org.eclipse.epsilon.evl.engine.test.acceptance;
import static org.junit.Assert.*;
+import static org.junit.Assume.*;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
@@ -21,15 +22,20 @@
import org.eclipse.epsilon.eol.execute.context.FrameStack;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.models.IModel;
+import org.eclipse.epsilon.evl.EvlModule;
import org.eclipse.epsilon.evl.IEvlModule;
+import org.eclipse.epsilon.evl.concurrent.EvlModuleParallel;
import org.eclipse.epsilon.evl.dom.Constraint;
import org.eclipse.epsilon.evl.dom.ConstraintContext;
import org.eclipse.epsilon.evl.dom.ConstraintSelectTransfomer;
import org.eclipse.epsilon.evl.execute.FixInstance;
import org.eclipse.epsilon.evl.execute.UnsatisfiedConstraint;
import org.eclipse.epsilon.evl.execute.context.*;
+import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
@@ -55,6 +61,9 @@
@Parameter
public Supplier<? extends IEvlModule> moduleGetter;
+ @Rule
+ public TestName testName = new TestName();
+
private IEvlModule module;
@Parameters(name = "{0}")
@@ -84,7 +93,7 @@
}
private static File getTestScript(String scriptName, IEvlModule module) {
- if (scriptName.equals("test")) {
+ if ("test".equals(scriptName)) {
FrameStack frameStack = module.getContext().getFrameStack();
frameStack.putGlobal(
Variable.createReadOnlyVariable("frameStack", frameStack),
@@ -113,8 +122,7 @@
}
private IEvlModule loadEVL(String scriptName) throws Exception {
- module = moduleGetter.get();
- loadEVL(module, false, scriptName);
+ loadEVL(module = moduleGetter.get(), false, scriptName);
return module;
}
@@ -283,4 +291,28 @@
}
fail("No exception thrown! "+module.getClass().getName());
}
+
+ @Before
+ public void assumeLegal() throws Exception {
+ module = moduleGetter.get();
+ if (testName.getMethodName().contains("ShortCircuit")) {
+ assumeTrue(module.getClass().equals(EvlModule.class));
+ assumeFalse(module instanceof EvlModuleParallel);
+ }
+ }
+
+ @Test
+ public void testGlobalShortCircuitExecution() throws Exception {
+ (module = loadEVL("shortCircuit")).getContext().setShortCircuit(true);
+ assertEquals(1, module.execute().size());
+ assertUnsatisfiedConstraints(1, "t_a", "TerminateOnFailNoAnnotation");
+ }
+
+ @Test
+ public void testAnnotatedShortCircuitExecution() throws Exception {
+ (module = loadEVL("shortCircuit")).getContext().setShortCircuit(false);
+ assertEquals(24, module.execute().size());
+ assertUnsatisfiedConstraints(1, "t_c", "TerminateOnFailWithAnnotation");
+ assertUnsatisfiedConstraints(0, "t_c", "NeverCheckedAfterTerminate");
+ }
}
diff --git a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/shortCircuit.evl b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/shortCircuit.evl
new file mode 100644
index 0000000..d0a0198
--- /dev/null
+++ b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/shortCircuit.evl
@@ -0,0 +1,175 @@
+@cached
+operation t_b foo(expr : Integer) : Boolean {
+ return expr <= 0;
+}
+
+operation t_c foo() : String {
+ return false.asString();
+}
+
+@cached
+operation returnFalse() : Boolean {
+ return false;
+}
+
+context t_a {
+
+ constraint SelfVisibleInGuard {
+ guard: self.isDefined()
+ check: true
+ }
+
+ constraint TerminateOnFailNoAnnotation {
+ check: false
+ }
+}
+
+$parallel preVar.length() > 3
+context t_a {
+
+ guard: not self.isDefined()
+
+ constraint NeverChecked {
+ check : false
+ }
+}
+
+context t_b {
+
+ @terminate
+ constraint DontTerminateOnFailWithAnnotation {
+ check: self.satisfies("AlwaysTrue")
+ }
+
+ constraint AlwaysFalse {
+ check: false
+ }
+
+ constraint AlwaysTrue {
+ check: true
+ }
+
+ constraint ElementOperationInConstraint {
+ check: self.foo(1 + 2)
+ }
+
+ @terminate
+ constraint DontTerminate {
+ guard: false
+ check {
+ throw "IllegalStateException in DontTerminate";
+ }
+ }
+
+ constraint NeverChecked {
+ guard: self.isTypeOf(t_c)
+ check: false
+ }
+
+ constraint RequiresNonLazyConstraint {
+ guard: self.satisfies("AlwaysFalse")
+ check {
+ return 1 > 0;
+ }
+ }
+
+ @lazy
+ constraint LazyWithGuard {
+ guard: "true".asBoolean()
+ check {
+ return true;
+ }
+ }
+
+ constraint RequiresLazyConstraint {
+ guard: self.satisfies("LazyWithGuard")
+ check: false
+ }
+
+ constraint RequiresContextlessLazy {
+ check {return self.satisfies("LazyContextlessCallsLazy");}
+ }
+
+ constraint InsaneLazyChain {
+ guard {return not self.satisfies("RequiresContextlessLazy");}
+ check: false
+ }
+}
+
+context t_c {
+
+ guard {
+ self.~extendedProperty = self.isTypeOf(t_c);
+ return true;
+ }
+
+ constraint WrongType {
+ check: self.isTypeOf(t_b)
+ }
+
+ constraint AlwaysTrueOperation {
+ check: not self.foo().asBoolean()
+ }
+
+ constraint AlwaysFalseOperation {
+ check: self.foo().asBoolean()
+ }
+
+ constraint SatisfiesOneLazyAndNonLazy {
+ guard: self.satisfiesOne("LazyAlwaysFalseOperation", "AlwaysTrueOperation", "AlwaysFalseOperation")
+ check: false
+ }
+
+ constraint SatisfiesAllLazyAndNonLazy {
+ check: self.satisfiesAll("LazyAlwaysFalseOperation", "AlwaysTrueOperation", "AlwaysFalseOperation")
+ }
+
+ constraint NeverCalledLazyGuard {
+ guard: self.satisfies("LazyAlwaysFalseOperation")
+ check: false
+ }
+
+ @lazy
+ constraint LazyAlwaysFalseOperation {
+ check: self.foo().asBoolean()
+ }
+
+ constraint ExtendedPropertyCanBeAccessed {
+ check: self.~extendedProperty
+ }
+}
+
+@lazy
+constraint LazyContextlessNeverCalled {
+ check: returnFalse()
+}
+
+@lazy
+constraint LazyContextlessCallsLazy {
+ check: self.satisfies("LazyContextlessDependedOn")
+}
+
+@lazy
+constraint LazyContextlessDependedOn {
+ guard: true
+ check: false
+}
+
+constraint Contextless {
+ check: t_a.all.size + t_b.all.size > 2
+}
+
+context t_c {
+
+ @terminate
+ constraint TerminateOnFailWithAnnotation {
+ check: false
+ }
+
+ constraint NeverCheckedAfterTerminate {
+ guard {
+ throw "Constraint 'NeverCheckedAfterTerminate' was executed!";
+ }
+ check: false
+ }
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/test.evl b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/test.evl
index 4ea7862..0f0914f 100644
--- a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/test.evl
+++ b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/scripts/test.evl
@@ -40,7 +40,7 @@
constraint GuardVariableInvisibleInBlock {
guard {
var condition = true;
- return true;
+ return condition;
}
check {
return frameStack.get("condition").isUndefined();
@@ -86,6 +86,14 @@
context t_b {
+ @terminate
+ constraint DontTerminate {
+ guard: false
+ check {
+ throw "IllegalStateException in DontTerminate";
+ }
+ }
+
$parallel t_b.all.size() > 1
constraint EolTest2 {
guard: '>'.isJavaToken()
diff --git a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/shortCircuit/EvlShortCircuitingTests.java b/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/shortCircuit/EvlShortCircuitingTests.java
deleted file mode 100644
index ffe7b8c..0000000
--- a/tests/org.eclipse.epsilon.evl.engine.test.acceptance/src/org/eclipse/epsilon/evl/engine/test/acceptance/shortCircuit/EvlShortCircuitingTests.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*********************************************************************
- * Copyright (c) 2019 The University of York.
- *
- * This program and the accompanying materials are made
- * available under the terms of the Eclipse Public License 2.0
- * which is available at https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
-**********************************************************************/
-package org.eclipse.epsilon.evl.engine.test.acceptance.shortCircuit;
-
-import static org.junit.Assert.*;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-/**
- *
- *
- * @author Sina Madani
- * @since 1.6
- */
-public class EvlShortCircuitingTests {
-
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- }
-
- @Test
- public void test() {
- fail("Not yet implemented");
- }
-
-}