You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by cl...@apache.org on 2015/05/10 15:04:14 UTC

[45/50] [abbrv] jena git commit: Added initial contract tests added testing_framework

Added initial contract tests
added testing_framework


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/b293ee8a
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/b293ee8a
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/b293ee8a

Branch: refs/heads/add-contract-tests
Commit: b293ee8a928fc8a4f84810f41f3235e5a547c23b
Parents: eb7760d
Author: Claude Warren <cl...@apache.org>
Authored: Sun Apr 26 20:41:15 2015 +0100
Committer: Claude Warren <cl...@apache.org>
Committed: Sat May 9 15:27:19 2015 +0100

----------------------------------------------------------------------
 jena-core/pom.xml                               |   14 +
 .../apache/jena/graph/GraphContractTest.java    | 1720 ++++++++++++++++++
 .../graph/GraphEventManagerContractTest.java    |  514 ++++++
 .../jena/graph/GraphMakerContractTest.java      |  314 ++++
 .../graph/GraphWithPerformContractTest.java     |   86 +
 .../jena/graph/RecordingGraphListener.java      |   92 +
 .../graph/TransactionHandlerContractTest.java   |  149 ++
 .../apache/jena/graph/compose/DeltaTest.java    |  110 ++
 .../jena/graph/compose/DifferenceTest.java      |   97 +
 .../jena/graph/compose/IntersectionTest.java    |  109 ++
 .../apache/jena/graph/compose/UnionTest.java    |  185 ++
 .../jena/graph/impl/TestCollectionGraph.java    |   42 +-
 .../jena/graph/impl/TestWrappedGraph.java       |   63 +
 .../graph/impl/TripleStoreContractTest.java     |  176 ++
 .../apache/jena/mem/BunchMapContractTest.java   |  163 ++
 .../java/org/apache/jena/mem/GraphMemTest.java  |  115 ++
 .../apache/jena/mem/GraphTripleStoreTest.java   |   51 +
 .../jena/mem/TripleBunchContractTest.java       |  205 +++
 .../AbstractGraphProducer.java                  |  104 ++
 .../AbstractInfModelProducer.java               |   91 +
 .../AbstractModelProducer.java                  |   88 +
 .../AbstractRecordingListener.java              |  190 ++
 .../testing_framework/ContractTemplate.java     |   17 +
 .../GraphEventManagerProducerInterface.java     |   35 +
 .../jena/testing_framework/GraphHelper.java     |  508 ++++++
 .../GraphProducerInterface.java                 |   41 +
 .../testing_framework/IContainerProducer.java   |   19 +
 .../testing_framework/IIteratorProducer.java    |   42 +
 .../jena/testing_framework/INodeProducer.java   |   39 +
 .../testing_framework/IResourceProducer.java    |   10 +
 .../testing_framework/IStatementProducer.java   |   45 +
 .../testing_framework/ITripleStoreProducer.java |   35 +
 .../jena/testing_framework/ModelHelper.java     |  409 +++++
 .../jena/testing_framework/NodeCreateUtils.java |  177 ++
 .../NodeProducerInterface.java                  |   32 +
 .../jena/testing_framework/TestFileData.java    |  380 ++++
 .../jena/testing_framework/TestUtils.java       |  320 ++++
 .../testing_framework/manifest/Manifest.java    |  228 +++
 .../manifest/ManifestException.java             |   48 +
 .../manifest/ManifestFile.java                  |   38 +
 .../manifest/ManifestItem.java                  |   53 +
 .../manifest/ManifestItemHandler.java           |   35 +
 .../manifest/ManifestSuite.java                 |  143 ++
 .../manifest/ManifestTest.java                  |   13 +
 .../manifest/ManifestTestRunner.java            |   75 +
 .../jena/testing_framework/package-info.java    |  109 ++
 .../testing_framework/tuples/TupleItem.java     |   84 +
 .../jena/testing_framework/tuples/TupleSet.java |  274 +++
 48 files changed, 7880 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/pom.xml
----------------------------------------------------------------------
diff --git a/jena-core/pom.xml b/jena-core/pom.xml
index 6cb0722..a29e559 100644
--- a/jena-core/pom.xml
+++ b/jena-core/pom.xml
@@ -75,6 +75,20 @@
       <scope>test</scope>
     </dependency>
 
