| /* |
| * 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.configuration2.BaseConfiguration; |
| import org.apache.commons.configuration2.Configuration; |
| import org.apache.tinkerpop.gremlin.GraphHelper; |
| import org.apache.tinkerpop.gremlin.TestHelper; |
| import org.apache.tinkerpop.gremlin.process.computer.Computer; |
| import org.apache.tinkerpop.gremlin.process.traversal.P; |
| 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.strategy.verification.ReservedKeysVerificationStrategy; |
| import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.VerificationException; |
| import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics; |
| import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; |
| import org.apache.tinkerpop.gremlin.structure.Edge; |
| import org.apache.tinkerpop.gremlin.structure.Graph; |
| import org.apache.tinkerpop.gremlin.structure.T; |
| import org.apache.tinkerpop.gremlin.structure.Vertex; |
| import org.apache.tinkerpop.gremlin.structure.VertexProperty; |
| import org.apache.tinkerpop.gremlin.structure.io.Io; |
| import org.apache.tinkerpop.gremlin.structure.io.GraphReader; |
| import org.apache.tinkerpop.gremlin.structure.io.GraphWriter; |
| import org.apache.tinkerpop.gremlin.structure.io.IoCore; |
| import org.apache.tinkerpop.gremlin.structure.io.IoTest; |
| import org.apache.tinkerpop.gremlin.structure.io.Mapper; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; |
| import org.apache.tinkerpop.gremlin.structure.io.graphson.TypeInfo; |
| import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoClassResolverV1d0; |
| import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoMapper; |
| import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoVersion; |
| import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter; |
| import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; |
| import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; |
| import org.apache.tinkerpop.shaded.kryo.ClassResolver; |
| import org.apache.tinkerpop.shaded.kryo.Kryo; |
| import org.apache.tinkerpop.shaded.kryo.Registration; |
| import org.apache.tinkerpop.shaded.kryo.Serializer; |
| import org.apache.tinkerpop.shaded.kryo.io.Input; |
| import org.apache.tinkerpop.shaded.kryo.io.Output; |
| import org.junit.Test; |
| |
| import java.awt.Color; |
| import java.io.BufferedOutputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.concurrent.TimeUnit; |
| import java.util.function.Consumer; |
| import java.util.function.Supplier; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.hamcrest.Matchers.greaterThan; |
| import static org.hamcrest.Matchers.is; |
| import static org.hamcrest.core.StringContains.containsString; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeThat; |
| import static org.mockito.Mockito.mock; |
| |
| /** |
| * @author Marko A. Rodriguez (http://markorodriguez.com) |
| * @author Stephen Mallette (http://stephen.genoprime.com) |
| */ |
| public class TinkerGraphTest { |
| |
| @Test |
| public void shouldManageIndices() { |
| final TinkerGraph g = TinkerGraph.open(); |
| |
| Set<String> keys = g.getIndexedKeys(Vertex.class); |
| assertEquals(0, keys.size()); |
| keys = g.getIndexedKeys(Edge.class); |
| assertEquals(0, keys.size()); |
| |
| g.createIndex("name1", Vertex.class); |
| g.createIndex("name2", Vertex.class); |
| g.createIndex("oid1", Edge.class); |
| g.createIndex("oid2", Edge.class); |
| |
| // add the same one twice to check idempotency |
| g.createIndex("name1", Vertex.class); |
| |
| keys = g.getIndexedKeys(Vertex.class); |
| assertEquals(2, keys.size()); |
| for (String k : keys) { |
| assertTrue(k.equals("name1") || k.equals("name2")); |
| } |
| |
| keys = g.getIndexedKeys(Edge.class); |
| assertEquals(2, keys.size()); |
| for (String k : keys) { |
| assertTrue(k.equals("oid1") || k.equals("oid2")); |
| } |
| |
| g.dropIndex("name2", Vertex.class); |
| keys = g.getIndexedKeys(Vertex.class); |
| assertEquals(1, keys.size()); |
| assertEquals("name1", keys.iterator().next()); |
| |
| g.dropIndex("name1", Vertex.class); |
| keys = g.getIndexedKeys(Vertex.class); |
| assertEquals(0, keys.size()); |
| |
| g.dropIndex("oid1", Edge.class); |
| keys = g.getIndexedKeys(Edge.class); |
| assertEquals(1, keys.size()); |
| assertEquals("oid2", keys.iterator().next()); |
| |
| g.dropIndex("oid2", Edge.class); |
| keys = g.getIndexedKeys(Edge.class); |
| assertEquals(0, keys.size()); |
| |
| g.dropIndex("better-not-error-index-key-does-not-exist", Vertex.class); |
| g.dropIndex("better-not-error-index-key-does-not-exist", Edge.class); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldNotCreateVertexIndexWithNullKey() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex(null, Vertex.class); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldNotCreateEdgeIndexWithNullKey() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex(null, Edge.class); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldNotCreateVertexIndexWithEmptyKey() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("", Vertex.class); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void shouldNotCreateEdgeIndexWithEmptyKey() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("", Edge.class); |
| } |
| |
| @Test |
| public void shouldUpdateVertexIndicesInNewGraph() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("name", Vertex.class); |
| |
| g.addVertex("name", "marko", "age", 29); |
| g.addVertex("name", "stephen", "age", 35); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is used because only "stephen" ages should pass through the pipeline due to the inclusion of the |
| // key index lookup on "name". If there's an age of something other than 35 in the pipeline being evaluated |
| // then something is wrong. |
| assertEquals(new Long(1), g.traversal().V().has("age", P.test((t, u) -> { |
| assertEquals(35, t); |
| return true; |
| }, 35)).has("name", "stephen").count().next()); |
| } |
| |
| @Test |
| public void shouldRemoveAVertexFromAnIndex() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("name", Vertex.class); |
| |
| g.addVertex("name", "marko", "age", 29); |
| g.addVertex("name", "stephen", "age", 35); |
| final Vertex v = g.addVertex("name", "stephen", "age", 35); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is used because only "stephen" ages should pass through the pipeline due to the inclusion of the |
| // key index lookup on "name". If there's an age of something other than 35 in the pipeline being evaluated |
| // then something is wrong. |
| assertEquals(new Long(2), g.traversal().V().has("age", P.test((t, u) -> { |
| assertEquals(35, t); |
| return true; |
| }, 35)).has("name", "stephen").count().next()); |
| |
| v.remove(); |
| assertEquals(new Long(1), g.traversal().V().has("age", P.test((t, u) -> { |
| assertEquals(35, t); |
| return true; |
| }, 35)).has("name", "stephen").count().next()); |
| } |
| |
| @Test |
| public void shouldUpdateVertexIndicesInExistingGraph() { |
| final TinkerGraph g = TinkerGraph.open(); |
| |
| g.addVertex("name", "marko", "age", 29); |
| g.addVertex("name", "stephen", "age", 35); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is not used because "stephen" and "marko" ages both pass through the pipeline. |
| assertEquals(new Long(1), g.traversal().V().has("age", P.test((t, u) -> { |
| assertTrue(t.equals(35) || t.equals(29)); |
| return true; |
| }, 35)).has("name", "stephen").count().next()); |
| |
| g.createIndex("name", Vertex.class); |
| |
| // another spy into the pipeline for index check. in this case, we know that at index |
| // is used because only "stephen" ages should pass through the pipeline due to the inclusion of the |
| // key index lookup on "name". If there's an age of something other than 35 in the pipeline being evaluated |
| // then something is wrong. |
| assertEquals(new Long(1), g.traversal().V().has("age", P.test((t, u) -> { |
| assertEquals(35, t); |
| return true; |
| }, 35)).has("name", "stephen").count().next()); |
| } |
| |
| @Test |
| public void shouldUpdateEdgeIndicesInNewGraph() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("oid", Edge.class); |
| |
| final Vertex v = g.addVertex(); |
| v.addEdge("friend", v, "oid", "1", "weight", 0.5f); |
| v.addEdge("friend", v, "oid", "2", "weight", 0.6f); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is used because only oid 1 should pass through the pipeline due to the inclusion of the |
| // key index lookup on "oid". If there's an weight of something other than 0.5f in the pipeline being |
| // evaluated then something is wrong. |
| assertEquals(new Long(1), g.traversal().E().has("weight", P.test((t, u) -> { |
| assertEquals(0.5f, t); |
| return true; |
| }, 0.5)).has("oid", "1").count().next()); |
| } |
| |
| @Test |
| public void shouldRemoveEdgeFromAnIndex() { |
| final TinkerGraph g = TinkerGraph.open(); |
| g.createIndex("oid", Edge.class); |
| |
| final Vertex v = g.addVertex(); |
| v.addEdge("friend", v, "oid", "1", "weight", 0.5f); |
| final Edge e = v.addEdge("friend", v, "oid", "1", "weight", 0.5f); |
| v.addEdge("friend", v, "oid", "2", "weight", 0.6f); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is used because only oid 1 should pass through the pipeline due to the inclusion of the |
| // key index lookup on "oid". If there's an weight of something other than 0.5f in the pipeline being |
| // evaluated then something is wrong. |
| assertEquals(new Long(2), g.traversal().E().has("weight", P.test((t, u) -> { |
| assertEquals(0.5f, t); |
| return true; |
| }, 0.5)).has("oid", "1").count().next()); |
| |
| e.remove(); |
| assertEquals(new Long(1), g.traversal().E().has("weight", P.test((t, u) -> { |
| assertEquals(0.5f, t); |
| return true; |
| }, 0.5)).has("oid", "1").count().next()); |
| } |
| |
| @Test |
| public void shouldUpdateEdgeIndicesInExistingGraph() { |
| final TinkerGraph g = TinkerGraph.open(); |
| |
| final Vertex v = g.addVertex(); |
| v.addEdge("friend", v, "oid", "1", "weight", 0.5f); |
| v.addEdge("friend", v, "oid", "2", "weight", 0.6f); |
| |
| // a tricky way to evaluate if indices are actually being used is to pass a fake BiPredicate to has() |
| // to get into the Pipeline and evaluate what's going through it. in this case, we know that at index |
| // is not used because "1" and "2" weights both pass through the pipeline. |
| assertEquals(new Long(1), g.traversal().E().has("weight", P.test((t, u) -> { |
| assertTrue(t.equals(0.5f) || t.equals(0.6f)); |
| return true; |
| }, 0.5)).has("oid", "1").count().next()); |
| |
| g.createIndex("oid", Edge.class); |
| |
| // another spy into the pipeline for index check. in this case, we know that at index |
| // is used because only oid 1 should pass through the pipeline due to the inclusion of the |
| // key index lookup on "oid". If there's an weight of something other than 0.5f in the pipeline being |
| // evaluated then something is wrong. |
| assertEquals(new Long(1), g.traversal().E().has("weight", P.test((t, u) -> { |
| assertEquals(0.5f, t); |
| return true; |
| }, 0.5)).has("oid", "1").count().next()); |
| } |
| |
| @Test |
| public void shouldSerializeTinkerGraphToGryo() throws Exception { |
| final TinkerGraph graph = TinkerFactory.createModern(); |
| try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| graph.io(IoCore.gryo()).writer().create().writeObject(out, graph); |
| final byte[] b = out.toByteArray(); |
| try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(b)) { |
| final TinkerGraph target = graph.io(IoCore.gryo()).reader().create().readObject(inputStream, TinkerGraph.class); |
| IoTest.assertModernGraph(target, true, false); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldSerializeTinkerGraphWithMultiPropertiesToGryo() throws Exception { |
| final TinkerGraph graph = TinkerFactory.createTheCrew(); |
| try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| graph.io(IoCore.gryo()).writer().create().writeObject(out, graph); |
| final byte[] b = out.toByteArray(); |
| try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(b)) { |
| final TinkerGraph target = graph.io(IoCore.gryo()).reader().create().readObject(inputStream, TinkerGraph.class); |
| IoTest.assertCrewGraph(target, false); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldSerializeTinkerGraphToGraphSON() throws Exception { |
| final TinkerGraph graph = TinkerFactory.createModern(); |
| try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| graph.io(IoCore.graphson()).writer().create().writeObject(out, graph); |
| try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray())) { |
| final TinkerGraph target = graph.io(IoCore.graphson()).reader().create().readObject(inputStream, TinkerGraph.class); |
| IoTest.assertModernGraph(target, true, false); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldSerializeTinkerGraphWithMultiPropertiesToGraphSON() throws Exception { |
| final TinkerGraph graph = TinkerFactory.createTheCrew(); |
| try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| graph.io(IoCore.graphson()).writer().create().writeObject(out, graph); |
| try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray())) { |
| final TinkerGraph target = graph.io(IoCore.graphson()).reader().create().readObject(inputStream, TinkerGraph.class); |
| IoTest.assertCrewGraph(target, false); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldSerializeTinkerGraphToGraphSONWithTypes() throws Exception { |
| final TinkerGraph graph = TinkerFactory.createModern(); |
| final Mapper<ObjectMapper> mapper = graph.io(IoCore.graphson()).mapper().typeInfo(TypeInfo.PARTIAL_TYPES).create(); |
| try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| final GraphWriter writer = GraphSONWriter.build().mapper(mapper).create(); |
| writer.writeObject(out, graph); |
| try (final ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray())) { |
| final GraphReader reader = GraphSONReader.build().mapper(mapper).create(); |
| final TinkerGraph target = reader.readObject(inputStream, TinkerGraph.class); |
| IoTest.assertModernGraph(target, true, false); |
| } |
| } |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void shouldRequireGraphLocationIfFormatIsSet() { |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "graphml"); |
| TinkerGraph.open(conf); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void shouldNotModifyAVertexThatWasRemoved() { |
| final TinkerGraph graph = TinkerGraph.open(); |
| final Vertex v = graph.addVertex(); |
| v.property("name", "stephen"); |
| |
| assertEquals("stephen", v.value("name")); |
| v.remove(); |
| |
| v.property("status", 1); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void shouldNotAddEdgeToAVertexThatWasRemoved() { |
| final TinkerGraph graph = TinkerGraph.open(); |
| final Vertex v = graph.addVertex(); |
| v.property("name", "stephen"); |
| |
| assertEquals("stephen", v.value("name")); |
| v.remove(); |
| v.addEdge("self", v); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void shouldNotReadValueOfPropertyOnVertexThatWasRemoved() { |
| final TinkerGraph graph = TinkerGraph.open(); |
| final Vertex v = graph.addVertex(); |
| v.property("name", "stephen"); |
| |
| assertEquals("stephen", v.value("name")); |
| v.remove(); |
| v.value("name"); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void shouldRequireGraphFormatIfLocationIsSet() { |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, TestHelper.makeTestDataDirectory(TinkerGraphTest.class)); |
| TinkerGraph.open(conf); |
| } |
| |
| @Test |
| public void shouldPersistToGraphML() { |
| final String graphLocation = TestHelper.makeTestDataDirectory(TinkerGraphTest.class) + "shouldPersistToGraphML.xml"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "graphml"); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateModern(graph); |
| graph.close(); |
| |
| final TinkerGraph reloadedGraph = TinkerGraph.open(conf); |
| IoTest.assertModernGraph(reloadedGraph, true, true); |
| reloadedGraph.close(); |
| } |
| |
| @Test |
| public void shouldPersistToGraphSON() { |
| final String graphLocation = TestHelper.makeTestDataDirectory(TinkerGraphTest.class) + "shouldPersistToGraphSON.json"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "graphson"); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateModern(graph); |
| graph.close(); |
| |
| final TinkerGraph reloadedGraph = TinkerGraph.open(conf); |
| IoTest.assertModernGraph(reloadedGraph, true, false); |
| reloadedGraph.close(); |
| } |
| |
| @Test |
| public void shouldPersistToGryo() { |
| final String graphLocation = TestHelper.makeTestDataDirectory(TinkerGraphTest.class) + "shouldPersistToGryo.kryo"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "gryo"); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateModern(graph); |
| graph.close(); |
| |
| final TinkerGraph reloadedGraph = TinkerGraph.open(conf); |
| IoTest.assertModernGraph(reloadedGraph, true, false); |
| reloadedGraph.close(); |
| } |
| |
| @Test |
| public void shouldPersistToGryoAndHandleMultiProperties() { |
| final String graphLocation = TestHelper.makeTestDataDirectory(TinkerGraphTest.class) + "shouldPersistToGryoMulti.kryo"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "gryo"); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateTheCrew(graph); |
| graph.close(); |
| |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY, VertexProperty.Cardinality.list.toString()); |
| final TinkerGraph reloadedGraph = TinkerGraph.open(conf); |
| IoTest.assertCrewGraph(reloadedGraph, false); |
| reloadedGraph.close(); |
| } |
| |
| @Test |
| public void shouldPersistWithRelativePath() { |
| final String graphLocation = TestHelper.convertToRelative(TinkerGraphTest.class, |
| new File(TestHelper.makeTestDataDirectory(TinkerGraphTest.class))) + "shouldPersistToGryoRelative.kryo"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, "gryo"); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateModern(graph); |
| graph.close(); |
| |
| final TinkerGraph reloadedGraph = TinkerGraph.open(conf); |
| IoTest.assertModernGraph(reloadedGraph, true, false); |
| reloadedGraph.close(); |
| } |
| |
| @Test |
| public void shouldPersistToAnyGraphFormat() { |
| final String graphLocation = TestHelper.makeTestDataDirectory(TinkerGraphTest.class) + "shouldPersistToAnyGraphFormat.dat"; |
| final File f = new File(graphLocation); |
| if (f.exists() && f.isFile()) f.delete(); |
| |
| final Configuration conf = new BaseConfiguration(); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_FORMAT, TestIoBuilder.class.getName()); |
| conf.setProperty(TinkerGraph.GREMLIN_TINKERGRAPH_GRAPH_LOCATION, graphLocation); |
| final TinkerGraph graph = TinkerGraph.open(conf); |
| TinkerFactory.generateModern(graph); |
| |
| //Test write graph |
| graph.close(); |
| assertEquals(TestIoBuilder.calledOnMapper, 1); |
| assertEquals(TestIoBuilder.calledGraph, 1); |
| assertEquals(TestIoBuilder.calledCreate, 1); |
| |
| try (BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(f))){ |
| os.write("dummy string".getBytes()); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| //Test read graph |
| final TinkerGraph readGraph = TinkerGraph.open(conf); |
| assertEquals(TestIoBuilder.calledOnMapper, 1); |
| assertEquals(TestIoBuilder.calledGraph, 1); |
| assertEquals(TestIoBuilder.calledCreate, 1); |
| } |
| |
| @Test |
| public void shouldSerializeWithColorClassResolverToTinkerGraph() throws Exception { |
| final Map<String,Color> colors = new HashMap<>(); |
| colors.put("red", Color.RED); |
| colors.put("green", Color.GREEN); |
| |
| final ArrayList<Color> colorList = new ArrayList<>(Arrays.asList(Color.RED, Color.GREEN)); |
| |
| final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier(); |
| final GryoMapper mapper = GryoMapper.build().version(GryoVersion.V3_0).addRegistry(TinkerIoRegistryV3d0.instance()).classResolver(classResolver).create(); |
| final Kryo kryo = mapper.createMapper(); |
| try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { |
| final Output out = new Output(stream); |
| |
| kryo.writeObject(out, colorList); |
| out.flush(); |
| final byte[] b = stream.toByteArray(); |
| |
| try (final InputStream inputStream = new ByteArrayInputStream(b)) { |
| final Input input = new Input(inputStream); |
| final List m = kryo.readObject(input, ArrayList.class); |
| final TinkerGraph readX = (TinkerGraph) m.get(0); |
| assertEquals(104, IteratorUtils.count(readX.vertices())); |
| assertEquals(102, IteratorUtils.count(readX.edges())); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldSerializeWithColorClassResolverToTinkerGraphUsingDeprecatedTinkerIoRegistry() throws Exception { |
| final Map<String,Color> colors = new HashMap<>(); |
| colors.put("red", Color.RED); |
| colors.put("green", Color.GREEN); |
| |
| final ArrayList<Color> colorList = new ArrayList<>(Arrays.asList(Color.RED, Color.GREEN)); |
| |
| final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier(); |
| final GryoMapper mapper = GryoMapper.build().version(GryoVersion.V3_0).addRegistry(TinkerIoRegistryV3d0.instance()).classResolver(classResolver).create(); |
| final Kryo kryo = mapper.createMapper(); |
| try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { |
| final Output out = new Output(stream); |
| |
| kryo.writeObject(out, colorList); |
| out.flush(); |
| final byte[] b = stream.toByteArray(); |
| |
| try (final InputStream inputStream = new ByteArrayInputStream(b)) { |
| final Input input = new Input(inputStream); |
| final List m = kryo.readObject(input, ArrayList.class); |
| final TinkerGraph readX = (TinkerGraph) m.get(0); |
| assertEquals(104, IteratorUtils.count(readX.vertices())); |
| assertEquals(102, IteratorUtils.count(readX.edges())); |
| } |
| } |
| } |
| |
| @Test |
| public void shouldCloneTinkergraph() { |
| final TinkerGraph original = TinkerGraph.open(); |
| final TinkerGraph clone = TinkerGraph.open(); |
| |
| final Vertex marko = original.addVertex("name", "marko", "age", 29); |
| final Vertex stephen = original.addVertex("name", "stephen", "age", 35); |
| marko.addEdge("knows", stephen); |
| GraphHelper.cloneElements(original, clone); |
| |
| final Vertex michael = clone.addVertex("name", "michael"); |
| michael.addEdge("likes", marko); |
| michael.addEdge("likes", stephen); |
| clone.traversal().V().property("newProperty", "someValue").toList(); |
| clone.traversal().E().property("newProperty", "someValue").toList(); |
| |
| assertEquals("original graph should be unchanged", new Long(2), original.traversal().V().count().next()); |
| assertEquals("original graph should be unchanged", new Long(1), original.traversal().E().count().next()); |
| assertEquals("original graph should be unchanged", new Long(0), original.traversal().V().has("newProperty").count().next()); |
| |
| assertEquals("cloned graph should contain new elements", new Long(3), clone.traversal().V().count().next()); |
| assertEquals("cloned graph should contain new elements", new Long(3), clone.traversal().E().count().next()); |
| assertEquals("cloned graph should contain new property", new Long(3), clone.traversal().V().has("newProperty").count().next()); |
| assertEquals("cloned graph should contain new property", new Long(3), clone.traversal().E().has("newProperty").count().next()); |
| |
| assertNotSame("cloned elements should reference to different objects", |
| original.traversal().V().has("name", "stephen").next(), |
| clone.traversal().V().has("name", "stephen").next()); |
| } |
| |
| /** |
| * This isn't a TinkerGraph specific test, but TinkerGraph is probably best suited for the testing of this |
| * particular problem originally noted in TINKERPOP-1992. |
| */ |
| @Test |
| public void shouldProperlyTimeReducingBarrierForProfile() { |
| final GraphTraversalSource g = TinkerFactory.createModern().traversal(); |
| |
| TraversalMetrics m = g.V().group().by().by(__.bothE().count()).profile().next(); |
| for (Metrics i : m.getMetrics(1).getNested()) { |
| assertThat(i.getDuration(TimeUnit.NANOSECONDS), greaterThan(0L)); |
| } |
| |
| m = g.withComputer().V().group().by().by(__.bothE().count()).profile().next(); |
| for (Metrics i : m.getMetrics(1).getNested()) { |
| assertThat(i.getDuration(TimeUnit.NANOSECONDS), greaterThan(0L)); |
| } |
| } |
| |
| /** |
| * Just validating that property folding works nicely given TINKERPOP-2112 |
| */ |
| @Test |
| public void shouldFoldPropertyStepForTokens() { |
| final GraphTraversalSource g = TinkerGraph.open().traversal(); |
| |
| g.addV("person").property(VertexProperty.Cardinality.single, "k", "v"). |
| property(T.id , "id"). |
| property(VertexProperty.Cardinality.list, "l", 1). |
| property("x", "y"). |
| property(VertexProperty.Cardinality.list, "l", 2). |
| property("m", "m", "mm", "mm"). |
| property("y", "z").iterate(); |
| |
| assertThat(g.V("id").hasNext(), is(true)); |
| } |
| |
| @Test |
| public void shouldOptionalUsingWithComputer() { |
| // not all systems will have 3+ available processors (e.g. travis) |
| assumeThat(Runtime.getRuntime().availableProcessors(), greaterThan(2)); |
| |
| // didn't add this as a general test as it basically was only failing under a specific condition for |
| // TinkerGraphComputer - see more here: https://issues.apache.org/jira/browse/TINKERPOP-1619 |
| final GraphTraversalSource g = TinkerFactory.createModern().traversal(); |
| |
| final List<Edge> expected = g.E(7, 7, 8, 9).order().by(T.id).toList(); |
| assertEquals(expected, g.withComputer(Computer.compute().workers(3)).V(1, 2).optional(__.bothE().dedup()).order().by(T.id).toList()); |
| assertEquals(expected, g.withComputer(Computer.compute().workers(4)).V(1, 2).optional(__.bothE().dedup()).order().by(T.id).toList()); |
| } |
| |
| @Test |
| public void shouldReservedKeyVerify() { |
| final Set<String> reserved = new HashSet<>(Arrays.asList("something", "id", "label")); |
| final GraphTraversalSource g = TinkerGraph.open().traversal().withStrategies( |
| ReservedKeysVerificationStrategy.build().reservedKeys(reserved).throwException().create()); |
| |
| g.addV("person").property(T.id, 123).iterate(); |
| |
| try { |
| g.addV("person").property("id", 123).iterate(); |
| fail("Verification exception expected"); |
| } catch (IllegalStateException ve) { |
| assertThat(ve.getMessage(), containsString("that is setting a property key to a reserved word")); |
| } |
| |
| try { |
| g.addV("person").property("something", 123).iterate(); |
| fail("Verification exception expected"); |
| } catch (IllegalStateException ve) { |
| assertThat(ve.getMessage(), containsString("that is setting a property key to a reserved word")); |
| } |
| } |
| |
| /** |
| * Coerces a {@code Color} to a {@link TinkerGraph} during serialization. Demonstrates how custom serializers |
| * can be developed that can coerce one value to another during serialization. |
| */ |
| public final static class ColorToTinkerGraphSerializer extends Serializer<Color> { |
| public ColorToTinkerGraphSerializer() { |
| } |
| |
| @Override |
| public void write(final Kryo kryo, final Output output, final Color color) { |
| final TinkerGraph graph = TinkerGraph.open(); |
| final Vertex v = graph.addVertex(T.id, 1, T.label, "color", "name", color.toString()); |
| final Vertex vRed = graph.addVertex(T.id, 2, T.label, "primary", "name", "red"); |
| final Vertex vGreen = graph.addVertex(T.id, 3, T.label, "primary", "name", "green"); |
| final Vertex vBlue = graph.addVertex(T.id, 4, T.label, "primary", "name", "blue"); |
| |
| v.addEdge("hasComponent", vRed, "amount", color.getRed()); |
| v.addEdge("hasComponent", vGreen, "amount", color.getGreen()); |
| v.addEdge("hasComponent", vBlue, "amount", color.getBlue()); |
| |
| // make some junk so the graph is kinda big |
| generate(graph); |
| |
| try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { |
| GryoWriter.build().mapper(() -> kryo).create().writeGraph(stream, graph); |
| final byte[] bytes = stream.toByteArray(); |
| output.writeInt(bytes.length); |
| output.write(bytes); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public Color read(final Kryo kryo, final Input input, final Class<Color> colorClass) { |
| throw new UnsupportedOperationException("IoX writes to DetachedVertex and can't be read back in as IoX"); |
| } |
| |
| private static void generate(final Graph graph) { |
| final int size = 100; |
| final List<Object> ids = new ArrayList<>(); |
| final Vertex v = graph.addVertex("sin", 0.0f, "cos", 1.0f, "ii", 0f); |
| ids.add(v.id()); |
| |
| final GraphTraversalSource g = graph.traversal(); |
| |
| final Random rand = new Random(); |
| for (int ii = 1; ii < size; ii++) { |
| final Vertex t = graph.addVertex("ii", ii, "sin", Math.sin(ii / 5.0f), "cos", Math.cos(ii / 5.0f)); |
| final Vertex u = g.V(ids.get(rand.nextInt(ids.size()))).next(); |
| t.addEdge("linked", u); |
| ids.add(u.id()); |
| ids.add(v.id()); |
| } |
| } |
| } |
| |
| public static class CustomClassResolverSupplier implements Supplier<ClassResolver> { |
| @Override |
| public ClassResolver get() { |
| return new CustomClassResolver(); |
| } |
| } |
| |
| public static class CustomClassResolver extends GryoClassResolverV1d0 { |
| private ColorToTinkerGraphSerializer colorToGraphSerializer = new ColorToTinkerGraphSerializer(); |
| |
| public Registration getRegistration(final Class clazz) { |
| if (Color.class.isAssignableFrom(clazz)) { |
| final Registration registration = super.getRegistration(TinkerGraph.class); |
| return new Registration(registration.getType(), colorToGraphSerializer, registration.getId()); |
| } else { |
| return super.getRegistration(clazz); |
| } |
| } |
| } |
| |
| public static class TestIoBuilder implements Io.Builder { |
| |
| static int calledGraph, calledCreate, calledOnMapper; |
| |
| public TestIoBuilder(){ |
| //Looks awkward to reset static vars inside a constructor, but makes sense from testing perspective |
| calledGraph = 0; |
| calledCreate = 0; |
| calledOnMapper = 0; |
| } |
| |
| @Override |
| public Io.Builder<? extends Io> onMapper(final Consumer onMapper) { |
| calledOnMapper++; |
| return this; |
| } |
| |
| @Override |
| public Io.Builder<? extends Io> graph(final Graph graph) { |
| calledGraph++; |
| return this; |
| } |
| |
| @Override |
| public Io create() { |
| calledCreate++; |
| return mock(Io.class); |
| } |
| |
| @Override |
| public boolean requiresVersion(final Object version) { |
| return false; |
| } |
| } |
| } |