Package org.openrdf.sail

Source Code of org.openrdf.sail.RDFStoreTest

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2008.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.sail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;

import junit.framework.TestCase;

import org.openrdf.cursor.Cursor;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Namespace;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.NumericLiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.model.vocabulary.XMLSchema;
import org.openrdf.query.Binding;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.impl.EmptyBindingSet;
import org.openrdf.query.impl.MapBindingSet;
import org.openrdf.query.parser.QueryParserUtil;
import org.openrdf.query.parser.TupleQueryModel;
import org.openrdf.store.ConnectionClosedException;
import org.openrdf.store.StoreException;

/**
* A JUnit test for testing Sail implementations that store RDF data. This is
* purely a test for data storage and retrieval which assumes that no
* inferencing or whatsoever is performed. This is an abstract class that should
* be extended for specific Sail implementations.
*/
public abstract class RDFStoreTest extends TestCase {

  /*-----------*
   * Constants *
   *-----------*/

  private static final String EXAMPLE_NS = "http://example.org/";

  private static final String PAINTER = "Painter";

  private static final String PAINTS = "paints";

  private static final String PAINTING = "Painting";

  private static final String PICASSO = "picasso";

  private static final String REMBRANDT = "rembrandt";

  private static final String GUERNICA = "guernica";

  private static final String NIGHTWATCH = "nightwatch";

  private static final String CONTEXT_1 = "context1";

  private static final String CONTEXT_2 = "context2";

  /*-----------*
   * Variables *
   *-----------*/

  protected URI painter;

  protected URI paints;

  protected URI painting;

  protected URI picasso;

  protected URI rembrandt;

  protected URI guernica;

  protected URI nightwatch;

  protected URI context1;

  protected URI context2;

  protected Sail sail;

  protected SailConnection con;

  protected ValueFactory vf;

  /*--------------*
   * Constructors *
   *--------------*/

  public RDFStoreTest(String name) {
    super(name);
  }

  /*---------*
   * Methods *
   *---------*/

  /**
   * Gets an instance of the Sail that should be tested. The returned
   * repository should already have been initialized.
   *
   * @return an initialized Sail.
   * @throws StoreException
   *         If the initialization of the repository failed.
   */
  protected abstract Sail createSail()
    throws StoreException;

  @Override
  protected void setUp()
    throws Exception
  {
    sail = createSail();

    con = sail.getConnection();

    // Create values
    vf = con.getValueFactory();

    painter = vf.createURI(EXAMPLE_NS, PAINTER);
    paints = vf.createURI(EXAMPLE_NS, PAINTS);
    painting = vf.createURI(EXAMPLE_NS, PAINTING);
    picasso = vf.createURI(EXAMPLE_NS, PICASSO);
    guernica = vf.createURI(EXAMPLE_NS, GUERNICA);
    rembrandt = vf.createURI(EXAMPLE_NS, REMBRANDT);
    nightwatch = vf.createURI(EXAMPLE_NS, NIGHTWATCH);

    context1 = vf.createURI(EXAMPLE_NS, CONTEXT_1);
    context2 = vf.createURI(EXAMPLE_NS, CONTEXT_2);

  }

  @Override
  protected void tearDown()
    throws Exception
  {
    try {
      if (con.isOpen()) {
        if (!con.isAutoCommit()) {
          con.rollback();
        }
        con.close();
      }
    }
    finally {
      sail.shutDown();
      sail = null;
    }
  }

  public void testEmptyRepository()
    throws Exception
  {
    // repository should be empty
    assertEquals("Empty repository should not return any statements", 0, countAllElements());

    assertEquals("Named context should be empty", 0, countContext1Elements());

    assertEquals("Empty repository should not return any context identifiers", 0,
        countElements(con.getContextIDs()));

    assertEquals("Empty repository should not return any query results", 0,
        countQueryResults("select * from {S} P {O}"));
  }

  public void testValueRoundTrip1()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    URI obj = new URIImpl(EXAMPLE_NS + GUERNICA);

