From e232eb8d0681fa6806ec6b3e23e4183d245b04bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Mon, 12 Apr 2021 13:53:27 +0200
Subject: [PATCH 1/8] Predefined path generator and stop condition added
---
.../PredefinedPathStopCondition.java | 19 +++++++++
.../core/generator/PredefinedPath.java | 40 +++++++++++++++++++
.../org/graphwalker/core/machine/Context.java | 4 ++
.../core/machine/ExecutionContext.java | 12 ++++++
.../org/graphwalker/core/model/Model.java | 19 +++++++++
.../dsl/antlr/generator/GeneratorLoader.java | 12 +++---
6 files changed, 99 insertions(+), 7 deletions(-)
create mode 100644 graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
create mode 100644 graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
new file mode 100644
index 000000000..02d556a29
--- /dev/null
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
@@ -0,0 +1,19 @@
+package org.graphwalker.core.condition;
+
+public class PredefinedPathStopCondition extends StopConditionBase {
+
+ public PredefinedPathStopCondition(String value) {
+ super(value);
+ }
+
+ @Override
+ public boolean isFulfilled() {
+ return getContext().getPredefinedPathCurrentElementIndex() == getContext().getModel().getPredefinedPath().size();
+ }
+
+ @Override
+ public double getFulfilment() {
+ return (double) getContext().getPredefinedPathCurrentElementIndex() / getContext().getModel().getPredefinedPath().size();
+ }
+
+}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
new file mode 100644
index 000000000..5745bd663
--- /dev/null
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
@@ -0,0 +1,40 @@
+package org.graphwalker.core.generator;
+
+import org.graphwalker.core.condition.StopCondition;
+import org.graphwalker.core.machine.Context;
+import org.graphwalker.core.model.Element;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class PredefinedPath extends PathGeneratorBase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PredefinedPath.class);
+
+ public PredefinedPath(StopCondition stopCondition) {
+ setStopCondition(stopCondition);
+ }
+
+ @Override
+ public Context getNextStep() {
+ Context context = super.getNextStep();
+ Element currentElement = context.getCurrentElement();
+ List elements = context.filter(context.getModel().getElements(currentElement));
+ if (elements.isEmpty()) {
+ LOG.error("currentElement: " + currentElement);
+ LOG.error("context.getModel().getElements(): " + context.getModel().getElements());
+ throw new NoPathFoundException(context.getCurrentElement());
+ }
+ int predefinedPathCurrentElementIndex = context.getPredefinedPathCurrentElementIndex();
+ Element nextElement = context.getModel().getPredefinedPath().get(predefinedPathCurrentElementIndex + 1);
+ context.setCurrentElement(nextElement);
+ context.setPredefinedPathCurrentElementIndex(predefinedPathCurrentElementIndex + 1);
+ return context;
+ }
+
+ @Override
+ public boolean hasNextStep() {
+ return !getStopCondition().isFulfilled();
+ }
+}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java b/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
index 719193bc6..402dc4963 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
@@ -76,6 +76,10 @@ public interface Context {
Context setNextElement(Element nextElement);
+ Integer getPredefinedPathCurrentElementIndex();
+
+ Context setPredefinedPathCurrentElementIndex(Integer predefinedPathCurrentElementIndex);
+
List getRequirements();
List getRequirements(RequirementStatus status);
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java b/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
index 5bcff1354..dd25eb14f 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
@@ -71,6 +71,8 @@ public abstract class ExecutionContext implements Context {
private Element nextElement;
private Element lastElement;
+ private Integer predefinedPathCurrentElementIndex;
+
private String REGEXP_GLOBAL = "global\\.";
private final Map, Object> algorithms = new HashMap<>();
@@ -80,6 +82,7 @@ public abstract class ExecutionContext implements Context {
public ExecutionContext() {
executionEnvironment = org.graalvm.polyglot.Context.newBuilder().allowAllAccess(true).build();
executionEnvironment.getBindings("js").putMember(getClass().getSimpleName(), this);
+ predefinedPathCurrentElementIndex = 0;
}
public ExecutionContext(Model model, PathGenerator pathGenerator) {
@@ -179,6 +182,15 @@ public Context setNextElement(Element nextElement) {
return this;
}
+ public Integer getPredefinedPathCurrentElementIndex() {
+ return predefinedPathCurrentElementIndex;
+ }
+
+ public Context setPredefinedPathCurrentElementIndex(Integer predefinedPathCurrentElementIndex) {
+ this.predefinedPathCurrentElementIndex = predefinedPathCurrentElementIndex;
+ return this;
+ }
+
public Context setRequirementStatus(Requirement requirement, RequirementStatus requirementStatus) {
requirements.put(requirement, requirementStatus);
return this;
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
index ca593f153..ccf9d778b 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
@@ -54,6 +54,7 @@ public class Model extends BuilderBase {
private List vertices = new ArrayList<>();
private List edges = new ArrayList<>();
private List actions = new ArrayList<>();
+ private List predefinedPath = new ArrayList<>();
/**
* Create a new Model
@@ -224,6 +225,18 @@ public List getEdges() {
return edges;
}
+ public Model setPredefinedPath(List predefinedPath) {
+ if (!getEdges().containsAll(predefinedPath)) {
+ // TODO not all edges from predefined path can be found
+ }
+ this.predefinedPath = predefinedPath;
+ return this;
+ }
+
+ public List getPredefinedPath() {
+ return predefinedPath;
+ }
+
/**
* Creates an immutable model from this model.
*
@@ -253,6 +266,7 @@ public static class RuntimeModel extends RuntimeBase {
private final Map> inEdgesByVertexCache;
private final Map> outEdgesByVertexCache;
private final Map> sharedStateCache;
+ private final List predefinedPath;
private RuntimeModel(Model model) {
super(model.getId(), model.getName(), model.getActions(), model.getRequirements(), model.getProperties());
@@ -266,6 +280,7 @@ private RuntimeModel(Model model) {
this.elementsByNameCache = createElementsByNameCache();
this.elementsByElementCache = createElementsByElementCache(elementsCache, outEdgesByVertexCache);
this.sharedStateCache = createSharedStateCache();
+ this.predefinedPath = BuilderFactory.build(model.getPredefinedPath());
}
/**
@@ -379,6 +394,10 @@ public List findEdges(String name) {
return edgesByNameCache.get(name);
}
+ public List getPredefinedPath() {
+ return predefinedPath;
+ }
+
/**
* Searches the model for any element matching the search string.
*
diff --git a/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java b/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
index 0d49eb2b2..6261a34ba 100644
--- a/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
+++ b/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
@@ -32,13 +32,7 @@
import java.util.concurrent.TimeUnit;
import org.graphwalker.core.condition.*;
-import org.graphwalker.core.generator.AStarPath;
-import org.graphwalker.core.generator.CombinedPath;
-import org.graphwalker.core.generator.PathGenerator;
-import org.graphwalker.core.generator.QuickRandomPath;
-import org.graphwalker.core.generator.RandomPath;
-import org.graphwalker.core.generator.ShortestAllPaths;
-import org.graphwalker.core.generator.WeightedRandomPath;
+import org.graphwalker.core.generator.*;
import org.graphwalker.dsl.generator.GeneratorParser;
import org.graphwalker.dsl.generator.GeneratorParserBaseListener;
@@ -83,6 +77,8 @@ public void exitStopCondition(GeneratorParser.StopConditionContext ctx) {
stopConditions.add(new RequirementCoverage(Integer.parseInt(ctx.getChild(2).getText())));
} else if ("length".equals(conditionName)) {
stopConditions.add(new Length(Integer.parseInt(ctx.getChild(2).getText())));
+ } else if ("predefined_path".equals(conditionName) || "predefinedpath".equals(conditionName)) {
+ stopConditions.add(new PredefinedPathStopCondition(ctx.getChild(2).getText()));
}
}
@@ -111,6 +107,8 @@ public void exitGenerator(GeneratorParser.GeneratorContext context) {
pathGenerators.add(new AStarPath((ReachedStopCondition) stopCondition));
} else if ("shortest_all_paths".equals(generatorName) || "shortestallpaths".equals(generatorName)) {
pathGenerators.add(new ShortestAllPaths(stopCondition));
+ } else if ("predefined_path".equals(generatorName) || "predefinedpath".equals(generatorName)) {
+ pathGenerators.add(new PredefinedPath(stopCondition));
} else {
Class generatorClass = GeneratorFactoryScanner.get(generatorName);
try {
From 7267f5e9d0e598d08ed74b5a892ce6cbdfda0b46 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Mon, 12 Apr 2021 17:10:55 +0200
Subject: [PATCH 2/8] JsonModel updated with predefined path, generator fix
---
.../PredefinedPathStopCondition.java | 4 +-
.../core/generator/PredefinedPath.java | 32 ++++++++++++++--
.../org/graphwalker/core/machine/Context.java | 2 +-
.../core/machine/ExecutionContext.java | 11 +++---
.../org/graphwalker/core/model/Model.java | 11 +++++-
.../PredefinedPathStopConditionTest.java | 4 ++
.../core/generator/PredefinedPathTest.java | 4 ++
.../io/factory/json/JsonModel.java | 38 ++++++++++++++++---
8 files changed, 88 insertions(+), 18 deletions(-)
create mode 100644 graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
create mode 100644 graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
index 02d556a29..17516ca85 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
@@ -8,12 +8,12 @@ public PredefinedPathStopCondition(String value) {
@Override
public boolean isFulfilled() {
- return getContext().getPredefinedPathCurrentElementIndex() == getContext().getModel().getPredefinedPath().size();
+ return getContext().getPredefinedPathCurrentEdgeIndex() == getContext().getModel().getPredefinedPath().size();
}
@Override
public double getFulfilment() {
- return (double) getContext().getPredefinedPathCurrentElementIndex() / getContext().getModel().getPredefinedPath().size();
+ return (double) getContext().getPredefinedPathCurrentEdgeIndex() / getContext().getModel().getPredefinedPath().size();
}
}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
index 5745bd663..05e407614 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
@@ -2,7 +2,9 @@
import org.graphwalker.core.condition.StopCondition;
import org.graphwalker.core.machine.Context;
+import org.graphwalker.core.model.Edge;
import org.graphwalker.core.model.Element;
+import org.graphwalker.core.model.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,13 +28,37 @@ public Context getNextStep() {
LOG.error("context.getModel().getElements(): " + context.getModel().getElements());
throw new NoPathFoundException(context.getCurrentElement());
}
- int predefinedPathCurrentElementIndex = context.getPredefinedPathCurrentElementIndex();
- Element nextElement = context.getModel().getPredefinedPath().get(predefinedPathCurrentElementIndex + 1);
+ Element nextElement;
+ if (currentElement instanceof Edge.RuntimeEdge) {
+ nextElement = getNextElementFromEdge(context, elements, (Edge.RuntimeEdge) currentElement);
+ } else if (currentElement instanceof Vertex.RuntimeVertex) {
+ nextElement = getNextElementFromVertex(context, elements, (Vertex.RuntimeVertex) currentElement);
+ context.setPredefinedPathCurrentElementIndex(context.getPredefinedPathCurrentEdgeIndex() + 1);
+ } else {
+ LOG.error("Current element is neither an edge or a vertex");
+ throw new NoPathFoundException(context.getCurrentElement());
+ }
context.setCurrentElement(nextElement);
- context.setPredefinedPathCurrentElementIndex(predefinedPathCurrentElementIndex + 1);
return context;
}
+ private Element getNextElementFromEdge(Context context, List reachableElements, Edge.RuntimeEdge currentElement) {
+ if (reachableElements.size() != 1) {
+ LOG.error("Next vertex of predefined path is ambiguous (after step " + context.getPredefinedPathCurrentEdgeIndex() + ", from edge with id \"" + currentElement.getId() + "\")");
+ throw new NoPathFoundException(currentElement);
+ }
+ return reachableElements.get(0);
+ }
+
+ private Element getNextElementFromVertex(Context context, List reachableElements, Vertex.RuntimeVertex currentElement) {
+ Element nextElement = context.getModel().getPredefinedPath().get(context.getPredefinedPathCurrentEdgeIndex() + 1);
+ if (!reachableElements.contains(nextElement)) {
+ LOG.error("Next edge with id \"" + nextElement.getId() + "\" from predefined path is unreachable (either the guarding condition was not met or the edge has a different source vertex.");
+ throw new NoPathFoundException(currentElement);
+ }
+ return nextElement;
+ }
+
@Override
public boolean hasNextStep() {
return !getStopCondition().isFulfilled();
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java b/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
index 402dc4963..4f78f6c39 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/machine/Context.java
@@ -76,7 +76,7 @@ public interface Context {
Context setNextElement(Element nextElement);
- Integer getPredefinedPathCurrentElementIndex();
+ Integer getPredefinedPathCurrentEdgeIndex();
Context setPredefinedPathCurrentElementIndex(Integer predefinedPathCurrentElementIndex);
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java b/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
index dd25eb14f..ec0a685d5 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/machine/ExecutionContext.java
@@ -70,8 +70,7 @@ public abstract class ExecutionContext implements Context {
private Element currentElement;
private Element nextElement;
private Element lastElement;
-
- private Integer predefinedPathCurrentElementIndex;
+ private Integer predefinedPathCurrentEdgeIndex;
private String REGEXP_GLOBAL = "global\\.";
@@ -82,7 +81,7 @@ public abstract class ExecutionContext implements Context {
public ExecutionContext() {
executionEnvironment = org.graalvm.polyglot.Context.newBuilder().allowAllAccess(true).build();
executionEnvironment.getBindings("js").putMember(getClass().getSimpleName(), this);
- predefinedPathCurrentElementIndex = 0;
+ predefinedPathCurrentEdgeIndex = 0;
}
public ExecutionContext(Model model, PathGenerator pathGenerator) {
@@ -182,12 +181,12 @@ public Context setNextElement(Element nextElement) {
return this;
}
- public Integer getPredefinedPathCurrentElementIndex() {
- return predefinedPathCurrentElementIndex;
+ public Integer getPredefinedPathCurrentEdgeIndex() {
+ return predefinedPathCurrentEdgeIndex;
}
public Context setPredefinedPathCurrentElementIndex(Integer predefinedPathCurrentElementIndex) {
- this.predefinedPathCurrentElementIndex = predefinedPathCurrentElementIndex;
+ this.predefinedPathCurrentEdgeIndex = predefinedPathCurrentElementIndex;
return this;
}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
index ccf9d778b..f0c05eb8c 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
@@ -27,6 +27,7 @@
*/
import org.graphwalker.core.common.Objects;
+import org.graphwalker.core.machine.MachineException;
import java.util.*;
@@ -227,7 +228,7 @@ public List getEdges() {
public Model setPredefinedPath(List predefinedPath) {
if (!getEdges().containsAll(predefinedPath)) {
- // TODO not all edges from predefined path can be found
+ throw new MachineException("Not all edges from predefined path exist in the model");
}
this.predefinedPath = predefinedPath;
return this;
@@ -237,6 +238,10 @@ public List getPredefinedPath() {
return predefinedPath;
}
+ public boolean hasPredefinedPath() {
+ return isNotNullOrEmpty(predefinedPath);
+ }
+
/**
* Creates an immutable model from this model.
*
@@ -398,6 +403,10 @@ public List getPredefinedPath() {
return predefinedPath;
}
+ public boolean hasPredefinedPath() {
+ return isNotNullOrEmpty(predefinedPath);
+ }
+
/**
* Searches the model for any element matching the search string.
*
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
new file mode 100644
index 000000000..422721efe
--- /dev/null
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
@@ -0,0 +1,4 @@
+package org.graphwalker.core.condition;
+
+public class PredefinedPathStopConditionTest {
+}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
new file mode 100644
index 000000000..9b6969153
--- /dev/null
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
@@ -0,0 +1,4 @@
+package org.graphwalker.core.generator;
+
+public class PredefinedPathTest {
+}
diff --git a/graphwalker-io/src/main/java/org/graphwalker/io/factory/json/JsonModel.java b/graphwalker-io/src/main/java/org/graphwalker/io/factory/json/JsonModel.java
index 3dd257764..953ca38d4 100644
--- a/graphwalker-io/src/main/java/org/graphwalker/io/factory/json/JsonModel.java
+++ b/graphwalker-io/src/main/java/org/graphwalker/io/factory/json/JsonModel.java
@@ -30,11 +30,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.graphwalker.core.model.Action;
-import org.graphwalker.core.model.Edge;
-import org.graphwalker.core.model.Model;
-import org.graphwalker.core.model.Requirement;
-import org.graphwalker.core.model.Vertex;
+import java.util.stream.Collectors;
+
+import org.graphwalker.core.model.*;
+import org.graphwalker.io.factory.ContextFactoryException;
/**
* @author Nils Olsson
@@ -50,6 +49,7 @@ public class JsonModel {
private Map properties;
private List vertices;
private List edges;
+ private List predefinedPathEdgeIds;
public String getId() {
return id;
@@ -123,6 +123,14 @@ public void setEdges(List edges) {
this.edges = edges;
}
+ public List getPredefinedPathEdgeIds() {
+ return predefinedPathEdgeIds;
+ }
+
+ public void setPredefinedPathEdgeIds(List predefinedPathEdgeIds) {
+ this.predefinedPathEdgeIds = predefinedPathEdgeIds;
+ }
+
public boolean isModel() {
return !(null == name || null == generator || null == edges || null == vertices);
}
@@ -162,6 +170,14 @@ public Model getModel() {
model.addEdge(edge);
}
}
+
+ if (predefinedPathEdgeIds != null) {
+ List predefinedPath = getPredefinedPathEdgeIds().stream()
+ .map(this::findEdgeById)
+ .collect(Collectors.toList());
+ model.setPredefinedPath(predefinedPath);
+ }
+
return model;
}
@@ -201,6 +217,12 @@ public void setModel(Model.RuntimeModel model) {
jsonEdge.setEdge(edge);
edges.add(jsonEdge);
}
+
+ if (model.hasPredefinedPath()) {
+ predefinedPathEdgeIds = model.getPredefinedPath().stream()
+ .map(RuntimeBase::getId).collect(Collectors.toList());
+ }
+
}
public void setModel(Model model) {
@@ -233,4 +255,10 @@ public void copyValuesTo(Model model) {
model.getProperties().putAll(properties);
}
}
+
+ private Edge findEdgeById(String edgeId) {
+ return getEdges().stream().filter(edge -> edgeId.equals(edge.getEdge().getId()))
+ .findFirst().orElseThrow(() -> new ContextFactoryException("Edge with id \"" + edgeId + "\" could not be found in JsonModel"))
+ .getEdge();
+ }
}
From a2a9caec9967379f2978a29830af4c980aea8bd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Mon, 12 Apr 2021 19:32:15 +0200
Subject: [PATCH 3/8] filterBlockedElements blocking predefined path element
fixed
---
.../PredefinedPathStopCondition.java | 8 ++--
.../org/graphwalker/core/model/Model.java | 29 +++++++++---
.../dsl/antlr/generator/GeneratorLoader.java | 2 +-
.../json/ModelWithPredefinedPath.json | 45 +++++++++++++++++++
4 files changed, 74 insertions(+), 10 deletions(-)
create mode 100644 graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
index 17516ca85..bb3d6666b 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
@@ -2,18 +2,18 @@
public class PredefinedPathStopCondition extends StopConditionBase {
- public PredefinedPathStopCondition(String value) {
- super(value);
+ public PredefinedPathStopCondition() {
+ super("PredefinedPath");
}
@Override
public boolean isFulfilled() {
- return getContext().getPredefinedPathCurrentEdgeIndex() == getContext().getModel().getPredefinedPath().size();
+ return getContext().getPredefinedPathCurrentEdgeIndex() == getContext().getModel().getPredefinedPath().size() - 1;
}
@Override
public double getFulfilment() {
- return (double) getContext().getPredefinedPathCurrentEdgeIndex() / getContext().getModel().getPredefinedPath().size();
+ return (double) getContext().getPredefinedPathCurrentEdgeIndex() / (getContext().getModel().getPredefinedPath().size() - 1);
}
}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
index f0c05eb8c..5af6fab91 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
@@ -30,6 +30,7 @@
import org.graphwalker.core.machine.MachineException;
import java.util.*;
+import java.util.stream.Collectors;
import static org.graphwalker.core.common.Objects.*;
import static org.graphwalker.core.model.Edge.RuntimeEdge;
@@ -99,6 +100,19 @@ public Model(RuntimeModel model) {
edge.setProperties(runtimeEdge.getProperties());
this.edges.add(edge);
}
+ for (RuntimeEdge runtimeEdge : model.getPredefinedPath()) {
+ Edge edge = new Edge();
+ edge.setId(runtimeEdge.getId());
+ edge.setName(runtimeEdge.getName());
+ edge.setSourceVertex(cache.get(runtimeEdge.getSourceVertex()));
+ edge.setTargetVertex(cache.get(runtimeEdge.getTargetVertex()));
+ edge.setGuard(runtimeEdge.getGuard());
+ edge.setActions(runtimeEdge.getActions());
+ edge.setRequirements(runtimeEdge.getRequirements());
+ edge.setWeight(runtimeEdge.getWeight());
+ edge.setProperties(runtimeEdge.getProperties());
+ this.predefinedPath.add(edge);
+ }
}
/**
@@ -227,10 +241,11 @@ public List getEdges() {
}
public Model setPredefinedPath(List predefinedPath) {
- if (!getEdges().containsAll(predefinedPath)) {
- throw new MachineException("Not all edges from predefined path exist in the model");
- }
- this.predefinedPath = predefinedPath;
+ this.predefinedPath = predefinedPath.stream()
+ .map(predefinedPathEdge -> getEdges().stream()
+ .filter(localEdge -> localEdge.getId().equals(predefinedPathEdge.getId()))
+ .findFirst().orElseThrow(() -> new RuntimeException("Not all edges from predefined path exist in the model")))
+ .collect(Collectors.toList());
return this;
}
@@ -285,7 +300,11 @@ private RuntimeModel(Model model) {
this.elementsByNameCache = createElementsByNameCache();
this.elementsByElementCache = createElementsByElementCache(elementsCache, outEdgesByVertexCache);
this.sharedStateCache = createSharedStateCache();
- this.predefinedPath = BuilderFactory.build(model.getPredefinedPath());
+ this.predefinedPath = model.getPredefinedPath().stream()
+ .map(predefinedPathEdge -> this.edges.stream()
+ .filter(localEdge -> localEdge.getId().equals(predefinedPathEdge.getId()))
+ .findFirst().orElseThrow(() -> new RuntimeException("Not all edges from predefined path exist in the model")))
+ .collect(Collectors.toList());
}
/**
diff --git a/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java b/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
index 6261a34ba..98fa33e79 100644
--- a/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
+++ b/graphwalker-dsl/src/main/java/org/graphwalker/dsl/antlr/generator/GeneratorLoader.java
@@ -78,7 +78,7 @@ public void exitStopCondition(GeneratorParser.StopConditionContext ctx) {
} else if ("length".equals(conditionName)) {
stopConditions.add(new Length(Integer.parseInt(ctx.getChild(2).getText())));
} else if ("predefined_path".equals(conditionName) || "predefinedpath".equals(conditionName)) {
- stopConditions.add(new PredefinedPathStopCondition(ctx.getChild(2).getText()));
+ stopConditions.add(new PredefinedPathStopCondition());
}
}
diff --git a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
new file mode 100644
index 000000000..dc618239d
--- /dev/null
+++ b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
@@ -0,0 +1,45 @@
+{
+ "models": [
+ {
+ "name": "Small model",
+ "generator": "predefined_path(predefined_path)",
+ "startElementId": "e0",
+ "vertices": [
+ {
+ "name": "v_VerifySomeAction",
+ "id": "n0"
+ },
+ {
+ "name": "v_VerifySomeOtherAction",
+ "id": "n1"
+ }
+ ],
+ "edges": [
+ {
+ "name": "e_FirstAction",
+ "id": "e0",
+ "targetVertexId": "n0"
+ },
+ {
+ "name": "e_AnotherAction",
+ "id": "e1",
+ "sourceVertexId": "n0",
+ "targetVertexId": "n1"
+ },
+ {
+ "name": "e_SomeOtherAction",
+ "id": "e2",
+ "sourceVertexId": "n1",
+ "targetVertexId": "n1"
+ },
+ {
+ "name": "e_SomeOtherAction",
+ "id": "e3",
+ "sourceVertexId": "n1",
+ "targetVertexId": "n0"
+ }
+ ],
+ "predefinedPathEdgeIds": ["e0", "e1", "e2", "e3"]
+ }
+ ]
+}
From fc28f64adfcbdfa6f48d3b6c2af23237f748fe60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Tue, 13 Apr 2021 16:53:00 +0200
Subject: [PATCH 4/8] Generator and stop condition tests added
---
.../PredefinedPathStopCondition.java | 20 ++++-
.../core/generator/PredefinedPath.java | 2 +-
.../PredefinedPathStopConditionTest.java | 77 +++++++++++++++++
.../core/generator/PredefinedPathTest.java | 83 +++++++++++++++++++
.../graphwalker/dsl/GeneratorFactoryTest.java | 28 +++----
5 files changed, 189 insertions(+), 21 deletions(-)
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
index bb3d6666b..81fdebeee 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/condition/PredefinedPathStopCondition.java
@@ -1,5 +1,8 @@
package org.graphwalker.core.condition;
+import org.graphwalker.core.model.Edge;
+import org.graphwalker.core.model.Vertex;
+
public class PredefinedPathStopCondition extends StopConditionBase {
public PredefinedPathStopCondition() {
@@ -8,12 +11,25 @@ public PredefinedPathStopCondition() {
@Override
public boolean isFulfilled() {
- return getContext().getPredefinedPathCurrentEdgeIndex() == getContext().getModel().getPredefinedPath().size() - 1;
+ return getCurrentStepCount() == getTotalStepCount();
}
@Override
public double getFulfilment() {
- return (double) getContext().getPredefinedPathCurrentEdgeIndex() / (getContext().getModel().getPredefinedPath().size() - 1);
+ return (double) getCurrentStepCount() / getTotalStepCount();
+ }
+
+ private int getCurrentStepCount() {
+ // *2 because each index increment corresponds to a vertex-edge step pair
+ int currentStepCount = getContext().getPredefinedPathCurrentEdgeIndex() * 2;
+ if (getContext().getCurrentElement() instanceof Vertex.RuntimeVertex) {
+ return currentStepCount + 1;
+ }
+ return currentStepCount;
+ }
+
+ private int getTotalStepCount() {
+ return (getContext().getModel().getPredefinedPath().size() * 2) + 1;
}
}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
index 05e407614..5c92a8b15 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
@@ -51,7 +51,7 @@ private Element getNextElementFromEdge(Context context, List reachableE
}
private Element getNextElementFromVertex(Context context, List reachableElements, Vertex.RuntimeVertex currentElement) {
- Element nextElement = context.getModel().getPredefinedPath().get(context.getPredefinedPathCurrentEdgeIndex() + 1);
+ Element nextElement = context.getModel().getPredefinedPath().get(context.getPredefinedPathCurrentEdgeIndex());
if (!reachableElements.contains(nextElement)) {
LOG.error("Next edge with id \"" + nextElement.getId() + "\" from predefined path is unreachable (either the guarding condition was not met or the edge has a different source vertex.");
throw new NoPathFoundException(currentElement);
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
index 422721efe..bcb8d48d2 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
@@ -1,4 +1,81 @@
package org.graphwalker.core.condition;
+import org.graphwalker.core.generator.PredefinedPath;
+import org.graphwalker.core.machine.Context;
+import org.graphwalker.core.machine.TestExecutionContext;
+import org.graphwalker.core.model.Edge;
+import org.graphwalker.core.model.Model;
+import org.graphwalker.core.model.Vertex;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+
public class PredefinedPathStopConditionTest {
+
+ @Test()
+ public void testFulfilment() {
+ Vertex v1 = new Vertex();
+ Vertex v2 = new Vertex();
+ Edge e1 = new Edge().setSourceVertex(v1).setTargetVertex(v2).setId("e1");
+ Edge e2 = new Edge().setSourceVertex(v2).setTargetVertex(v1).setId("e2");
+ List predefinedPath = Arrays.asList(e1, e2);
+ Model model = new Model().addEdge(e1).addEdge(e2).setPredefinedPath(predefinedPath);
+ StopCondition condition = new PredefinedPathStopCondition();
+ Context context = new TestExecutionContext(model, new PredefinedPath(condition));
+
+ context.setPredefinedPathCurrentElementIndex(0);
+
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is((double) 1/5));
+
+ context.setCurrentElement(e1.build());
+ context.setPredefinedPathCurrentElementIndex(1);
+ assertThat(condition.getFulfilment(), is((double) 2/5));
+
+ context.setCurrentElement(v2.build());
+ assertThat(condition.getFulfilment(), is((double) 3/5));
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(2);
+ assertThat(condition.getFulfilment(), is((double) 4/5));
+
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is(1.0));
+ }
+
+ @Test()
+ public void testIsFulfilled() {
+ Vertex v1 = new Vertex();
+ Vertex v2 = new Vertex();
+ Edge e1 = new Edge().setSourceVertex(v1).setTargetVertex(v2).setId("e1");
+ Edge e2 = new Edge().setSourceVertex(v2).setTargetVertex(v1).setId("e2");
+ List predefinedPath = Arrays.asList(e1, e2);
+ Model model = new Model().addEdge(e1).addEdge(e2).setPredefinedPath(predefinedPath);
+ StopCondition condition = new PredefinedPathStopCondition();
+ Context context = new TestExecutionContext(model, new PredefinedPath(condition));
+
+ context.setPredefinedPathCurrentElementIndex(0);
+
+ context.setCurrentElement(v1.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e1.build());
+ context.setPredefinedPathCurrentElementIndex(1);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v2.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(2);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v1.build());
+ assertTrue(condition.isFulfilled());
+ }
+
}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
index 9b6969153..23e530e2f 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
@@ -1,4 +1,87 @@
package org.graphwalker.core.generator;
+import org.graphwalker.core.condition.PredefinedPathStopCondition;
+import org.graphwalker.core.condition.VertexCoverage;
+import org.graphwalker.core.machine.*;
+import org.graphwalker.core.model.Edge;
+import org.graphwalker.core.model.Model;
+import org.graphwalker.core.model.Vertex;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.graphwalker.core.Models.*;
+import static org.junit.Assert.*;
+
public class PredefinedPathTest {
+
+ @Test
+ public void simpleTest() {
+ // Model
+ Model model = fourEdgesModel();
+ Edge edgeAB = model.getEdges().stream().filter(edge -> "ab".equals(edge.getId())).findFirst().get();
+ Edge edgeAB_2 = model.getEdges().stream().filter(edge -> "ab_2".equals(edge.getId())).findFirst().get();
+ Edge edgeAB_3 = model.getEdges().stream().filter(edge -> "ab_3".equals(edge.getId())).findFirst().get();
+ Edge edgeBA = model.getEdges().stream().filter(edge -> "ba".equals(edge.getId())).findFirst().get();
+ model.setPredefinedPath(Arrays.asList(edgeAB, edgeBA, edgeAB_2, edgeBA, edgeAB_3));
+
+ // Runtime model
+ Model.RuntimeModel runtimeModel = model.build();
+ Vertex.RuntimeVertex source = findVertex(runtimeModel, "A");
+ Vertex.RuntimeVertex target = findVertex(runtimeModel, "B");
+ Edge.RuntimeEdge runtimeEdgeAB = findEdge(runtimeModel, "ab");
+ Edge.RuntimeEdge runtimeEdgeAB_2 = findEdge(runtimeModel, "ab_2");
+ Edge.RuntimeEdge runtimeEdgeAB_3 = findEdge(runtimeModel, "ab_3");
+ Edge.RuntimeEdge runtimeEdgeBA = findEdge(runtimeModel, "ba");
+
+ // Context, generator and machine
+ Context context = new TestExecutionContext().setModel(runtimeModel).setNextElement(source);
+ PathGenerator generator = new PredefinedPath(new PredefinedPathStopCondition());
+ context.setPathGenerator(generator);
+ Machine machine = new SimpleMachine(context);
+
+ // Tests
+ assertTrue(machine.hasNextStep());
+ assertEquals(machine.getNextStep().getCurrentElement(), source);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeAB);
+ assertEquals(machine.getNextStep().getCurrentElement(), target);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeBA);
+ assertEquals(machine.getNextStep().getCurrentElement(), source);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeAB_2);
+ assertEquals(machine.getNextStep().getCurrentElement(), target);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeBA);
+ assertEquals(machine.getNextStep().getCurrentElement(), source);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeAB_3);
+ assertEquals(machine.getNextStep().getCurrentElement(), target);
+ assertFalse(machine.hasNextStep());
+ }
+
+ @Test(expected = MachineException.class)
+ public void testUnreachableEdge() throws Exception {
+ // Model
+ Model model = simpleModel();
+ Edge edgeAB = model.getEdges().get(0);
+ model.setPredefinedPath(Arrays.asList(edgeAB, edgeAB));
+
+ // Runtime model
+ Model.RuntimeModel runtimeModel = model.build();
+ Vertex.RuntimeVertex source = findVertex(runtimeModel, "A");
+ Vertex.RuntimeVertex target = findVertex(runtimeModel, "B");
+ Edge.RuntimeEdge edge = findEdge(runtimeModel, "ab");
+
+ // Context, generator and machine
+ Context context = new TestExecutionContext().setModel(runtimeModel).setNextElement(source);
+ PathGenerator generator = new PredefinedPath(new PredefinedPathStopCondition());
+ context.setPathGenerator(generator);
+ Machine machine = new SimpleMachine(context);
+
+ // Tests
+ assertTrue(machine.hasNextStep());
+ assertEquals(machine.getNextStep().getCurrentElement(), source);
+ assertEquals(machine.getNextStep().getCurrentElement(), edge);
+ assertEquals(machine.getNextStep().getCurrentElement(), target);
+ assertTrue(machine.hasNextStep());
+ machine.getNextStep(); // should fail
+ }
+
}
diff --git a/graphwalker-dsl/src/test/java/org/graphwalker/dsl/GeneratorFactoryTest.java b/graphwalker-dsl/src/test/java/org/graphwalker/dsl/GeneratorFactoryTest.java
index c1a5635f9..fc3940e2b 100644
--- a/graphwalker-dsl/src/test/java/org/graphwalker/dsl/GeneratorFactoryTest.java
+++ b/graphwalker-dsl/src/test/java/org/graphwalker/dsl/GeneratorFactoryTest.java
@@ -30,24 +30,8 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import org.graphwalker.core.condition.AlternativeCondition;
-import org.graphwalker.core.condition.CombinedCondition;
-import org.graphwalker.core.condition.DependencyEdgeCoverage;
-import org.graphwalker.core.condition.EdgeCoverage;
-import org.graphwalker.core.condition.Length;
-import org.graphwalker.core.condition.ReachedEdge;
-import org.graphwalker.core.condition.ReachedVertex;
-import org.graphwalker.core.condition.RequirementCoverage;
-import org.graphwalker.core.condition.StopCondition;
-import org.graphwalker.core.condition.TimeDuration;
-import org.graphwalker.core.condition.VertexCoverage;
-import org.graphwalker.core.generator.AStarPath;
-import org.graphwalker.core.generator.CombinedPath;
-import org.graphwalker.core.generator.PathGenerator;
-import org.graphwalker.core.generator.QuickRandomPath;
-import org.graphwalker.core.generator.RandomPath;
-import org.graphwalker.core.generator.ShortestAllPaths;
-import org.graphwalker.core.generator.WeightedRandomPath;
+import org.graphwalker.core.condition.*;
+import org.graphwalker.core.generator.*;
import org.graphwalker.dsl.antlr.DslException;
import org.graphwalker.dsl.antlr.generator.GeneratorFactory;
import org.graphwalker.dsl.antlr.generator.GeneratorFactoryException;
@@ -314,6 +298,14 @@ public void multipleAlternativeStopConditions() {
assertThat(((AlternativeCondition) generator.getStopCondition()).getStopConditions().size(), is(3));
}
+ @Test
+ public void predefinedPath_predefinedPathStopCondition() {
+ PathGenerator generator = GeneratorFactory.parse("predefined_path(predefined_path)");
+ assertThat(generator, instanceOf(PredefinedPath.class));
+ assertThat(generator.getStopCondition(), instanceOf(PredefinedPathStopCondition.class));
+ assertThat(generator.getStopCondition().getValue(), is("PredefinedPath"));
+ }
+
/**
* Tries to load a plugin generator that should be found in the classpath
*/
From e7f302f0e8c84310c1a341a9ff85cea0ac78adda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Tue, 13 Apr 2021 17:46:33 +0200
Subject: [PATCH 5/8] Context and model tests added
---
.../core/generator/PredefinedPath.java | 5 +
.../org/graphwalker/core/model/Model.java | 7 ++
.../core/generator/PredefinedPathTest.java | 7 ++
.../core/machine/SimpleMachineTest.java | 33 ++++--
.../org/graphwalker/core/model/ModelTest.java | 100 ++++++++++++++++++
5 files changed, 144 insertions(+), 8 deletions(-)
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
index 5c92a8b15..9decf45ab 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/generator/PredefinedPath.java
@@ -1,6 +1,8 @@
package org.graphwalker.core.generator;
+import org.graphwalker.core.condition.PredefinedPathStopCondition;
import org.graphwalker.core.condition.StopCondition;
+import org.graphwalker.core.condition.StopConditionException;
import org.graphwalker.core.machine.Context;
import org.graphwalker.core.model.Edge;
import org.graphwalker.core.model.Element;
@@ -15,6 +17,9 @@ public class PredefinedPath extends PathGeneratorBase {
private static final Logger LOG = LoggerFactory.getLogger(PredefinedPath.class);
public PredefinedPath(StopCondition stopCondition) {
+ if (!(stopCondition instanceof PredefinedPathStopCondition)) {
+ throw new StopConditionException("PredefinedPath generator can only work with a PredefinedPathStopCondition instance");
+ }
setStopCondition(stopCondition);
}
diff --git a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
index 5af6fab91..1ea3b4c37 100644
--- a/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
+++ b/graphwalker-core/src/main/java/org/graphwalker/core/model/Model.java
@@ -157,6 +157,9 @@ public Model addEdge(Edge edge) {
* @return The model.
*/
public Model deleteEdge(Edge edge) {
+ if (isNotNull(predefinedPath) && predefinedPath.contains(edge)) {
+ throw new RuntimeException("Cannot remove edge contained in predefined path");
+ }
edges.remove(edge);
return this;
}
@@ -172,6 +175,10 @@ public Model deleteEdge(Edge edge) {
* @return The model.
*/
public Model deleteVertex(Vertex vertex) {
+ if (isNotNull(predefinedPath)
+ && predefinedPath.stream().anyMatch(edge -> vertex.equals(edge.getSourceVertex()) || vertex.equals(edge.getTargetVertex()))) {
+ throw new RuntimeException("Cannot remove vertex with connecting edge contained in predefined path");
+ }
edges.removeIf(edge -> vertex.equals(edge.getSourceVertex()) || vertex.equals(edge.getTargetVertex()));
vertices.remove(vertex);
return this;
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
index 23e530e2f..b7a7937d1 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
@@ -1,6 +1,8 @@
package org.graphwalker.core.generator;
+import org.graphwalker.core.condition.Never;
import org.graphwalker.core.condition.PredefinedPathStopCondition;
+import org.graphwalker.core.condition.StopConditionException;
import org.graphwalker.core.condition.VertexCoverage;
import org.graphwalker.core.machine.*;
import org.graphwalker.core.model.Edge;
@@ -84,4 +86,9 @@ public void testUnreachableEdge() throws Exception {
machine.getNextStep(); // should fail
}
+ @Test(expected = StopConditionException.class)
+ public void testIncompatibleStopConditionInstance() {
+ new PredefinedPath(new Never()); // should faild
+ }
+
}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
index cba7d426b..f2cb0a6a6 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
@@ -32,17 +32,12 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
-import org.graphwalker.core.condition.EdgeCoverage;
-import org.graphwalker.core.condition.ReachedVertex;
-import org.graphwalker.core.condition.StopConditionException;
-import org.graphwalker.core.condition.VertexCoverage;
-import org.graphwalker.core.generator.AStarPath;
-import org.graphwalker.core.generator.RandomPath;
-import org.graphwalker.core.generator.ShortestAllPaths;
-import org.graphwalker.core.generator.SingletonRandomGenerator;
+import org.graphwalker.core.condition.*;
+import org.graphwalker.core.generator.*;
import org.graphwalker.core.model.Action;
import org.graphwalker.core.model.Edge;
import org.graphwalker.core.model.Element;
@@ -506,4 +501,26 @@ public void resetMachine() throws Exception {
.map(Execution::getElement).collect(Collectors.toList());
assertThat(expectedPath, is(path));
}
+
+ @Test
+ public void predefinedPathCurrentEdgeIndex() {
+ Vertex vertex = new Vertex().setId("v");
+ Edge edge = new Edge()
+ .setId("e")
+ .setSourceVertex(vertex)
+ .setTargetVertex(new Vertex());
+ List predefinedPath = Collections.singletonList(edge);
+ Model model = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ Context context = new TestExecutionContext(model, new PredefinedPath(new PredefinedPathStopCondition()));
+ context.setNextElement(vertex);
+ Machine machine = new SimpleMachine(context);
+ assertEquals((Integer) 0, context.getPredefinedPathCurrentEdgeIndex());
+ machine.getNextStep();
+ assertEquals((Integer) 0, context.getPredefinedPathCurrentEdgeIndex());
+ machine.getNextStep();
+ assertEquals((Integer) 1, context.getPredefinedPathCurrentEdgeIndex());
+ }
}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/model/ModelTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/model/ModelTest.java
index 5ca17d197..6cac985ab 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/model/ModelTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/model/ModelTest.java
@@ -32,8 +32,10 @@
import static org.junit.Assert.*;
import java.util.Arrays;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import org.graphwalker.core.model.Model.RuntimeModel;
import org.junit.Test;
@@ -352,4 +354,102 @@ public void testInequality() throws Exception {
assertFalse(model1.build().equals(null));
assertThat(model1.build(), not(model1));
}
+
+ @Test
+ public void setPredefinedPath() {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ Model model = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ assertEquals(predefinedPath, model.getPredefinedPath());
+ }
+
+ @Test
+ public void buildModelWithPredefinedPath() {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ Model model = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ RuntimeModel runtimeModel = model.build();
+ String[] predefinedPathEdgeIds = predefinedPath.stream().map(Edge::getId).toArray(String[]::new);
+ String[] runtimeModelPredefinedPathEdgeIds = runtimeModel.getPredefinedPath().stream().map(RuntimeBase::getId).toArray(String[]::new);
+ assertArrayEquals(predefinedPathEdgeIds, runtimeModelPredefinedPathEdgeIds);
+ }
+
+ @Test
+ public void recreateModelWithPredefinedPath() throws Exception {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ Model model1 = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ Model model2 = new Model(model1.build());
+ String[] model1PredefinedPathEdgeIds = model1.getPredefinedPath().stream().map(Edge::getId).toArray(String[]::new);
+ String[] model2PredefinedPathEdgeIds = model2.getPredefinedPath().stream().map(Edge::getId).toArray(String[]::new);
+ assertArrayEquals(model1PredefinedPathEdgeIds, model2PredefinedPathEdgeIds);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSetPredefinedPathWithUnknownEdge() throws Exception {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ new Model().setPredefinedPath(predefinedPath); // should fail
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testRemoveEdgeContainedInPredefinedPath() throws Exception {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ Model model = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ model.deleteEdge(edge); // should fail
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testRemoveVertexWithConnectingEdgeContainedInPredefinedPath() throws Exception {
+ Vertex vertex = new Vertex()
+ .setId("v");
+ Edge edge = new Edge()
+ .setId("v")
+ .setSourceVertex(vertex)
+ .setTargetVertex(vertex);
+ List predefinedPath = Arrays.asList(edge, edge);
+ Model model = new Model()
+ .addVertex(vertex)
+ .addEdge(edge)
+ .setPredefinedPath(predefinedPath);
+ model.deleteVertex(vertex); // should fail
+ }
}
From 145d2b5670dc6148b29478ffa042e5debc7ee3c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Tue, 13 Apr 2021 18:10:55 +0200
Subject: [PATCH 6/8] IO tests added
---
.../factory/json/JsonContextFactoryTest.java | 23 ++++++++++
.../ModelWithPredefinedPath_unknown_edge.json | 45 +++++++++++++++++++
2 files changed, 68 insertions(+)
create mode 100644 graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
diff --git a/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java b/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
index 56680190d..99cdf2135 100644
--- a/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
+++ b/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
@@ -345,4 +345,27 @@ public void petClinicWithSeed() throws IOException {
assertThat(SingletonRandomGenerator.nextInt(), is(-2090749135));
assertThat(SingletonRandomGenerator.nextInt(), is(-287790814));
}
+
+ @Test
+ public void predefinedPath() throws IOException {
+ List contexts = new JsonContextFactory().create(Paths.get("json/ModelWithPredefinedPath.json"));
+ assertNotNull(contexts);
+ assertThat(contexts.size(), is(1));
+
+ Context context = contexts.get(0);
+
+ assertThat(context.getModel().getVertices().size(), is(2));
+ assertThat(context.getModel().getEdges().size(), is(4));
+ assertThat(context.getModel().getPredefinedPath().size(), is(4));
+
+ assertThat(context.getModel().getPredefinedPath().get(0).getId(), is("e0"));
+ assertThat(context.getModel().getPredefinedPath().get(1).getId(), is("e1"));
+ assertThat(context.getModel().getPredefinedPath().get(2).getId(), is("e2"));
+ assertThat(context.getModel().getPredefinedPath().get(3).getId(), is("e3"));
+ }
+
+ @Test(expected = ContextFactoryException.class)
+ public void testPredefinedPathWithUnknownEdge() throws Exception {
+ new JsonContextFactory().create(Paths.get("json/ModelWithPredefinedPath_unknown_edge.json")); // should fail
+ }
}
diff --git a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
new file mode 100644
index 000000000..e3c82277b
--- /dev/null
+++ b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
@@ -0,0 +1,45 @@
+{
+ "models": [
+ {
+ "name": "Small model",
+ "generator": "predefined_path(predefined_path)",
+ "startElementId": "e0",
+ "vertices": [
+ {
+ "name": "v_VerifySomeAction",
+ "id": "n0"
+ },
+ {
+ "name": "v_VerifySomeOtherAction",
+ "id": "n1"
+ }
+ ],
+ "edges": [
+ {
+ "name": "e_FirstAction",
+ "id": "e0",
+ "targetVertexId": "n0"
+ },
+ {
+ "name": "e_AnotherAction",
+ "id": "e1",
+ "sourceVertexId": "n0",
+ "targetVertexId": "n1"
+ },
+ {
+ "name": "e_SomeOtherAction",
+ "id": "e2",
+ "sourceVertexId": "n1",
+ "targetVertexId": "n1"
+ },
+ {
+ "name": "e_SomeOtherAction",
+ "id": "e3",
+ "sourceVertexId": "n1",
+ "targetVertexId": "n0"
+ }
+ ],
+ "predefinedPathEdgeIds": ["e0", "e1", "e2", "e3", "e4"]
+ }
+ ]
+}
From 8fceaf7c1541a073451cd42ed41f4594f94ff4cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Wed, 14 Apr 2021 10:52:44 +0200
Subject: [PATCH 7/8] Documentation added
---
README.md | 33 +++++++++++++++++++++++++++++++++
graphwalker-core/README.md | 3 +++
2 files changed, 36 insertions(+)
diff --git a/README.md b/README.md
index b662bad47..440dd758a 100644
--- a/README.md
+++ b/README.md
@@ -45,3 +45,36 @@ Run it like:
```bash
java -jar graphwalker-studio/target/graphwalker-studio-.jar
```
+
+===================
+
+Predefined Path
+===================
+This fork of the GraphWalker project enables the user to define an edge sequence in the input graph, along which the machine should execute.
+
+## Graph input format
+
+Currently a predefined path can only be specified in JSON GW3 input graphs.
+
+To define an edge sequence, the model has to contain an array element called *predefinedPathEdgeIds* containing the edge IDs in the sequence.
+
+The generator and stop condition has to be specified in the *generator* element.
+
+Example:
+
+```JSON
+{
+ "models": [
+ {
+ "generator": "predefined_path(predefined_path)",
+ ...
+ "edges": [
+ { "id": "e0", ... },
+ { "id": "e1", ... },
+ { "id": "e2", ... }
+ ],
+ "predefinedPathEdgeIds": [ "e0", "e1", "e2", "e0" ]
+ }
+ ]
+}
+```
\ No newline at end of file
diff --git a/graphwalker-core/README.md b/graphwalker-core/README.md
index 68a67ecb7..df86652a1 100644
--- a/graphwalker-core/README.md
+++ b/graphwalker-core/README.md
@@ -104,6 +104,7 @@ Are used by path generators. The [algorithm] provides the generators the logic f
### Stop conditions
Used by path generators to determine when to stop generating a path. Stop conditions can be logically AND'ed and OR'ed. When a stop condition (or a combination of several) evaluates to true, the generator stops. Examples are:
+
* **Edge coverage** - The condition is a percentage number. When, during execution, the percentage of traversed edges are reached, the test is stopped. If an edge is traversed more than one time, it still counts as 1, when calculating the percentage coverage.
* **Length** - The condition is a number, representing the total numbers of pairs of vertices and edges generated by a generator. For example, if the number is 110, the test sequence would be 220 lines long. (including 110 pairs of edges and vertices).
* **Never** - This special condition will never halt the generator.
@@ -112,6 +113,7 @@ Used by path generators to determine when to stop generating a path. Stop condit
* **Requirement coverage** - The condition is a percentage number. When, during execution, the percentage of traversed requirements is reached, the test is stopped. If an requirement is traversed more than one time, it still counts as 1, when calculating the percentage coverage.
* **Time duration** - The condition is a time, representing the number of seconds that the test generator is allowed to execute.
* **Vertex coverage** - The condition is a percentage number. When, during execution, the percentage of traversed vertices are reached, the test is stopped. If a vertex is traversed more than one time, it still counts as 1, when calculating the percentage coverage.
+* **PredefinedPath** - The condition is the end of the predefined edge sequence. When, during execution, the last edge and its target vertex is reached, the test is stopped. The stop condition only works when a predefined path is present in the model.
### Events
@@ -131,6 +133,7 @@ A generator used an algorithm to decide how to traverse a model. Different gener
The algorithm works well an very large models, and generates reasonably short sequences. The downside is when used in conjunction with EFSM, the algorithm can choose a path which is blocked by a guard.
* **RandomPath** - Navigate through the model in a completely random manor. Also called "Drunkard’s walk", or "Random walk". This algorithm selects an out-edge from a vertex by random, and repeats the process in the next vertex.
+* **PredefinedPath** - Navigate through the model deterministically along a predefined edge sequence. This generator only works with its corresponding PredefinedPath stop condition.
### Machines
From 0f7dc42ee4a721328c588b52c21649b8609070fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cs=C3=A1ky=20Zsolt?=
Date: Sun, 2 May 2021 19:04:58 +0200
Subject: [PATCH 8/8] Test graphs extended
---
.../PredefinedPathStopConditionTest.java | 142 ++++++++++++++----
.../core/generator/PredefinedPathTest.java | 18 ++-
.../core/machine/SimpleMachineTest.java | 39 +++--
.../factory/json/JsonContextFactoryTest.java | 7 +-
.../json/ModelWithPredefinedPath.json | 21 +--
.../ModelWithPredefinedPath_unknown_edge.json | 21 +--
6 files changed, 179 insertions(+), 69 deletions(-)
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
index bcb8d48d2..f876fa777 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/condition/PredefinedPathStopConditionTest.java
@@ -3,9 +3,7 @@
import org.graphwalker.core.generator.PredefinedPath;
import org.graphwalker.core.machine.Context;
import org.graphwalker.core.machine.TestExecutionContext;
-import org.graphwalker.core.model.Edge;
-import org.graphwalker.core.model.Model;
-import org.graphwalker.core.model.Vertex;
+import org.graphwalker.core.model.*;
import org.junit.Test;
import java.util.Arrays;
@@ -16,58 +14,122 @@
public class PredefinedPathStopConditionTest {
+ private Model createSimpleModel() {
+ Vertex v0 = new Vertex().setId("v0");
+ Vertex v1 = new Vertex().setId("v1");
+ Edge e0 = new Edge()
+ .setId("e0")
+ .setSourceVertex(v0)
+ .setTargetVertex(v1)
+ .setActions(Arrays.asList(
+ new Action("i = 1"),
+ new Action("b = false")
+ ));
+ Edge e1 = new Edge()
+ .setId("e1")
+ .setSourceVertex(v1)
+ .setTargetVertex(v0)
+ .setGuard(new Guard("i > (1 + 1) * 1.5 && b"));
+ Edge e2 = new Edge()
+ .setId("e2")
+ .setSourceVertex(v1)
+ .setTargetVertex(v1)
+ .setActions(Arrays.asList(
+ new Action("i = i + 1"),
+ new Action("b = i > 4")
+ ));
+ List predefinedPath = Arrays.asList(e0, e2, e2, e2, e2, e1);
+ return new Model()
+ .addVertex(v0)
+ .addVertex(v1)
+ .addEdge(e0)
+ .addEdge(e1)
+ .addEdge(e2)
+ .setPredefinedPath(predefinedPath);
+ }
+
@Test()
public void testFulfilment() {
- Vertex v1 = new Vertex();
- Vertex v2 = new Vertex();
- Edge e1 = new Edge().setSourceVertex(v1).setTargetVertex(v2).setId("e1");
- Edge e2 = new Edge().setSourceVertex(v2).setTargetVertex(v1).setId("e2");
- List predefinedPath = Arrays.asList(e1, e2);
- Model model = new Model().addEdge(e1).addEdge(e2).setPredefinedPath(predefinedPath);
+ Model model = createSimpleModel();
+ Vertex v0 = model.getVertices().stream().filter(vertex -> "v0".equals(vertex.getId())).findFirst().orElseThrow(() -> new RuntimeException("Vertex v0 not found"));
+ Vertex v1 = model.getVertices().stream().filter(vertex -> "v1".equals(vertex.getId())).findFirst().orElseThrow(() -> new RuntimeException("Vertex v1 not found"));
+ Edge e0 = model.getEdges().stream().filter(edge -> "e0".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e0 not found"));
+ Edge e1 = model.getEdges().stream().filter(edge -> "e1".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e1 not found"));
+ Edge e2 = model.getEdges().stream().filter(edge -> "e2".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e2 not found"));
+
StopCondition condition = new PredefinedPathStopCondition();
Context context = new TestExecutionContext(model, new PredefinedPath(condition));
context.setPredefinedPathCurrentElementIndex(0);
- context.setCurrentElement(v1.build());
- assertThat(condition.getFulfilment(), is((double) 1/5));
+ context.setCurrentElement(v0.build());
+ assertThat(condition.getFulfilment(), is((double) 1/13));
- context.setCurrentElement(e1.build());
+ context.setCurrentElement(e0.build());
context.setPredefinedPathCurrentElementIndex(1);
- assertThat(condition.getFulfilment(), is((double) 2/5));
+ assertThat(condition.getFulfilment(), is((double) 2/13));
- context.setCurrentElement(v2.build());
- assertThat(condition.getFulfilment(), is((double) 3/5));
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is((double) 3/13));
context.setCurrentElement(e2.build());
context.setPredefinedPathCurrentElementIndex(2);
- assertThat(condition.getFulfilment(), is((double) 4/5));
+ assertThat(condition.getFulfilment(), is((double) 4/13));
+
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is((double) 5/13));
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(3);
+ assertThat(condition.getFulfilment(), is((double) 6/13));
+
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is((double) 7/13));
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(4);
+ assertThat(condition.getFulfilment(), is((double) 8/13));
+
+ context.setCurrentElement(v1.build());
+ assertThat(condition.getFulfilment(), is((double) 9/13));
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(5);
+ assertThat(condition.getFulfilment(), is((double) 10/13));
context.setCurrentElement(v1.build());
- assertThat(condition.getFulfilment(), is(1.0));
+ assertThat(condition.getFulfilment(), is((double) 11/13));
+
+ context.setCurrentElement(e1.build());
+ context.setPredefinedPathCurrentElementIndex(6);
+ assertThat(condition.getFulfilment(), is((double) 12/13));
+
+ context.setCurrentElement(v0.build());
+ assertThat(condition.getFulfilment(), is((double) 13/13));
}
@Test()
public void testIsFulfilled() {
- Vertex v1 = new Vertex();
- Vertex v2 = new Vertex();
- Edge e1 = new Edge().setSourceVertex(v1).setTargetVertex(v2).setId("e1");
- Edge e2 = new Edge().setSourceVertex(v2).setTargetVertex(v1).setId("e2");
- List predefinedPath = Arrays.asList(e1, e2);
- Model model = new Model().addEdge(e1).addEdge(e2).setPredefinedPath(predefinedPath);
+ Model model = createSimpleModel();
+ Vertex v0 = model.getVertices().stream().filter(vertex -> "v0".equals(vertex.getId())).findFirst().orElseThrow(() -> new RuntimeException("Vertex v0 not found"));
+ Vertex v1 = model.getVertices().stream().filter(vertex -> "v1".equals(vertex.getId())).findFirst().orElseThrow(() -> new RuntimeException("Vertex v1 not found"));
+ Edge e0 = model.getEdges().stream().filter(edge -> "e0".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e0 not found"));
+ Edge e1 = model.getEdges().stream().filter(edge -> "e1".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e1 not found"));
+ Edge e2 = model.getEdges().stream().filter(edge -> "e2".equals(edge.getId())).findFirst().orElseThrow(() -> new RuntimeException("Edge e2 not found"));
+
StopCondition condition = new PredefinedPathStopCondition();
Context context = new TestExecutionContext(model, new PredefinedPath(condition));
context.setPredefinedPathCurrentElementIndex(0);
- context.setCurrentElement(v1.build());
+ context.setCurrentElement(v0.build());
assertFalse(condition.isFulfilled());
- context.setCurrentElement(e1.build());
+ context.setCurrentElement(e0.build());
context.setPredefinedPathCurrentElementIndex(1);
assertFalse(condition.isFulfilled());
- context.setCurrentElement(v2.build());
+ context.setCurrentElement(v1.build());
assertFalse(condition.isFulfilled());
context.setCurrentElement(e2.build());
@@ -75,6 +137,34 @@ public void testIsFulfilled() {
assertFalse(condition.isFulfilled());
context.setCurrentElement(v1.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(3);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v1.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(4);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v1.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e2.build());
+ context.setPredefinedPathCurrentElementIndex(5);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v1.build());
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(e1.build());
+ context.setPredefinedPathCurrentElementIndex(6);
+ assertFalse(condition.isFulfilled());
+
+ context.setCurrentElement(v0.build());
assertTrue(condition.isFulfilled());
}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
index b7a7937d1..e3d63f07f 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/generator/PredefinedPathTest.java
@@ -61,15 +61,21 @@ public void simpleTest() {
@Test(expected = MachineException.class)
public void testUnreachableEdge() throws Exception {
// Model
- Model model = simpleModel();
- Edge edgeAB = model.getEdges().get(0);
- model.setPredefinedPath(Arrays.asList(edgeAB, edgeAB));
+ Model model = fourEdgesModel();
+ Edge edgeAB = model.getEdges().stream().filter(edge -> "ab".equals(edge.getId())).findFirst().get();
+ Edge edgeAB_2 = model.getEdges().stream().filter(edge -> "ab_2".equals(edge.getId())).findFirst().get();
+ Edge edgeAB_3 = model.getEdges().stream().filter(edge -> "ab_3".equals(edge.getId())).findFirst().get();
+ Edge edgeBA = model.getEdges().stream().filter(edge -> "ba".equals(edge.getId())).findFirst().get();
+ model.setPredefinedPath(Arrays.asList(edgeAB, edgeAB_2));
// Runtime model
Model.RuntimeModel runtimeModel = model.build();
Vertex.RuntimeVertex source = findVertex(runtimeModel, "A");
Vertex.RuntimeVertex target = findVertex(runtimeModel, "B");
- Edge.RuntimeEdge edge = findEdge(runtimeModel, "ab");
+ Edge.RuntimeEdge runtimeEdgeAB = findEdge(runtimeModel, "ab");
+ Edge.RuntimeEdge runtimeEdgeAB_2 = findEdge(runtimeModel, "ab_2");
+ Edge.RuntimeEdge runtimeEdgeAB_3 = findEdge(runtimeModel, "ab_3");
+ Edge.RuntimeEdge runtimeEdgeBA = findEdge(runtimeModel, "ba");
// Context, generator and machine
Context context = new TestExecutionContext().setModel(runtimeModel).setNextElement(source);
@@ -80,7 +86,7 @@ public void testUnreachableEdge() throws Exception {
// Tests
assertTrue(machine.hasNextStep());
assertEquals(machine.getNextStep().getCurrentElement(), source);
- assertEquals(machine.getNextStep().getCurrentElement(), edge);
+ assertEquals(machine.getNextStep().getCurrentElement(), runtimeEdgeAB);
assertEquals(machine.getNextStep().getCurrentElement(), target);
assertTrue(machine.hasNextStep());
machine.getNextStep(); // should fail
@@ -88,7 +94,7 @@ public void testUnreachableEdge() throws Exception {
@Test(expected = StopConditionException.class)
public void testIncompatibleStopConditionInstance() {
- new PredefinedPath(new Never()); // should faild
+ new PredefinedPath(new Never()); // should fail
}
}
diff --git a/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java b/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
index f2cb0a6a6..5452ea70a 100644
--- a/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
+++ b/graphwalker-core/src/test/java/org/graphwalker/core/machine/SimpleMachineTest.java
@@ -504,23 +504,34 @@ public void resetMachine() throws Exception {
@Test
public void predefinedPathCurrentEdgeIndex() {
- Vertex vertex = new Vertex().setId("v");
- Edge edge = new Edge()
- .setId("e")
- .setSourceVertex(vertex)
- .setTargetVertex(new Vertex());
- List predefinedPath = Collections.singletonList(edge);
+ // Model
+ Vertex v0 = new Vertex().setId("v0");
+ Vertex v1 = new Vertex().setId("v1");
+ Edge e0 = new Edge().setId("e0").setSourceVertex(v0).setTargetVertex(v1);
+ Edge e1 = new Edge().setId("e1").setSourceVertex(v0).setTargetVertex(v1);
+ Edge e2 = new Edge().setId("e2").setSourceVertex(v0).setTargetVertex(v1);
+ Edge e3 = new Edge().setId("e3").setSourceVertex(v1).setTargetVertex(v0);
+ List predefinedPath = Arrays.asList(e0, e3, e1, e3, e2);
Model model = new Model()
- .addVertex(vertex)
- .addEdge(edge)
+ .addVertex(v0).addVertex(v1)
+ .addEdge(e0).addEdge(e1).addEdge(e2).addEdge(e3)
.setPredefinedPath(predefinedPath);
+
+ // Context and machine
Context context = new TestExecutionContext(model, new PredefinedPath(new PredefinedPathStopCondition()));
- context.setNextElement(vertex);
+ context.setNextElement(v0);
Machine machine = new SimpleMachine(context);
- assertEquals((Integer) 0, context.getPredefinedPathCurrentEdgeIndex());
- machine.getNextStep();
- assertEquals((Integer) 0, context.getPredefinedPathCurrentEdgeIndex());
- machine.getNextStep();
- assertEquals((Integer) 1, context.getPredefinedPathCurrentEdgeIndex());
+
+ // Tests
+ for (int stepCount = 0; stepCount < 5; ++stepCount) {
+ // Current element is a vertex
+ assertEquals((Integer) stepCount, context.getPredefinedPathCurrentEdgeIndex());
+ machine.getNextStep();
+ // Current element is an edge
+ assertEquals((Integer) stepCount, context.getPredefinedPathCurrentEdgeIndex());
+ machine.getNextStep();
+ }
+ // Last element is a vertex
+ assertEquals((Integer) 5, context.getPredefinedPathCurrentEdgeIndex());
}
}
diff --git a/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java b/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
index 99cdf2135..0dcea0ae5 100644
--- a/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
+++ b/graphwalker-io/src/test/java/org/graphwalker/io/factory/json/JsonContextFactoryTest.java
@@ -356,12 +356,13 @@ public void predefinedPath() throws IOException {
assertThat(context.getModel().getVertices().size(), is(2));
assertThat(context.getModel().getEdges().size(), is(4));
- assertThat(context.getModel().getPredefinedPath().size(), is(4));
+ assertThat(context.getModel().getPredefinedPath().size(), is(5));
assertThat(context.getModel().getPredefinedPath().get(0).getId(), is("e0"));
- assertThat(context.getModel().getPredefinedPath().get(1).getId(), is("e1"));
- assertThat(context.getModel().getPredefinedPath().get(2).getId(), is("e2"));
+ assertThat(context.getModel().getPredefinedPath().get(1).getId(), is("e3"));
+ assertThat(context.getModel().getPredefinedPath().get(2).getId(), is("e1"));
assertThat(context.getModel().getPredefinedPath().get(3).getId(), is("e3"));
+ assertThat(context.getModel().getPredefinedPath().get(4).getId(), is("e2"));
}
@Test(expected = ContextFactoryException.class)
diff --git a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
index dc618239d..2b02c2e06 100644
--- a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
+++ b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath.json
@@ -3,43 +3,44 @@
{
"name": "Small model",
"generator": "predefined_path(predefined_path)",
- "startElementId": "e0",
+ "startElementId": "n0",
"vertices": [
{
- "name": "v_VerifySomeAction",
+ "name": "n0",
"id": "n0"
},
{
- "name": "v_VerifySomeOtherAction",
+ "name": "n1",
"id": "n1"
}
],
"edges": [
{
- "name": "e_FirstAction",
+ "name": "e0",
"id": "e0",
- "targetVertexId": "n0"
+ "sourceVertexId": "n0",
+ "targetVertexId": "n1"
},
{
- "name": "e_AnotherAction",
+ "name": "e1",
"id": "e1",
"sourceVertexId": "n0",
"targetVertexId": "n1"
},
{
- "name": "e_SomeOtherAction",
+ "name": "e2",
"id": "e2",
- "sourceVertexId": "n1",
+ "sourceVertexId": "n0",
"targetVertexId": "n1"
},
{
- "name": "e_SomeOtherAction",
+ "name": "e3",
"id": "e3",
"sourceVertexId": "n1",
"targetVertexId": "n0"
}
],
- "predefinedPathEdgeIds": ["e0", "e1", "e2", "e3"]
+ "predefinedPathEdgeIds": ["e0", "e3", "e1", "e3", "e2"]
}
]
}
diff --git a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
index e3c82277b..71ecf7e89 100644
--- a/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
+++ b/graphwalker-io/src/test/resources/json/ModelWithPredefinedPath_unknown_edge.json
@@ -3,43 +3,44 @@
{
"name": "Small model",
"generator": "predefined_path(predefined_path)",
- "startElementId": "e0",
+ "startElementId": "n0",
"vertices": [
{
- "name": "v_VerifySomeAction",
+ "name": "n0",
"id": "n0"
},
{
- "name": "v_VerifySomeOtherAction",
+ "name": "n1",
"id": "n1"
}
],
"edges": [
{
- "name": "e_FirstAction",
+ "name": "e0",
"id": "e0",
- "targetVertexId": "n0"
+ "sourceVertexId": "n0",
+ "targetVertexId": "n1"
},
{
- "name": "e_AnotherAction",
+ "name": "e1",
"id": "e1",
"sourceVertexId": "n0",
"targetVertexId": "n1"
},
{
- "name": "e_SomeOtherAction",
+ "name": "e2",
"id": "e2",
- "sourceVertexId": "n1",
+ "sourceVertexId": "n0",
"targetVertexId": "n1"
},
{
- "name": "e_SomeOtherAction",
+ "name": "e3",
"id": "e3",
"sourceVertexId": "n1",
"targetVertexId": "n0"
}
],
- "predefinedPathEdgeIds": ["e0", "e1", "e2", "e3", "e4"]
+ "predefinedPathEdgeIds": ["e0", "e3", "e1", "e3", "e2", "e4"]
}
]
}