You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by dk...@apache.org on 2019/04/04 15:05:03 UTC

[tinkerpop] branch TINKERPOP-2191 updated (402e540 -> 0769976)

This is an automated email from the ASF dual-hosted git repository.

dkuppitz pushed a change to branch TINKERPOP-2191
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


    omit 402e540  Implemented EdgeLabelVerificationStrategy
     new 0769976  Implemented EdgeLabelVerificationStrategy

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (402e540)
            \
             N -- N -- N   refs/heads/TINKERPOP-2191 (0769976)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../traversal/strategy/verification/EdgeLabelVerificationStrategy.java | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)


[tinkerpop] 01/01: Implemented EdgeLabelVerificationStrategy

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dkuppitz pushed a commit to branch TINKERPOP-2191
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 0769976c267183ffb9aa22ab9a379d7163e70d84
Author: Daniel Kuppitz <da...@hotmail.com>
AuthorDate: Thu Mar 14 15:05:09 2019 -0700

    Implemented EdgeLabelVerificationStrategy
---
 CHANGELOG.asciidoc                                 |   2 +-
 docs/src/upgrade/release-3.3.x.asciidoc            |  38 +++++
 .../tinkerpop/gremlin/jsr223/CoreImports.java      |   2 +
 .../EdgeLabelVerificationStrategy.java             | 131 ++++++++++++++
 .../structure/io/graphson/GraphSONModule.java      |   5 +
 .../gremlin/structure/io/gryo/GryoVersion.java     |   7 +-
 .../EdgeLabelVerificationStrategyTest.java         | 188 +++++++++++++++++++++
 7 files changed, 370 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 88df8ad..106761a 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -24,7 +24,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 === TinkerPop 3.3.7 (Release Date: NOT OFFICIALLY RELEASED YET)
 
 * Ensure `gremlin.sh` works when directories contain spaces
-
+* Implemented `EdgeLabelVerificationStrategy`
 
 [[release-3-3-6]]
 === TinkerPop 3.3.6 (Release Date: March 18, 2019)
diff --git a/docs/src/upgrade/release-3.3.x.asciidoc b/docs/src/upgrade/release-3.3.x.asciidoc
index c1911d3..a5ece72 100644
--- a/docs/src/upgrade/release-3.3.x.asciidoc
+++ b/docs/src/upgrade/release-3.3.x.asciidoc
@@ -27,6 +27,44 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 
 Please see the link:https://github.com/apache/tinkerpop/blob/3.3.7/CHANGELOG.asciidoc#release-3-3-7[changelog] for a complete list of all the modifications that are part of this release.
 
