Allow VertexPrograms to declare their traverser requirements.

Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/a0cbe2d2
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/a0cbe2d2
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/a0cbe2d2

Branch: refs/heads/TINKERPOP-1404
Commit: a0cbe2d284a02cb69a0ef495372342b752dc88a4
Parents: 9004b4b
Author: Daniel Kuppitz <daniel_kupp...@hotmail.com>
Authored: Mon Sep 12 10:43:39 2016 +0200
Committer: Daniel Kuppitz <daniel_kupp...@hotmail.com>
Committed: Thu Sep 15 12:34:28 2016 +0200

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |   1 +
 .../gremlin/process/computer/VertexProgram.java |  11 +
 .../step/map/ProgramVertexProgramStep.java      |   9 +
 .../process/computer/GraphComputerTest.java     | 246 ++++++++++++++++++-
 .../decoration/TranslationStrategy.java         |  10 +-
 5 files changed, 270 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a0cbe2d2/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 24ed601..b359f37 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -28,6 +28,7 @@ TinkerPop 3.2.3 (Release Date: NOT OFFICIALLY RELEASED YET)
 
 * Fixed a `JavaTranslator` bug where `Bytecode` instructions were being 
mutated during translation.
 * Added `Path` to Gremlin-Python with respective GraphSON 2.0 deserializer.
+* VertexPrograms can now declare traverser requirements, e.g. to have access 
to the path when used with `.program()`.
 * Added missing `InetAddress` to GraphSON extension module.
 
 [[release-3-2-2]]

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a0cbe2d2/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexProgram.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexProgram.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexProgram.java
index 15243fa..1c8d0cb 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexProgram.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/VertexProgram.java
@@ -20,6 +20,7 @@
 package org.apache.tinkerpop.gremlin.process.computer;
 
 import org.apache.commons.configuration.Configuration;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
@@ -184,6 +185,16 @@ public interface VertexProgram<M> extends Cloneable {
     }
 
     /**
+     * The traverser requirements that are needed when this VP is used as part 
of a traversal.
+     * The default is an empty set.
+     *
+     * @return the traverser requirements
+     */
+    public default Set<TraverserRequirement> getTraverserRequirements() {
+        return Collections.emptySet();
+    }
+
+    /**
      * When multiple workers on a single machine need VertexProgram instances, 
it is possible to use clone.
      * This will provide a speedier way of generating instances, over the 
{@link VertexProgram#storeState} and {@link VertexProgram#loadState} model.
      * The default implementation simply returns the object as it assumes that 
the VertexProgram instance is a stateless singleton.

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a0cbe2d2/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ProgramVertexProgramStep.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ProgramVertexProgramStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ProgramVertexProgramStep.java
index 31eb04b..49add72 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ProgramVertexProgramStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/computer/traversal/step/map/ProgramVertexProgramStep.java
@@ -25,6 +25,7 @@ import org.apache.tinkerpop.gremlin.process.computer.Memory;
 import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
 import 
org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.process.traversal.util.PureTraversal;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.structure.Graph;
@@ -32,6 +33,7 @@ import 
org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -40,6 +42,7 @@ public final class ProgramVertexProgramStep extends 
VertexProgramStep {
 
     private final Map<String, Object> configuration;
     private final String toStringOfVertexProgram;
+    private final Set<TraverserRequirement> traverserRequirements;
 
     public ProgramVertexProgramStep(final Traversal.Admin traversal, final 
VertexProgram vertexProgram) {
         super(traversal);
@@ -48,6 +51,7 @@ public final class ProgramVertexProgramStep extends 
VertexProgramStep {
         base.setDelimiterParsingDisabled(true);
         vertexProgram.storeState(base);
         this.toStringOfVertexProgram = vertexProgram.toString();
+        this.traverserRequirements = vertexProgram.getTraverserRequirements();
     }
 
     @Override
@@ -62,6 +66,11 @@ public final class ProgramVertexProgramStep extends 
VertexProgramStep {
     }
 
     @Override
+    public Set<TraverserRequirement> getRequirements() {
+        return this.traverserRequirements;
+    }
+
+    @Override
     public int hashCode() {
         return super.hashCode() ^ this.configuration.hashCode();
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a0cbe2d2/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java
----------------------------------------------------------------------
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java
index 761ae06..108550a 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/computer/GraphComputerTest.java
@@ -18,34 +18,43 @@
  */
 package org.apache.tinkerpop.gremlin.process.computer;
 
