@Test
  public void testSize() throws Exception {
    assertEquals("Size of empty repository should be 0", 0, con.size());

    // 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("Size of repository should be 5", 5, con.size());
    assertEquals("Size of named context should be 3", 3, con.size(context1));

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

    assertEquals("Size of unknown context should be 0", 0, con.size(unknownContext));

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

    assertEquals(
        "Size of named context (defined as URIImpl) should be 3", 3, con.size(uriImplContext1));
  }
  @Test
  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();

    // Query 1
    ParsedTupleQuery tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL, "select X from {X} rdf:type {Y} rdf:type {rdfs:Class}", null);
    TupleExpr tupleExpr = tupleQuery.getTupleExpr();

    MapBindingSet bindings = new MapBindingSet(2);
    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;

    iter = con.evaluate(tupleExpr, null, bindings, false);
    int resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 2, resultCount);

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

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

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

    // Query 2
    tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL,
            "select X from {X} rdf:type {Y} rdf:type {rdfs:Class} where Y = Z",
            null);
    tupleExpr = tupleQuery.getTupleExpr();
    bindings.clear();

    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 0, resultCount);

    bindings.addBinding("Z", painter);
    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);
  }
 @Test
 public void testBNodeReuse() throws Exception {
   con.begin();
   con.addStatement(RDF.VALUE, RDF.VALUE, RDF.VALUE);
   assertEquals(1, con.size());
   BNode b1 = vf.createBNode();
   con.addStatement(b1, RDF.VALUE, b1);
   con.removeStatements(b1, RDF.VALUE, b1);
   assertEquals(1, con.size());
   BNode b2 = vf.createBNode();
   con.addStatement(b2, RDF.VALUE, b2);
   con.addStatement(b1, RDF.VALUE, b1);
   assertEquals(3, con.size());
   con.commit();
 }
  @Test
  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}";
      ParsedTupleQuery tupleQuery =
          QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, query, null);
      assertEquals(
          5,
          countElements(
              con2.evaluate(
                  tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false)));
      Runnable clearer =
          new Runnable() {

            public void run() {
              try {
                con.begin();
                con.clear();
                con.commit();
              } catch (SailException e) {
                throw new RuntimeException(e);
              }
            }
          };
      Thread thread = new Thread(clearer);
      thread.start();
      Thread.yield();
      Thread.yield();
      con2.commit();
      thread.join();
    } finally {
      con2.close();
    }
  }
 @Test
 public void testClose() {
   try {
     con.close();
     con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
     fail("Operation on connection after close should result in IllegalStateException");
   } catch (IllegalStateException e) {
     // do nothing, this is expected
   } catch (SailException e) {
     fail(e.getMessage());
   }
 }
  @Test
  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());
  }
  @Test
  public void testGetContextIDs() throws Exception {
    assertEquals(0, countElements(con.getContextIDs()));

    // load data
    con.begin();
    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();
  }
  @Test
  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.begin();
    con.removeStatements(painting, RDF.TYPE, RDFS.CLASS);
    con.commit();

    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.begin();
    con.removeStatements(null, null, null, context1);
    con.commit();

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

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

    con.begin();
    con.clear();
    con.commit();

    assertEquals("Repository should no longer contain any statements", 0, countAllElements());
  }
  @Test
  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();

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

    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
    iter = con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false);

    con.begin();

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

    con.commit();

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

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

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

    assertEquals(2, countElements(con.getStatements(null, RDF.TYPE, RDF.PROPERTY, false)));
  }
  protected void testValueRoundTrip(Resource subj, URI pred, Value obj) throws Exception {
    con.begin();
    con.addStatement(subj, pred, obj);
    con.commit();

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

    try {
      assertTrue(stIter.hasNext());

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

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

    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
    iter = con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false);

    try {
      assertTrue(iter.hasNext());

      BindingSet bindings = iter.next();
      assertEquals(subj, bindings.getValue("S"));
      assertEquals(pred, bindings.getValue("P"));
      assertEquals(obj, bindings.getValue("O"));
      assertTrue(!iter.hasNext());
    } finally {
      iter.close();
    }
  }
  @Test
  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.begin();
    con.clear();
    con.commit();

    con.begin();
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();
    assertEquals(1, countAllElements());
  }
  /** Not compatible with our MVCC semantics. */
  private void __testMultiThreadedAccess() {

    Runnable runnable =
        new Runnable() {

          SailConnection sharedCon = con;

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

            try {
              while (sharedCon.isActive()) {
                Thread.sleep(10);
              }
              sharedCon.begin();
              sharedCon.addStatement(painter, RDF.TYPE, RDFS.CLASS);
              sharedCon.commit();

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

              assertTrue(result.hasNext());
              int numberOfStatements = 0;
              while (result.hasNext()) {
                numberOfStatements++;
                Statement st = result.next();
                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 (SailException 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("should have caused an IllegalStateException");
            } catch (InterruptedException e) {
              fail(e.getMessage());
            } catch (SailException e) {
              e.printStackTrace();
              fail(e.getMessage());
            } catch (IllegalStateException e) {
              // do nothing, this is the expected behaviour
            }
          }
        }; // end anonymous class declaration

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

    try {
      while (con.isActive()) {
        Thread.sleep(10);
      }
      con.begin();
      con.addStatement(picasso, RDF.TYPE, painter);
      con.commit();
      // let this thread sleep to enable other thread to finish its business.
      Thread.sleep(1000L);
      con.close();
    } catch (SailException e) {
      e.printStackTrace();
      fail(e.getMessage());
    } catch (InterruptedException e) {
      fail(e.getMessage());
    }
  }
  @Test
  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.begin();
    con.removeStatements(picasso, null, null, context1);
    con.commit();

    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)));
  }
  @Test
  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}"));
  }