// Copyright 2017 JanusGraph Authors // // Licensed 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.janusgraph.graphdb; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.janusgraph.core.*; import org.janusgraph.core.Cardinality; import org.janusgraph.core.schema.JanusGraphIndex; import org.janusgraph.core.schema.JanusGraphManagement; import org.janusgraph.diskstorage.BackendException; import org.janusgraph.diskstorage.Backend; import org.janusgraph.diskstorage.configuration.*; import org.janusgraph.diskstorage.keycolumnvalue.KeyColumnValueStoreManager; import org.janusgraph.diskstorage.keycolumnvalue.StoreFeatures; import org.janusgraph.diskstorage.log.Log; import org.janusgraph.diskstorage.log.LogManager; import org.janusgraph.diskstorage.log.kcvs.KCVSLogManager; import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import org.janusgraph.graphdb.database.StandardJanusGraph; import org.janusgraph.graphdb.internal.Order; import org.janusgraph.graphdb.types.StandardEdgeLabelMaker; import org.janusgraph.testutil.TestGraphConfigs; import org.apache.tinkerpop.gremlin.structure.T; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.junit.After; import org.junit.Before; import java.time.Duration; import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * @author Matthias Broecheler (me@matthiasb.com) */ public abstract class JanusGraphBaseTest { public static final String LABEL_NAME = T.label.getAccessor(); public static final String ID_NAME = T.id.getAccessor(); public WriteConfiguration config; public BasicConfiguration readConfig; public StandardJanusGraph graph; public StoreFeatures features; public JanusGraphTransaction tx; public JanusGraphManagement mgmt; public Map<String,LogManager> logManagers; public JanusGraphBaseTest() { } public abstract WriteConfiguration getConfiguration(); public Configuration getConfig() { return new BasicConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); } public static void clearGraph(WriteConfiguration config) throws BackendException { ModifiableConfiguration adjustedConfig = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); adjustedConfig.set(GraphDatabaseConfiguration.LOCK_LOCAL_MEDIATOR_GROUP, "tmp"); adjustedConfig.set(GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID, "inst"); Backend backend = new Backend(adjustedConfig); backend.initialize(adjustedConfig); backend.clearStorage(); } @Before public void setUp() throws Exception { this.config = getConfiguration(); TestGraphConfigs.applyOverrides(config); Preconditions.checkNotNull(config); clearGraph(config); readConfig = new BasicConfiguration(GraphDatabaseConfiguration.ROOT_NS, config, BasicConfiguration.Restriction.NONE); open(config); logManagers = new HashMap<String,LogManager>(); } public void open(WriteConfiguration config) { graph = (StandardJanusGraph) JanusGraphFactory.open(config); features = graph.getConfiguration().getStoreFeatures(); tx = graph.newTransaction(); mgmt = graph.openManagement(); } @After public void tearDown() throws Exception { close(); closeLogs(); } public void finishSchema() { if (mgmt!=null && mgmt.isOpen()) mgmt.commit(); mgmt=graph.openManagement(); newTx(); graph.tx().commit(); } public void close() { if (mgmt!=null && mgmt.isOpen()) mgmt.rollback(); if (null != tx && tx.isOpen()) tx.commit(); if (null != graph && graph.isOpen()) graph.close(); } public void newTx() { if (null != tx && tx.isOpen()) tx.commit(); //tx = graph.newThreadBoundTransaction(); tx = graph.newTransaction(); } public static Map<TestConfigOption,Object> validateConfigOptions(Object... settings) { //Parse settings Preconditions.checkArgument(settings.length%2==0,"Expected even number of settings: %s",settings); Map<TestConfigOption,Object> options = Maps.newHashMap(); for (int i=0;i<settings.length;i=i+2) { Preconditions.checkArgument(settings[i] instanceof TestConfigOption,"Expected configuration option but got: %s",settings[i]); Preconditions.checkNotNull(settings[i+1],"Null setting at position [%s]",i+1); options.put((TestConfigOption)settings[i],settings[i+1]); } return options; } public void clopen(Object... settings) { config = getConfiguration(); if (mgmt!=null && mgmt.isOpen()) mgmt.rollback(); if (null != tx && tx.isOpen()) tx.commit(); if (settings!=null && settings.length>0) { Map<TestConfigOption,Object> options = validateConfigOptions(settings); JanusGraphManagement gconf = null; ModifiableConfiguration lconf = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config, BasicConfiguration.Restriction.LOCAL); for (Map.Entry<TestConfigOption,Object> option : options.entrySet()) { if (option.getKey().option.isLocal()) { lconf.set(option.getKey().option,option.getValue(),option.getKey().umbrella); } else { if (gconf==null) gconf = graph.openManagement(); gconf.set(ConfigElement.getPath(option.getKey().option,option.getKey().umbrella),option.getValue()); } } if (gconf!=null) gconf.commit(); lconf.close(); } if (null != graph && null != graph.tx() && graph.tx().isOpen()) graph.tx().commit(); if (null != graph && graph.isOpen()) graph.close(); Preconditions.checkNotNull(config); open(config); } public static final TestConfigOption option(ConfigOption option, String... umbrella) { return new TestConfigOption(option,umbrella); } public static final class TestConfigOption { public final ConfigOption option; public final String[] umbrella; public TestConfigOption(ConfigOption option, String... umbrella) { Preconditions.checkNotNull(option); this.option = option; if (umbrella==null) umbrella=new String[0]; this.umbrella = umbrella; } } /* ========= Log Helpers ============ */ private KeyColumnValueStoreManager logStoreManager = null; private void closeLogs() { try { for (LogManager lm : logManagers.values()) lm.close(); logManagers.clear(); if (logStoreManager!=null) { logStoreManager.close(); logStoreManager=null; } } catch (BackendException e) { throw new JanusGraphException(e); } } public void closeLogManager(String logManagerName) { if (logManagers.containsKey(logManagerName)) { try { logManagers.remove(logManagerName).close(); } catch (BackendException e) { throw new JanusGraphException("Could not close log manager " + logManagerName,e); } } } public Log openUserLog(String identifier) { return openLog(USER_LOG, GraphDatabaseConfiguration.USER_LOG_PREFIX +identifier); } public Log openTxLog() { return openLog(TRANSACTION_LOG, Backend.SYSTEM_TX_LOG_NAME); } private Log openLog(String logManagerName, String logName) { try { ModifiableConfiguration configuration = new ModifiableConfiguration(GraphDatabaseConfiguration.ROOT_NS,config.copy(), BasicConfiguration.Restriction.NONE); configuration.set(GraphDatabaseConfiguration.UNIQUE_INSTANCE_ID, "reader"); configuration.set(GraphDatabaseConfiguration.LOG_READ_INTERVAL, Duration.ofMillis(500L), logManagerName); if (logStoreManager==null) { logStoreManager = Backend.getStorageManager(configuration); } StoreFeatures f = logStoreManager.getFeatures(); boolean part = f.isDistributed() && f.isKeyOrdered(); if (part) { for (String logname : new String[]{USER_LOG,TRANSACTION_LOG,MANAGEMENT_LOG}) configuration.set(KCVSLogManager.LOG_MAX_PARTITIONS,8,logname); } assert logStoreManager!=null; if (!logManagers.containsKey(logManagerName)) { //Open log manager - only supports KCVSLog Configuration logConfig = configuration.restrictTo(logManagerName); Preconditions.checkArgument(logConfig.get(LOG_BACKEND).equals(LOG_BACKEND.getDefaultValue())); logManagers.put(logManagerName,new KCVSLogManager(logStoreManager,logConfig)); } assert logManagers.containsKey(logManagerName); return logManagers.get(logManagerName).openLog(logName); } catch (BackendException e) { throw new JanusGraphException("Could not open log: "+ logName,e); } } /* ========= Schema Type Definition Helpers ============ */ public PropertyKey makeVertexIndexedKey(String name, Class datatype) { PropertyKey key = mgmt.makePropertyKey(name).dataType(datatype).cardinality(Cardinality.SINGLE).make(); mgmt.buildIndex(name,Vertex.class).addKey(key).buildCompositeIndex(); return key; } public PropertyKey makeVertexIndexedUniqueKey(String name, Class datatype) { PropertyKey key = mgmt.makePropertyKey(name).dataType(datatype).cardinality(Cardinality.SINGLE).make(); mgmt.buildIndex(name,Vertex.class).addKey(key).unique().buildCompositeIndex(); return key; } public void createExternalVertexIndex(PropertyKey key, String backingIndex) { createExternalIndex(key,Vertex.class,backingIndex); } public void createExternalEdgeIndex(PropertyKey key, String backingIndex) { createExternalIndex(key,Edge.class,backingIndex); } public JanusGraphIndex getExternalIndex(Class<? extends Element> clazz, String backingIndex) { String prefix; if (Vertex.class.isAssignableFrom(clazz)) prefix = "v"; else if (Edge.class.isAssignableFrom(clazz)) prefix = "e"; else if (JanusGraphVertexProperty.class.isAssignableFrom(clazz)) prefix = "p"; else throw new AssertionError(clazz.toString()); String indexName = prefix+backingIndex; JanusGraphIndex index = mgmt.getGraphIndex(indexName); if (index==null) { index = mgmt.buildIndex(indexName,clazz).buildMixedIndex(backingIndex); } return index; } private void createExternalIndex(PropertyKey key, Class<? extends Element> clazz, String backingIndex) { mgmt.addIndexKey(getExternalIndex(clazz,backingIndex),key); } public PropertyKey makeKey(String name, Class datatype) { PropertyKey key = mgmt.makePropertyKey(name).dataType(datatype).cardinality(Cardinality.SINGLE).make(); return key; } public EdgeLabel makeLabel(String name) { return mgmt.makeEdgeLabel(name).make(); } public EdgeLabel makeKeyedEdgeLabel(String name, PropertyKey sort, PropertyKey signature) { EdgeLabel relType = ((StandardEdgeLabelMaker)tx.makeEdgeLabel(name)). sortKey(sort).signature(signature).directed().make(); return relType; } /* ========= General Helpers =========== */ public static final int DEFAULT_THREAD_COUNT = 4; public static int getThreadCount() { String s = System.getProperty("janusgraph.test.threads"); if (null != s) return Integer.valueOf(s); else return DEFAULT_THREAD_COUNT; } public static int wrapAround(int value, int maxValue) { value = value % maxValue; if (value < 0) value = value + maxValue; return value; } public JanusGraphVertex getVertex(String key, Object value) { return getVertex(tx,key,value); } public JanusGraphVertex getVertex(PropertyKey key, Object value) { return getVertex(tx,key,value); } public static JanusGraphVertex getVertex(JanusGraphTransaction tx, String key, Object value) { return (JanusGraphVertex)getOnlyElement(tx.query().has(key,value).vertices(),null); } public static JanusGraphVertex getVertex(JanusGraphTransaction tx, PropertyKey key, Object value) { return getVertex(tx, key.name(), value); } public static double round(double d) { return Math.round(d*1000.0)/1000.0; } public static JanusGraphVertex getOnlyVertex(JanusGraphQuery<?> query) { return (JanusGraphVertex)getOnlyElement(query.vertices()); } public static JanusGraphEdge getOnlyEdge(JanusGraphVertexQuery<?> query) { return (JanusGraphEdge)getOnlyElement(query.edges()); } public static<E> E getOnlyElement(Iterable<E> traversal) { return getOnlyElement(traversal.iterator()); } public static<E> E getOnlyElement(Iterator<E> traversal) { if (!traversal.hasNext()) throw new NoSuchElementException(); return getOnlyElement(traversal,null); } public static<E> E getOnlyElement(Iterable<E> traversal, E defaultElement) { return getOnlyElement(traversal.iterator(),defaultElement); } public static<E> E getOnlyElement(Iterator<E> traversal, E defaultElement) { if (!traversal.hasNext()) return defaultElement; E result = traversal.next(); if (traversal.hasNext()) throw new IllegalArgumentException("Traversal contains more than 1 element: " + result + ", " + traversal.next()); return result; } // public static<E> E getOnlyElement(GraphTraversal<?,E> traversal) { // if (!traversal.hasNext()) throw new NoSuchElementException(); // return getOnlyElement(traversal,null); // } // // public static<E> E getOnlyElement(GraphTraversal<?,E> traversal, E defaultElement) { // if (!traversal.hasNext()) return defaultElement; // E result = traversal.next(); // if (traversal.hasNext()) throw new IllegalArgumentException("Traversal contains more than 1 element: " + result + ", " + traversal.next()); // return result; // } public static void assertMissing(Transaction g, Object vid) { assertFalse(g.vertices(vid).hasNext()); } public static JanusGraphVertex getV(Transaction g, Object vid) { if (!g.vertices(vid).hasNext()) return null; return (JanusGraphVertex)g.vertices(vid).next(); } public static JanusGraphEdge getE(Transaction g, Object eid) { if (!g.edges(eid).hasNext()) return null; return (JanusGraphEdge)g.edges(eid).next(); } public static String n(Object obj) { if (obj instanceof RelationType) return ((RelationType)obj).name(); else return obj.toString(); } public static long getId(Element e) { return ((JanusGraphElement)e).longId(); } public static void verifyElementOrder(Iterable<? extends Element> elements, String key, Order order, int expectedCount) { verifyElementOrder(elements.iterator(), key, order, expectedCount); } public static void verifyElementOrder(Iterator<? extends Element> elements, String key, Order order, int expectedCount) { Comparable previous = null; int count = 0; while (elements.hasNext()) { Element element = elements.next(); Comparable current = element.value(key); if (previous != null) { int cmp = previous.compareTo(current); assertTrue(previous + " <> " + current + " @ " + count, order == Order.ASC ? cmp <= 0 : cmp >= 0); } previous = current; count++; } assertEquals(expectedCount, count); } public static <T> Stream<T> asStream(final Iterator<T> source) { final Iterable<T> iterable = () -> source; return StreamSupport.stream(iterable.spliterator(),false); } }