From 782c17594f76a4caa7068f4b53896ed0ac7afe8a Mon Sep 17 00:00:00 2001 From: Cole-Greer Date: Fri, 21 Jul 2023 11:04:20 -0700 Subject: [PATCH] TINKERPOP-2946 Detect ordering issues in Gherkin tests This commit addresses part 1. of TINKERPOP-2946 by introducing TinkerShuffleGraph and running feature tests against an embedded instance of this graph. TinkerShuffleGraph is derived from TinkerGraph and intended for test purposes only. It's only differentiation from TinkerGraph is that it will returned shuffled results when getting any of the following: vertices from the graph, edges from the graph, vertices from a vertex, edges from a vertex, vertices from an edge, properties from a vertex, properties from an edge, and properties from a VertexProperty --- .../structure/AbstractTinkerGraph.java | 16 ++ .../tinkergraph/structure/TinkerEdge.java | 2 +- .../tinkergraph/structure/TinkerGraph.java | 6 +- .../tinkergraph/structure/TinkerVertex.java | 14 +- .../TinkerShuffleGraphFeatureTest.java | 52 +++++ .../gremlin/tinkergraph/TinkerWorld.java | 56 +++++ .../structure/TinkerShuffleEdge.java | 50 ++++ .../structure/TinkerShuffleGraph.java | 86 +++++++ .../structure/TinkerShuffleGraphTest.java | 221 ++++++++++++++++++ .../structure/TinkerShuffleVertex.java | 66 ++++++ .../TinkerShuffleVertexProperty.java | 61 +++++ .../io.cucumber.core.backend.ObjectFactory | 3 +- 12 files changed, 625 insertions(+), 8 deletions(-) create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerShuffleGraphFeatureTest.java create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleEdge.java create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraph.java create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraphTest.java create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertex.java create mode 100644 tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertexProperty.java diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java index 237be062aa8..4ce7e1add9f 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/AbstractTinkerGraph.java @@ -281,6 +281,22 @@ public Configuration configuration() { protected abstract void addInEdge(final TinkerVertex vertex, final String label, final Edge edge); + protected TinkerVertex createTinkerVertex(final Object id, final String label, final AbstractTinkerGraph graph) { + return new TinkerVertex(id, label, graph); + } + + protected TinkerVertex createTinkerVertex(final Object id, final String label, final AbstractTinkerGraph graph, final long currentVersion) { + return new TinkerVertex(id, label, graph, currentVersion); + } + + protected TinkerEdge createTinkerEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex) { + return new TinkerEdge(id, outVertex, label, inVertex); + } + + protected TinkerEdge createTinkerEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex, final long currentVersion) { + return new TinkerEdge(id, outVertex, label, inVertex, currentVersion); + } + ///////////// Features /////////////// public class TinkerGraphVertexFeatures implements Features.VertexFeatures { diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java index 93dcf594d7c..43fcc29d131 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerEdge.java @@ -38,7 +38,7 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class TinkerEdge extends TinkerElement implements Edge { +public class TinkerEdge extends TinkerElement implements Edge { protected Map properties; diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java index 40e23253a99..acf540a613a 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java @@ -59,7 +59,7 @@ @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_COMPUTER) @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_LIMITED_STANDARD) @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_LIMITED_COMPUTER) -public final class TinkerGraph extends AbstractTinkerGraph { +public class TinkerGraph extends AbstractTinkerGraph { static { TraversalStrategies.GlobalCache.registerStrategies(TinkerGraph.class, TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies( @@ -79,7 +79,7 @@ public final class TinkerGraph extends AbstractTinkerGraph { /** * An empty private constructor that initializes {@link TinkerGraph}. */ - private TinkerGraph(final Configuration configuration) { + TinkerGraph(final Configuration configuration) { this.configuration = configuration; vertexIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_VERTEX_ID_MANAGER, Vertex.class); edgeIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_EDGE_ID_MANAGER, Edge.class); @@ -146,7 +146,7 @@ public Vertex addVertex(final Object... keyValues) { idValue = vertexIdManager.getNextId(this); } - final Vertex vertex = new TinkerVertex(idValue, label, this); + final Vertex vertex = createTinkerVertex(idValue, label, this); ElementHelper.attachProperties(vertex, VertexProperty.Cardinality.list, keyValues); this.vertices.put(vertex.id(), vertex); diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java index 3e1f131e89e..80a0f806427 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerVertex.java @@ -41,7 +41,7 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ -public final class TinkerVertex extends TinkerElement implements Vertex { +public class TinkerVertex extends TinkerElement implements Vertex { protected Map> properties; // Edges should be used by non-transaction Graph due to performance @@ -50,7 +50,7 @@ public final class TinkerVertex extends TinkerElement implements Vertex { // Edge ids are for transactional Graph protected Map> outEdgesId; protected Map> inEdgesId; - private final AbstractTinkerGraph graph; + protected final AbstractTinkerGraph graph; private boolean allowNullPropertyValues; private final boolean isTxMode; @@ -160,7 +160,7 @@ public VertexProperty property(final VertexProperty.Cardinality cardinali graph.vertexPropertyIdManager.convert(optionalId.get()) : graph.vertexPropertyIdManager.getNextId(graph); - final VertexProperty vertexProperty = new TinkerVertexProperty(idValue, this, key, value); + final VertexProperty vertexProperty = createTinkerVertexProperty(idValue, this, key, value); if (null == this.properties) this.properties = new ConcurrentHashMap<>(); final List list = this.properties.getOrDefault(key, new ArrayList<>()); @@ -230,6 +230,14 @@ public Iterator vertices(final Direction direction, final String... edge : (Iterator) TinkerHelper.getVertices(this, direction, edgeLabels); } + protected TinkerVertexProperty createTinkerVertexProperty(final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + return new TinkerVertexProperty(vertex, key, value, propertyKeyValues); + } + + protected TinkerVertexProperty createTinkerVertexProperty(final Object id, final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + return new TinkerVertexProperty(id, vertex, key, value, propertyKeyValues); + } + @Override public Iterator> properties(final String... propertyKeys) { if (this.removed) return Collections.emptyIterator(); diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerShuffleGraphFeatureTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerShuffleGraphFeatureTest.java new file mode 100644 index 00000000000..6b86ecc4527 --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerShuffleGraphFeatureTest.java @@ -0,0 +1,52 @@ +/* + * 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.tinkergraph; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Stage; +import io.cucumber.guice.CucumberModules; +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.apache.tinkerpop.gremlin.features.AbstractGuiceFactory; +import org.apache.tinkerpop.gremlin.features.World; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions( + tags = "not @RemoteOnly and not @GraphComputerOnly and not @AllowNullPropertyValues", + glue = { "org.apache.tinkerpop.gremlin.features" }, + objectFactory = TinkerShuffleGraphFeatureTest.TinkerShuffleGraphGuiceFactory.class, + features = { "classpath:/org/apache/tinkerpop/gremlin/test/features" }, + plugin = {"progress", "junit:target/cucumber.xml"}) +public class TinkerShuffleGraphFeatureTest { + + public static class TinkerShuffleGraphGuiceFactory extends AbstractGuiceFactory { + public TinkerShuffleGraphGuiceFactory() { + super(Guice.createInjector(Stage.PRODUCTION, CucumberModules.createScenarioModule(), new ServiceModule())); + } + } + + public static final class ServiceModule extends AbstractModule { + @Override + protected void configure() { + bind(World.class).to(TinkerWorld.TinkerShuffleGraphWorld.class); + } + } +} diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java index b0abbdef752..eb7e981e219 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/TinkerWorld.java @@ -36,6 +36,7 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerTransactionGraph; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerShuffleGraph; import org.junit.AssumptionViolatedException; import java.io.File; @@ -149,6 +150,61 @@ public AbstractTinkerGraph open(final Configuration configuration) { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * The {@link World} implementation for TinkerShuffleGraph that provides the {@link GraphTraversalSource} + * instances required by the Gherkin test suite. + */ + public static class TinkerShuffleGraphWorld extends TinkerWorld { + private static final TinkerGraph modern; + private static final TinkerGraph classic; + private static final TinkerGraph crew; + private static final TinkerGraph sink; + private static final TinkerGraph grateful; + + static { + modern = TinkerShuffleGraph.open(); + TinkerFactory.generateModern(modern); + registerTestServices(modern); + classic = TinkerShuffleGraph.open(); + TinkerFactory.generateClassic(classic); + registerTestServices(classic); + crew = TinkerShuffleGraph.open(); + TinkerFactory.generateTheCrew(crew); + registerTestServices(crew); + sink = TinkerShuffleGraph.open(); + TinkerFactory.generateKitchenSink(sink); + registerTestServices(sink); + grateful = TinkerShuffleGraph.open(); + TinkerFactory.generateGratefulDead(grateful); + registerTestServices(grateful); + } + + @Override + public GraphTraversalSource getGraphTraversalSource(final LoadGraphWith.GraphData graphData) { + if (null == graphData) + return registerTestServices(TinkerGraph.open(getNumberIdManagerConfiguration())).traversal(); + else if (graphData == LoadGraphWith.GraphData.CLASSIC) + return classic.traversal(); + else if (graphData == LoadGraphWith.GraphData.CREW) + return crew.traversal(); + else if (graphData == LoadGraphWith.GraphData.MODERN) + return modern.traversal(); + else if (graphData == LoadGraphWith.GraphData.SINK) + return sink.traversal(); + else if (graphData == LoadGraphWith.GraphData.GRATEFUL) + return grateful.traversal(); + else + throw new UnsupportedOperationException("GraphData not supported: " + graphData.name()); + } + + @Override + public AbstractTinkerGraph open(final Configuration configuration) { + return TinkerShuffleGraph.open(configuration); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** * Enables the storing of {@code null} property values when testing. This is a terminal decorator and shouldn't be * used as an input into another decorator. diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleEdge.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleEdge.java new file mode 100644 index 00000000000..48e20e499a1 --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleEdge.java @@ -0,0 +1,50 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.util.Iterator; + +import static org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerShuffleGraph.shuffleIterator; + +/** + * @author Cole Greer (https://github.com/Cole-Greer) + */ +public final class TinkerShuffleEdge extends TinkerEdge { + protected TinkerShuffleEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex) { + this(id, outVertex, label, inVertex, 0); + } + + protected TinkerShuffleEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex, final long currentVersion) { + super(id, outVertex, label, inVertex, currentVersion); + } + + @Override + public Iterator vertices(final Direction direction) { + return shuffleIterator(super.vertices(direction)); + } + + @Override + public Iterator> properties(final String... propertyKeys) { + return shuffleIterator(super.properties(propertyKeys)); + } +} diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraph.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraph.java new file mode 100644 index 00000000000..f12a156f65c --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraph.java @@ -0,0 +1,86 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.configuration2.BaseConfiguration; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * @author Cole Greer (https://github.com/Cole-Greer) + */ +public class TinkerShuffleGraph extends TinkerGraph { + + private static final Configuration EMPTY_CONFIGURATION = new BaseConfiguration() {{ + this.setProperty(Graph.GRAPH, TinkerShuffleGraph.class.getName()); + }}; + + private TinkerShuffleGraph(Configuration configuration) { + super(configuration); + } + + public static TinkerShuffleGraph open() { + return open(EMPTY_CONFIGURATION); + } + + public static TinkerShuffleGraph open(Configuration configuration) { + return new TinkerShuffleGraph(configuration); + } + + @Override + public Iterator vertices(final Object... vertexIds) { + return shuffleIterator(super.vertices(vertexIds)); + } + + @Override + public Iterator edges(final Object... edgeIds) { + return shuffleIterator(super.edges(edgeIds)); + } + + @Override + protected TinkerVertex createTinkerVertex(final Object id, final String label, final AbstractTinkerGraph graph) { + return new TinkerShuffleVertex(id, label, graph); + } + + protected TinkerVertex createTinkerVertex(final Object id, final String label, final AbstractTinkerGraph graph, final long currentVersion) { + return new TinkerShuffleVertex(id, label, graph, currentVersion); + } + + protected TinkerEdge createTinkerEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex) { + return new TinkerShuffleEdge(id, outVertex, label, inVertex); + } + + protected TinkerEdge createTinkerEdge(final Object id, final Vertex outVertex, final String label, final Vertex inVertex, final long currentVersion) { + return new TinkerShuffleEdge(id, outVertex, label, inVertex, currentVersion); + } + + static Iterator shuffleIterator(Iterator iter) { + List list = IteratorUtils.toList(iter); + Collections.shuffle(list); + return list.iterator(); + } + +} diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraphTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraphTest.java new file mode 100644 index 00000000000..577b824898f --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleGraphTest.java @@ -0,0 +1,221 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; + +/** + * @author Cole Greer (https://github.com/Cole-Greer) + */ +public class TinkerShuffleGraphTest { + + Logger logger = LoggerFactory.getLogger(TinkerShuffleGraphTest.class); + + // Tests are checking for random shuffling of results. If random shuffling happens to return sorted order, the test + // will fail. Worst case scenario are the tests which only return 2 results as these have a 50% chance of a failure. + // Tests are configured with up to 50 retries to drive the failure probability down to the order of 10^-16. + @Rule + public RetryRule retryRule = new RetryRule(); + + @Test + public void shouldShuffleVertices() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List resultRegular = gReg.V().toList(); + Object[] resultShuffle = gShuff.V().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffleEdges() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List resultRegular = gReg.E().toList(); + Object[] resultShuffle = gShuff.E().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffle_g_V_both() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List resultRegular = gReg.V(1).both().toList(); + Object[] resultShuffle = gShuff.V(1).both().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffle_g_V_bothE() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List resultRegular = gReg.V(1).bothE().toList(); + Object[] resultShuffle = gShuff.V(1).bothE().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffle_g_E_bothV() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List resultRegular = gReg.V(1).outE("knows").bothV().toList(); + Object[] resultShuffle = gShuff.V(1).outE("knows").bothV().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffleVertexProperties() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + List> resultRegular = (List>) gReg.V(1).properties().toList(); + Object[] resultShuffle = gShuff.V(1).properties().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffleEdgeProperties() { + GraphTraversalSource gShuff = createShuffleModern(); + GraphTraversalSource gReg = TinkerFactory.createModern().traversal(); + + gShuff.V(1).outE("knows").property("extraProp", "no toy graph has 2 props on an edge"); + gReg.V(1).outE("knows").property("extraProp", "no toy graph has 2 props on an edge"); + + List> resultRegular = (List>) gReg.V(1).outE("knows").properties().toList(); + Object[] resultShuffle = gShuff.V(1).outE("knows").properties().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffleMetaProperties() { + GraphTraversalSource gShuff = createShuffleTheCrew(); + GraphTraversalSource gReg = TinkerFactory.createTheCrew().traversal(); + + List> resultRegular = (List>) gReg.V(1).properties("location").hasValue("san diego").properties().toList(); + Object[] resultShuffle = gShuff.V(1).properties("location").hasValue("san diego").properties().toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + @Test + public void shouldShuffleMultiProperties() { + GraphTraversalSource gShuff = createShuffleTheCrew(); + GraphTraversalSource gReg = TinkerFactory.createTheCrew().traversal(); + + List> resultRegular = (List>) gReg.V(1).properties("location").toList(); + Object[] resultShuffle = gShuff.V(1).properties("location").toList().toArray(); + + //Assert contents are correct + assertThat(resultRegular, containsInAnyOrder(resultShuffle)); + //Assert order is shuffled + assertThat(resultRegular, not(contains(resultShuffle))); + } + + private GraphTraversalSource createShuffleModern() { + TinkerShuffleGraph graph = TinkerShuffleGraph.open(); + TinkerFactory.generateModern(graph); + return traversal().withEmbedded(graph); + } + + private GraphTraversalSource createShuffleTheCrew() { + TinkerShuffleGraph graph = TinkerShuffleGraph.open(); + TinkerFactory.generateTheCrew(graph); + return traversal().withEmbedded(graph); + } + + private class RetryRule implements TestRule { + final int maxRetries = 50; + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + for(int i = 1; i <= maxRetries; i++) { + try{ + base.evaluate(); + return; + } + catch(AssertionError e){ + String msg = String.format("Failed test %s.%s (attempt %s/%s): ", description.getClassName(), description.getMethodName(), i, maxRetries); + if(i == maxRetries) { + logger.warn(msg, e); + } else { + logger.debug(msg, e); + } + } + } + } + }; + } + } + +} diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertex.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertex.java new file mode 100644 index 00000000000..b7ab5b6ef99 --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertex.java @@ -0,0 +1,66 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; + +import java.util.Iterator; + +import static org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerShuffleGraph.shuffleIterator; + +/** + * @author Cole Greer (https://github.com/Cole-Greer) + */ +public final class TinkerShuffleVertex extends TinkerVertex { + protected TinkerShuffleVertex(final Object id, final String label, final AbstractTinkerGraph graph) { + super(id, label, graph); + } + + protected TinkerShuffleVertex(final Object id, final String label, final AbstractTinkerGraph graph, final long currentVersion) { + super(id, label, graph, currentVersion); + } + + @Override + public Iterator edges(final Direction direction, final String... edgeLabels) { + return shuffleIterator(super.edges(direction, edgeLabels)); + } + + @Override + public Iterator vertices(final Direction direction, final String... edgeLabels) { + return shuffleIterator(super.vertices(direction, edgeLabels)); + } + + @Override + public Iterator> properties(final String... propertyKeys) { + return shuffleIterator(super.properties(propertyKeys)); + } + + @Override + protected TinkerVertexProperty createTinkerVertexProperty(final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + return new TinkerShuffleVertexProperty(vertex, key, value, propertyKeyValues); + } + + @Override + protected TinkerVertexProperty createTinkerVertexProperty(final Object id, final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + return new TinkerShuffleVertexProperty(id, vertex, key, value, propertyKeyValues); + } +} diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertexProperty.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertexProperty.java new file mode 100644 index 00000000000..6c6128ec836 --- /dev/null +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerShuffleVertexProperty.java @@ -0,0 +1,61 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputerView; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; + +import java.util.Collections; +import java.util.Iterator; +import java.util.stream.Collectors; + +import static org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerShuffleGraph.shuffleIterator; + +/** + * @author Cole Greer (https://github.com/Cole-Greer) + */ +public class TinkerShuffleVertexProperty extends TinkerVertexProperty { + + + /** + * This constructor will not validate the ID type against the {@link Graph}. It will always just use a + * {@code Long} for its identifier. This is useful for constructing a {@link VertexProperty} for usage + * with {@link TinkerGraphComputerView}. + */ + public TinkerShuffleVertexProperty(final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + super(vertex, key, value, propertyKeyValues); + } + + /** + * Use this constructor to construct {@link VertexProperty} instances for {@link TinkerGraph} where the {@code id} + * can be explicitly set and validated against the expected data type. + */ + public TinkerShuffleVertexProperty(final Object id, final TinkerVertex vertex, final String key, final V value, final Object... propertyKeyValues) { + super(id, vertex, key, value, propertyKeyValues); + } + + @Override + public Iterator> properties(final String... propertyKeys) { + return shuffleIterator(super.properties(propertyKeys)); + } +} diff --git a/tinkergraph-gremlin/src/test/resources/META-INF/services/io.cucumber.core.backend.ObjectFactory b/tinkergraph-gremlin/src/test/resources/META-INF/services/io.cucumber.core.backend.ObjectFactory index 9eb15f9a66a..1e9b622ffb9 100644 --- a/tinkergraph-gremlin/src/test/resources/META-INF/services/io.cucumber.core.backend.ObjectFactory +++ b/tinkergraph-gremlin/src/test/resources/META-INF/services/io.cucumber.core.backend.ObjectFactory @@ -3,4 +3,5 @@ org.apache.tinkerpop.gremlin.tinkergraph.TinkerGraphComputerFeatureTest$TinkerGr org.apache.tinkerpop.gremlin.tinkergraph.TinkerGraphAllowNullFeatureTest$TinkerGraphGuiceFactory org.apache.tinkerpop.gremlin.tinkergraph.TinkerTransactionGraphFeatureTest$TinkerGraphGuiceFactory org.apache.tinkerpop.gremlin.tinkergraph.TinkerTransactionGraphComputerFeatureTest$TinkerGraphGuiceFactory -org.apache.tinkerpop.gremlin.tinkergraph.TinkerTransactionGraphAllowNullFeatureTest$TinkerGraphGuiceFactory \ No newline at end of file +org.apache.tinkerpop.gremlin.tinkergraph.TinkerTransactionGraphAllowNullFeatureTest$TinkerGraphGuiceFactory +org.apache.tinkerpop.gremlin.tinkergraph.TinkerShuffleGraphFeatureTest$TinkerShuffleGraphGuiceFactory