blob: 1b9043516936d141c6732ad1fbb1f415dddc9e53 [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;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.tinkerpop.gremlin.process.computer.ComputerResult;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
import org.apache.tinkerpop.gremlin.process.computer.MapReduce;
import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
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.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Matthias Broecheler (me@matthiasb.com)
* @author Marko A. Rodriguez (marko@markorodriguez.com)
*/
public class TraversalStrategiesTest {
@Test
public void shouldAllowUserManipulationOfGlobalCache() {
///////////
// GRAPH //
///////////
TestGraph graph = new TestGraph();
TraversalStrategies strategies = graph.traversal().getStrategies();
assertFalse(TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList().isEmpty());
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()) {
assertTrue(strategies.getStrategy(strategy.getClass()).isPresent());
}
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(TestGraphComputer.class).toList()) {
assertFalse(strategies.getStrategy(strategy.getClass()).isPresent());
}
assertTrue(strategies.getStrategy(StrategyA.class).isPresent());
assertTrue(strategies.getStrategy(StrategyB.class).isPresent());
assertFalse(strategies.getStrategy(StrategyC.class).isPresent());
assertFalse(strategies.getStrategy(StrategyD.class).isPresent());
strategies.addStrategies(new StrategyD());
strategies.removeStrategies(StrategyA.class);
assertFalse(strategies.getStrategy(StrategyA.class).isPresent());
assertTrue(strategies.getStrategy(StrategyD.class).isPresent());
///
graph = new TestGraph();
strategies = graph.traversal().getStrategies();
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(Graph.class).toList()) {
assertTrue(strategies.getStrategy(strategy.getClass()).isPresent());
}
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(TestGraphComputer.class).toList()) {
assertFalse(strategies.getStrategy(strategy.getClass()).isPresent());
}
assertFalse(strategies.getStrategy(StrategyA.class).isPresent());
assertTrue(strategies.getStrategy(StrategyB.class).isPresent());
assertFalse(strategies.getStrategy(StrategyC.class).isPresent());
assertTrue(strategies.getStrategy(StrategyD.class).isPresent());
//////////////////////
/// GRAPH COMPUTER ///
//////////////////////
strategies = TraversalStrategies.GlobalCache.getStrategies(TestGraphComputer.class);
assertFalse(TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).toList().isEmpty());
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).toList()) {
assertTrue(strategies.getStrategy(strategy.getClass()).isPresent());
}
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(TestGraph.class).toList()) {
assertFalse(strategies.getStrategy(strategy.getClass()).isPresent());
}
assertFalse(strategies.getStrategy(StrategyA.class).isPresent());
assertFalse(strategies.getStrategy(StrategyB.class).isPresent());
assertTrue(strategies.getStrategy(StrategyC.class).isPresent());
strategies.addStrategies(new StrategyE());
strategies.removeStrategies(StrategyC.class);
//
strategies = TraversalStrategies.GlobalCache.getStrategies(TestGraphComputer.class);
assertFalse(TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).toList().isEmpty());
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).toList()) {
assertTrue(strategies.getStrategy(strategy.getClass()).isPresent());
}
for (final TraversalStrategy strategy : TraversalStrategies.GlobalCache.getStrategies(TestGraph.class).toList()) {
assertFalse(strategies.getStrategy(strategy.getClass()).isPresent());
}
assertFalse(strategies.getStrategy(StrategyA.class).isPresent());
assertFalse(strategies.getStrategy(StrategyB.class).isPresent());
assertFalse(strategies.getStrategy(StrategyC.class).isPresent());
assertFalse(strategies.getStrategy(StrategyD.class).isPresent());
assertTrue(strategies.getStrategy(StrategyE.class).isPresent());
}
public static class TestGraphComputer implements GraphComputer {
static {
TraversalStrategies.GlobalCache.registerStrategies(TestGraphComputer.class,
TraversalStrategies.GlobalCache.getStrategies(GraphComputer.class).clone().addStrategies(new StrategyC()));
}
@Override
public GraphComputer result(ResultGraph resultGraph) {
return this;
}
@Override
public GraphComputer persist(Persist persist) {
return this;
}
@Override
public GraphComputer program(VertexProgram vertexProgram) {
return this;
}
@Override
public GraphComputer mapReduce(MapReduce mapReduce) {
return this;
}
@Override
public GraphComputer workers(int workers) {
return this;
}
@Override
public GraphComputer vertices(Traversal<Vertex, Vertex> vertexFilter) throws IllegalArgumentException {
return this;
}
@Override
public GraphComputer edges(Traversal<Vertex, Edge> edgeFilter) throws IllegalArgumentException {
return this;
}
@Override
public Future<ComputerResult> submit() {
return new CompletableFuture<>();
}
}
public static class TestGraph implements Graph {
static {
TraversalStrategies.GlobalCache.registerStrategies(TestGraph.class,
TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(new StrategyA(), new StrategyB()));
}
@Override
public Vertex addVertex(Object... keyValues) {
return null;
}
@Override
public <C extends GraphComputer> C compute(Class<C> graphComputerClass) throws IllegalArgumentException {
return (C) new TestGraphComputer();
}
@Override
public GraphComputer compute() throws IllegalArgumentException {
return new TestGraphComputer();
}
@Override
public Iterator<Vertex> vertices(Object... vertexIds) {
return Collections.emptyIterator();
}
@Override
public Iterator<Edge> edges(Object... edgeIds) {
return Collections.emptyIterator();
}
@Override
public Transaction tx() {
return null;
}
@Override
public void close() throws Exception {
}
@Override
public Variables variables() {
return null;
}
@Override
public Configuration configuration() {
return new BaseConfiguration();
}
}
/**
* Tests that {@link org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies#sortStrategies(java.util.Set)}
* works as advertised. This class defines a bunch of dummy strategies which define an order. It is verified
* that the right order is being returned.
*/
@Test
public void testTraversalStrategySorting() {
TraversalStrategy
a = new StrategyA(),
b = new StrategyB(),
c = new StrategyC(),
d = new StrategyD(),
e = new StrategyE(),
k = new StrategyK(),
l = new StrategyL(),
m = new StrategyM(),
n = new StrategyN(),
o = new StrategyO();
Set<TraversalStrategy<?>> s;
//Dependency well defined
s = new LinkedHashSet<>((Collection) Arrays.asList(b, a));
s = TraversalStrategies.sortStrategies(s);
Iterator<TraversalStrategy<?>> it = s.iterator();
assertEquals(2, s.size());
assertEquals(a, it.next());
assertEquals(b, it.next());
//No dependency
s = new LinkedHashSet<>((Collection) Arrays.asList(c, a));
s = TraversalStrategies.sortStrategies(s);
assertEquals(2, s.size());
//Dependency well defined
s = new LinkedHashSet<>((Collection)Arrays.asList(c, a, b));
s = TraversalStrategies.sortStrategies(s);
assertEquals(3, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(c, it.next());
//Circular dependency => throws exception
s = new LinkedHashSet<>((Collection)Arrays.asList(c, k, a, b));
try {
TraversalStrategies.sortStrategies(s);
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().toLowerCase().contains("cyclic"));
}
//Dependency well defined
s = new LinkedHashSet<>((Collection) Arrays.asList(d, c, a, e, b));
s = TraversalStrategies.sortStrategies(s);
assertEquals(5, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(d, it.next());
assertEquals(c, it.next());
assertEquals(e, it.next());
//Circular dependency => throws exception
s = new LinkedHashSet<>((Collection) Arrays.asList(d, c, k, a, e, b));
try {
TraversalStrategies.sortStrategies(s);
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().toLowerCase().contains("cyclic"));
}
//Lots of strategies
s = new LinkedHashSet<>((Collection) Arrays.asList(b, l, m, n, o, a));
s = TraversalStrategies.sortStrategies(s);
List<TraversalStrategy<?>> list = new ArrayList<>(s);
assertTrue(list.indexOf(a) < list.indexOf(b));
// sort and then add more
s = new LinkedHashSet<>(new LinkedHashSet<>((Collection) Arrays.asList(b, a, c)));
s = TraversalStrategies.sortStrategies(s);
assertEquals(3, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(c, it.next());
s.add(d);
s = TraversalStrategies.sortStrategies(s);
assertEquals(4, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(d, it.next());
assertEquals(c, it.next());
s.add(e);
s = TraversalStrategies.sortStrategies(s);
assertEquals(5, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(d, it.next());
assertEquals(c, it.next());
assertEquals(e, it.next());
}
public static class StrategyA extends DummyStrategy {
@Override
public Set<Class<? extends TraversalStrategy>> applyPost() {
return Stream.of(StrategyB.class).collect(Collectors.toSet());
}
}
public static class StrategyB extends DummyStrategy {
@Override
public Set<Class<? extends TraversalStrategy>> applyPrior() {
return Stream.of(StrategyA.class).collect(Collectors.toSet());
}
@Override
public Set<Class<? extends TraversalStrategy>> applyPost() {
return Stream.of(StrategyC.class).collect(Collectors.toSet());
}
}
public static class StrategyC extends DummyStrategy {
}
public static class StrategyD extends DummyStrategy {
@Override
public Set<Class<? extends TraversalStrategy>> applyPrior() {
return Stream.of(StrategyB.class).collect(Collectors.toSet());
}
@Override
public Set<Class<? extends TraversalStrategy>> applyPost() {
return Stream.of(StrategyC.class).collect(Collectors.toSet());
}
}
public static class StrategyE extends DummyStrategy {
@Override
public Set<Class<? extends TraversalStrategy>> applyPrior() {
return Stream.of(StrategyC.class).collect(Collectors.toSet());
}
}
public static class StrategyK extends DummyStrategy {
@Override
public Set<Class<? extends TraversalStrategy>> applyPrior() {
return Stream.of(StrategyC.class).collect(Collectors.toSet());
}
@Override
public Set<Class<? extends TraversalStrategy>> applyPost() {
return Stream.of(StrategyA.class).collect(Collectors.toSet());
}
}
public static class StrategyL extends DummyStrategy {
}
public static class StrategyM extends DummyStrategy {
}
public static class StrategyN extends DummyStrategy {
}
public static class StrategyO extends DummyStrategy {
}
private static class DummyStrategy<S extends TraversalStrategy> extends AbstractTraversalStrategy<S> {
@Override
public void apply(Traversal.Admin<?, ?> traversal) {
//Do nothing
}
}
////////////////////////////////////////////
////////////////////////////////////////////
////////////////////////////////////////////
@Test
public void testTraversalStrategySortingWithCategories() {
TraversalStrategy
a = new StrategyADecoration(),
b = new StrategyBDecoration(),
c = new StrategyCOptimization(),
d = new StrategyDOptimization(),
e = new StrategyEFinalization(),
k = new StrategyKVerification();
Set<TraversalStrategy<?>> s;
//in category sorting
s = new LinkedHashSet<>((Collection) Arrays.asList(b, a));
s = TraversalStrategies.sortStrategies(s);
assertEquals(2, s.size());
Iterator<TraversalStrategy<?>> it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
//mixed category sorting
s = new LinkedHashSet<>((Collection) Arrays.asList(a, e, b, d));
s = TraversalStrategies.sortStrategies(s);
assertEquals(4, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(d, it.next());
assertEquals(e, it.next());
//full reverse sorting
s = new LinkedHashSet<>((Collection) Arrays.asList(k, e, d, c, b, a));
s = TraversalStrategies.sortStrategies(s);
assertEquals(6, s.size());
it = s.iterator();
assertEquals(a, it.next());
assertEquals(b, it.next());
assertEquals(c, it.next());
assertEquals(d, it.next());
assertEquals(e, it.next());
assertEquals(k, it.next());
}
public static class StrategyADecoration extends DummyStrategy<TraversalStrategy.DecorationStrategy> implements TraversalStrategy.DecorationStrategy {
@Override
public Set<Class<? extends DecorationStrategy>> applyPost() {
return Stream.of(StrategyBDecoration.class).collect(Collectors.toSet());
}
}
public static class StrategyBDecoration extends DummyStrategy<TraversalStrategy.DecorationStrategy> implements TraversalStrategy.DecorationStrategy {
}
public static class StrategyCOptimization extends DummyStrategy<TraversalStrategy.OptimizationStrategy> implements TraversalStrategy.OptimizationStrategy {
@Override
public Set<Class<? extends OptimizationStrategy>> applyPost() {
return Stream.of(StrategyDOptimization.class).collect(Collectors.toSet());
}
}
public static class StrategyDOptimization extends DummyStrategy<TraversalStrategy.OptimizationStrategy> implements TraversalStrategy.OptimizationStrategy {
@Override
public Set<Class<? extends OptimizationStrategy>> applyPrior() {
return Stream.of(StrategyCOptimization.class).collect(Collectors.toSet());
}
}
public static class StrategyEFinalization extends DummyStrategy<TraversalStrategy.FinalizationStrategy> implements TraversalStrategy.FinalizationStrategy {
}
public static class StrategyKVerification extends DummyStrategy<TraversalStrategy.VerificationStrategy> implements TraversalStrategy.VerificationStrategy {
}
}