+=== Upgrading for Providers
+
+==== Detection of Anti-Patterns
+
+This release adds a strategy named `EdgeLabelVerificationStrategy`. The strategy will not be added by default to the traversal source, however, providers can add it explicitly to encourage (or enforce)
+users to always specify at least one edge label. `EdgeLabelVerificationStrategy` can be configured to either throw an exception if no edge label was specified, log a warning or do both.
+
+[source,text]
+----
+gremlin> // throw an exception if edge label was not specified
+gremlin> g = TinkerFactory.createModern().traversal().withStrategies(EdgeLabelVerificationStrategy.build().throwException().create())
+==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
+gremlin> g.V(1).out('knows')
+==>v[2]
+==>v[4]
+gremlin> g.V(1).out()
+The provided traversal contains a vertex step without any specified edge label: VertexStep(OUT,vertex)
+Type ':help' or ':h' for help.
+Display stack trace? [yN]
+----
+
+[source,text]
+----
+gremlin> // log a warning if edge label was not specified (Log4j has to be configured properly)
+gremlin> g = TinkerFactory.createModern().traversal().withStrategies(EdgeLabelVerificationStrategy.build().logWarning().create())
+==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
+gremlin> g.V(1).out('knows')
+==>v[2]
+==>v[4]
+gremlin> g.V(1).out()
+WARN  org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy  - The provided traversal contains a vertex step without any specified edge label: VertexStep(OUT,vertex)
+==>v[3]
+==>v[2]
+==>v[4]
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2191[TINKERPOP-2191]
+
 == TinkerPop 3.3.6
 
 *Release Date: March 18, 2019*
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
index 576d0de..e298951 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
@@ -86,6 +86,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Orde
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
@@ -241,6 +242,7 @@ public final class CoreImports {
         CLASS_IMPORTS.add(LambdaRestrictionStrategy.class);
         CLASS_IMPORTS.add(ReadOnlyStrategy.class);
         CLASS_IMPORTS.add(StandardVerificationStrategy.class);
+        CLASS_IMPORTS.add(EdgeLabelVerificationStrategy.class);
         // graph traversal
         CLASS_IMPORTS.add(AnonymousTraversalSource.class);
         CLASS_IMPORTS.add(__.class);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java
new file mode 100644
index 0000000..47a016f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.MapConfiguration;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@code EdgeLabelVerificationStrategy} does not allow edge traversal steps to have no label specified.
+ * Providing one or more labels is considered to be a best practice, however, TinkerPop will not force the specification
+ * of edge labels; instead, providers or users will have to enable this strategy explicitly.
+ * <p/>
+ *
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ * @example <pre>
+ * __.outE()           // throws an IllegalStateException
+ * __.out()            // throws an IllegalStateException
+ * __.bothE()          // throws an IllegalStateException
+ * __.to(OUT)          // throws an IllegalStateException
+ * __.toE(IN)          // throws an IllegalStateException
+ * </pre>
+ */
+public final class EdgeLabelVerificationStrategy
+        extends AbstractTraversalStrategy<TraversalStrategy.VerificationStrategy>
+        implements TraversalStrategy.VerificationStrategy {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(EdgeLabelVerificationStrategy.class);
+
+    private static final String THROW_EXCEPTION = "throwException";
+    private static final String LOG_WARNING = "logWarning";
+
+    private final boolean throwException;
+    private final boolean logWarning;
+
+    private EdgeLabelVerificationStrategy(final boolean throwException, final boolean logWarning) {
+        this.throwException = throwException;
+        this.logWarning = logWarning;
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        for (final Step<?, ?> step : traversal.getSteps()) {
+            if (step instanceof VertexStep && ((VertexStep) step).getEdgeLabels().length == 0) {
+                final String msg = String.format(
+                        "The provided traversal contains a vertex step without any specified edge label: %s\nAlways " +
+                                "specify edge labels which restrict traversal paths ensuring optimal performance.", step);
+                if (logWarning) {
+                    LOGGER.warn(msg);
+                }
+                if (throwException) {
+                    throw new VerificationException(msg, traversal);
+                }
+            }
+        }
+    }
+
+    public static EdgeLabelVerificationStrategy create(final Configuration configuration) {
+        return new EdgeLabelVerificationStrategy(
+                configuration.getBoolean(THROW_EXCEPTION, false),
+                configuration.getBoolean(LOG_WARNING, false));
+    }
+
+    @Override
+    public Configuration getConfiguration() {
+        final Map<String, Object> m = new HashMap<>(2);
+        m.put(THROW_EXCEPTION, this.throwException);
+        m.put(LOG_WARNING, this.logWarning);
+        return new MapConfiguration(m);
+    }
+
+    public static EdgeLabelVerificationStrategy.Builder build() {
+        return new EdgeLabelVerificationStrategy.Builder();
+    }
+
+    public final static class Builder {
+
+        private boolean throwException;
+        private boolean logWarning;
+
+        private Builder() {
+        }
+
+        public EdgeLabelVerificationStrategy.Builder throwException() {
+            return this.throwException(true);
+        }
+
+        public EdgeLabelVerificationStrategy.Builder throwException(final boolean throwException) {
+            this.throwException = throwException;
+            return this;
+        }
+
+        public EdgeLabelVerificationStrategy.Builder logWarning() {
+            return this.logWarning(true);
+        }
+
+        public EdgeLabelVerificationStrategy.Builder logWarning(final boolean logWarning) {
+            this.logWarning = logWarning;
+            return this;
+        }
+
+        public EdgeLabelVerificationStrategy create() {
+            return new EdgeLabelVerificationStrategy(this.throwException, this.logWarning);
+        }
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index 1078a6b..3bbea66 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -54,6 +54,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Path
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
@@ -183,6 +184,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             ReadOnlyStrategy.class,
                             StandardVerificationStrategy.class,
                             EarlyLimitStrategy.class,
+                            EdgeLabelVerificationStrategy.class,
                             //
                             GraphFilterStrategy.class,
                             VertexProgramStrategy.class
@@ -300,6 +302,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     ReadOnlyStrategy.class,
                     StandardVerificationStrategy.class,
                     EarlyLimitStrategy.class,
+                    EdgeLabelVerificationStrategy.class,
                     //
                     GraphFilterStrategy.class,
                     VertexProgramStrategy.class
@@ -400,6 +403,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                             ReadOnlyStrategy.class,
                             StandardVerificationStrategy.class,
                             EarlyLimitStrategy.class,
+                            EdgeLabelVerificationStrategy.class,
                             //
                             GraphFilterStrategy.class,
                             VertexProgramStrategy.class
@@ -509,6 +513,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
                     ReadOnlyStrategy.class,
                     StandardVerificationStrategy.class,
                     EarlyLimitStrategy.class,
+                    EdgeLabelVerificationStrategy.class,
                     //
                     GraphFilterStrategy.class,
                     VertexProgramStrategy.class
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 7c1dd5a..974542d 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
@@ -63,6 +63,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Path
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_O_P_S_SE_SL_Traverser;
@@ -336,9 +337,10 @@ public enum GryoVersion {
             add(GryoTypeReg.of(GraphFilterStrategy.class, 157));
             add(GryoTypeReg.of(LambdaRestrictionStrategy.class, 158));
             add(GryoTypeReg.of(ReadOnlyStrategy.class, 159));
-            add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));   // ***LAST ID***
+            add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));
             add(GryoTypeReg.of(MatchStep.CountMatchAlgorithm.class, 160));
             add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 164));