+import org.apache.commons.configuration.BaseConfiguration;
 import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationUtils;
 import org.apache.tinkerpop.gremlin.ExceptionCoverage;
 import org.apache.tinkerpop.gremlin.LoadGraphWith;
 import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
 import 
org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram;
 import 
org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram;
 import 
org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
+import 
org.apache.tinkerpop.gremlin.process.computer.util.AbstractVertexProgramBuilder;
 import org.apache.tinkerpop.gremlin.process.computer.util.StaticMapReduce;
 import org.apache.tinkerpop.gremlin.process.computer.util.StaticVertexProgram;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import 
org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.VerificationException;
+import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.junit.Test;
 
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -54,13 +63,17 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentSkipListSet;
 import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.GRATEFUL;
 import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.outE;
+import static org.apache.tinkerpop.gremlin.structure.T.id;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -94,6 +107,9 @@ import static org.junit.Assert.fail;
 @ExceptionCoverage(exceptionClass = Graph.Exceptions.class, methods = {
         "graphDoesNotSupportProvidedGraphComputer"
 })
+@ExceptionCoverage(exceptionClass = Path.Exceptions.class, methods = {
+        "shouldFailWithImproperTraverserRequirements"
+})
 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
 public class GraphComputerTest extends AbstractGremlinProcessTest {
 
@@ -1603,7 +1619,7 @@ public class GraphComputerTest extends 
AbstractGremlinProcessTest {
         
graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).edges(__.<Vertex>bothE("knows").has("weight",
 P.gt(0.5f))).program(new 
VertexProgramM(VertexProgramM.PEOPLE_KNOWS_WELL_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>bothE().limit(0)).program(new
 VertexProgramM(VertexProgramM.VERTICES_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>outE().limit(1)).program(new
 VertexProgramM(VertexProgramM.ONE_OUT_EDGE_ONLY)).submit().get();
-        graphProvider.getGraphComputer(graph).edges(__.outE()).program(new 
VertexProgramM(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
+        graphProvider.getGraphComputer(graph).edges(outE()).program(new 
VertexProgramM(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
 
         /// VERTEX PROGRAM + MAP REDUCE
         
graphProvider.getGraphComputer(graph).vertices(__.hasLabel("software")).program(new
 VertexProgramM(VertexProgramM.SOFTWARE_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.SOFTWARE_ONLY)).submit().get();
@@ -1613,7 +1629,7 @@ public class GraphComputerTest extends 
AbstractGremlinProcessTest {
         
graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).edges(__.<Vertex>bothE("knows").has("weight",
 P.gt(0.5f))).program(new 
VertexProgramM(VertexProgramM.PEOPLE_KNOWS_WELL_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.PEOPLE_KNOWS_WELL_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>bothE().limit(0)).program(new
 VertexProgramM(VertexProgramM.VERTICES_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.VERTICES_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>outE().limit(1)).program(new
 VertexProgramM(VertexProgramM.ONE_OUT_EDGE_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.ONE_OUT_EDGE_ONLY)).submit().get();
-        graphProvider.getGraphComputer(graph).edges(__.outE()).program(new 
VertexProgramM(VertexProgramM.OUT_EDGES_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
+        graphProvider.getGraphComputer(graph).edges(outE()).program(new 
VertexProgramM(VertexProgramM.OUT_EDGES_ONLY)).mapReduce(new 
MapReduceJ(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
 
         /// MAP REDUCE ONLY
         
graphProvider.getGraphComputer(graph).vertices(__.hasLabel("software")).mapReduce(new
 MapReduceJ(VertexProgramM.SOFTWARE_ONLY)).submit().get();
@@ -1623,7 +1639,7 @@ public class GraphComputerTest extends 
AbstractGremlinProcessTest {
         
graphProvider.getGraphComputer(graph).vertices(__.hasLabel("person")).edges(__.<Vertex>bothE("knows").has("weight",
 P.gt(0.5f))).mapReduce(new 
MapReduceJ(VertexProgramM.PEOPLE_KNOWS_WELL_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>bothE().limit(0)).mapReduce(new
 MapReduceJ(VertexProgramM.VERTICES_ONLY)).submit().get();
         
graphProvider.getGraphComputer(graph).edges(__.<Vertex>outE().limit(1)).mapReduce(new
 MapReduceJ(VertexProgramM.ONE_OUT_EDGE_ONLY)).submit().get();
-        graphProvider.getGraphComputer(graph).edges(__.outE()).mapReduce(new 
MapReduceJ(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
+        graphProvider.getGraphComputer(graph).edges(outE()).mapReduce(new 
MapReduceJ(VertexProgramM.OUT_EDGES_ONLY)).submit().get();
 
         // EXCEPTION HANDLING
         try {
@@ -2327,4 +2343,226 @@ public class GraphComputerTest extends 
AbstractGremlinProcessTest {
             return GraphComputer.Persist.VERTEX_PROPERTIES;
         }
     }
-}
+
+    ///////////////////////////////////
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void shouldSucceedWithProperTraverserRequirements() throws 
Exception {
+
+        final AtomicInteger counter = new AtomicInteger(0);
+        final Map<String, Object> idsByName = new HashMap<>();
+        final VertexProgramQ vp = 
VertexProgramQ.build().from("a").property("coworkers").create();
+
+        g.V().hasLabel("person").filter(outE("created")).valueMap(true, 
"name").forEachRemaining((Map map) ->
+                idsByName.put((String) ((List) map.get("name")).get(0), 
map.get(id)));
+
+        try {
+            g.V().as("a").out("created").in("created").program(vp).dedup()
+                    .valueMap("name", 
"coworkers").forEachRemaining((Map<String, Object> map) -> {
+
+                final String name = (String) ((List) map.get("name")).get(0);
+                final Map<Object, Long> coworkers = (Map<Object, Long>) 
((List) map.get("coworkers")).get(0);
+                assertTrue(idsByName.containsKey(name));
+                assertEquals(2, coworkers.size());
+                idsByName.keySet().stream().filter(cn -> 
!cn.equals(name)).forEach(cn -> {
+                    final Object cid = idsByName.get(cn);
+                    assertTrue(coworkers.containsKey(cid));
+                    assertEquals(1L, coworkers.get(cid).longValue());
+                });
+                counter.incrementAndGet();
+            });
+
+            assertEquals(3, counter.intValue());
+        } catch (VerificationException ex) {
+            assumeNoException(ex);
+        }
+    }
+
+    @Test
+    @LoadGraphWith(MODERN)
+    public void shouldFailWithImproperTraverserRequirements() throws Exception 
{
+
+        final AtomicInteger counter = new AtomicInteger(0);
+        final Map<String, Object> idsByName = new HashMap<>();
+        final VertexProgramQ vp = 
VertexProgramQ.build().from("a").property("coworkers").
+                useTraverserRequirements(false).create();
+
+        g.V().hasLabel("person").filter(outE("created")).valueMap(true, 
"name").forEachRemaining((Map map) ->
+                idsByName.put((String) ((List) map.get("name")).get(0), 
map.get(id)));
+
+        try {
+            g.V().as("a").out("created").in("created").program(vp).dedup()
+                    .valueMap("name", 
"coworkers").forEachRemaining((Map<String, Object> map) -> {
+
+                final String name = (String) ((List) map.get("name")).get(0);
+                final Map coworkers = (Map) ((List) 
map.get("coworkers")).get(0);
+                assertTrue(idsByName.containsKey(name));
+                assertTrue(coworkers.isEmpty());
+                counter.incrementAndGet();
+            });
+
+            assertEquals(3, counter.intValue());
+        } catch (VerificationException ex) {
+            assumeNoException(ex);
+        }
+    }
+
+    private static class VertexProgramQ implements VertexProgram<Object> {
+
+        private static final String VERTEX_PROGRAM_Q_CFG_PREFIX = 
"gremlin.vertexProgramQ";
+        private static final String MAP_KEY_CFG_KEY = 
VERTEX_PROGRAM_Q_CFG_PREFIX + ".source";
+        private static final String PROPERTY_CFG_KEY = 
VERTEX_PROGRAM_Q_CFG_PREFIX + ".property";
+        private static final String USE_TRAVERSER_REQUIREMENTS_CFG_KEY = 
VERTEX_PROGRAM_Q_CFG_PREFIX + ".useTraverserRequirements";
+
+        private final Set<VertexComputeKey> elementComputeKeys;
+        private Configuration configuration;
+        private String sourceKey;
+        private String propertyKey;
+        private Set<TraverserRequirement> traverserRequirements;
+
+        private VertexProgramQ() {
+            elementComputeKeys = new HashSet<>();
+        }
+
+        @Override
+        public void storeState(final Configuration config) {
+            VertexProgram.super.storeState(config);
+            if (configuration != null) {
+                ConfigurationUtils.copy(configuration, config);
+            }
+        }
+
+        @Override
+        public void loadState(final Graph graph, final Configuration config) {
+            configuration = new BaseConfiguration();
+            if (config != null) {
+                ConfigurationUtils.copy(config, configuration);
+            }
+            sourceKey = configuration.getString(MAP_KEY_CFG_KEY);
+            propertyKey = configuration.getString(PROPERTY_CFG_KEY);
+            traverserRequirements = 
configuration.getBoolean(USE_TRAVERSER_REQUIREMENTS_CFG_KEY, true)
+                    ? Collections.singleton(TraverserRequirement.LABELED_PATH) 
: Collections.emptySet();
+            elementComputeKeys.add(VertexComputeKey.of(propertyKey, false));
+        }
+
+        @Override
+        public void setup(final Memory memory) {
+        }
+
+        @Override
+        public void execute(final Vertex vertex, final Messenger messenger, 
final Memory memory) {
+            final Property<TraverserSet> haltedTraversers = 
vertex.property(TraversalVertexProgram.HALTED_TRAVERSERS);
+            if (!haltedTraversers.isPresent()) return;
+            final Iterator iterator = haltedTraversers.value().iterator();
+            if (iterator.hasNext()) {
+                List<Map.Entry<Object, Long>> list = new ArrayList<>();
+                while (iterator.hasNext()) {
+                    final Traverser t = (Traverser) iterator.next();
+                    try {
+                        final Vertex source = (Vertex) t.path(sourceKey);
+                        if (!source.id().equals(vertex.id())) {
+                            final Map.Entry<Object, Long> entry = new 
AbstractMap.SimpleEntry<>(source.id(), t.bulk());
+                            list.add(entry);
+                        }
+                        assertFalse(traverserRequirements.isEmpty());
+                    } catch (Exception ex) {
+                        assertTrue(traverserRequirements.isEmpty());
+                        
validateException(Path.Exceptions.stepWithProvidedLabelDoesNotExist(sourceKey), 
ex);
+                    }
+                }
+                final Map<Object, Number> map = new HashMap<>(list.size(), 1f);
+                for (Map.Entry<Object, Long> entry : list) 
map.put(entry.getKey(), entry.getValue());
+                vertex.property(propertyKey, map);
+            }
+        }
+
+        @Override
+        public boolean terminate(final Memory memory) {
+            return memory.isInitialIteration();
+        }
+
+        @Override
+        public Set<MessageScope> getMessageScopes(final Memory memory) {
+            return Collections.emptySet();
+        }
+
+        @Override
+        public Set<VertexComputeKey> getVertexComputeKeys() {
+            return elementComputeKeys;
+        }
+
+        @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException", 
"CloneDoesntCallSuperClone"})
+        @Override
+        public VertexProgram<Object> clone() {
+            return this;
+        }
+
+        @Override
+        public GraphComputer.ResultGraph getPreferredResultGraph() {
+            return GraphComputer.ResultGraph.NEW;
+        }
+
+        @Override
+        public GraphComputer.Persist getPreferredPersist() {
+            return GraphComputer.Persist.VERTEX_PROPERTIES;
+        }
+
+        @Override
+        public Set<TraverserRequirement> getTraverserRequirements() {
+            return this.traverserRequirements;
+        }
+
+        @Override
+        public Features getFeatures() {
+            return new Features() {
+                @Override
+                public boolean requiresVertexPropertyAddition() {
+                    return true;
+                }
+            };
+        }
+
+        public static Builder build() {
+            return new Builder();
+        }
+
+        static class Builder extends AbstractVertexProgramBuilder<Builder> {
+
+            private Builder() {
+                super(VertexProgramQ.class);
+            }
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public VertexProgramQ create(final Graph graph) {
+                if (graph != null) {
+                    
ConfigurationUtils.append(graph.configuration().subset(VERTEX_PROGRAM_Q_CFG_PREFIX),
 configuration);
+                }
+                return (VertexProgramQ) 
VertexProgram.createVertexProgram(graph, configuration);
+            }
+
+            public VertexProgramQ create() {
+                return create(null);
+            }
+
+            public Builder from(final String label) {
+                configuration.setProperty(MAP_KEY_CFG_KEY, label);
+                return this;
+            }
+
+            public Builder property(final String name) {
+                configuration.setProperty(PROPERTY_CFG_KEY, name);
+                return this;
+            }
+
+            /**
+             * This is only configurable for the purpose of testing. In a 
real-world VP this would be a bad pattern.
+             */
+            public Builder useTraverserRequirements(final boolean value) {
+                configuration.setProperty(USE_TRAVERSER_REQUIREMENTS_CFG_KEY, 
value);
+                return this;
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a0cbe2d2/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java
----------------------------------------------------------------------
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java
index 92c9483..6b06d60 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/TranslationStrategy.java
@@ -21,6 +21,7 @@ package 
org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
 
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.jsr223.SingleGremlinScriptEngineManager;
+import 
org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ProgramVertexProgramStep;
 import 
org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Step;
@@ -70,9 +71,12 @@ public final class TranslationStrategy extends 
AbstractTraversalStrategy<Travers
             return;
 
         // verifications to ensure unsupported steps do not exist in the 
traversal
-        if (Boolean.valueOf(System.getProperty("is.testing", "false")) &&
-                (traversal.getBytecode().toString().contains("$") || 
traversal.getBytecode().toString().contains("HashSetSupplier")))
-            throw new VerificationException("Test suite does not support 
lambdas", traversal);
+        if (Boolean.valueOf(System.getProperty("is.testing", "false"))) {
+            if (traversal.getBytecode().toString().contains("$") || 
traversal.getBytecode().toString().contains("HashSetSupplier"))
+                throw new VerificationException("Test suite does not support 
lambdas", traversal);
+            if 
(TraversalHelper.hasStepOfAssignableClassRecursively(ProgramVertexProgramStep.class,
 traversal))
+                throw new VerificationException("Test suite does not support 
embedded vertex programs", traversal);
+        }
 
         final Traversal.Admin<?, ?> translatedTraversal;
         final Bytecode bytecode = 
Boolean.valueOf(System.getProperty("is.testing", "false")) ?

Reply via email to