/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Mar 19, 2012 */ package com.bigdata.gom; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.openrdf.model.Resource; import org.openrdf.model.URI; import org.openrdf.model.ValueFactory; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParseException; import com.bigdata.gom.gpo.BasicSkin; import com.bigdata.gom.gpo.GPO; import com.bigdata.gom.gpo.IGPO; import com.bigdata.gom.gpo.ILinkSet; import com.bigdata.gom.om.ObjectMgrModel; import com.bigdata.gom.skin.GenericSkinRegistry; /** * Base test suite for the embedded (local) GOM. * * @author Martyn Cutcher */ public class TestGOM extends ProxyGOMTest { private static final Logger log = Logger.getLogger(TestGOM.class); /** * Simple test loads data from a file and navigates around */ public void testSimpleDirectData() throws IOException, RDFParseException, RepositoryException { final URL n3 = TestGOM.class.getResource("testgom.n3"); // print(n3); ((IGOMProxy) m_delegate).load(n3, RDFFormat.N3); final ValueFactory vf = om.getValueFactory(); final URI s = vf.createURI("gpo:#root"); final URI rootAttr = vf.createURI("attr:/root"); om.getGPO(s).getValue(rootAttr); final URI rootId = (URI) om.getGPO(s).getValue(rootAttr); final IGPO rootGPO = om.getGPO(rootId); if (log.isInfoEnabled()) { log.info("--------\n" + rootGPO.pp() + "\n" + rootGPO.getType().pp() + "\n" + rootGPO.getType().getStatements()); } final URI typeName = vf.createURI("attr:/type#name"); assertTrue("Company".equals(rootGPO.getType().getValue(typeName) .stringValue())); // find set of workers for the Company final URI worksFor = vf.createURI("attr:/employee#worksFor"); final ILinkSet linksIn = rootGPO.getLinksIn(worksFor); final Iterator<IGPO> workers = linksIn.iterator(); while (workers.hasNext()) { final IGPO tmp = workers.next(); if (log.isInfoEnabled()) log.info("Returned: " + tmp.pp()); } } /** * Creates a simple GPO within a transaction. Commits and and checks * materialization */ public void testSimpleCreate() throws RepositoryException, IOException { final ValueFactory vf = om.getValueFactory(); final URI keyname = vf.createURI("attr:/test#name"); final Resource id = vf.createURI("gpo:test#1"); // om.checkValue(id); // ensure is imported! final int transCounter = om.beginNativeTransaction(); try { final IGPO gpo = om.getGPO(id); gpo.setValue(keyname, vf.createLiteral("Martyn")); om.commitNativeTransaction(transCounter); } catch (Throwable t) { om.rollbackNativeTransaction(); throw new RuntimeException(t); } // clear cached data ((ObjectMgrModel) om).clearCache(); // reads from backing journal final IGPO gpo = om.getGPO(vf.createURI("gpo:test#1")); assertTrue("Martyn".equals(gpo.getValue(keyname).stringValue())); } /** * Checks getPropertyURIs of committed GPO */ public void testSimpleProperties() throws RepositoryException, IOException { final ValueFactory vf = om.getValueFactory(); final URI keyname = vf.createURI("attr:/test#name"); final URI keyage = vf.createURI("attr:/test#age"); final URI keyfriend = vf.createURI("attr:/test#friend"); final Resource id = vf.createURI("gpo:test#1"); final Resource id2 = vf.createURI("gpo:test#2"); // om.checkValue(id); // ensure is imported! // om.checkValue(id2); // ensure is imported! final int transCounter = om.beginNativeTransaction(); try { final IGPO gpo = om.getGPO(id); gpo.setValue(keyname, vf.createLiteral("Martyn")); gpo.setValue(keyage, vf.createLiteral(53)); Iterator<URI> uris = ((GPO) gpo).getPropertyURIs(); // should be two uris.next(); uris.next(); // .. and no more assertFalse(uris.hasNext()); // add simple reverse link om.getGPO(id2).setValue(keyfriend, id); om.commitNativeTransaction(transCounter); uris = ((GPO) gpo).getPropertyURIs(); // should be two uris.next(); uris.next(); // .. and no more assertFalse(uris.hasNext()); } catch (Throwable t) { om.rollbackNativeTransaction(); throw new RuntimeException(t); } // clear cached data ((ObjectMgrModel) om).clearCache(); { // reads from backing journal final IGPO gpo = om.getGPO(vf.createURI("gpo:test#1")); final Iterator<URI> uris = ((GPO) gpo).getPropertyURIs(); // should be two uris.next(); uris.next(); // .. and no more assertFalse(uris.hasNext()); } } public void testSimpleReverseLinkProperties() throws RepositoryException, IOException { final ValueFactory vf = om.getValueFactory(); final URI keyname = vf.createURI("attr:/test#name"); final URI keyage = vf.createURI("attr:/test#age"); final URI parent = vf.createURI("attr:/test#parent"); final URI parent2 = vf.createURI("attr:/test#parent2"); final URI nme = vf.createURI("gpo:ROOT"); final int transCounter = om.beginNativeTransaction(); try { IGPO gpo = om.createGPO(); gpo.setValue(keyname, vf.createLiteral("Martyn")); gpo.setValue(keyage, vf.createLiteral(53)); om.save(nme, gpo.getId()); om.createGPO().setValue(parent, gpo.getId()); om.createGPO().setValue(parent, gpo.getId()); om.createGPO().setValue(parent, gpo.getId()); om.createGPO().setValue(parent2, gpo.getId()); om.createGPO().setValue(parent2, gpo.getId()); om.createGPO().setValue(parent2, gpo.getId()); om.commitNativeTransaction(transCounter); } catch (Throwable t) { om.rollbackNativeTransaction(); throw new RuntimeException(t); } // clear cached data ((ObjectMgrModel) om).clearCache(); IGPO gpo = om.getGPO((Resource) om.recall(nme)); // reads from // backing journal Map<URI, Long> map = gpo.getReverseLinkProperties(); // there should be 3 - the two parent properties plus the name // manager assertTrue(map.size() == 3); } /** * SimpleClassObjects tests navigation around a constructed network of GPOs. * First some class objects are created, with superclass and metaclass data. * Then instances of the classes are written. The purpose of this test is * two fold: 1) To exercise the developing system and 2) To explore and * develop utilities around the GPO usage. * * Class: Person Class: Employee Class: Manager Class: Company */ public void testSimpleClassObjects() throws RepositoryException, IOException { final ValueFactory vf = om.getValueFactory(); // This is the only required root! final URI clssclssName = vf.createURI("attr:/classClass"); final URI descr = vf.createURI("attr:/classClass#description"); final URI superClss = vf.createURI("attr:/class#super"); final URI className = vf.createURI("attr:/class#name"); final URI gpoType = vf.createURI("attr:/gpo#type"); final int transCounter = om.beginNativeTransaction(); try { // Object ClassClass provides hook into class hierarchy and // thence // into associated instances final IGPO clssclss = om.createGPO(); om.save(clssclssName, clssclss.getId()); // remember the // ClassClass clssclss.setValue(descr, vf.createLiteral("The class of all classes")); final IGPO clssPerson = om.createGPO(); clssPerson.setValue(gpoType, clssclss.getId()); clssPerson.setValue(className, vf.createLiteral("Person")); final IGPO clssEmployee = om.createGPO(); clssEmployee.setValue(gpoType, clssclss.getId()); clssEmployee.setValue(superClss, clssPerson.getId()); clssEmployee.setValue(className, vf.createLiteral("Employee")); om.commitNativeTransaction(transCounter); } catch (Throwable t) { om.rollbackNativeTransaction(); throw new RuntimeException(t); } // check state post commit : TODO Tests what? Just iterating... { final IGPO clssclss = om.recallAsGPO(clssclssName); final Iterator<IGPO> classes = clssclss.getLinksIn(gpoType) .iterator(); while (classes.hasNext()) { final IGPO cls = classes.next(); if (log.isInfoEnabled()) log.info("Class: " + cls.pp()); } } // clear cached data and run again rebuilding ((ObjectMgrModel) om).clearCache(); { final IGPO clssclss = om.recallAsGPO(clssclssName); final Iterator<IGPO> classes = clssclss.getLinksIn(gpoType) .iterator(); while (classes.hasNext()) { final IGPO clss = classes.next(); if (log.isInfoEnabled()) log.info("Class: " + clss.pp()); } } } /** * This tests both the usability and functionality of the Skinning mechanism */ public void testBasicSkin() { GenericSkinRegistry.registerClass(BasicSkin.class); final GPO gpo = (GPO) om.createGPO(); final BasicSkin skin = (BasicSkin) gpo.getSkin(BasicSkin.class); assertTrue(skin == gpo.getSkin(BasicSkin.class)); // check identity skin.setValue("attr:#name", "Martyn"); } /** * Throughput test for updates. */ public void testUpdateThroughput() throws RepositoryException, IOException { final ValueFactory vf = om.getValueFactory(); final int transCounter = om.beginNativeTransaction(); final URI name = vf.createURI("attr:/test#name"); final URI ni = vf.createURI("attr:/test#ni"); final URI age = vf.createURI("attr:/test#age"); final URI mob = vf.createURI("attr:/test#mobile"); final URI gender = vf.createURI("attr:/test#mail"); try { // warmup for (int i = 0; i < 10000; i++) { final IGPO tst = om.createGPO(); tst.setValue(name, vf.createLiteral("Test" + i)); } // go for it final long start = System.currentTimeMillis(); // The LocalGOMTestcase can handle much larger tests if // incremental updating is enabled final int creates = 50000; for (int i = 0; i < creates; i++) { final IGPO tst = om.createGPO(); tst.setValue(name, vf.createLiteral("Name" + i)); tst.setValue(ni, vf.createLiteral("NI" + i)); tst.setValue(age, vf.createLiteral(i)); tst.setValue(mob, vf.createLiteral("0123-" + i)); tst.setValue(gender, vf.createLiteral(1 % 3 == 0)); } om.commitNativeTransaction(transCounter); final long duration = (System.currentTimeMillis() - start); final long objectsPS = creates * 1000 / duration; final long statementsPS = objectsPS * 5; /* * Note that this is a conservative estimate for statements per * second since there is only one per object, requiring the object * URI and the Value to be added. */ if (log.isInfoEnabled()) { log.info("Creation rate of " + objectsPS + " objects per second"); log.info("Creation rate of " + statementsPS + " statements per second"); } // Anything less than 5000 statements per second is a failure assertTrue(statementsPS > 5000); } catch (Throwable t) { // t.printStackTrace(); om.rollbackNativeTransaction(); throw new RuntimeException(t); } } /** * A simple test to compare native String interns with own-rolled ConcurrentHashMap based * dictionary. Figures suggest that the ConcurrentHashMap is a little slower building the * table but then around 10 times faster processing the lookup. * * The intention is to store such an "interned" String reference in the GPOs and to perform * a sequential search for property keys rather than use a map. This will minimise memory * overhead and object creation times. */ public void notestPerfStringIntern() { final Random r = new Random(); final int limit = 10000; final ArrayList<String> strs = new ArrayList<String>(limit); for (int i = 0; i < limit; i++) { strs.add("url://this/is/a/long/string/" + (987654 + r.nextInt(1000000))); } for (int i = 0; i < 20; i++) { final long start = System.nanoTime(); final Iterator<String> striter = strs.iterator(); while (striter.hasNext()) { assertTrue(striter.next().intern() != null); // just to keep find bugs happy } final long end = System.nanoTime(); if (log.isInfoEnabled()) log.info("Interns#" + i + ":" + (end - start)); } final ConcurrentHashMap<String, String> dict = new ConcurrentHashMap<String, String>(); for (int i = 0; i < 20; i++) { final long start = System.nanoTime(); final Iterator<String> striter = strs.iterator(); while (striter.hasNext()) { final String str = striter.next(); final String ret = dict.get(str); if (ret == null) { dict.put(str, str); } } final long end = System.nanoTime(); if (log.isInfoEnabled()) log.info("HashMap#" + i + ":" + (end - start)); } } }