blob: 7fbc8b5aead6bf71b306fa5d5a8cfddf31c04c20 [file] [log] [blame]
/*
* 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;
import org.apache.tinkerpop.gremlin.ExceptionCoverage;
import org.apache.tinkerpop.gremlin.FeatureRequirement;
import org.apache.tinkerpop.gremlin.LoadGraphWith;
import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
import org.apache.tinkerpop.gremlin.process.remote.EmbeddedRemoteConnection;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
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.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.VerificationException;
import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Transaction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.junit.Ignore;
import org.junit.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
import static org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.inject;
import static org.apache.tinkerpop.gremlin.structure.Graph.Features.GraphFeatures.FEATURE_TRANSACTIONS;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Pieter Martin
*/
@ExceptionCoverage(exceptionClass = Traversal.Exceptions.class, methods = {
"traversalIsLocked"
})
@ExceptionCoverage(exceptionClass = Graph.Exceptions.class, methods = {
"idArgsMustBeEitherIdOrElement"
})
public class CoreTraversalTest extends AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
@FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
@FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY)
@FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_NULL_PROPERTY_VALUES)
public void g_addVXpersonX_propertyXname_nullX() {
final Traversal<Vertex, Vertex> traversal = g.addV("person").property("name", null);
printTraversalForm(traversal);
final Vertex nulled = traversal.next();
assertFalse(traversal.hasNext());
assertEquals("person", nulled.label());
assertNull(nulled.value("name"));
assertEquals(1, IteratorUtils.count(nulled.properties()));
assertEquals(7, IteratorUtils.count(g.V()));
}
@Test
@LoadGraphWith(MODERN)
@FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
@FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_NULL_PROPERTY_VALUES)
public void g_VX1X_asXaX_outXcreatedX_addEXcreatedByX_toXaX_propertyXweight_nullX() {
final Traversal<Vertex, Edge> traversal = g.V(convertToVertexId("marko")).as("a").out("created").addE("createdBy").to("a").property("weight", null);
printTraversalForm(traversal);
int count = 0;
while (traversal.hasNext()) {
final Edge edge = traversal.next();
assertEquals("createdBy", edge.label());
assertNull(g.E(edge).<Double>values("weight").next());
assertEquals(1, g.E(edge).properties().count().next().intValue());
count++;
}
assertEquals(1, count);
assertEquals(7, IteratorUtils.count(g.E()));
assertEquals(6, IteratorUtils.count(g.V()));
}
@Test
@LoadGraphWith
public void shouldNeverPropagateANoBulkTraverser() {
try {
assertFalse(g.V().dedup().sideEffect(t -> t.asAdmin().setBulk(0)).hasNext());
assertEquals(0, g.V().dedup().sideEffect(t -> t.asAdmin().setBulk(0)).toList().size());
g.V().dedup().sideEffect(t -> t.asAdmin().setBulk(0)).sideEffect(t -> fail("this should not have happened")).iterate();
} catch (VerificationException e) {
// its okay if lambdas can't be serialized by the test suite
}
}
@Test
@LoadGraphWith(MODERN)
public void shouldFilterOnIterate() {
final Traversal<Vertex,String> traversal = g.V().out().out().<String>values("name").aggregate("x").iterate();
assertFalse(traversal.hasNext());
assertEquals(2, traversal.asAdmin().getSideEffects().<BulkSet>get("x").size());
assertTrue(traversal.asAdmin().getSideEffects().<BulkSet>get("x").contains("ripple"));
assertTrue(traversal.asAdmin().getSideEffects().<BulkSet>get("x").contains("lop"));
assertEquals(Traversal.Symbols.none, traversal.asAdmin().getBytecode().getStepInstructions().get(traversal.asAdmin().getBytecode().getStepInstructions().size()-1).getOperator());
}
@Test
@LoadGraphWith(MODERN)
public void shouldLoadVerticesViaIds() {
final List<Vertex> vertices = g.V().toList();
final List<Object> ids = vertices.stream().map(Vertex::id).collect(Collectors.toList());
final List<Vertex> verticesReloaded = g.V(ids.toArray()).toList();
assertEquals(vertices.size(), verticesReloaded.size());
assertEquals(new HashSet<>(vertices), new HashSet<>(verticesReloaded));
}
@Test
@LoadGraphWith(MODERN)
public void shouldLoadEdgesViaIds() {
final List<Edge> edges = g.E().toList();
final List<Object> ids = edges.stream().map(Edge::id).collect(Collectors.toList());
final List<Edge> edgesReloaded = g.E(ids.toArray()).toList();
assertEquals(edges.size(), edgesReloaded.size());
assertEquals(new HashSet<>(edges), new HashSet<>(edgesReloaded));
}
@Test
@LoadGraphWith(MODERN)
public void shouldLoadVerticesViaVertices() {
final List<Vertex> vertices = g.V().toList();
final List<Vertex> verticesReloaded = g.V(vertices.toArray()).toList();
assertEquals(vertices.size(), verticesReloaded.size());
assertEquals(new HashSet<>(vertices), new HashSet<>(verticesReloaded));
}
@Test
@LoadGraphWith(MODERN)
public void shouldLoadEdgesViaEdges() {
final List<Edge> edges = g.E().toList();
final List<Edge> edgesReloaded = g.E(edges.toArray()).toList();
assertEquals(edges.size(), edgesReloaded.size());
assertEquals(new HashSet<>(edges), new HashSet<>(edgesReloaded));
}
@Test
@LoadGraphWith(MODERN)
public void shouldThrowExceptionWhenIdsMixed() {
final List<Vertex> vertices = g.V().toList();
try {
g.V(vertices.get(0), vertices.get(1).id()).toList();
} catch (Exception ex) {
validateException(Graph.Exceptions.idArgsMustBeEitherIdOrElement(), ex);
}
}
@Test
@LoadGraphWith(MODERN)
public void shouldHaveNextAndToCollectionWorkProperly() {
final Traversal<Vertex, Vertex> traversal = g.V();
assertTrue(traversal.next() instanceof Vertex);
assertEquals(4, traversal.next(4).size());
assertTrue(traversal.hasNext());
assertTrue(traversal.tryNext().isPresent());
assertFalse(traversal.hasNext());
assertFalse(traversal.tryNext().isPresent());
assertFalse(traversal.hasNext());
Traversal<Integer, Integer> intTraversal = inject(7, 7, 2, 3, 6);
assertTrue(intTraversal.hasNext());
final List<Integer> list = intTraversal.toList();
assertFalse(intTraversal.hasNext());
assertEquals(5, list.size());
assertEquals(7, list.get(0).intValue());
assertEquals(7, list.get(1).intValue());
assertEquals(2, list.get(2).intValue());
assertEquals(3, list.get(3).intValue());
assertEquals(6, list.get(4).intValue());
assertFalse(intTraversal.hasNext());
assertFalse(intTraversal.tryNext().isPresent());
intTraversal = inject(7, 7, 2, 3, 6);
assertTrue(intTraversal.hasNext());
final Set<Integer> set = intTraversal.toSet();
assertFalse(intTraversal.hasNext());
assertEquals(4, set.size());
assertTrue(set.contains(7));
assertTrue(set.contains(2));
assertTrue(set.contains(3));
assertTrue(set.contains(6));
assertFalse(intTraversal.hasNext());
assertFalse(intTraversal.tryNext().isPresent());
intTraversal = inject(7, 7, 2, 3, 6);
assertTrue(intTraversal.hasNext());
final BulkSet<Integer> bulkSet = intTraversal.toBulkSet();
assertFalse(intTraversal.hasNext());
assertEquals(4, bulkSet.uniqueSize());
assertEquals(5, bulkSet.longSize());
assertEquals(5, bulkSet.size());
assertTrue(bulkSet.contains(7));
assertTrue(bulkSet.contains(2));
assertTrue(bulkSet.contains(3));
assertTrue(bulkSet.contains(6));
assertEquals(2, bulkSet.get(7));
assertFalse(intTraversal.hasNext());
assertFalse(intTraversal.tryNext().isPresent());
}
@Test
@LoadGraphWith(MODERN)
public void shouldHavePropertyForEachRemainingBehaviorEvenWithStrategyRewrite() {
final GraphTraversal<Vertex, Map<Object, Long>> traversal = g.V().out().groupCount();
traversal.forEachRemaining(Map.class, map -> assertTrue(map instanceof Map));
}
@Test
@Ignore
@LoadGraphWith(MODERN)
public void shouldNotAlterTraversalAfterTraversalBecomesLocked() {
final GraphTraversal<Vertex, Vertex> traversal = this.g.V();
assertTrue(traversal.hasNext());
try {
traversal.count().next();
fail("Should throw: " + Traversal.Exceptions.traversalIsLocked());
} catch (IllegalStateException e) {
assertEquals(Traversal.Exceptions.traversalIsLocked().getMessage(), e.getMessage());
} catch (Exception e) {
fail("Should throw: " + Traversal.Exceptions.traversalIsLocked() + " not " + e + ":" + e.getMessage());
}
traversal.iterate();
assertFalse(traversal.hasNext());
}
@Test
@LoadGraphWith(MODERN)
@FeatureRequirement(featureClass = Graph.Features.GraphFeatures.class, feature = FEATURE_TRANSACTIONS)
public void shouldTraverseIfAutoTxEnabledAndOriginalTxIsClosed() {
// this should be the default, but manually set in just in case the implementation has other ideas
g.tx().onReadWrite(Transaction.READ_WRITE_BEHAVIOR.AUTO);
// close down the current transaction
final Traversal t = g.V().has("name", "marko");
g.tx().rollback();
// the traversal should still work since there are auto transactions
assertEquals(1, IteratorUtils.count(t));
}
@Test
@LoadGraphWith(MODERN)
@FeatureRequirement(featureClass = Graph.Features.GraphFeatures.class, feature = FEATURE_TRANSACTIONS)
public void shouldTraverseIfManualTxEnabledAndOriginalTxIsClosed() {
// auto should be the default, so force manual
g.tx().onReadWrite(Transaction.READ_WRITE_BEHAVIOR.MANUAL);
// close down the current transaction and fire up a fresh one
g.tx().open();
final Traversal t = g.V().has("name", "marko");
g.tx().rollback();
// the traversal should still work since there are auto transactions
g.tx().open();
assertEquals(1, IteratorUtils.count(t));
g.tx().rollback();
}
@Test
@LoadGraphWith(MODERN)
public void shouldNotThrowFastNoSuchElementException() {
//FastNoSuchElement exceptions don't have a stack trace.
//They should be converted to regular exceptions before returning to the the user.
try {
g.V().has("foo").next();
fail("Expected a user facing NoSuchElementException");
} catch (NoSuchElementException e) {
assertEquals(NoSuchElementException.class, e.getClass());
}
}
@Test
@LoadGraphWith(MODERN)
public void shouldThrowFastNoSuchElementExceptionInNestedTraversals() {
//The nested traversal should throw a regular FastNoSuchElementException
final GraphTraversal<Object, Object> nestedTraversal = __.has("name", "foo");
final GraphTraversal<Vertex, Object> traversal = g.V().has("name", "marko").branch(nestedTraversal);
final GraphTraversal.Admin<Object, Object> nestedTraversalAdmin = nestedTraversal.asAdmin();
nestedTraversalAdmin.reset();
nestedTraversalAdmin.addStart(nestedTraversalAdmin.getTraverserGenerator().generate(g.V().has("name", "marko").next(), (Step) traversal.asAdmin().getStartStep(), 1l));
try {
nestedTraversal.next();
} catch (NoSuchElementException e) {
assertEquals(FastNoSuchElementException.class, e.getClass());
}
}
@Test
@LoadGraphWith(MODERN)
public void shouldAllowEmbeddedRemoteConnectionUsage() {
final GraphTraversalSource simulatedRemoteG = traversal().withRemote(new EmbeddedRemoteConnection(g));
assertEquals(6, simulatedRemoteG.V().count().next().intValue());
assertEquals("marko", simulatedRemoteG.V().has("name", "marko").values("name").next());
}
@Test
@LoadGraphWith(MODERN)
public void shouldThrowNiceExceptionWhenMapKeyNotFoundInMathStep() {
try {
g.V().hasLabel("person").project("age").by("age").as("x").math("x").by("aged").iterate();
fail("Traversal should no have succeeded since the 'aged' key does not exist");
} catch (IllegalStateException ise) {
assertThat(ise.getMessage(), startsWith("The variable x for math() step must resolve to a Number"));
}
}
@Test
@LoadGraphWith(MODERN)
public void shouldThrowNiceExceptionWhenMapKeyIsNotResolvingToNumberInMathStep() {
try {
g.V().hasLabel("person").project("age").by("age").as("x").math("x").by("name").iterate();
fail("Traversal should no have succeeded since the 'name' key does not resolve to Number");
} catch (IllegalStateException ise) {
assertThat(ise.getMessage(), startsWith("The variable x for math() step must resolve to a Number"));
}
}
}