Repository: tinkerpop Updated Branches: refs/heads/shortest-path-wip a8e2ea464 -> 0f6cdcd91
wip Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/0f6cdcd9 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/0f6cdcd9 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/0f6cdcd9 Branch: refs/heads/shortest-path-wip Commit: 0f6cdcd9152839b9704d3576a4c52b9150accfe4 Parents: a8e2ea4 Author: Daniel Kuppitz <daniel_kupp...@hotmail.com> Authored: Sat Jun 2 01:35:46 2018 -0700 Committer: Daniel Kuppitz <daniel_kupp...@hotmail.com> Committed: Sat Jun 2 01:35:46 2018 -0700 ---------------------------------------------------------------------- .../search/path/ShortestPathVertexProgram.java | 39 +++++++------------- .../gremlin/structure/io/gryo/GryoVersion.java | 7 +++- .../structure/io/gryo/UtilSerializers.java | 15 ++++++++ .../gremlin/structure/util/Attachable.java | 3 +- .../tinkerpop/gremlin/AbstractGremlinTest.java | 2 +- .../process/AbstractGremlinProcessTest.java | 11 +++--- .../search/path/ShortestPathTestHelper.java | 36 ++++++++++++++++-- .../traversal/step/map/ShortestPathTest.java | 1 + 8 files changed, 75 insertions(+), 39 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java index 6d46f88..f3a3293 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathVertexProgram.java @@ -26,15 +26,13 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.VertexPr import org.apache.tinkerpop.gremlin.process.computer.util.AbstractVertexProgramBuilder; import org.apache.tinkerpop.gremlin.process.traversal.*; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.process.traversal.lambda.ConstantTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.lambda.ElementValueTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.lambda.IdentityTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.IndexedTraverserSet; import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet; import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceFactory; import org.apache.tinkerpop.gremlin.util.NumberHelper; import org.javatuples.Pair; import org.javatuples.Triplet; @@ -65,9 +63,11 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed private static final int COLLECT_PATHS = 1; private static final int UPDATE_HALTED_TRAVERSERS = 2; - public static final PureTraversal<Vertex, ?> DEFAULT_VERTEX_FILTER_TRAVERSAL = new PureTraversal<>(new IdentityTraversal<>()); + public static final PureTraversal<Vertex, ?> DEFAULT_VERTEX_FILTER_TRAVERSAL = new PureTraversal<>( + __.<Vertex> identity().asAdmin()); // todo: new IdentityTraversal<>() public static final PureTraversal<Vertex, Edge> DEFAULT_EDGE_TRAVERSAL = new PureTraversal<>(__.bothE().asAdmin()); - public static final PureTraversal<Edge, Number> DEFAULT_DISTANCE_TRAVERSAL = new PureTraversal<>(new ConstantTraversal<>(1)); + public static final PureTraversal<Edge, Number> DEFAULT_DISTANCE_TRAVERSAL = new PureTraversal<>( + __.<Edge> start().<Number> constant(1).asAdmin()); // todo: new ConstantTraversal<>(1) private TraverserSet<Vertex> haltedTraversers; private IndexedTraverserSet<Vertex, Vertex> haltedTraversersIndex; @@ -133,7 +133,6 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed for (final Traverser.Admin<Vertex> traverser : this.haltedTraversers) { this.haltedTraversersIndex.add(traverser.split()); } - this.haltedTraversers.clear(); this.memoryComputeKeys.add(MemoryComputeKey.of(SHORTEST_PATHS, Operator.addAll, true, !standalone)); } @@ -151,6 +150,7 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed this.traversal.storeState(configuration, ProgramVertexProgramStep.ROOT_TRAVERSAL); configuration.setProperty(ProgramVertexProgramStep.STEP_ID, this.programStep.getId()); } + TraversalVertexProgram.storeHaltedTraversers(configuration, this.haltedTraversers); } @Override @@ -367,7 +367,9 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed } } for (final Element element : elements) { - if (element != null) result = result.extend(element, Collections.emptySet()); + if (element != null) { + result = result.extend(ReferenceFactory.detach(element), Collections.emptySet()); + } } return result; } @@ -378,27 +380,14 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed filterTraversal.addStart(filterTraversal.getTraverserGenerator().generate(vertex, filterTraversal.getStartStep(), 1)); return filterTraversal.hasNext(); } - if (vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent()) return true; - for (final Traverser<?> traverser : this.haltedTraversers) { - if (vertex.equals(traverser.get())) return true; - } - return false; + return vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS).isPresent(); } - private boolean isEndVertex(final Vertex vertex, final Vertex startVertex) { + private boolean isEndVertex(final Vertex vertex) { final Traversal.Admin<Vertex, ?> filterTraversal = this.targetVertexFilterTraversal.getPure(); //noinspection unchecked final Step<Vertex, Vertex> startStep = (Step<Vertex, Vertex>) filterTraversal.getStartStep(); - if (this.standalone) { - filterTraversal.addStart(filterTraversal.getTraverserGenerator().generate(vertex, startStep, 1)); - } else { - final Property<TraverserSet<Vertex>> haltedTraversers = startVertex.property(TraversalVertexProgram.HALTED_TRAVERSERS); - if (haltedTraversers.isPresent()) { - for (final Traverser.Admin<Vertex> traverser : haltedTraversers.value()) { - filterTraversal.addStart(traverser.split(vertex, startStep)); - } - } - } + filterTraversal.addStart(filterTraversal.getTraverserGenerator().generate(vertex, startStep, 1)); return filterTraversal.hasNext(); } @@ -469,7 +458,7 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed for (final Pair<Number, Set<Path>> pair : paths.values()) { for (final Path path : pair.getValue1()) { - if (isEndVertex(path.get(path.size() - 1), path.get(0))) { + if (isEndVertex(vertex)) { result.add(path); } } @@ -521,7 +510,7 @@ public class ShortestPathVertexProgram implements VertexProgram<Triplet<Path, Ed public Builder distanceProperty(final String distance) { //noinspection unchecked return distance != null - ? distanceTraversal((Traversal) new ElementValueTraversal<>(distance)) + ? distanceTraversal(__.values(distance)) // todo: (Traversal) new ElementValueTraversal<>(distance) : distanceTraversal(DEFAULT_DISTANCE_TRAVERSAL.getPure()); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java index 6bb7b34..45ff1ef 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java @@ -109,6 +109,7 @@ import org.apache.tinkerpop.shaded.kryo.ClassResolver; import org.apache.tinkerpop.shaded.kryo.KryoSerializable; import org.apache.tinkerpop.shaded.kryo.serializers.JavaSerializer; import org.javatuples.Pair; +import org.javatuples.Triplet; import java.math.BigDecimal; import java.math.BigInteger; @@ -346,6 +347,7 @@ public enum GryoVersion { add(GryoTypeReg.of(MapReduce.NullObject.class, 74)); add(GryoTypeReg.of(AtomicLong.class, 79)); add(GryoTypeReg.of(Pair.class, 88, new UtilSerializers.PairSerializer())); + add(GryoTypeReg.of(Triplet.class, 174, new UtilSerializers.TripletSerializer())); // ***LAST ID*** add(GryoTypeReg.of(TraversalExplanation.class, 106, new JavaSerializer())); add(GryoTypeReg.of(Duration.class, 93, new JavaTimeSerializers.DurationSerializer())); @@ -373,7 +375,7 @@ public enum GryoVersion { add(GryoTypeReg.of(RangeGlobalStep.RangeBiOperator.class, 114)); add(GryoTypeReg.of(OrderGlobalStep.OrderBiOperator.class, 118)); add(GryoTypeReg.of(ProfileStep.ProfileBiOperator.class, 119)); - add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173)); // ***LAST ID*** + add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173)); // placeholder serializers for classes that don't live here in core. this will allow them to be used if // present or ignored if the class isn't available. either way the registration numbers are held as @@ -499,6 +501,7 @@ public enum GryoVersion { add(GryoTypeReg.of(MapReduce.NullObject.class, 74)); add(GryoTypeReg.of(AtomicLong.class, 79)); add(GryoTypeReg.of(Pair.class, 88, new UtilSerializers.PairSerializer())); + add(GryoTypeReg.of(Triplet.class, 174, new UtilSerializers.TripletSerializer())); // ***LAST ID*** add(GryoTypeReg.of(TraversalExplanation.class, 106, new JavaSerializer())); add(GryoTypeReg.of(Duration.class, 93, new JavaTimeSerializers.DurationSerializer())); @@ -552,7 +555,7 @@ public enum GryoVersion { add(GryoTypeReg.of(MatchStep.CountMatchAlgorithm.class, 160)); add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 167)); // skip 171, 172 to sync with tp33 - add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173)); // ***LAST ID*** + add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173)); }}; } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java index 5182e6c..aff6059 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/UtilSerializers.java @@ -28,6 +28,7 @@ import org.apache.tinkerpop.shaded.kryo.Serializer; import org.apache.tinkerpop.shaded.kryo.io.Input; import org.apache.tinkerpop.shaded.kryo.io.Output; import org.javatuples.Pair; +import org.javatuples.Triplet; import java.net.InetAddress; import java.net.URI; @@ -216,6 +217,20 @@ final class UtilSerializers { } } + static final class TripletSerializer implements SerializerShim<Triplet> { + @Override + public <O extends OutputShim> void write(final KryoShim<?, O> kryo, final O output, final Triplet triplet) { + kryo.writeClassAndObject(output, triplet.getValue0()); + kryo.writeClassAndObject(output, triplet.getValue1()); + kryo.writeClassAndObject(output, triplet.getValue2()); + } + + @Override + public <I extends InputShim> Triplet read(final KryoShim<I, ?> kryo, final I input, final Class<Triplet> tripletClass) { + return Triplet.with(kryo.readClassAndObject(input), kryo.readClassAndObject(input), kryo.readClassAndObject(input)); + } + } + static final class EntrySerializer extends Serializer<Map.Entry> { @Override public void write(final Kryo kryo, final Output output, final Map.Entry entry) { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java index fa999aa..3cd76f2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/Attachable.java @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; import java.util.Iterator; import java.util.Optional; @@ -70,7 +71,7 @@ public interface Attachable<V> { */ public static class Method { public static <V> Function<Attachable<V>, V> get(final Host hostVertexOrGraph) { - return (Attachable<V> attachable) -> { + return hostVertexOrGraph instanceof EmptyGraph ? Attachable::get : (Attachable<V> attachable) -> { final Object base = attachable.get(); if (base instanceof Vertex) { final Optional<Vertex> optional = hostVertexOrGraph instanceof Graph ? http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java index fde1b8d..e0d0e6c 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/AbstractGremlinTest.java @@ -243,7 +243,7 @@ public abstract class AbstractGremlinTest { public void printTraversalForm(final Traversal traversal) { logger.info(String.format("Testing: %s", name.getMethodName())); logger.info(" pre-strategy:" + traversal); - traversal.hasNext(); + if (!traversal.asAdmin().isLocked()) traversal.asAdmin().applyStrategies(); logger.info(" post-strategy:" + traversal); verifyUniqueStepIds(traversal.asAdmin()); } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java index 2861724..e15fff8 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/AbstractGremlinProcessTest.java @@ -119,7 +119,7 @@ public abstract class AbstractGremlinProcessTest extends AbstractGremlinTest { public static <T> void checkResults(final List<T> expectedResults, final Traversal<?, T> traversal) { final List<T> results = traversal.toList(); - assertFalse(traversal.hasNext()); + assertThat(traversal.hasNext(), is(false)); if (expectedResults.size() != results.size()) { logger.error("Expected results: " + expectedResults); logger.error("Actual results: " + results); @@ -128,20 +128,19 @@ public abstract class AbstractGremlinProcessTest extends AbstractGremlinTest { for (T t : results) { if (t instanceof Map) { - assertThat("Checking map result existence: " + t, expectedResults.stream().filter(e -> e instanceof Map).filter(e -> internalCheckMap((Map) e, (Map) t)).findAny().isPresent(), is(true)); + assertThat("Checking map result existence: " + t, expectedResults.stream().anyMatch(e -> e instanceof Map && internalCheckMap((Map) e, (Map) t)), is(true)); } else if (t instanceof List) { - assertThat("Checking list result existence: " + t, expectedResults.stream().filter(e -> e instanceof List).filter(e -> internalCheckList((List) e, (List) t)).findAny().isPresent(), is(true)); + assertThat("Checking list result existence: " + t, expectedResults.stream().anyMatch(e -> e instanceof List && internalCheckList((List) e, (List) t)), is(true)); } else { assertThat("Checking result existence: " + t, expectedResults.contains(t), is(true)); } } final Map<T, Long> expectedResultsCount = new HashMap<>(); final Map<T, Long> resultsCount = new HashMap<>(); + expectedResults.forEach(t -> MapHelper.incr(expectedResultsCount, t, 1L)); + results.forEach(t -> MapHelper.incr(resultsCount, t, 1L)); assertEquals("Checking indexing is equivalent", expectedResultsCount.size(), resultsCount.size()); - expectedResults.forEach(t -> MapHelper.incr(expectedResultsCount, t, 1l)); - results.forEach(t -> MapHelper.incr(resultsCount, t, 1l)); expectedResultsCount.forEach((k, v) -> assertEquals("Checking result group counts", v, resultsCount.get(k))); - assertThat(traversal.hasNext(), is(false)); } public static <T> void checkResults(final Map<T, Long> expectedResults, final Traversal<?, T> traversal) { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathTestHelper.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathTestHelper.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathTestHelper.java index cf17578..7f3aa63 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathTestHelper.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/search/path/ShortestPathTestHelper.java @@ -24,11 +24,21 @@ import org.apache.tinkerpop.gremlin.process.traversal.Path; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.util.ImmutablePath; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.MapHelper; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.hamcrest.Matchers; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; /** * @author Daniel Kuppitz (http://gremlin.guru) @@ -37,10 +47,14 @@ public class ShortestPathTestHelper { private final AbstractGremlinProcessTest test; private final GraphTraversalSource g; + private final Map<String, Vertex> vertexCache; + private final Map<Object, Map<Object, Edge>> edgeCache; public ShortestPathTestHelper(final AbstractGremlinProcessTest test, final GraphTraversalSource g) { this.test = test; this.g = g; + this.vertexCache = new HashMap<>(); + this.edgeCache = new HashMap<>(); } public void checkResults(final List<Path> expected, final List<Path> actual) { @@ -55,12 +69,20 @@ public class ShortestPathTestHelper { Path path = ImmutablePath.make(); boolean first = true; for (final String name : names) { - final Vertex vertex = test.convertToVertex(name); + final Vertex vertex = vertexCache.computeIfAbsent(name, test::convertToVertex); if (!first) { if (includeEdges) { - final Edge edge = g.V(((Vertex) path.get(path.size() - 1)).id()) - .bothE().filter(__.otherV().is(P.eq(vertex))) - .next(); + final Object id1 = ((Vertex) path.get(path.size() - 1)).id(); + final Object id2 = vertex.id(); + final Edge edge; + if (edgeCache.containsKey(id1)) { + edge = edgeCache.get(id1).computeIfAbsent(id2, id -> getEdge(id1, id)); + } else if (edgeCache.containsKey(id2)) { + edge = edgeCache.get(id2).computeIfAbsent(id1, id -> getEdge(id, id2)); + } else { + edgeCache.put(id1, new HashMap<>()); + edgeCache.get(id1).put(id2, edge = getEdge(id1, id2)); + } path = path.extend(edge, Collections.emptySet()); } } @@ -69,4 +91,10 @@ public class ShortestPathTestHelper { } return path; } + + private Edge getEdge(final Object id1, final Object id2) { + return g.V(id1) + .bothE().filter(__.otherV().hasId(id2)) + .next(); + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/0f6cdcd9/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ShortestPathTest.java ---------------------------------------------------------------------- diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ShortestPathTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ShortestPathTest.java index 71f4469..a9d5ccb 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ShortestPathTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/ShortestPathTest.java @@ -210,6 +210,7 @@ public abstract class ShortestPathTest extends AbstractGremlinProcessTest { assertEquals(helper.makePath("marko", "lop", "josh"), traversal.next()); assertFalse(traversal.hasNext()); } + @Test @LoadGraphWith(CREW) public void g_V_hasXname_danielX_shortestPath_targetXhasXname_stephenXX_edgesXbothEXusesXX() {