    testValueRoundTrip(subj, pred, obj);
  }

  public void testValueRoundTrip2()
    throws Exception
  {
    BNode subj = vf.createBNode();
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    URI obj = new URIImpl(EXAMPLE_NS + GUERNICA);

    testValueRoundTrip(subj, pred, obj);
  }

  public void testValueRoundTrip3()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("guernica");

    testValueRoundTrip(subj, pred, obj);
  }

  public void testValueRoundTrip4()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("guernica", "es");

    testValueRoundTrip(subj, pred, obj);
  }

  public void testValueRoundTrip5()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new NumericLiteralImpl(3);

    testValueRoundTrip(subj, pred, obj);
  }

  public void testDecimalRoundTrip()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new NumericLiteralImpl(3, XMLSchema.DECIMAL);

    testValueRoundTrip(subj, pred, obj);
  }

  public void testTimeZoneRoundTrip()
    throws Exception
  {
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("2006-08-23+00:00", XMLSchema.DATE);
    testValueRoundTrip(subj, pred, obj);

    con.removeStatements(null, null, null);
    obj = new LiteralImpl("2006-08-23", XMLSchema.DATE);
    testValueRoundTrip(subj, pred, obj);
  }

  public void testLongURIRoundTrip()
    throws Exception
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 512; i++) {
      sb.append(Character.toChars('A' + (i % 26)));
    }
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    URI obj = new URIImpl(EXAMPLE_NS + GUERNICA + sb.toString());

    testValueRoundTrip(subj, pred, obj);
  }

  public void testLongLiteralRoundTrip()
    throws Exception
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 512; i++) {
      sb.append(Character.toChars('A' + (i % 26)));
    }
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("guernica" + sb.toString());

    testValueRoundTrip(subj, pred, obj);
  }

  public void testReallyLongLiteralRoundTrip()
    throws Exception
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 1024000; i++) {
      sb.append(Character.toChars('A' + (i % 26)));
    }
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("guernica" + sb.toString());

    testValueRoundTrip(subj, pred, obj);
  }

  public void testLongLangRoundTrip()
    throws Exception
  {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < 512; i++) {
      sb.append(Character.toChars('A' + (i % 26)));
    }
    URI subj = new URIImpl(EXAMPLE_NS + PICASSO);
    URI pred = new URIImpl(EXAMPLE_NS + PAINTS);
    Literal obj = new LiteralImpl("guernica" + sb.toString(), "es");

    testValueRoundTrip(subj, pred, obj);
  }

  private void testValueRoundTrip(Resource subj, URI pred, Value obj)
    throws Exception
  {
    con.addStatement(subj, pred, obj);

    Cursor<? extends Statement> stIter = con.getStatements(null, null, null, false);

    try {

      Statement st = stIter.next();
      assertEquals(subj, st.getSubject());
      assertEquals(pred, st.getPredicate());
      assertEquals(obj, st.getObject());
      assertNull(stIter.next());
    }
    finally {
      stIter.close();
    }

    TupleQueryModel tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL,
        "SELECT S, P, O FROM {S} P {O} WHERE P = <" + pred.stringValue() + ">", null);

    Cursor<? extends BindingSet> iter;
    iter = con.evaluate(tupleQuery, EmptyBindingSet.getInstance(), false);

    try {
      BindingSet bindings = iter.next();
      assertEquals(subj, bindings.getValue("S"));
      assertEquals(pred, bindings.getValue("P"));
      assertEquals(obj, bindings.getValue("O"));
      assertNull(iter.next());
    }
    finally {
      iter.close();
    }
  }

  public void testCreateURI1()
    throws Exception
  {
    URI picasso1 = vf.createURI(EXAMPLE_NS, PICASSO);
    URI picasso2 = vf.createURI(EXAMPLE_NS + PICASSO);
    con.begin();
    con.addStatement(picasso1, paints, guernica);
    con.addStatement(picasso2, paints, guernica);
    con.commit();

    assertEquals("createURI(Sring) and createURI(String, String) should create equal URIs", 1, con.size(
        null, null, null, false));
  }

  public void testCreateURI2()
    throws Exception
  {
    URI picasso1 = vf.createURI(EXAMPLE_NS + PICASSO);
    URI picasso2 = vf.createURI(EXAMPLE_NS, PICASSO);
    con.begin();
    con.addStatement(picasso1, paints, guernica);
    con.addStatement(picasso2, paints, guernica);
    con.commit();

    assertEquals("createURI(Sring) and createURI(String, String) should create equal URIs", 1, con.size(
        null, null, null, false));
  }

  public void testSize()
    throws Exception
  {
    assertEmpty(con);

    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();

    assertEquals(5, con.size(null, null, null, false));
    assertEquals(3, con.size(null, null, null, false, context1));
    assertEquals(4, con.size(null, RDF.TYPE, null, false));
    assertEquals(1, con.size(null, paints, null, false));
    assertEquals(2, con.size(picasso, null, null, false));

    URI unknownContext = new URIImpl(EXAMPLE_NS + "unknown");

    assertEquals(0, con.size(null, null, null, false, unknownContext));
    assertEquals(0, con.size(null, picasso, null, false));

    URIImpl uriImplContext1 = new URIImpl(context1.toString());

    assertEquals(3, con.size(null, null, null, false, uriImplContext1));
  }

  private void assertEmpty(SailConnection con)
    throws StoreException
  {
    URI unknownContext = new URIImpl(EXAMPLE_NS + "unknown");
    for (Resource subj : Arrays.asList(null, picasso)) {
      for (URI pred : Arrays.asList(null, paints, RDF.TYPE)) {
        for (Value obj : Arrays.asList(null, guernica)) {
          for (Resource[] ctx : Arrays.asList(new Resource[0], new Resource[] { context1 },
              new Resource[] { unknownContext }))
          {
            assertEquals(0, con.size(subj, pred, obj, false, ctx));
          }
        }
      }
    }
  }

  public void testAddData()
    throws Exception
  {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();

    assertEquals("Repository should contain 5 statements in total", 5, countAllElements());

    assertEquals("Named context should contain 3 statements", 3, countContext1Elements());

    assertEquals("Repository should have 1 context identifier", 1, countElements(con.getContextIDs()));

    assertEquals("Repository should contain 5 statements in total", 5,
        countQueryResults("select * from {S} P {O}"));

    // Check for presence of the added statements
    assertEquals("Statement (Painter, type, Class) should be in the repository", 1,
        countQueryResults("select 1 from {ex:Painter} rdf:type {rdfs:Class}"));

    assertEquals("Statement (picasso, type, Painter) should be in the repository", 1,
        countQueryResults("select 1 from {ex:picasso} rdf:type {ex:Painter}"));

    // Check for absense of non-added statements
    assertEquals("Statement (Painter, paints, Painting) should not be in the repository", 0,
        countQueryResults("select 1 from {ex:Painter} ex:paints {ex:Painting}"));

    assertEquals("Statement (picasso, creates, guernica) should not be in the repository", 0,
        countQueryResults("select 1 from {ex:picasso} ex:creates {ex:guernica}"));

    // Various other checks
    assertEquals("Repository should contain 2 statements matching (picasso, _, _)", 2,
        countQueryResults("select * from {ex:picasso} P {O}"));

    assertEquals("Repository should contain 1 statement matching (picasso, paints, _)", 1,
        countQueryResults("select * from {ex:picasso} ex:paints {O}"));

    assertEquals("Repository should contain 4 statements matching (_, type, _)", 4,
        countQueryResults("select * from {S} rdf:type {O}"));

    assertEquals("Repository should contain 2 statements matching (_, _, Class)", 2,
        countQueryResults("select * from {S} P {rdfs:Class}"));

    assertEquals("Repository should contain 0 statements matching (_, _, type)", 0,
        countQueryResults("select * from {S} P {rdf:type}"));
  }

  public void testAddWhileQuerying()
    throws Exception
  {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter);
    con.addStatement(guernica, RDF.TYPE, painting);
    con.addStatement(picasso, paints, guernica);
    con.commit();

    TupleQueryModel tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL,
        "SELECT C FROM {} rdf:type {C}", null);

    Cursor<? extends BindingSet> iter;
    iter = con.evaluate(tupleQuery, EmptyBindingSet.getInstance(), false);

    con.begin();

    BindingSet bindings;
    while ((bindings = iter.next()) != null) {
      Value c = bindings.getValue("C");
      if (c instanceof Resource) {
        con.addStatement((Resource)c, RDF.TYPE, RDFS.CLASS);
      }
    }

    con.commit();

    // Simulate auto-commit

    assertEquals(3, countElements(con.getStatements(null, RDF.TYPE, RDFS.CLASS, false)));

    tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, "SELECT P FROM {} P {}", null);
    iter = con.evaluate(tupleQuery, EmptyBindingSet.getInstance(), false);

    while ((bindings = iter.next()) != null) {
      Value p = bindings.getValue("P");
      if (p instanceof URI) {
        con.addStatement((URI)p, RDF.TYPE, RDF.PROPERTY);
      }
    }

    assertEquals(2, countElements(con.getStatements(null, RDF.TYPE, RDF.PROPERTY, false)));
  }

  public void testRemoveAndClear()
    throws Exception
  {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();

    // Test removal of statements
    con.removeStatements(painting, RDF.TYPE, RDFS.CLASS);

    assertEquals("Repository should contain 4 statements in total", 4, countAllElements());

    assertEquals("Named context should contain 3 statements", 3, countContext1Elements());

    assertEquals("Statement (Painting, type, Class) should no longer be in the repository", 0,
        countQueryResults("select 1 from {ex:Painting} rdf:type {rdfs:Class}"));

    con.removeStatements(null, null, null, context1);

    assertEquals("Repository should contain 1 statement in total", 1, countAllElements());

    assertEquals("Named context should be empty", 0, countContext1Elements());

    con.removeStatements(null, null, null);

    assertEquals("Repository should no longer contain any statements", 0, countAllElements());
  }

  public void testClose()
    throws Exception
  {
    con.close();

    try {
      con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
      fail("Operation on connection after close should result in IllegalStateException");
    }
    catch (StoreException e) {
      // do nothing, this is expected
    }
  }

  public void testContexts()
    throws Exception
  {
    con.begin();

    // Add schema data to the repository, no context
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);

    // Add stuff about picasso to context1
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);

    // Add stuff about rembrandt to context2
    con.addStatement(rembrandt, RDF.TYPE, painter, context2);
    con.addStatement(nightwatch, RDF.TYPE, painting, context2);
    con.addStatement(rembrandt, paints, nightwatch, context2);

    con.commit();

    assertEquals("context1 should contain 3 statements", 3, countContext1Elements());
    assertEquals("context2 should contain 3 statements", 3, countElements(con.getStatements(null, null,
        null, false, context2)));
    assertEquals("Repository should contain 8 statements", 8, countAllElements());
    assertEquals("statements without context should equal 2", 2, countElements(con.getStatements(null,
        null, null, false, (Resource)null)));

    assertEquals("Statements without context and statements in context 1 together should total 5", 5,
        countElements(con.getStatements(null, null, null, false, null, context1)));

    assertEquals("Statements without context and statements in context 2 together should total 5", 5,
        countElements(con.getStatements(null, null, null, false, null, context2)));

    assertEquals("Statements in context 1 and in context 2 together should total 6", 6,
        countElements(con.getStatements(null, null, null, false, context1, context2)));

    // remove two statements from context1.
    con.removeStatements(picasso, null, null, context1);

    assertEquals("context1 should contain 1 statements", 1, countContext1Elements());

    assertEquals("Repository should contain 6 statements", 6, countAllElements());

    assertEquals("Statements without context and statements in context 1 together should total 3", 3,
        countElements(con.getStatements(null, null, null, false, null, context1)));

    assertEquals("Statements without context and statements in context 2 together should total 5", 5,
        countElements(con.getStatements(null, null, null, false, context2, null)));

    assertEquals("Statements in context 1 and in context 2 together should total 4", 4,
        countElements(con.getStatements(null, null, null, false, context1, context2)));
  }

  public void testQueryBindings()
    throws Exception
  {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();

    TupleQueryModel tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL,
        "select X from {X} rdf:type {Y} rdf:type {rdfs:Class}", null);

    MapBindingSet bindings = new MapBindingSet(1);

    Cursor<? extends BindingSet> iter;
    iter = con.evaluate(tupleQuery, bindings, false);

    int resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 2, resultCount);

    bindings.addBinding("Y", painter);
    iter = con.evaluate(tupleQuery, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);

    bindings.addBinding("Z", painting);
    iter = con.evaluate(tupleQuery, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);

    bindings.removeBinding("Y");
    iter = con.evaluate(tupleQuery, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 2, resultCount);
  }

  public void testMultiThreadedAccess() {
    final CountDownLatch latch = new CountDownLatch(1);

    Runnable runnable = new Runnable() {

      SailConnection sharedCon = con;

      public void run() {
        assertTrue(sharedCon != null);

        try {
          latch.countDown();
          latch.await();
          sharedCon.addStatement(painter, RDF.TYPE, RDFS.CLASS);

          // wait a bit to allow other thread to add stuff as well.
          Thread.sleep(500L);
          Cursor<? extends Statement> result = sharedCon.getStatements(null, null, null, true);

          int numberOfStatements = 0;
          Statement st;
          while ((st = result.next()) != null) {
            numberOfStatements++;
            assertTrue(st.getSubject().equals(painter) || st.getSubject().equals(picasso));
            assertTrue(st.getPredicate().equals(RDF.TYPE));
            assertTrue(st.getObject().equals(RDFS.CLASS) || st.getObject().equals(painter));
          }
          assertTrue("we should have retrieved statements from both threads", numberOfStatements == 2);

        }
        catch (StoreException e) {
          e.printStackTrace();
          fail(e.getMessage());
        }
        catch (InterruptedException e) {
          fail(e.getMessage());
        }

        // let this thread sleep so the other thread can invoke close()
        // first.
        try {
          Thread.sleep(1000L);

          // the connection should now be closed (by the other thread),
          // invoking any further operation should cause a
          // IllegalStateException
          sharedCon.getStatements(null, null, null, true);
          fail("expected a ConnectionClosedException");
        }
        catch (ConnectionClosedException e) {
          // do nothing, this is the expected behaviour
        }
        catch (Exception e) {
          e.printStackTrace();
          fail("expected a ConnectionClosedException");
        }
      }
    }; // end anonymous class declaration

    // execute the other thread
    Thread newThread = new Thread(runnable, "B (parallel)");
    newThread.start();

    try {
      latch.countDown();
      latch.await();
      con.addStatement(picasso, RDF.TYPE, painter);
      // let this thread sleep to enable other thread to finish its business.
      Thread.sleep(1000L);
      con.close();
    }
    catch (StoreException e) {
      e.printStackTrace();
      fail(e.getMessage());
    }
    catch (InterruptedException e) {
      fail(e.getMessage());
    }
  }

  public void testStatementEquals()
    throws Exception
  {
    Statement st = vf.createStatement(picasso, RDF.TYPE, painter);
    assertFalse(st.equals(vf.createStatement(picasso, RDF.TYPE, painter, context1)));
    assertFalse(st.equals(vf.createStatement(picasso, RDF.TYPE, painter, context2)));
  }

  public void testStatementSerialization()
    throws Exception
  {
    Statement st = vf.createStatement(picasso, RDF.TYPE, painter);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(baos);
    out.writeObject(st);
    out.close();

    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream in = new ObjectInputStream(bais);
    Statement deserializedStatement = (Statement)in.readObject();
    in.close();

    assertTrue(st.equals(deserializedStatement));
  }

  public void testGetNamespaces()
    throws Exception
  {
    con.setNamespace("rdf", RDF.NAMESPACE);

    Cursor<? extends Namespace> namespaces = con.getNamespaces();
    try {
      Namespace rdf = namespaces.next();
      assertEquals("rdf", rdf.getPrefix());
      assertEquals(RDF.NAMESPACE, rdf.getName());
      assertNull(namespaces.next());
    }
    finally {
      namespaces.close();
    }
  }

  public void testGetNamespace()
    throws Exception
  {
    con.setNamespace("rdf", RDF.NAMESPACE);
    assertEquals(RDF.NAMESPACE, con.getNamespace("rdf"));
  }

  public void testClearNamespaces()
    throws Exception
  {
    con.begin();
    con.setNamespace("rdf", RDF.NAMESPACE);
    con.setNamespace("rdfs", RDFS.NAMESPACE);
    con.clearNamespaces();
    con.commit();
    assertNull(con.getNamespaces().next());
  }

  public void testRemoveNamespaces()
    throws Exception
  {
    con.begin();
    con.setNamespace("rdf", RDF.NAMESPACE);
    con.removeNamespace("rdf");
    con.commit();
    assertNull(con.getNamespace("rdf"));
  }

  public void testGetContextIDs()
    throws Exception
  {
    assertEquals(0, countElements(con.getContextIDs()));

    con.begin();

    // load data
    con.addStatement(picasso, paints, guernica, context1);
    assertEquals(1, countElements(con.getContextIDs()));
    assertEquals(context1, first(con.getContextIDs()));

    con.removeStatements(picasso, paints, guernica, context1);
    assertEquals(0, countElements(con.getContextIDs()));
    con.commit();

    assertEquals(0, countElements(con.getContextIDs()));

    con.begin();
    con.addStatement(picasso, paints, guernica, context2);
    assertEquals(1, countElements(con.getContextIDs()));
    assertEquals(context2, first(con.getContextIDs()));
    con.commit();
  }

  public void testGetMultipleContextIDs()
    throws Exception
  {
    assertEquals(0, countElements(con.getContextIDs()));

    // load data
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context2);
    con.addStatement(picasso, paints, guernica, context1);
    assertEquals(2, countElements(con.getContextIDs()));
  }

  public void testOldURI()
    throws Exception
  {
    assertEquals(0, countAllElements());
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    assertEquals(5, countAllElements());
    con.commit();
    con.removeStatements(null, null, null);
    con.addStatement(picasso, paints, guernica, context1);
    assertEquals(1, countAllElements());
  }

  public void testDualConnections()
    throws Exception
  {
    SailConnection con2 = sail.getConnection();
    try {
      assertEquals(0, countAllElements());
      con.begin();
      con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
      con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
      con.addStatement(picasso, RDF.TYPE, painter, context1);
      con.addStatement(guernica, RDF.TYPE, painting, context1);
      con.commit();

      assertEquals(4, countAllElements());

      con2.begin();
      con2.addStatement(RDF.NIL, RDF.TYPE, RDF.LIST);
      String query = "SELECT S, P, O FROM {S} P {O}";
      TupleQueryModel tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, query, null);
      assertEquals(5, countElements(con2.evaluate(tupleQuery, EmptyBindingSet.getInstance(), false)));
      Runnable clearer = new Runnable() {

        public void run() {
          try {
            con.removeStatements(null, null, null);
          }
          catch (StoreException e) {
            throw new RuntimeException(e);
          }
        }
      };
      Thread thread = new Thread(clearer);
      thread.start();
      Thread.yield();
      Thread.yield();
      con2.commit();
      thread.join();
    }
    finally {
      con2.close();
    }
  }

  public void testBNodeReuse()
    throws Exception
  {
    con.addStatement(RDF.VALUE, RDF.VALUE, RDF.VALUE);
    assertEquals(1, con.size(null, null, null, false));
    BNode b1 = vf.createBNode();
    con.addStatement(b1, RDF.VALUE, b1);
    con.removeStatements(b1, RDF.VALUE, b1);
    assertEquals(1, con.size(null, null, null, false));
    BNode b2 = vf.createBNode();
    con.addStatement(b2, RDF.VALUE, b2);
    con.addStatement(b1, RDF.VALUE, b1);
    assertEquals(3, con.size(null, null, null, false));
  }

  private <T> T first(Cursor<T> iter)
    throws Exception
  {
    try {
      return iter.next();
    }
    finally {
      iter.close();
    }
  }

  protected int countContext1Elements()
    throws Exception, StoreException
  {
    return countElements(con.getStatements(null, null, null, false, context1));
  }

  protected int countAllElements()
    throws Exception, StoreException
  {
    return countElements(con.getStatements(null, null, null, false));
  }

  private int countElements(Cursor<?> iter)
    throws Exception
  {
    int count = 0;

    try {
      while (iter.next() != null) {
        count++;
      }
    }
    finally {
      iter.close();
    }

    return count;
  }

  protected int countQueryResults(String query)
    throws Exception
  {
    TupleQueryModel tupleQuery = QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, query
        + " using namespace ex = <" + EXAMPLE_NS + ">", null);

    return countElements(con.evaluate(tupleQuery, EmptyBindingSet.getInstance(), false));
  }

  private int verifyQueryResult(Cursor<? extends BindingSet> resultIter, int expectedBindingCount)
    throws StoreException
  {
    int resultCount = 0;

    BindingSet resultBindings;
    while ((resultBindings = resultIter.next()) != null) {
      resultCount++;

      assertEquals("Wrong number of binding names for binding set", expectedBindingCount,
          resultBindings.getBindingNames().size());

      int bindingCount = 0;
      Iterator<Binding> bindingIter = resultBindings.iterator();
      while (bindingIter.hasNext()) {
        bindingIter.next();
        bindingCount++;
      }

      assertEquals("Wrong number of bindings in binding set", expectedBindingCount, bindingCount);
    }

    return resultCount;
  }
}
TOP

Related Classes of org.openrdf.sail.RDFStoreTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.