+	<dependency>
+		<groupId>org.xenei</groupId>
+		<artifactId>junit-contracts</artifactId>
+		<version>0.0.5</version>
+		<scope>test</scope>
+	</dependency>
+	
+	<dependency>
+		<groupId>org.mockito</groupId>
+		<artifactId>mockito-all</artifactId>
+		<version>1.9.5</version>
+		<scope>test</scope>
+	</dependency>
+
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/graph/GraphContractTest.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/graph/GraphContractTest.java b/jena-core/src/test/java/org/apache/jena/graph/GraphContractTest.java
new file mode 100644
index 0000000..cf79da5
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/graph/GraphContractTest.java
@@ -0,0 +1,1720 @@
+/*
+ * 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.jena.graph;
+
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.junit.After;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xenei.junit.contract.Contract;
+import org.xenei.junit.contract.ContractTest;
+
+import static org.junit.Assert.*;
+
+import org.apache.jena.graph.Capabilities;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphStatisticsHandler;
+import org.apache.jena.graph.GraphUtil;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.LiteralLabelFactory;
+import org.apache.jena.mem.TrackingTripleIterator;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdf.model.impl.ReifierStd;
+import org.apache.jena.shared.ClosedException;
+import org.apache.jena.shared.DeleteDeniedException;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.testing_framework.AbstractGraphProducer;
+import org.apache.jena.testing_framework.ContractTemplate;
+import org.apache.jena.testing_framework.NodeCreateUtils;
+import org.apache.jena.util.iterator.ClosableIterator;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.Map1;
+
+import static org.apache.jena.testing_framework.GraphHelper.*;
+
+/**
+ * AbstractTestGraph provides a bunch of basic tests for something that purports
+ * to be a Graph. The abstract method getGraph must be overridden in subclasses
+ * to deliver a Graph of interest.
+ */
+@Contract(Graph.class)
+public class GraphContractTest<T extends Graph> extends
+		ContractTemplate<AbstractGraphProducer<T>> {
+
+	private static final Logger LOG = LoggerFactory
+			.getLogger(GraphContractTest.class);
+
+	protected RecordingGraphListener GL = new RecordingGraphListener();
+
+	@Contract.Inject
+	public final void setGraphContractTestProducer(
+			AbstractGraphProducer<T> graphProducer) {
+		super.setProducer(graphProducer);
+	}
+
+	@After
+	public final void afterGraphContractTest() {
+		getProducer().cleanUp();
+		GL.clear();
+	}
+
+	@ContractTest
+	public void testAdd_Triple() {
+		Graph graph = getProducer().newInstance();
+		graph.getEventManager().register(GL);
+		txnBegin(graph);
+		graph.add(triple("S P O"));
+		txnCommit(graph);
+		GL.assertHasStart("add", graph, triple("S P O"));
+		assertTrue("Graph should contain <S P O>",
+				graph.contains(triple("S P O")));
+	}
+
+	/**
+	 * Inference graphs can not be truly empty.
+	 * 
+	 * @param g
+	 * @param b
+	 */
+	private void assertEmpty(Graph g, Graph b) {
+		if (b.isEmpty()) {
+			assertTrue("Graph should be empty", g.isEmpty());
+		} else {
+			assertEquals("Graph should be in base state", b.find(Triple.ANY)
+					.toList(), g.find(Triple.ANY).toList());
+		}
+	}
+
+	/**
+	 * Inference graphs can not be truly empty
+	 * 
+	 * @param g
+	 * @param b
+	 */
+	private void assertNotEmpty(Graph g, Graph b) {
+		if (b.isEmpty()) {
+			assertFalse("Graph not should be empty", g.isEmpty());
+		} else {
+			assertNotEquals("Graph should not be in base state",
+					b.find(Triple.ANY).toList(), g.find(Triple.ANY).toList());
+		}
+	}
+
+	/**
+	 * Test that clear works, in the presence of inferencing graphs that mean
+	 * emptyness isn't available. This is why we go round the houses and test
+	 * that expected ~= initialContent + addedStuff - removed - initialContent.
+	 */
+	@ContractTest
+	public void testClear() {
+		Graph graph = getProducer().newInstance();
+		Graph base = copy(graph);
+
+		graph.getEventManager().register(GL);
+		txnBegin(graph);
+		graph.clear();
+		txnCommit(graph);
+		assertEmpty(graph, base);
+		GL.assertHasStart("someEvent", graph, GraphEvents.removeAll);
+		GL.clear();
+
+		// test after adding
+		graph = graphWith(getProducer().newInstance(),
+				"S P O; S e:ff 27; _1 P P3; S4 P4 'en'");
+		graph.getEventManager().register(GL);
+		txnBegin(graph);
+		graph.clear();
+		txnCommit(graph);
+		assertEmpty(graph, base);
+		if (GL.contains("delete")) {
+			// deletes are listed -- ensure all deletes are listed
+			GL.assertContains("delete", graph, triple("S P O"));
+			GL.assertContains("delete", graph, triple("S e:ff 27"));
+			GL.assertContains("delete", graph, triple("_1 P P3"));
+			GL.assertContains("delete", graph, triple("S4 P4 'en'"));
+		}
+		GL.assertHasEnd("someEvent", graph, GraphEvents.removeAll);
+		GL.clear();
+
+	}
+
+	@ContractTest
+	public void testClose() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S P2 O2; S3 P P3");
+		graph.getEventManager().register(GL);
+		assertFalse("Graph was constructed closed", graph.isClosed());
+
+		graph.close();
+		assertTrue("Graph should be closed", graph.isClosed());
+
+		// exception may be thrown on begin or on execution.
+		try {
+			txnBegin(graph);
+			try {
+				graph.add(triple("S P O"));
+				fail("added when closed");
+			} catch (Exception expected) {
+				GL.assertEmpty();
+				// expected
+			} finally {
+				txnRollback(graph);
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.delete(triple("x R y"));
+				fail("delete when closed");
+			} catch (ClosedException c) {
+				// Expected
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+
+			try {
+				graph.add(triple("x R y"));
+				fail("add when closed");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.contains(triple("x R y"));
+				fail("contains[triple] when closed");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.contains(Node.ANY, Node.ANY, Node.ANY);
+				fail("contains[SPO] when closed");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.find(triple("x R y"));
+				fail("find [triple] when closed");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.find(Node.ANY, Node.ANY, Node.ANY);
+				fail("find[SPO] when closed");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+
+		try {
+			txnBegin(graph);
+			try {
+				graph.size();
+				fail("size when closed (" + this.getClass() + ")");
+			} catch (ClosedException c) { /* as required */
+			} finally {
+				txnRollback(graph);
+				GL.assertEmpty();
+			}
+		} catch (Exception expected) {
+			GL.assertEmpty();
+			// expected
+		}
+	}
+
+	@ContractTest
+	public void testContains_Node_Node_Node() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+
+		assertTrue(graph.contains(node("S"), node("P"), node("O")));
+		assertFalse(graph.contains(node("S"), node("P"), node("O2")));
+		assertFalse(graph.contains(node("S"), node("P2"), node("O")));
+		assertFalse(graph.contains(node("S2"), node("P"), node("O")));
+		assertTrue(graph.contains(Node.ANY, Node.ANY, Node.ANY));
+		assertTrue(graph.contains(Node.ANY, Node.ANY, node("O")));
+		assertTrue(graph.contains(Node.ANY, node("P"), Node.ANY));
+		assertTrue(graph.contains(node("S"), Node.ANY, Node.ANY));
+	}
+
+	@ContractTest
+	public void testContains_Node_Node_Node_RepeatedSubjectDoesNotConceal() {
+
+		Graph g = graphWith(getProducer().newInstance(), "s P o; s Q r");
+		Node s = node("s");
+		Node P = node("P");
+		Node o = node("o");
+		Node Q = node("Q");
+		Node r = node("r");
+		Node any = node("??");
+		assertTrue(g.contains(s, P, o));
+		assertTrue(g.contains(s, Q, r));
+		assertTrue(g.contains(any, P, o));
+		assertTrue(g.contains(any, Q, r));
+		assertTrue(g.contains(any, P, any));
+		assertTrue(g.contains(any, Q, any));
+	}
+
+	@ContractTest
+	public void testContains_Node_Node_Node_ByValue() {
+		Node x = node("x");
+		Node P = node("P");
+		if (getProducer().newInstance().getCapabilities()
+				.handlesLiteralTyping()) {
+			Graph g1 = graphWith(getProducer().newInstance(),
+					"x P '1'xsd:integer");
+			assertTrue(
+					String.format(
+							"literal type equality failed, does %s really implement literal typing",
+							g1.getClass()), g1.contains(x, P,
+							node("'01'xsd:int")));
+			//
+			Graph g2 = graphWith(getProducer().newInstance(), "x P '1'xsd:int");
+			assertTrue("Literal equality with '1'xsd:integer failed",
+					g2.contains(x, P, node("'1'xsd:integer")));
+			//
+			Graph g3 = graphWith(getProducer().newInstance(),
+					"x P '123'xsd:string");
+			assertTrue("Literal equality with '123' failed",
+					g3.contains(x, P, node("'123'")));
+		}
+	}
+
+	@ContractTest
+	public void testContains_Node_Node_Node_Concrete() {
+		Node s = node("s");
+		Node P = node("P");
+		Node o = node("o");
+
+		Node _x = node("_x");
+		Node _R = node("_R");
+		Node _y = node("_y");
+
+		Node x = node("x");
+		Node S = node("S");
+
+		Graph g = graphWith(getProducer().newInstance(),
+				"s P o; _x _R _y; x S 0");
+		assertTrue("Graph should have contained s P o", g.contains(s, P, o));
+		assertTrue("Graph should have contained _x _R _y",
+				g.contains(_x, _R, _y));
+		assertTrue("Graph should have contained x S 'O'",
+				g.contains(x, S, node("0")));
+		/* */
+		assertFalse(g.contains(s, P, node("Oh")));
+		assertFalse(g.contains(S, P, node("O")));
+		assertFalse(g.contains(s, node("p"), o));
+		assertFalse(g.contains(_x, node("_r"), _y));
+		assertFalse(g.contains(x, S, node("1")));
+	}
+
+	@ContractTest
+	public void testContains_Node_Node_Node_Fluid() {
+		Node x = node("x");
+		Node R = node("R");
+		Node P = node("P");
+		Node y = node("y");
+		Node a = node("a");
+		Node b = node("b");
+		Graph g = graphWith(getProducer().newInstance(), "x R y; a P b");
+		assertTrue(g.contains(Node.ANY, R, y));
+		assertTrue(g.contains(x, Node.ANY, y));
+		assertTrue(g.contains(x, R, Node.ANY));
+		assertTrue(g.contains(Node.ANY, P, b));
+		assertTrue(g.contains(a, Node.ANY, b));
+		assertTrue(g.contains(a, P, Node.ANY));
+		assertTrue(g.contains(Node.ANY, R, y));
+		/* */
+		assertFalse(g.contains(Node.ANY, R, b));
+		assertFalse(g.contains(a, Node.ANY, y));
+		assertFalse(g.contains(x, P, Node.ANY));
+		assertFalse(g.contains(Node.ANY, R, x));
+		assertFalse(g.contains(x, Node.ANY, R));
+		assertFalse(g.contains(a, node("S"), Node.ANY));
+	}
+
+	@ContractTest
+	public void testContains_Triple() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+
+		assertTrue(graph.contains(triple("S P O")));
+		assertFalse(graph.contains(triple("S P O2")));
+		assertFalse(graph.contains(triple("S P2 O")));
+		assertFalse(graph.contains(triple("S2 P O")));
+		assertTrue(graph.contains(Triple.ANY));
+		assertTrue(graph.contains(new Triple(Node.ANY, Node.ANY, node("O"))));
+		assertTrue(graph.contains(new Triple(Node.ANY, node("P"), Node.ANY)));
+		assertTrue(graph.contains(new Triple(node("S"), Node.ANY, Node.ANY)));
+
+	}
+
+	@ContractTest
+	public void testContains_Triple_RepeatedSubjectDoesNotConceal() {
+
+		Graph g = graphWith(getProducer().newInstance(), "s P o; s Q r");
+		assertTrue(g.contains(triple("s P o")));
+		assertTrue(g.contains(triple("s Q r")));
+		assertTrue(g.contains(triple("?? P o")));
+		assertTrue(g.contains(triple("?? Q r")));
+		assertTrue(g.contains(triple("?? P ??")));
+		assertTrue(g.contains(triple("?? Q ??")));
+	}
+
+	@ContractTest
+	public void testContains_Triple_ByValue() {
+
+		if (getProducer().newInstance().getCapabilities()
+				.handlesLiteralTyping()) {
+			Graph g1 = graphWith(getProducer().newInstance(),
+					"x P '1'xsd:integer");
+			assertTrue(
+					String.format(
+							"did not find x P '01'xsd:int, does %s really implement literal typing",
+							g1.getClass()),
+					g1.contains(triple("x P '01'xsd:int")));
+			//
+			Graph g2 = graphWith(getProducer().newInstance(), "x P '1'xsd:int");
+			assertTrue("did not find x P '1'xsd:integer",
+					g2.contains(triple("x P '1'xsd:integer")));
+			//
+			Graph g3 = graphWith(getProducer().newInstance(),
+					"x P '123'xsd:string");
+			assertTrue("did not find x P '123'xsd:string",
+					g3.contains(triple("x P '123'")));
+		}
+	}
+
+	@ContractTest
+	public void testContains_Triple_Concrete() {
+		Graph g = graphWith(getProducer().newInstance(),
+				"s P o; _x _R _y; x S 0");
+		assertTrue(g.contains(triple("s P o")));
+		assertTrue(g.contains(triple("_x _R _y")));
+		assertTrue(g.contains(triple("x S 0")));
+		/* */
+		assertFalse(g.contains(triple("s P Oh")));
+		assertFalse(g.contains(triple("S P O")));
+		assertFalse(g.contains(triple("s p o")));
+		assertFalse(g.contains(triple("_x _r _y")));
+		assertFalse(g.contains(triple("x S 1")));
+	}
+
+	@ContractTest
+	public void testContains_Triple_Fluid() {
+		Graph g = graphWith(getProducer().newInstance(), "x R y; a P b");
+		assertTrue(g.contains(triple("?? R y")));
+		assertTrue(g.contains(triple("x ?? y")));
+		assertTrue(g.contains(triple("x R ??")));
+		assertTrue(g.contains(triple("?? P b")));
+		assertTrue(g.contains(triple("a ?? b")));
+		assertTrue(g.contains(triple("a P ??")));
+		assertTrue(g.contains(triple("?? R y")));
+		/* */
+		assertFalse(g.contains(triple("?? R b")));
+		assertFalse(g.contains(triple("a ?? y")));
+		assertFalse(g.contains(triple("x P ??")));
+		assertFalse(g.contains(triple("?? R x")));
+		assertFalse(g.contains(triple("x ?? R")));
+		assertFalse(g.contains(triple("a S ??")));
+	}
+
+	/**
+	 * Inference graphs can not be empty
+	 */
+	@ContractTest
+	public void testDelete_Triple() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+		Graph base = getProducer().newInstance();
+		graph.getEventManager().register(GL);
+		txnBegin(graph);
+		graph.delete(triple("S P O"));
+		txnCommit(graph);
+		GL.assertContains("delete", graph, triple("S P O"));
+		assertFalse("Graph should not contain <S P O>",
+				graph.contains(triple("S P O")));
+		assertNotEmpty(graph, base);
+		assertTrue("Graph should contain <S2 P2 O2>",
+				graph.contains(triple("S2 P2 O2")));
+		assertTrue("Graph should contain <S3 P3 O3>",
+				graph.contains(triple("S3 P3 O3")));
+
+		// should not modify anything on wildcard delete
+		GL.clear();
+		try {
+			txnBegin(graph);
+			graph.delete(new Triple(node("S2"), node("P2"), Node.ANY));
+			txnCommit(graph);
+		} catch (DeleteDeniedException expected) {
+			txnRollback(graph);
+		}
+		assertTrue("Graph should contain <S2 P2 O2>",
+				graph.contains(triple("S2 P2 O2")));
+		assertTrue("Graph should contain <S3 P3 O3>",
+				graph.contains(triple("S3 P3 O3")));
+		GL.assertHas("delete", graph, new Triple(node("S2"), node("P2"),
+				Node.ANY));
+	}
+
+	@ContractTest
+	public void testDelete_Triple_FromNothing() {
+		Graph g = getProducer().newInstance();
+		g.getEventManager().register(GL);
+		txnBegin(g);
+		g.delete(triple("quint rdf:subject S"));
+		txnCommit(g);
+		GL.assertContains("delete", g, triple("quint rdf:subject S"));
+	}
+
+	@ContractTest
+	public void testDependsOn() {
+		Graph g = getProducer().newInstance();
+		Graph[] depGraphs = getProducer().getDependsOn(g);
+		if (depGraphs != null) {
+			for (Graph dg : depGraphs) {
+				assertTrue(
+						String.format("Graph %s should depend upon %s", g, dg),
+						g.dependsOn(dg));
+			}
+		}
+		depGraphs = getProducer().getNotDependsOn(g);
+		if (depGraphs != null) {
+			for (Graph dg : depGraphs) {
+				assertFalse(String.format("Graph %s should not depend upon %s",
+						g, dg), g.dependsOn(dg));
+			}
+		}
+	}
+
+	@ContractTest
+	public void testFind_Node_Node_Node() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+		List<Triple> s = graph.find(Node.ANY, Node.ANY, Node.ANY).toList();
+		assertEquals(3, s.size());
+		List<Triple> expected = Arrays.asList(new Triple[] { triple("S P O"),
+				triple("S2 P2 O2"), triple("S3 P3 O3") });
+		assertTrue("Missing some values",
+				expected.containsAll(s) && s.containsAll(expected));
+
+		s = graph.find(node("S"), Node.ANY, Node.ANY).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(Node.ANY, node("P"), Node.ANY).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(Node.ANY, Node.ANY, node("O")).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(node("S2"), node("P2"), node("O2")).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S2 P2 O2")));
+
+		s = graph.find(node("S2"), node("P3"), node("O2")).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(Node.ANY, node("P3"), node("O2")).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(node("S3"), Node.ANY, node("O2")).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(node("S3"), node("P2"), Node.ANY).toList();
+		assertEquals(0, s.size());
+
+	}
+
+	@ContractTest
+	public void testFind_Node_Node_Node_ByFluidTriple() {
+		Node x = node("x");
+		Node y = node("y");
+		Node z = node("z");
+		Graph g = graphWith(getProducer().newInstance(), "x y z ");
+		Set<Triple> expect = tripleSet("x y z");
+		assertEquals(expect, g.find(Node.ANY, y, z).toSet());
+		assertEquals(expect, g.find(x, Node.ANY, z).toSet());
+		assertEquals(expect, g.find(x, y, Node.ANY).toSet());
+	}
+
+	@ContractTest
+	public void testFind_Node_Node_Node_ProgrammaticValues() {
+		Graph g = getProducer().newInstance();
+		if (g.getCapabilities().handlesLiteralTyping()) {
+			Node ab = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Byte((byte) 42)));
+			Node as = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Short((short) 42)));
+			Node ai = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Integer(42)));
+			Node al = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Long(42)));
+
+			Node SB = NodeCreateUtils.create("SB");
+			Node SS = NodeCreateUtils.create("SS");
+			Node SI = NodeCreateUtils.create("SI");
+			Node SL = NodeCreateUtils.create("SL");
+			Node P = NodeCreateUtils.create("P");
+
+			txnBegin(g);
+			try {
+				g.add(Triple.create(SB, P, ab));
+				g.add(Triple.create(SS, P, as));
+				g.add(Triple.create(SI, P, ai));
+				g.add(Triple.create(SL, P, al));
+			} catch (Exception e) {
+				txnRollback(g);
+				fail(e.getMessage());
+			}
+			txnCommit(g);
+			assertEquals(
+					String.format(
+							"Should have found 4 elements, does %s really implement literal typing",
+							g.getClass()),
+					4,
+					iteratorToSet(
+							g.find(Node.ANY, P, NodeCreateUtils.create("42")))
+							.size());
+		}
+	}
+
+	@ContractTest
+	public void testFind_Node_Node_Node_MatchLanguagedLiteralCaseInsensitive() {
+		Graph m = graphWith(getProducer().newInstance(), "a p 'chat'en");
+		if (m.getCapabilities().handlesLiteralTyping()) {
+			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
+			assertDiffer(chaten, chatEN);
+			assertTrue(chaten.sameValueAs(chatEN));
+			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
+			assertEquals(1, m.find(Node.ANY, Node.ANY, chaten).toList().size());
+			assertEquals(1, m.find(Node.ANY, Node.ANY, chatEN).toList().size());
+		}
+	}
+
+	@ContractTest
+	public void testFind_Node_Node_Node_NoMatchAgainstUnlanguagesLiteral() {
+		Graph m = graphWith(getProducer().newInstance(),
+				"a p 'chat'en; a p 'chat'");
+		if (m.getCapabilities().handlesLiteralTyping()) {
+			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
+			assertDiffer(chaten, chatEN);
+			assertTrue(chaten.sameValueAs(chatEN));
+			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
+			assertEquals(1, m.find(Node.ANY, Node.ANY, chaten).toList().size());
+			assertEquals(1, m.find(Node.ANY, Node.ANY, chatEN).toList().size());
+		}
+	}
+
+	@ContractTest
+	public void testFind_Triple() {
+		Graph graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+		List<Triple> s = graph.find(Triple.ANY).toList();
+		assertEquals(3, s.size());
+		List<Triple> expected = Arrays.asList(new Triple[] { triple("S P O"),
+				triple("S2 P2 O2"), triple("S3 P3 O3") });
+		assertTrue("Missing some values", expected.containsAll(s));
+
+		s = graph.find(new Triple(node("S"), Node.ANY, Node.ANY)).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(new Triple(Node.ANY, node("P"), Node.ANY)).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(new Triple(Node.ANY, Node.ANY, node("O"))).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S P O")));
+
+		s = graph.find(new Triple(node("S2"), node("P2"), node("O2"))).toList();
+		assertEquals(1, s.size());
+		assertTrue("Missing some values", s.contains(triple("S2 P2 O2")));
+
+		s = graph.find(new Triple(node("S2"), node("P3"), node("O2"))).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(new Triple(Node.ANY, node("P3"), node("O2"))).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(new Triple(node("S3"), Node.ANY, node("O2"))).toList();
+		assertEquals(0, s.size());
+
+		s = graph.find(new Triple(node("S3"), node("P2"), Node.ANY)).toList();
+		assertEquals(0, s.size());
+
+	}
+
+	@ContractTest
+	public void testFind_Triple_ByFluidTriple() {
+		Graph g = graphWith(getProducer().newInstance(), "x y z ");
+		Set<Triple> expect = tripleSet("x y z");
+		assertEquals(expect, g.find(triple("?? y z")).toSet());
+		assertEquals(expect, g.find(triple("x ?? z")).toSet());
+		assertEquals(expect, g.find(triple("x y ??")).toSet());
+	}
+
+	@ContractTest
+	public void testFind_Triple_ProgrammaticValues() {
+		Graph g = getProducer().newInstance();
+		if (g.getCapabilities().handlesLiteralTyping()) {
+			Node ab = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Byte((byte) 42)));
+			Node as = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Short((short) 42)));
+			Node ai = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Integer(42)));
+			Node al = NodeFactory.createLiteral(LiteralLabelFactory
+					.createTypedLiteral(new Long(42)));
+
+			Node SB = NodeCreateUtils.create("SB");
+			Node SS = NodeCreateUtils.create("SS");
+			Node SI = NodeCreateUtils.create("SI");
+			Node SL = NodeCreateUtils.create("SL");
+			Node P = NodeCreateUtils.create("P");
+
+			txnBegin(g);
+			try {
+				g.add(Triple.create(SB, P, ab));
+				g.add(Triple.create(SS, P, as));
+				g.add(Triple.create(SI, P, ai));
+				g.add(Triple.create(SL, P, al));
+			} catch (Exception e) {
+				txnRollback(g);
+				fail(e.getMessage());
+			}
+			txnCommit(g);
+			assertEquals(
+					String.format(
+							"Should have found 4 elements, does %s really implement literal typing",
+							g.getClass()),
+					4,
+					iteratorToSet(
+							g.find(new Triple(Node.ANY, P, NodeCreateUtils
+									.create("42")))).size());
+		}
+	}
+
+	@ContractTest
+	public void testFind_Triple_MatchLanguagedLiteralCaseInsensitive() {
+		Graph m = graphWith(getProducer().newInstance(), "a p 'chat'en");
+		//if (m.getCapabilities().handlesLiteralTyping()) {
+			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
+			assertDiffer(chaten, chatEN);
+			assertTrue(chaten.sameValueAs(chatEN));
+			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
+			assertEquals(1, m.find(new Triple(Node.ANY, Node.ANY, chaten))
+					.toList().size());
+			assertEquals(1, m.find(new Triple(Node.ANY, Node.ANY, chatEN))
+					.toList().size());
+		//}
+	}
+
+	@ContractTest
+	public void testFind_Triple_NoMatchAgainstUnlanguagesLiteral() {
+		Graph m = graphWith(getProducer().newInstance(),
+				"a p 'chat'en; a p 'chat'");
+		//if (m.getCapabilities().handlesLiteralTyping()) {
+			Node chaten = node("'chat'en"), chatEN = node("'chat'EN");
+			assertDiffer(chaten, chatEN);
+			assertTrue(chaten.sameValueAs(chatEN));
+			assertEquals(chaten.getIndexingValue(), chatEN.getIndexingValue());
+			assertEquals(1, m.find(new Triple(Node.ANY, Node.ANY, chaten))
+					.toList().size());
+			assertEquals(1, m.find(new Triple(Node.ANY, Node.ANY, chatEN))
+					.toList().size());
+		//}
+	}
+
+	@ContractTest
+	public void testGetCapabilities() {
+		Graph g = getProducer().newInstance();
+		Capabilities c = g.getCapabilities();
+		assertNotNull("Capabilities are not returned", c);
+		try {
+			c.sizeAccurate();
+		} catch (Exception e) {
+			fail("sizeAccurate() threw Exception: " + e.toString());
+		}
+		try {
+			c.addAllowed();
+		} catch (Exception e) {
+			fail("addAllowed() threw Exception: " + e.toString());
+		}
+		try {
+			c.addAllowed(true);
+		} catch (Exception e) {
+			fail("addAllowed( boolean ) threw Exception: " + e.toString());
+		}
+		try {
+			c.deleteAllowed();
+		} catch (Exception e) {
+			fail("deleteAllowed() threw Exception: " + e.toString());
+		}
+		try {
+			c.deleteAllowed(true);
+		} catch (Exception e) {
+			fail("deleteAllowed( boolean ) threw Exception: " + e.toString());
+		}
+		try {
+			c.canBeEmpty();
+		} catch (Exception e) {
+			fail("canBeEmpty() threw Exception: " + e.toString());
+		}
+	}
+
+	@ContractTest
+	public void testGetEventManager() {
+		assertNotNull("Must return an EventManager", getProducer()
+				.newInstance().getEventManager());
+	}
+
+	@ContractTest
+	public void testGetPrefixMapping() {
+		Graph g = getProducer().newInstance();
+		PrefixMapping pm = g.getPrefixMapping();
+		assertNotNull("Must return prefix mapping", pm);
+		assertSame("getPrefixMapping must always return the same object", pm,
+				g.getPrefixMapping());
+
+	
+		pm.setNsPrefix("pfx1", "http://example.com/");
+		pm.setNsPrefix("pfx2", "scheme:rope/string#");
+
+		// assert same after adding to other mapl
+		assertSame("getPrefixMapping must always return the same object", pm,
+				g.getPrefixMapping());
+
+	}
+
+	@ContractTest
+	public void testGetStatisticsHandler() {
+		Graph g = getProducer().newInstance();
+		GraphStatisticsHandler sh = g.getStatisticsHandler();
+		if (sh != null) {
+			assertSame(
+					"getStatisticsHandler must always return the same object",
+					sh, g.getStatisticsHandler());
+		}
+	}
+
+	@ContractTest
+	public void testGetTransactionHandler() {
+		Graph g = getProducer().newInstance();
+		assertNotNull("Must return a Transaction handler",
+				g.getTransactionHandler());
+	}
+
+	@ContractTest
+	public void testIsClosed() {
+		Graph g = getProducer().newInstance();
+		assertFalse("Graph created in closed state", g.isClosed());
+		g.close();
+		assertTrue("Graph does not report closed state after close called",
+				g.isClosed());
+	}
+
+	@ContractTest
+	public void testIsEmpty() {
+		Graph g = getProducer().newInstance();
+		if (!g.isEmpty()) {
+			LOG.warn(String.format(
+					"Graph type %s can not be empty (Empty test skipped)",
+					g.getClass()));
+		} else {
+			graphAddTxn(g, "S P O");
+			assertFalse("Graph reports empty after add", g.isEmpty());
+			txnBegin(g);
+			g.add(NodeCreateUtils.createTriple("Foo B C"));
+			g.delete(NodeCreateUtils.createTriple("S P O"));
+			txnCommit(g);
+			assertFalse("Should not report empty", g.isEmpty());
+			txnBegin(g);
+			g.delete(NodeCreateUtils.createTriple("Foo B C"));
+			txnCommit(g);
+			assertTrue("Should report empty after all entries deleted",
+					g.isEmpty());
+		}
+	}
+
+	@ContractTest
+	public void testIsIsomorphicWith_Graph() {
+		Graph graph = getProducer().newInstance();
+		Graph g2 = memGraph();
+		assertTrue("Empty graphs should be isomorphic",
+				graph.isIsomorphicWith(g2));
+
+		graph = graphWith(getProducer().newInstance(),
+				"S P O; S2 P2 O2; S3 P3 O3");
+		g2 = graphWith("S3 P3 O3; S2 P2 O2; S P O");
+		assertTrue("Should be isomorphic", graph.isIsomorphicWith(g2));
+		txnBegin(graph);
+		graph.add(triple("_1, P4 S4"));
+		txnCommit(graph);
+
+		txnBegin(g2);
+		g2.add(triple("_2, P4 S4"));
+		txnCommit(g2);
+		assertTrue("Should be isomorphic after adding anonymous nodes",
+				graph.isIsomorphicWith(g2));
+
+		txnBegin(graph);
+		graph.add(triple("_1, P3 S4"));
+		txnCommit(graph);
+
+		txnBegin(g2);
+		g2.add(triple("_2, P4 S4"));
+		txnCommit(g2);
+		assertFalse("Should not be isomorphic", graph.isIsomorphicWith(g2));
+	}
+
+	private Graph copy(Graph g) {
+		Graph result = getProducer().newInstance();
+		txnBegin(result);
+		GraphUtil.addInto(result, g);
+		txnCommit(result);
+		return result;
+	}
+
+	private Graph remove(Graph toUpdate, Graph toRemove) {
+		txnBegin(toUpdate);
+		GraphUtil.deleteFrom(toUpdate, toRemove);
+		txnCommit(toUpdate);
+		return toUpdate;
+	}
+
+	/**
+	 * Test that remove(s, p, o) works, in the presence of inferencing graphs
+	 * that mean emptyness isn't available. This is why we go round the houses
+	 * and test that expected ~= initialContent + addedStuff - removed -
+	 * initialContent.
+	 */
+	@ContractTest
+	public void testRemove_Node_Node_Node() {
+		for (int i = 0; i < cases.length; i += 1)
+			for (int j = 0; j < 3; j += 1) {
+				Graph content = getProducer().newInstance();
+
+				Graph baseContent = copy(content);
+				graphAddTxn(content, cases[i][0]);
+				Triple remove = triple(cases[i][1]);
+				Graph expected = graphWith(cases[i][2]);
+				Triple[] removed = tripleArray(cases[i][3]);
+				content.getEventManager().register(GL);
+				GL.clear();
+				txnBegin(content);
+				content.remove(remove.getSubject(), remove.getPredicate(),
+						remove.getObject());
+				txnCommit(content);
+
+				// check for optional delete notifications
+				if (GL.contains("delete")) {
+					// if it contains any it must contain all.
+					for (Triple t : removed) {
+						GL.assertContains("delete", content, t);
+					}
+				}
+				GL.assertHasEnd(
+						"someEvent",
+						content,
+						GraphEvents.remove(remove.getSubject(),
+								remove.getPredicate(), remove.getObject()));
+
+				content.getEventManager().unregister(GL);
+				Graph finalContent = remove(copy(content), baseContent);
+				assertIsomorphic(cases[i][1], expected, finalContent);
+			}
+	}
+
+	@ContractTest
+	public void testRemove_ByIterator() {
+		testRemove("?? ?? ??", "?? ?? ??");
+		testRemove("S ?? ??", "S ?? ??");
+		testRemove("S ?? ??", "?? P ??");
+		testRemove("S ?? ??", "?? ?? O");
+		testRemove("?? P ??", "S ?? ??");
+		testRemove("?? P ??", "?? P ??");
+		testRemove("?? P ??", "?? ?? O");
+		testRemove("?? ?? O", "S ?? ??");
+		testRemove("?? ?? O", "?? P ??");
+		testRemove("?? ?? O", "?? ?? O");
+	}
+
+	private void testRemove(String findRemove, String findCheck) {
+		Graph g = graphWith(getProducer().newInstance(), "S P O");
+		ExtendedIterator<Triple> it = g.find(NodeCreateUtils
+				.createTriple(findRemove));
+		try {
+			it.next();
+			it.remove();
+			it.close();
+			assertEquals("remove with " + findRemove + ":", 0, g.size());
+			assertFalse(g.contains(NodeCreateUtils.createTriple(findCheck)));
+		} catch (UnsupportedOperationException e) {
+			it.close();
+			assertFalse(
+					"delete failed but capailities indicates it should work", g
+							.getCapabilities().iteratorRemoveAllowed());
+		}
+	}
+
+	/**
+	 * This test case was generated by Ian and was caused by GraphMem not
+	 * keeping up with changes to the find interface.
+	 */
+	@ContractTest
+	public void testFindAndContains() {
+		Graph g = getProducer().newInstance();
+		Node r = NodeCreateUtils.create("r"), s = NodeCreateUtils.create("s"), p = NodeCreateUtils
+				.create("P");
+		txnBegin(g);
+		try {
+			g.add(Triple.create(r, p, s));
+			txnCommit(g);
+			assertTrue(g.contains(r, p, Node.ANY));
+			assertEquals(1, g.find(r, p, Node.ANY).toList().size());
+		} catch (Exception e) {
+			txnRollback(g);
+			fail(e.getMessage());
+		}
+	}
+
+	/**
+	 * Check that contains respects by-value semantics.
+	 */
+
+	@ContractTest
+	public void testAGraph() {
+		String title = this.getClass().getName();
+		Graph g = getProducer().newInstance();
+		int baseSize = g.size();
+		graphAddTxn(g, "x R y; p S q; a T b");
+		/* */
+		assertContainsAll(title + ": simple graph", g, "x R y; p S q; a T b");
+		assertEquals(title + ": size", baseSize + 3, g.size());
+
+		graphAddTxn(g,
+				"spindizzies lift cities; Diracs communicate instantaneously");
+		assertEquals(title + ": size after adding", baseSize + 5, g.size());
+		txnBegin(g);
+		g.delete(triple("x R y"));
+		g.delete(triple("a T b"));
+		txnCommit(g);
+		assertEquals(title + ": size after deleting", baseSize + 3, g.size());
+		assertContainsAll(title + ": modified simple graph", g,
+				"p S q; spindizzies lift cities; Diracs communicate instantaneously");
+		assertOmitsAll(title + ": modified simple graph", g, "x R y; a T b");
+		/* */
+		ClosableIterator<Triple> it = g.find(Node.ANY, node("lift"), Node.ANY);
+		assertTrue(title + ": finds some triple(s)", it.hasNext());
+		assertEquals(title + ": finds a 'lift' triple",
+				triple("spindizzies lift cities"), it.next());
+		assertFalse(title + ": finds exactly one triple", it.hasNext());
+		it.close();
+	}
+
+	// public void testStuff()
+	// {
+	// // testAGraph( "StoreMem", new GraphMem() );
+	// // testAGraph( "StoreMemBySubject", new GraphMem() );
+	// // String [] empty = new String [] {};
+	// // Graph g = graphWith( "x R y; p S q; a T b" );
+	// // /* */
+	// // assertContainsAll( "simple graph", g, "x R y; p S q; a T b" );
+	// // graphAdd( g,
+	// "spindizzies lift cities; Diracs communicate instantaneously" );
+	// // g.delete( triple( "x R y" ) );
+	// // g.delete( triple( "a T b" ) );
+	// // assertContainsAll( "modified simple graph", g,
+	// "p S q; spindizzies lift cities; Diracs communicate instantaneously" );
+	// // assertOmitsAll( "modified simple graph", g, "x R y; a T b" );
+	// }
+
+	// /**
+	// Test that Graphs have transaction support methods, and that if they fail
+	// on some g they fail because they do not support the operation.
+	// */
+	// @ContractTest
+	// public void testHasTransactions()
+	// {
+	// Graph g = getProducer().newInstance();
+	// TransactionHandler th = g.getTransactionHandler();
+	// th.transactionsSupported();
+	// try { th.begin(); } catch (UnsupportedOperationException x) {}
+	// try { th.abort(); } catch (UnsupportedOperationException x) {}
+	// try { th.begin(); th.commit(); } catch (UnsupportedOperationException x)
+	// {}
+	// /* */
+	// Command cmd = new Command()
+	// { @Override
+	// public Object execute() { return null; } };
+	// try { th.executeInTransaction( cmd ); }
+	// catch (UnsupportedOperationException x) {}
+	// }
+	//
+	// @ContractTest
+	// public void testExecuteInTransactionCatchesThrowable()
+	// {Graph g = getProducer().newInstance();
+	// TransactionHandler th = g.getTransactionHandler();
+	// if (th.transactionsSupported())
+	// {
+	// Command cmd = new Command()
+	// { @Override
+	// public Object execute() throws Error { throw new Error(); } };
+	// try { th.executeInTransaction( cmd ); }
+	// catch (JenaException x) {}
+	// }
+	// }
+
+	@ContractTest
+	public void testAddWithReificationPreamble() {
+		Graph g = getProducer().newInstance();
+		txnBegin(g);
+		xSPO(g);
+		txnCommit(g);
+		assertFalse(g.isEmpty());
+	}
+
+	protected void xSPOyXYZ(Graph g) {
+		xSPO(g);
+		ReifierStd.reifyAs(g, NodeCreateUtils.create("y"),
+				NodeCreateUtils.createTriple("X Y Z"));
+	}
+
+	protected void aABC(Graph g) {
+		ReifierStd.reifyAs(g, NodeCreateUtils.create("a"),
+				NodeCreateUtils.createTriple("Foo B C"));
+	}
+
+	protected void xSPO(Graph g) {
+		ReifierStd.reifyAs(g, NodeCreateUtils.create("x"),
+				NodeCreateUtils.createTriple("S P O"));
+	}
+
+	@ContractTest
+	public void failingTestDoubleRemoveAll() {
+		final Graph g = getProducer().newInstance();
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				graphAddTxn(g, "c S d; e:ff GGG hhhh; _i J 27; Ell Em 'en'");
+				Iterator<Triple> it = new TrackingTripleIterator(
+						g.find(Triple.ANY)) {
+					@Override
+					public void remove() {
+						super.remove(); // removes current
+						g.delete(current); // no-op.
+					}
+				};
+				while (it.hasNext()) {
+					it.next();
+					it.remove();
+				}
+				assertTrue(g.isEmpty());
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+	/**
+	 * Test cases for RemoveSPO(); each entry is a triple (add, remove, result).
+	 * <ul>
+	 * <li>add - the triples to add to the graph to start with
+	 * <li>remove - the pattern to use in the removal
+	 * <li>result - the triples that should remain in the graph
+	 * </ul>
+	 */
+	protected static String[][] cases = { { "x R y", "x R y", "", "x R y" },
+			{ "x R y; a P b", "x R y", "a P b", "x R y" },
+			{ "x R y; a P b", "?? R y", "a P b", "x R y" },
+			{ "x R y; a P b", "x R ??", "a P b", "x R y" },
+			{ "x R y; a P b", "x ?? y", "a P b", "x R y" },
+			{ "x R y; a P b", "?? ?? ??", "", "x R y; a P b" },
+			{ "x R y; a P b; c P d", "?? P ??", "x R y", "a P b; c P d" },
+			{ "x R y; a P b; x S y", "x ?? ??", "a P b", "x R y; x S y" }, };
+
+	/**
+	 * testIsomorphism from file data
+	 * 
+	 * @throws FileNotFoundException
+	 */
+	@ContractTest
+	public void testIsomorphismFile() throws URISyntaxException,
+			MalformedURLException {
+		testIsomorphismXMLFile(1, true);
+		testIsomorphismXMLFile(2, true);
+		testIsomorphismXMLFile(3, true);
+		testIsomorphismXMLFile(4, true);
+		testIsomorphismXMLFile(5, false);
+		testIsomorphismXMLFile(6, false);
+		testIsomorphismNTripleFile(7, true);
+		testIsomorphismNTripleFile(8, false);
+
+	}
+
+	private void testIsomorphismNTripleFile(int i, boolean result)
+			throws URISyntaxException, MalformedURLException {
+		testIsomorphismFile(i, "N-TRIPLE", "nt", result);
+	}
+
+	private void testIsomorphismXMLFile(int i, boolean result)
+			throws URISyntaxException, MalformedURLException {
+		testIsomorphismFile(i, "RDF/XML", "rdf", result);
+
+	}
+
+	private InputStream getInputStream(int n, int n2, String suffix)
+			throws URISyntaxException, MalformedURLException {
+		String urlStr = String.format("regression/testModelEquals/%s-%s.%s", n,
+				n2, suffix);
+		return GraphContractTest.class.getClassLoader().getResourceAsStream(
+				urlStr);
+	}
+
+	private void testIsomorphismFile(int n, String lang, String suffix,
+			boolean result) throws URISyntaxException, MalformedURLException {
+
+		Graph g1 = getProducer().newInstance();
+		Graph g2 = getProducer().newInstance();
+		Model m1 = ModelFactory.createModelForGraph(g1);
+		Model m2 = ModelFactory.createModelForGraph(g2);
+
+		m1.read(getInputStream(n, 1, suffix), "http://www.example.org/", lang);
+
+		m2.read(getInputStream(n, 2, suffix), "http://www.example.org/", lang);
+
+		boolean rslt = g1.isIsomorphicWith(g2) == result;
+		if (!rslt) {
+			System.out.println("g1:");
+			m1.write(System.out, "N-TRIPLE");
+			System.out.println("g2:");
+			m2.write(System.out, "N-TRIPLE");
+		}
+		assertTrue("Isomorphism test failed", rslt);
+	}
+
+	protected Graph getClosed() {
+		Graph result = getProducer().newInstance();
+		result.close();
+		return result;
+	}
+
+	// @ContractTest
+	// public void testTransactionCommit()
+	// {
+	// Graph g = getProducer().newInstance();
+	// if (g.getTransactionHandler().transactionsSupported())
+	// {
+	// Graph initial = graphWithTxn( "initial hasValue 42; also hasURI hello" );
+	// Graph extra = graphWithTxn( "extra hasValue 17; also hasURI world" );
+	// //File foo = FileUtils.tempFileName( "fileGraph", ".nt" );
+	//
+	// //Graph g = new FileGraph( foo, true, true );
+	//
+	// GraphUtil.addInto( g, initial );
+	// g.getTransactionHandler().begin();
+	// GraphUtil.addInto( g, extra );
+	// g.getTransactionHandler().commit();
+	// Graph union = graphWithTxn( "" );
+	// GraphUtil.addInto(union, initial );
+	// GraphUtil.addInto(union, extra );
+	// assertIsomorphic( union, g );
+	// //Model inFile = ModelFactory.createDefaultModel();
+	// //inFile.read( "file:///" + foo, "N-TRIPLES" );
+	// //assertIsomorphic( union, inFile.getGraph() );
+	// }
+	// }
+	//
+	// @ContractTest
+	// public void testTransactionAbort()
+	// {
+	// Graph g = getProducer().newInstance();
+	// if (g.getTransactionHandler().transactionsSupported())
+	// {
+	// Graph initial = graphWithTxn( "initial hasValue 42; also hasURI hello" );
+	// Graph extra = graphWithTxn( "extra hasValue 17; also hasURI world" );
+	// File foo = FileUtils.tempFileName( "fileGraph", ".n3" );
+	// //Graph g = new FileGraph( foo, true, true );
+	// GraphUtil.addInto( g, initial );
+	// g.getTransactionHandler().begin();
+	// GraphUtil.addInto( g, extra );
+	// g.getTransactionHandler().abort();
+	// assertIsomorphic( initial, g );
+	// }
+	// }
+	//
+	// @ContractTest
+	// public void testTransactionCommitThenAbort()
+	// {
+	// Graph g = getProducer().newInstance();
+	// if (g.getTransactionHandler().transactionsSupported())
+	// {
+	// Graph initial = graphWithTxn( "Foo pings B; B pings C" );
+	// Graph extra = graphWithTxn( "C pingedBy B; fileGraph rdf:type Graph" );
+	// //Graph g = getProducer().newInstance();
+	// //File foo = FileUtils.tempFileName( "fileGraph", ".nt" );
+	// //Graph g = new FileGraph( foo, true, true );
+	// g.getTransactionHandler().begin();
+	// GraphUtil.addInto( g, initial );
+	// g.getTransactionHandler().commit();
+	// g.getTransactionHandler().begin();
+	// GraphUtil.addInto( g, extra );
+	// g.getTransactionHandler().abort();
+	// assertIsomorphic( initial, g );
+	// //Model inFile = ModelFactory.createDefaultModel();
+	// // inFile.read( "file:///" + foo, "N-TRIPLES" );
+	// //assertIsomorphic( initial, inFile.getGraph() );
+	// }
+	// }
+
+	/**
+	 * This test exposed that the update-existing-graph functionality was broken
+	 * if the target graph already contained any statements with a subject S
+	 * appearing as subject in the source graph - no further Spo statements were
+	 * added.
+	 */
+	@ContractTest
+	public void testPartialUpdate() {
+		Graph source = graphWith(getProducer().newInstance(), "a R b; b S e");
+		Graph dest = graphWith(getProducer().newInstance(), "b R d");
+		GraphExtract e = new GraphExtract(TripleBoundary.stopNowhere);
+		e.extractInto(dest, node("a"), source);
+		assertIsomorphic(
+				graphWith(getProducer().newInstance(), "a R b; b S e; b R d"),
+				dest);
+	}
+
+	/**
+	 * Ensure that triples removed by calling .remove() on the iterator returned
+	 * by a find() will generate deletion notifications.
+	 */
+	@ContractTest
+	public void testIterator_Remove() {
+		Graph graph = graphWith(getProducer().newInstance(), "a R b; b S e");
+		if (graph.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				graph.getEventManager().register(GL);
+				txnBegin(graph);
+
+				Triple toRemove = triple("a R b");
+				ExtendedIterator<Triple> rtr = graph.find(toRemove);
+				assertTrue("ensure a(t least) one triple", rtr.hasNext());
+				rtr.next();
+				rtr.remove();
+				rtr.close();
+				GL.assertHas("delete", graph, toRemove);
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+
+		}
+	}
+
+	@ContractTest
+	public void testTransactionHandler_Commit() {
+		Graph g = getProducer().newInstance();
+		if (g.getTransactionHandler().transactionsSupported()) {
+			Graph initial = graphWith(getProducer().newInstance(),
+					"initial hasValue 42; also hasURI hello");
+			Graph extra = graphWith(getProducer().newInstance(),
+					"extra hasValue 17; also hasURI world");
+
+			GraphUtil.addInto(g, initial);
+			g.getTransactionHandler().begin();
+			GraphUtil.addInto(g, extra);
+			g.getTransactionHandler().commit();
+			Graph union = memGraph();
+			GraphUtil.addInto(union, initial);
+			GraphUtil.addInto(union, extra);
+			assertIsomorphic(union, g);
+			// Model inFiIProducer<TransactionHandler>le =
+			// ModelFactory.createDefaultModel();
+			// inFile.read( "file:///" + foo, "N-TRIPLES" );
+			// assertIsomorphic( union, inFile.getGraph() );
+		}
+	}
+
+	@ContractTest
+	public void testTransactionHandler_Abort() {
+		Graph g = getProducer().newInstance();
+		if (g.getTransactionHandler().transactionsSupported()) {
+			Graph initial = graphWith(getProducer().newInstance(),
+					"initial hasValue 42; also hasURI hello");
+			Graph extra = graphWith(getProducer().newInstance(),
+					"extra hasValue 17; also hasURI world");
+			GraphUtil.addInto(g, initial);
+			g.getTransactionHandler().begin();
+			GraphUtil.addInto(g, extra);
+			g.getTransactionHandler().abort();
+			assertIsomorphic(initial, g);
+		}
+	}
+
+	@ContractTest
+	public void testTransactionHandler_CommitThenAbort() {
+		Graph g = getProducer().newInstance();
+		if (g.getTransactionHandler().transactionsSupported()) {
+			Graph initial = graphWith(getProducer().newInstance(),
+					"Foo pings B; B pings C");
+			Graph extra = graphWith(getProducer().newInstance(),
+					"C pingedBy B; fileGraph rdf:type Graph");
+			g.getTransactionHandler().begin();
+			GraphUtil.addInto(g, initial);
+			g.getTransactionHandler().commit();
+			g.getTransactionHandler().begin();
+			GraphUtil.addInto(g, extra);
+			g.getTransactionHandler().abort();
+			assertIsomorphic(initial, g);
+			// Model inFile = ModelFactory.createDefaultModel();
+			// inFile.read( "file:///" + foo, "N-TRIPLES" );
+			// assertIsomorphic( initial, inFile.getGraph() );
+		}
+	}
+
+	//
+	// Test that literal typing works when supported
+	//
+
+	// used to find the object set from the returned set for literal testing
+	private static final Map1<Triple, Node> getObject = new Map1<Triple, Node>() {
+		@Override
+		public Node map1(Triple o) {
+			return o.getObject();
+		}
+	};
+
+	private void testLiteralTypingBasedFind(final String data, final int size,
+			final String search, final String results, boolean reqLitType) {
+
+		Graph g = getProducer().newInstance();
+
+		if (!reqLitType || g.getCapabilities().handlesLiteralTyping()) {
+			graphWith(g, data);
+
+			Node literal = NodeCreateUtils.create(search);
+			//
+			assertEquals("graph has wrong size", size, g.size());
+			Set<Node> got = g.find(Node.ANY, Node.ANY, literal)
+					.mapWith(getObject).toSet();
+			assertEquals(nodeSet(results), got);
+		}
+	}
+
+	@ContractTest
+	public void testLiteralTypingBasedFind() {
+		testLiteralTypingBasedFind("a P 'simple'", 1, "'simple'", "'simple'",
+				false);
+		testLiteralTypingBasedFind("a P 'simple'xsd:string", 1, "'simple'",
+				"'simple'xsd:string", true);
+		testLiteralTypingBasedFind("a P 'simple'", 1, "'simple'xsd:string",
+				"'simple'", true);
+		testLiteralTypingBasedFind("a P 'simple'xsd:string", 1,
+				"'simple'xsd:string", "'simple'xsd:string", false);
+//		testLiteralTypingBasedFind("a P 'simple'; a P 'simple'xsd:string", 2,
+//				"'simple'", "'simple' 'simple'xsd:string", true);
+//		testLiteralTypingBasedFind("a P 'simple'; a P 'simple'xsd:string", 2,
+//				"'simple'xsd:string", "'simple' 'simple'xsd:string", true);
+		testLiteralTypingBasedFind("a P 'simple'; a P 'simple'xsd:string", 1,
+				"'simple'", "'simple'", true);
+		testLiteralTypingBasedFind("a P 'simple'; a P 'simple'xsd:string", 1,
+				"'simple'xsd:string", "'simple'xsd:string", true);
+		testLiteralTypingBasedFind("a P 1", 1, "1", "1", false);
+		testLiteralTypingBasedFind("a P '1'xsd:float", 1, "'1'xsd:float",
+				"'1'xsd:float", false);
+		testLiteralTypingBasedFind("a P '1'xsd:double", 1, "'1'xsd:double",
+				"'1'xsd:double", false);
+		testLiteralTypingBasedFind("a P '1'xsd:float", 1, "'1'xsd:float",
+				"'1'xsd:float", false);
+		testLiteralTypingBasedFind("a P '1.1'xsd:float", 1, "'1'xsd:float", "",
+				false);
+		testLiteralTypingBasedFind("a P '1'xsd:double", 1, "'1'xsd:int", "",
+				false);
+		testLiteralTypingBasedFind("a P 'abc'rdf:XMLLiteral", 1, "'abc'", "",
+				false);
+		testLiteralTypingBasedFind("a P 'abc'", 1, "'abc'rdf:XMLLiteral", "",
+				false);
+		//
+		// floats & doubles are not compatible
+		//
+		testLiteralTypingBasedFind("a P '1'xsd:float", 1, "'1'xsd:double", "",
+				false);
+		testLiteralTypingBasedFind("a P '1'xsd:double", 1, "'1'xsd:float", "",
+				false);
+		testLiteralTypingBasedFind("a P 1", 1, "'1'", "", false);
+		testLiteralTypingBasedFind("a P 1", 1, "'1'xsd:integer",
+				"'1'xsd:integer", false);
+		testLiteralTypingBasedFind("a P 1", 1, "'1'", "", false);
+		testLiteralTypingBasedFind("a P '1'xsd:short", 1, "'1'xsd:integer",
+				"'1'xsd:short", true);
+		testLiteralTypingBasedFind("a P '1'xsd:int", 1, "'1'xsd:integer",
+				"'1'xsd:int", true);
+	}
+
+	@ContractTest
+	public void testQuadRemove() {
+		Graph g = getProducer().newInstance();
+		assertEquals(0, g.size());
+		Triple s = triple("x rdf:subject s");
+		Triple p = triple("x rdf:predicate p");
+		Triple o = triple("x rdf:object o");
+		Triple t = triple("x rdf:type rdf:Statement");
+		txnBegin(g);
+		g.add(s);
+		g.add(p);
+		g.add(o);
+		g.add(t);
+		txnCommit(g);
+		assertEquals(4, g.size());
+		txnBegin(g);
+		g.delete(s);
+		g.delete(p);
+		g.delete(o);
+		g.delete(t);
+		txnCommit(g);
+		assertEquals(0, g.size());
+	}
+
+	@ContractTest
+	public void testSizeAfterRemove() {
+		Graph g = graphWith(getProducer().newInstance(), "x p y");
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				ExtendedIterator<Triple> it = g.find(triple("x ?? ??"));
+				it.removeNext();
+				assertEquals(0, g.size());
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+	@ContractTest
+	public void testSingletonStatisticsWithSingleTriple() {
+
+		Graph g = graphWith(getProducer().newInstance(), "a P b");
+		GraphStatisticsHandler h = g.getStatisticsHandler();
+		if (h != null) {
+			assertEquals(1L, h.getStatistic(node("a"), Node.ANY, Node.ANY));
+			assertEquals(0L, h.getStatistic(node("x"), Node.ANY, Node.ANY));
+			//
+			assertEquals(1L, h.getStatistic(Node.ANY, node("P"), Node.ANY));
+			assertEquals(0L, h.getStatistic(Node.ANY, node("Q"), Node.ANY));
+			//
+			assertEquals(1L, h.getStatistic(Node.ANY, Node.ANY, node("b")));
+			assertEquals(0L, h.getStatistic(Node.ANY, Node.ANY, node("y")));
+		}
+	}
+
+	@ContractTest
+	public void testSingletonStatisticsWithSeveralTriples() {
+
+		Graph g = graphWith(getProducer().newInstance(),
+				"a P b; a P c; a Q b; x S y");
+		GraphStatisticsHandler h = g.getStatisticsHandler();
+		if (h != null) {
+			assertEquals(3L, h.getStatistic(node("a"), Node.ANY, Node.ANY));
+			assertEquals(1L, h.getStatistic(node("x"), Node.ANY, Node.ANY));
+			assertEquals(0L, h.getStatistic(node("y"), Node.ANY, Node.ANY));
+			//
+			assertEquals(2L, h.getStatistic(Node.ANY, node("P"), Node.ANY));
+			assertEquals(1L, h.getStatistic(Node.ANY, node("Q"), Node.ANY));
+			assertEquals(0L, h.getStatistic(Node.ANY, node("R"), Node.ANY));
+			//
+			assertEquals(2L, h.getStatistic(Node.ANY, Node.ANY, node("b")));
+			assertEquals(1L, h.getStatistic(Node.ANY, Node.ANY, node("c")));
+			assertEquals(0L, h.getStatistic(Node.ANY, Node.ANY, node("d")));
+		}
+	}
+
+	@ContractTest
+	public void testDoubletonStatisticsWithTriples() {
+
+		Graph g = graphWith(getProducer().newInstance(),
+				"a P b; a P c; a Q b; x S y");
+		GraphStatisticsHandler h = g.getStatisticsHandler();
+		if (h != null) {
+			assertEquals(-1L, h.getStatistic(node("a"), node("P"), Node.ANY));
+			assertEquals(-1L, h.getStatistic(Node.ANY, node("P"), node("b")));
+			assertEquals(-1L, h.getStatistic(node("a"), Node.ANY, node("b")));
+			//
+			assertEquals(0L, h.getStatistic(node("no"), node("P"), Node.ANY));
+		}
+	}
+
+	@ContractTest
+	public void testStatisticsWithOnlyVariables() {
+		testStatsWithAllVariables("");
+		testStatsWithAllVariables("a P b");
+		testStatsWithAllVariables("a P b; a P c");
+		testStatsWithAllVariables("a P b; a P c; a Q b; x S y");
+	}
+
+	private void testStatsWithAllVariables(String triples) {
+		Graph g = graphWith(getProducer().newInstance(), triples);
+		GraphStatisticsHandler h = g.getStatisticsHandler();
+		if (h != null) {
+			assertEquals(g.size(), h.getStatistic(Node.ANY, Node.ANY, Node.ANY));
+		}
+	}
+
+	@ContractTest
+	public void testStatsWithConcreteTriple() {
+		testStatsWithConcreteTriple(0, "x P y", "");
+	}
+
+	private void testStatsWithConcreteTriple(int expect, String triple,
+			String graph) {
+		Graph g = graphWith(getProducer().newInstance(), graph);
+		GraphStatisticsHandler h = g.getStatisticsHandler();
+		if (h != null) {
+			Triple t = triple(triple);
+			assertEquals(
+					expect,
+					h.getStatistic(t.getSubject(), t.getPredicate(),
+							t.getObject()));
+		}
+	}
+
+	@ContractTest
+	public void testBrokenIndexes() {
+		Graph g = graphWith(getProducer().newInstance(), "x R y; x S z");
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				ExtendedIterator<Triple> it = g.find(Node.ANY, Node.ANY,
+						Node.ANY);
+				it.removeNext();
+				it.removeNext();
+				assertFalse(g.find(node("x"), Node.ANY, Node.ANY).hasNext());
+				assertFalse(g.find(Node.ANY, node("R"), Node.ANY).hasNext());
+				assertFalse(g.find(Node.ANY, Node.ANY, node("y")).hasNext());
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+	@ContractTest
+	public void testBrokenSubject() {
+		Graph g = graphWith(getProducer().newInstance(), "x brokenSubject y");
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				ExtendedIterator<Triple> it = g.find(node("x"), Node.ANY,
+						Node.ANY);
+				it.removeNext();
+				assertFalse(g.find(Node.ANY, Node.ANY, Node.ANY).hasNext());
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+	@ContractTest
+	public void testBrokenPredicate() {
+		Graph g = graphWith(getProducer().newInstance(), "x brokenPredicate y");
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				ExtendedIterator<Triple> it = g.find(Node.ANY,
+						node("brokenPredicate"), Node.ANY);
+				it.removeNext();
+				assertFalse(g.find(Node.ANY, Node.ANY, Node.ANY).hasNext());
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+	@ContractTest
+	public void testBrokenObject() {
+		Graph g = graphWith(getProducer().newInstance(), "x brokenObject y");
+		if (g.getCapabilities().iteratorRemoveAllowed()) {
+			try {
+				ExtendedIterator<Triple> it = g.find(Node.ANY, Node.ANY,
+						node("y"));
+				it.removeNext();
+				assertFalse(g.find(Node.ANY, Node.ANY, Node.ANY).hasNext());
+
+			} catch (UnsupportedOperationException e) {
+				fail("Error attempting to remove nodes " + e.getMessage());
+			}
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/graph/GraphEventManagerContractTest.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/graph/GraphEventManagerContractTest.java b/jena-core/src/test/java/org/apache/jena/graph/GraphEventManagerContractTest.java
new file mode 100644
index 0000000..426002d
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/graph/GraphEventManagerContractTest.java
@@ -0,0 +1,514 @@
+/*
+ * 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.jena.graph;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.Mockito;
+import org.xenei.junit.contract.Contract;
+import org.xenei.junit.contract.ContractTest;
+
+import static org.junit.Assert.*;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphEventManager;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.Factory;
+import org.apache.jena.testing_framework.ContractTemplate;
+import org.apache.jena.testing_framework.GraphHelper;
+import org.xenei.junit.contract.IProducer;
+import org.apache.jena.testing_framework.NodeCreateUtils;
+
+import static org.apache.jena.testing_framework.GraphHelper.*;
+
+/**
+ * An abstract test that tests EventManager implementations to ensure they
+ * comply with the EventManager contract.
+ * 
+ * Subclasses of this class must implement the getEventManagerProducer() method
+ * to create a new instance of the EventManager for testing.
+ */
+
+@Contract(GraphEventManager.class)
+public class GraphEventManagerContractTest<T extends GraphEventManager> extends
+		ContractTemplate<IProducer<T>> {
+
+	protected static final Triple[] tripleArray = tripleArray("S P O; Foo R B; X Q Y");
+
+	// the graph that is used as the source of the events.
+	private Graph mockGraph;
+	// The event manager we are working with.
+	private GraphEventManager gem;
+
+	public GraphEventManagerContractTest() {
+//		setProducer(new IProducer<T>() {
+//
+//			@SuppressWarnings("unchecked")
+//			@Override
+//			public T newInstance() {
+//				return (T) Factory.createGraphMem().getEventManager();
+//			}
+//
+//			@Override
+//			public void cleanUp() {
+//			}
+//		});
+
+	}
+
+	@Contract.Inject
+	public final void setGraphEventManagerContractTestProducer(
+			IProducer<T> producer) {
+		super.setProducer(producer);
+	}
+
+	@After
+	public final void afterGraphEventManagerContractTest() {
+		getProducer().cleanUp();
+	}
+
+	@Before
+	public final void beforeGraphEventManagerContractTest() {
+		mockGraph = Mockito.mock(Graph.class);
+		gem = getProducer().newInstance();
+		L = new RecordingGraphListener();
+	}
+
+	/**
+	 * Test that when a listener is registered the same EventManager is
+	 * returned.
+	 */
+	@ContractTest
+	public void testEventRegister() {
+		assertSame(gem, gem.register(new RecordingGraphListener()));
+	}
+
+	/**
+	 * Test that we can safely unregister a listener that isn't registered.
+	 */
+	@ContractTest
+	public void testEventUnregister() {
+		assertSame(gem, gem.unregister(L));
+	}
+
+	/**
+	 * Handy triple for test purposes.
+	 */
+	protected Triple SPO = NodeCreateUtils.createTriple("S P O");
+
+	/**
+	 * The RecordingListener that reports all the events sent from the
+	 * EventManager.
+	 */
+	protected RecordingGraphListener L;
+
+	/**
+	 * Test that adding a triple is reported.
+	 */
+	@ContractTest
+	public void testAddTriple() {
+		gem.register(L);
+		gem.notifyAddTriple(mockGraph, SPO);
+		L.assertHas(new Object[] { "add", mockGraph, SPO });
+	}
+
+	/**
+	 * Test that deleting a triple is reported.
+	 */
+	@ContractTest
+	public void testDeleteTriple() {
+		gem.register(L);
+		gem.notifyDeleteTriple(mockGraph, SPO);
+		L.assertHas(new Object[] { "delete", mockGraph, SPO });
+	}
+
+	/**
+	 * Test that when 2 listeners are added both receive events.
+	 */
+	@ContractTest
+	public void testTwoListeners() {
+		RecordingGraphListener L1 = new RecordingGraphListener();
+		RecordingGraphListener L2 = new RecordingGraphListener();
+		gem.register(L1).register(L2);
+		gem.notifyAddTriple(mockGraph, SPO);
+		L2.assertHas(new Object[] { "add", mockGraph, SPO });
+		L1.assertHas(new Object[] { "add", mockGraph, SPO });
+	}
+
+	/**
+	 * Test that unregistering a listener after registering it results in it not
+	 * receiving messages.
+	 */
+	@ContractTest
+	public void testUnregisterWorks() {
+		gem.register(L).unregister(L);
+		gem.notifyAddTriple(mockGraph, SPO);
+		L.assertHas(new Object[] {});
+	}
+
+	/**
+	 * Test that registering a listener twice results in the listener receiving
+	 * the events twice.
+	 */
+	@ContractTest
+	public void testRegisterTwice() {
+		gem.register(L).register(L);
+		gem.notifyAddTriple(mockGraph, SPO);
+		L.assertHas(new Object[] { "add", mockGraph, SPO, "add", mockGraph, SPO });
+	}
+
+	/**
+	 * Test that registering a listener twice and unregistering it once will
+	 * result in the listener receiving each event one time.
+	 */
+	@ContractTest
+	public void testUnregisterOnce() {
+		gem.register(L).register(L).unregister(L);
+		gem.notifyDeleteTriple(mockGraph, SPO);
+		L.assertHas(new Object[] { "delete", mockGraph, SPO });
+	}
+
+	/**
+	 * Test that adding an array is reported as adding an array.
+	 */
+	@ContractTest
+	public void testAddArray() {
+		gem.register(L);
+		gem.notifyAddArray(mockGraph, tripleArray);
+		L.assertHas(new Object[] { "add[]", mockGraph, tripleArray });
+	}
+
+	/**
+	 * Test that adding a list is reported as adding a list
+	 */
+	@ContractTest
+	public void testAddList() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyAddList(mockGraph, elems);
+		L.assertHas(new Object[] { "addList", mockGraph, elems });
+	}
+
+	/**
+	 * Test that deleting an array is reported as deleting an array.
+	 */
+	@ContractTest
+	public void testDeleteArray() {
+		gem.register(L);
+		gem.notifyDeleteArray(mockGraph, tripleArray);
+		L.assertHas(new Object[] { "delete[]", mockGraph, tripleArray });
+	}
+
+	/**
+	 * Test that deleting a list is reported as deleting a list.
+	 */
+	@ContractTest
+	public void testDeleteList() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyDeleteList(mockGraph, elems);
+		L.assertHas(new Object[] { "deleteList", mockGraph, elems });
+	}
+
+	/**
+	 * Test that adding a list as an iterator is reported as an add iterator.
+	 */
+	@ContractTest
+	public void testAddListAsIterator() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyAddIterator(mockGraph, elems);
+		L.assertHas(new Object[] { "addIterator", mockGraph, elems });
+	}
+
+	/**
+	 * Test that adding an iterator is reported as adding an iterator.
+	 */
+	@ContractTest
+	public void testAddIterator() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyAddIterator(mockGraph, elems.iterator());
+		L.assertHas(new Object[] { "addIterator", mockGraph, elems });
+	}
+
+	/**
+	 * Test that deleting an iterator is reported as a deleting an iterator.
+	 */
+	@ContractTest
+	public void testDeleteIterator() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyDeleteIterator(mockGraph, elems.iterator());
+		L.assertHas(new Object[] { "deleteIterator", mockGraph, elems });
+	}
+
+	/**
+	 * Test that deleting a list as an iterator is reported as deleting an
+	 * iterator.
+	 */
+	@ContractTest
+	public void testDeleteListAsIterator() {
+		gem.register(L);
+		List<Triple> elems = Arrays.asList(tripleArray);
+		gem.notifyDeleteIterator(mockGraph, elems);
+		L.assertHas(new Object[] { "deleteIterator", mockGraph, elems });
+	}
+
+	/**
+	 * Test that adding a graph is reported as adding a graph.
+	 */
+	@ContractTest
+	public void testAddGraph() {
+		gem.register(L);
+		Graph other = Mockito.mock(Graph.class);
+		gem.notifyAddGraph(mockGraph, other);
+		L.assertHas(new Object[] { "addGraph", mockGraph, other });
+	}
+
+	/**
+	 * Test that deleting a graph is reported as deleting a graph.
+	 */
+	@ContractTest
+	public void testDeleteGraph() {
+		gem.register(L);
+		Graph other = Mockito.mock(Graph.class);
+		gem.notifyDeleteGraph(mockGraph, other);
+		L.assertHas(new Object[] { "deleteGraph", mockGraph, other });
+	}
+
+	/**
+	 * Test that sending a general event is reported as an event and the value
+	 * is saved.
+	 */
+	@ContractTest
+	public void testGeneralEvent() {
+		gem.register(L);
+		Object value = new int[] {};
+		gem.notifyEvent(mockGraph, value);
+		L.assertHas(new Object[] { "someEvent", mockGraph, value });
+	}
+
+	@ContractTest
+	public void testListening() {
+		assertFalse("Should not be listening", gem.listening());
+		gem.register(L);
+		assertTrue("Should be listening", gem.listening());
+		gem.unregister(L);
+		assertFalse("Should not be listening", gem.listening());
+	}
+
+	//
+	// Foo series of tests to check modifying the manger in mid notification
+	//
+
+	private ComeAndGoListener all[];
+
+	abstract private static class ComeAndGoListener implements GraphListener {
+
+		// Was I registered when start() was called, and have not been
+		// unregistered.
+		boolean inPlay = false;
+		// currently registered or not.
+		boolean registered = false;
+		boolean notified = false;
+
+		void register(GraphEventManager gem) {
+			registered = true;
+			gem.register(this);
+		}
+
+		void unregister(GraphEventManager gem) {
+			registered = false;
+			inPlay = false;
+			gem.unregister(this);
+		}
+
+		void start() {
+			if (registered)
+				inPlay = true;
+		}
+
+		void check() {
+			if (inPlay && !notified)
+				fail("listener that was in-play was not notified of adding triple.");
+		}
+
+		@Override
+		final public void notifyAddTriple(Graph g, Triple t) {
+			notified = true;
+			doSomeDamage();
+		}
+
+		abstract void doSomeDamage();
+
+		@Override
+		public void notifyAddArray(Graph g, Triple[] triples) {
+		}
+
+		@Override
+		public void notifyAddGraph(Graph g, Graph added) {
+		}
+
+		@Override
+		public void notifyAddIterator(Graph g, Iterator<Triple> it) {
+		}
+
+		@Override
+		public void notifyAddList(Graph g, List<Triple> triples) {
+		}
+
+		@Override
+		public void notifyDeleteArray(Graph g, Triple[] triples) {
+		}
+
+		@Override
+		public void notifyDeleteGraph(Graph g, Graph removed) {
+		}
+
+		@Override
+		public void notifyDeleteIterator(Graph g, Iterator<Triple> it) {
+		}
+
+		@Override
+		public void notifyDeleteList(Graph g, List<Triple> L) {
+		}
+
+		@Override
+		public void notifyDeleteTriple(Graph g, Triple t) {
+		}
+
+		@Override
+		public void notifyEvent(Graph source, Object value) {
+		}
+
+	}
+
+	/**
+	 * ComeAndGoListener implementation that does no damage
+	 * 
+	 */
+	private static final class SimpleListener extends ComeAndGoListener {
+		@Override
+		void doSomeDamage() {
+		}
+	}
+
+	/**
+	 * Test adding a triple to trigger event.
+	 * 
+	 * @param registerTo
+	 *            Number of listeners to register.
+	 * @param allx
+	 */
+	private void testAddingTriple(int registerTo, ComeAndGoListener... allx) {
+		all = allx;
+		// register addMe
+		for (int i = 0; i < registerTo; i++) {
+			all[i].register(gem);
+		}
+
+		// start them all
+		for (ComeAndGoListener l : all) {
+			l.start();
+		}
+		// send the notification.
+		gem.notifyAddTriple(mockGraph, GraphHelper.triple("make a change"));
+		// check them
+		for (ComeAndGoListener l : all) {
+			l.check();
+		}
+	}
+
+	/**
+	 * Test that a listener added during event processing does not receive the
+	 * event.
+	 */
+	@ContractTest
+	public void testAddOne() {
+		testAddingTriple(2, new ComeAndGoListener() {
+			@Override
+			void doSomeDamage() {
+				all[2].register(gem);
+			}
+		}, new SimpleListener(), new SimpleListener());
+	}
+
+	/**
+	 * Test that when a listener that has not yet been handled is removed during
+	 * event processing it receive the event.
+	 */
+	@ContractTest
+	public void testDelete2nd() {
+		testAddingTriple(3, new ComeAndGoListener() {
+			@Override
+			void doSomeDamage() {
+				all[1].unregister(gem);
+			}
+		}, new SimpleListener(), new SimpleListener());
+	}
+
+	/**
+	 * Test that when a listener that has been handled is removed during event
+	 * processing it receives the event.
+	 */
+	@ContractTest
+	public void testDelete1st() {
+		testAddingTriple(3, new SimpleListener(), new ComeAndGoListener() {
+			@Override
+			void doSomeDamage() {
+				all[0].unregister(gem);
+			}
+		}, new SimpleListener());
+	}
+
+	/**
+	 * Test that when a listener that removes itself during event processing it
+	 * receives the event.
+	 */
+	@ContractTest
+	public void testDeleteSelf() {
+		testAddingTriple(3, new ComeAndGoListener() {
+			@Override
+			void doSomeDamage() {
+				unregister(gem);
+			}
+		}, new SimpleListener(), new SimpleListener());
+	}
+
+	/**
+	 * Test that when a listener that removes and adds itself during event
+	 * processing it receives the event.
+	 */
+	@ContractTest
+	public void testDeleteAndAddSelf() {
+		testAddingTriple(3, new ComeAndGoListener() {
+			@Override
+			void doSomeDamage() {
+				unregister(gem);
+				register(gem);
+			}
+		}, new SimpleListener(), new SimpleListener());
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/graph/GraphMakerContractTest.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/graph/GraphMakerContractTest.java b/jena-core/src/test/java/org/apache/jena/graph/GraphMakerContractTest.java
new file mode 100644
index 0000000..5fbd9c2
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/graph/GraphMakerContractTest.java
@@ -0,0 +1,314 @@
+/*
+ * 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.jena.graph;
+
+import static org.junit.Assert.*;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.xenei.junit.contract.Contract;
+import org.xenei.junit.contract.ContractTest;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphMaker;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.SimpleGraphMaker;
+import org.apache.jena.shared.AlreadyExistsException;
+import org.apache.jena.shared.DoesNotExistException;
+import org.apache.jena.testing_framework.GraphHelper;
+import org.xenei.junit.contract.IProducer;
+import org.apache.jena.testing_framework.TestUtils;
+
+/**
+ * Abstract base class for testing graph factories. Subclasses define the method
+ * <code>getGraphFactory()</code> which supplies a new graph factory to be
+ * tested: ATGF invokes that during <code>setUp</code> and closes it in
+ * <code>tearDown</code>.
+ * <p>
+ * This bunch of tests is not remotely exhaustive, but it was sufficent to drive
+ * the development of the first full graph factory. (Although at the time it
+ * wasn't abstract.)
+ */
+@Contract(GraphMaker.class)
+public class GraphMakerContractTest {
+
+	private IProducer<GraphMaker> producer;
+
+	private GraphMaker graphMaker;
+
+	public GraphMakerContractTest() {
+//		producer = new IProducer<GraphMaker>() {
+//
+//			@Override
+//			public GraphMaker newInstance() {
+//				return new SimpleGraphMaker();
+//			}
+//
+//			@Override
+//			public void cleanUp() {
+//
+//			}
+//		};
+	}
+
+	@Contract.Inject
+	public final void setGraphMaker(IProducer<GraphMaker> producer) {
+		this.producer = producer;
+	}
+
+	protected final IProducer<GraphMaker> getGraphMakerProducer() {
+		return producer;
+	}
+
+	@After
+	public final void afterGraphMakerContractTest() {
+		producer.cleanUp();
+	}
+
+	@Before
+	public final void beforeGraphMakerContractTest() {
+		graphMaker = producer.newInstance();
+	}
+
+	@After
+	public void tearDown() {
+		graphMaker.close();
+		getGraphMakerProducer().cleanUp();
+	}
+
+	/**
+	 * Foo trivial test that getGraph delivers a proper graph, not cheating with
+	 * null, and that getGraph() "always" delivers the same Graph.
+	 */
+	@ContractTest
+	public void testGetGraph() {
+		Graph g1 = graphMaker.getGraph();
+		assertFalse("should deliver a Graph", g1 == null);
+		assertSame(g1, graphMaker.getGraph());
+		g1.close();
+	}
+
+	@ContractTest
+	public void testCreateGraph() {
+		TestUtils.assertDiffer("each created graph must differ",
+				graphMaker.createGraph(), graphMaker.createGraph());
+	}
+
+	@ContractTest
+	public void testAnyName() {
+		graphMaker.createGraph("plain").close();
+		graphMaker.createGraph("with.dot").close();
+		graphMaker.createGraph("http://electric-hedgehog.net/topic#marker")
+				.close();
+	}
+
+	/**
+	 * Test that we can't create a graph with the same name twice.
+	 */
+	@ContractTest
+	public void testCannotCreateTwice() {
+		String name = jName("bonsai");
+		graphMaker.createGraph(name, true);
+		try {
+			graphMaker.createGraph(name, true);
+			fail("should not be able to create " + name + " twice");
+		} catch (AlreadyExistsException e) {
+		}
+	}
+
+	private String jName(String name) {
+		return "jena-test-AbstractTestGraphMaker-" + name;
+	}
+
+	@ContractTest
+	public void testCanCreateTwice() {
+		String name = jName("bridge");
+		Graph g1 = graphMaker.createGraph(name, true);
+		Graph g2 = graphMaker.createGraph(name, false);
+		assertTrue("graphs should be the same", sameGraph(g1, g2));
+		Graph g3 = graphMaker.createGraph(name);
+		assertTrue("graphs should be the same", sameGraph(g1, g3));
+	}
+
+	/**
+	 * Test that we cannot open a graph that does not exist.
+	 */
+	@ContractTest
+	public void testCannotOpenUncreated() {
+		String name = jName("noSuchGraph");
+		try {
+			graphMaker.openGraph(name, true);
+			fail(name + " should not exist");
+		} catch (DoesNotExistException e) {
+		}
+	}
+
+	/**
+	 * Test that we *can* open a graph that hasn't been created
+	 */
+	@ContractTest
+	public void testCanOpenUncreated() {
+		String name = jName("willBeCreated");
+		Graph g1 = graphMaker.openGraph(name);
+		g1.close();
+		graphMaker.openGraph(name, true);
+	}
+
+	/**
+	 * Utility - test that a graph with the given name exists.
+	 */
+	private void testExists(String name) {
+		assertTrue(name + " should exist", graphMaker.hasGraph(name));
+	}
+
+	/**
+	 * Utility - test that no graph with the given name exists.
+	 */
+	private void testDoesNotExist(String name) {
+		assertFalse(name + " should exist", graphMaker.hasGraph(name));
+	}
+
+	/**
+	 * Test that we can find a graph once its been created. We need to know if
+	 * two graphs are "the same" here: we have a temporary work-around but it is
+	 * not sound.
+	 * 
+	 */
+	@ContractTest
+	public void testCanFindCreatedGraph() {
+		String alpha = jName("alpha"), beta = jName("beta");
+		Graph g1 = graphMaker.createGraph(alpha, true);
+		Graph h1 = graphMaker.createGraph(beta, true);
+		Graph g2 = graphMaker.openGraph(alpha, true);
+		Graph h2 = graphMaker.openGraph(beta, true);
+		assertTrue("should find alpha", sameGraph(g1, g2));
+		assertTrue("should find beta", sameGraph(h1, h2));
+	}
+
+	/**
+	 * Weak test for "same graph": adding this to one is visible in t'other.
+	 * Stopgap for use in testCanFindCreatedGraph. TODO: clean that test up
+	 * (left over from RDB days)
+	 */
+	private boolean sameGraph(Graph g1, Graph g2) {
+		Node S = GraphHelper.node("S"), P = GraphHelper.node("P"), O = GraphHelper
+				.node("O");
+		g1.add(Triple.create(S, P, O));
+		g2.add(Triple.create(O, P, S));
+		return g2.contains(S, P, O) && g1.contains(O, P, S);
+	}
+
+	/**
+	 * Test that we can remove a graph from the factory without disturbing
+	 * another graph's binding.
+	 */
+	@ContractTest
+	public void testCanRemoveGraph() {
+		String alpha = jName("bingo"), beta = jName("brillo");
+		graphMaker.createGraph(alpha, true);
+		graphMaker.createGraph(beta, true);
+		testExists(alpha);
+		testExists(beta);
+		graphMaker.removeGraph(alpha);
+		testExists(beta);
+		testDoesNotExist(alpha);
+	}
+
+	@ContractTest
+	public void testHasnt() {
+		assertFalse("no such graph", graphMaker.hasGraph("john"));
+		assertFalse("no such graph", graphMaker.hasGraph("paul"));
+		assertFalse("no such graph", graphMaker.hasGraph("george"));
+		/* */
+		graphMaker.createGraph("john", true);
+		assertTrue("john now exists", graphMaker.hasGraph("john"));
+		assertFalse("no such graph", graphMaker.hasGraph("paul"));
+		assertFalse("no such graph", graphMaker.hasGraph("george"));
+		/* */
+		graphMaker.createGraph("paul", true);
+		assertTrue("john still exists", graphMaker.hasGraph("john"));
+		assertTrue("paul now exists", graphMaker.hasGraph("paul"));
+		assertFalse("no such graph", graphMaker.hasGraph("george"));
+		/* */
+		graphMaker.removeGraph("john");
+		assertFalse("john has been removed", graphMaker.hasGraph("john"));
+		assertTrue("paul still exists", graphMaker.hasGraph("paul"));
+		assertFalse("no such graph", graphMaker.hasGraph("george"));
+	}
+
+	@ContractTest
+	public void testCarefulClose() {
+		Graph x = graphMaker.createGraph("x");
+		Graph y = graphMaker.openGraph("x");
+		x.add(GraphHelper.triple("a BB c"));
+		x.close();
+		y.add(GraphHelper.triple("p RR q"));
+		y.close();
+	}
+
+	/**
+	 * Test that a maker with no graphs lists no names.
+	 */
+	@ContractTest
+	public void testListNoGraphs() {
+		Set<String> s = graphMaker.listGraphs().toSet();
+		if (s.size() > 0)
+			fail("found names from 'empty' graph maker: " + s);
+	}
+
+	/**
+	 * Test that a maker with three graphs inserted lists those three grapsh; we
+	 * don't mind what order they appear in. We also use funny names to ensure
+	 * that the spelling that goes in is the one that comes out [should really
+	 * be in a separate test].
+	 */
+	@ContractTest
+	public void testListThreeGraphs() {
+		String x = "x", y = "y/sub", z = "z:boo";
+		Graph X = graphMaker.createGraph(x);
+		Graph Y = graphMaker.createGraph(y);
+		Graph Z = graphMaker.createGraph(z);
+		Set<String> wanted = TestUtils.setOfStrings(x + " " + y + " " + z);
+		assertEquals(wanted, GraphHelper.iteratorToSet(graphMaker.listGraphs()));
+		X.close();
+		Y.close();
+		Z.close();
+	}
+
+	/**
+	 * Test that a maker with some things put in and then some removed gets the
+	 * right things listed.
+	 */
+	@ContractTest
+	public void testListAfterDelete() {
+		String x = "x_y", y = "y//zub", z = "a:b/c";
+		Graph X = graphMaker.createGraph(x);
+		Graph Y = graphMaker.createGraph(y);
+		Graph Z = graphMaker.createGraph(z);
+		graphMaker.removeGraph(x);
+		Set<String> s = GraphHelper.iteratorToSet(graphMaker.listGraphs());
+		assertEquals(TestUtils.setOfStrings(y + " " + z), s);
+		X.close();
+		Y.close();
+		Z.close();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/graph/GraphWithPerformContractTest.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/graph/GraphWithPerformContractTest.java b/jena-core/src/test/java/org/apache/jena/graph/GraphWithPerformContractTest.java
new file mode 100644
index 0000000..aaaa85a
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/graph/GraphWithPerformContractTest.java
@@ -0,0 +1,86 @@
+package org.apache.jena.graph;
+
+import static org.apache.jena.testing_framework.GraphHelper.graphWith;
+import static org.apache.jena.testing_framework.GraphHelper.triple;
+import static org.apache.jena.testing_framework.GraphHelper.txnBegin;
+import static org.apache.jena.testing_framework.GraphHelper.txnCommit;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xenei.junit.contract.Contract;
+import org.xenei.junit.contract.ContractTest;
+
+import org.apache.jena.graph.impl.GraphWithPerform;
+import org.apache.jena.mem.GraphMem;
+import org.apache.jena.testing_framework.ContractTemplate;
+
+import org.xenei.junit.contract.IProducer;
+
+/**
+ * GraphWithPerform is an implementation interface that extends Graph with the
+ * performAdd and performDelete methods used by GraphBase to invoke
+ * non-notifying versions of add and delete.
+ */
+@Contract(GraphWithPerform.class)
+public class GraphWithPerformContractTest<T extends GraphWithPerform> extends
+		ContractTemplate<IProducer<T>> {
+	// Recording listener for tests
+	protected RecordingGraphListener GL = new RecordingGraphListener();
+
+	private static final Logger LOG = LoggerFactory
+			.getLogger(GraphWithPerformContractTest.class);
+
+	@SuppressWarnings("unchecked")
+	public GraphWithPerformContractTest() {
+//		setProducer((IProducer<T>) new IProducer<GraphWithPerform>() {
+//
+//			@Override
+//			public GraphWithPerform newInstance() {
+//				return new GraphMem();
+//			}
+//
+//			@Override
+//			public void cleanUp() {
+//
+//			}
+//		});
+	}
+
+	@Contract.Inject
+	public void setGraphWithPerformContractTestProducer(IProducer<T> producer) {
+		super.setProducer(producer);
+	}
+
+	@After
+	public final void afterGraphWithPerformContractTest() {
+		getProducer().cleanUp();
+	}
+
+	@ContractTest
+	public void testPerformAdd_Triple() {
+		GraphWithPerform g = (GraphWithPerform) graphWith(getProducer()
+				.newInstance(), "S P O; S2 P2 O2");
+		g.getEventManager().register(GL);
+		txnBegin(g);
+		g.performAdd(triple("S3 P3 O3"));
+		txnCommit(g);
+		GL.assertEmpty();
+		assertTrue(g.contains(triple("S3 P3 O3")));
+	}
+
+	@ContractTest
+	public void testPerformDelete_Triple() {
+		GraphWithPerform g = (GraphWithPerform) graphWith(getProducer()
+				.newInstance(), "S P O; S2 P2 O2");
+		g.getEventManager().register(GL);
+		txnBegin(g);
+		g.performDelete(triple("S2 P2 O2"));
+		txnCommit(g);
+		GL.assertEmpty();
+		assertFalse(g.contains(triple("S2 P2 O2")));
+	}
+
+}