+            add(GryoTypeReg.of(EdgeLabelVerificationStrategy.class, 189));   // ***LAST ID***
 
             add(GryoTypeReg.of(TraverserSet.class, 58));
             add(GryoTypeReg.of(Tree.class, 61));
@@ -555,9 +557,10 @@ public enum GryoVersion {
             add(GryoTypeReg.of(GraphFilterStrategy.class, 157));
             add(GryoTypeReg.of(LambdaRestrictionStrategy.class, 158));
             add(GryoTypeReg.of(ReadOnlyStrategy.class, 159));
-            add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));   // ***LAST ID***
+            add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));
             add(GryoTypeReg.of(MatchStep.CountMatchAlgorithm.class, 160));
             add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 167));
+            add(GryoTypeReg.of(EdgeLabelVerificationStrategy.class, 189));   // ***LAST ID***
             // skip 171, 172 to sync with tp33
             add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173));
         }};
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java
new file mode 100644
index 0000000..72a8841
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+@RunWith(Parameterized.class)
+public class EdgeLabelVerificationStrategyTest {
+
+    private final static Predicate<String> MSG_PREDICATE = Pattern.compile(
+            "^The provided traversal contains a vertex step without any specified edge label: VertexStep.*")
+            .asPredicate();
+
+    private TestLogAppender logAppender;
+    private Level previousLogLevel;
+
+    @Before
+    public void setupForEachTest() {
+        final org.apache.log4j.Logger strategyLogger = org.apache.log4j.Logger.getLogger(EdgeLabelVerificationStrategy.class);
+        previousLogLevel = strategyLogger.getLevel();
+        strategyLogger.setLevel(Level.WARN);
+        Logger.getRootLogger().addAppender(logAppender = new TestLogAppender());
+    }
+
+    @After
+    public void teardownForEachTest() {
+        final org.apache.log4j.Logger strategyLogger = org.apache.log4j.Logger.getLogger(EdgeLabelVerificationStrategy.class);
+        strategyLogger.setLevel(previousLogLevel);
+        Logger.getRootLogger().removeAppender(logAppender);
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {"__.inE()", __.inE(), false},
+                {"__.outE()", __.outE(), false},
+                {"__.bothE()", __.bothE(), false},
+                {"__.to(OUT)", __.to(Direction.OUT), false},
+                {"__.toE(IN)", __.toE(Direction.IN), false},
+                {"__.inE('knows')", __.inE("knows"), true},
+                {"__.outE('knows')", __.outE("knows"), true},
+                {"__.bothE('created','knows')", __.bothE("created", "knows"), true},
+                {"__.to(OUT,'created','knows')", __.to(Direction.OUT, "created", "knows"), true},
+                {"__.toE(IN,'knows')", __.toE(Direction.IN, "knows"), true}
+        });
+    }
+
+    @Parameterized.Parameter(value = 0)
+    public String name;
+
+    @Parameterized.Parameter(value = 1)
+    public Traversal traversal;
+
+    @Parameterized.Parameter(value = 2)
+    public boolean allow;
+
+    @Test
+    public void shouldIgnore() {
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(EdgeLabelVerificationStrategy.build().create());
+        final Traversal traversal = this.traversal.asAdmin().clone();
+        traversal.asAdmin().setStrategies(strategies);
+        traversal.asAdmin().applyStrategies();
+        assertTrue(logAppender.isEmpty());
+    }
+
+    @Test
+    public void shouldOnlyThrow() {
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(EdgeLabelVerificationStrategy.build().throwException().create());
+        final Traversal traversal = this.traversal.asAdmin().clone();
+        traversal.asAdmin().setStrategies(strategies);
+        if (allow) {
+            traversal.asAdmin().applyStrategies();
+        } else {
+            try {
+                traversal.asAdmin().applyStrategies();
+                fail("The strategy should not allow vertex steps with unspecified edge labels: " + this.traversal);
+            } catch (VerificationException ise) {
+                assertTrue(MSG_PREDICATE.test(ise.getMessage()));
+            }
+        }
+        assertTrue(logAppender.isEmpty());
+    }
+
+    @Test
+    public void shouldOnlyLog() {
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(EdgeLabelVerificationStrategy.build().logWarning().create());
+        final Traversal traversal = this.traversal.asAdmin().clone();
+        traversal.asAdmin().setStrategies(strategies);
+        traversal.asAdmin().applyStrategies();
+        if (!allow) {
+            assertTrue(String.format("Expected log entry not found in %s", logAppender.messages),
+                    logAppender.messages().anyMatch(MSG_PREDICATE));
+        }
+    }
+
+    @Test
+    public void shouldThrowAndLog() {
+        final TraversalStrategies strategies = new DefaultTraversalStrategies();
+        strategies.addStrategies(EdgeLabelVerificationStrategy.build().throwException().logWarning().create());
+        final Traversal traversal = this.traversal.asAdmin().clone();
+        traversal.asAdmin().setStrategies(strategies);
+        if (allow) {
+            traversal.asAdmin().applyStrategies();
+            assertTrue(logAppender.isEmpty());
+        } else {
+            try {
+                traversal.asAdmin().applyStrategies();
+                fail("The strategy should not allow vertex steps with unspecified edge labels: " + this.traversal);
+            } catch (VerificationException ise) {
+                assertTrue(MSG_PREDICATE.test(ise.getMessage()));
+            }
+            assertTrue(String.format("Expected log entry not found in %s", logAppender.messages),
+                    logAppender.messages().anyMatch(MSG_PREDICATE));
+        }
+    }
+
+    class TestLogAppender extends AppenderSkeleton {
+
+        private List<String> messages = new ArrayList<>();
+
+        boolean isEmpty() {
+            return messages.isEmpty();
+        }
+
+        Stream<String> messages() {
+            return messages.stream();
+        }
+
+        @Override
+        protected void append(org.apache.log4j.spi.LoggingEvent loggingEvent) {
+            messages.add(loggingEvent.getMessage().toString());
+        }
+
+        @Override
+        public void close() {
+
+        }
+
+        @Override
+        public boolean requiresLayout() {
+            return false;
+        }
+    }
+}
\ No newline at end of file