http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/GraphHelper.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/GraphHelper.java 
b/jena-core/src/test/java/org/apache/jena/testing_framework/GraphHelper.java
new file mode 100644
index 0000000..c208e8d
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/testing_framework/GraphHelper.java
@@ -0,0 +1,508 @@
+/*
+ * 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.testing_framework;
+
+/**
+ * Foo set of static test helpers.  Generally included as a static.
+ */
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.jena.graph.Factory;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphUtil;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.shared.JenaException;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.util.CollectionFactory;
+import org.apache.jena.util.IteratorCollection;
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+public class GraphHelper extends TestUtils {
+
+       /**
+        * Answer a Node as described by <code>x</code>; a shorthand for
+        * <code>Node.create(x)</code>, which see.
+        */
+       public static Node node(String x) {
+               return NodeCreateUtils.create(x);
+       }
+
+       /**
+        * Answer a set containing the elements from the iterator 
<code>it</code>; a
+        * shorthand for <code>IteratorCollection.iteratorToSet(it)</code>, 
which
+        * see.
+        */
+       public static <T> Set<T> iteratorToSet(Iterator<? extends T> it) {
+               return IteratorCollection.iteratorToSet(it);
+       }
+
+       /**
+        * Answer a list containing the elements from the iterator 
<code>it</code>,
+        * in order; a shorthand for
+        * <code>IteratorCollection.iteratorToList(it)</code>, which see.
+        */
+       public static <T> List<T> iteratorToList(Iterator<? extends T> it) {
+               return IteratorCollection.iteratorToList(it);
+       }
+
+       /**
+        * Answer a set of the nodes described (as per <code>node()</code>) by 
the
+        * space-separated substrings of <code>nodes</code>.
+        */
+       public static Set<Node> nodeSet(String nodes) {
+               Set<Node> result = CollectionFactory.createHashedSet();
+               StringTokenizer st = new StringTokenizer(nodes);
+               while (st.hasMoreTokens())
+                       result.add(node(st.nextToken()));
+               return result;
+       }
+
+       /**
+        * Answer a set of the elements of <code>Foo</code>.
+        */
+       public static <T> Set<T> arrayToSet(T[] A) {
+               return CollectionFactory.createHashedSet(Arrays.asList(A));
+       }
+
+       /**
+        * Answer a triple described by the three space-separated node 
descriptions
+        * in <code>fact</code>; a shorthand for 
<code>Triple.create(fact)</code>,
+        * which see.
+        */
+       public static Triple triple(String fact) {
+               return NodeCreateUtils.createTriple(fact);
+       }
+
+       /**
+        * Answer a triple described by the three space-separated node 
descriptions
+        * in <code>fact</code>, using prefix-mappings from <code>pm</code>; a
+        * shorthand for <code>Triple.create(pm, fact)</code>, which see.
+        */
+       public static Triple triple(PrefixMapping pm, String fact) {
+               return NodeCreateUtils.createTriple(pm, fact);
+       }
+
+       /**
+        * Answer an array of triples; each triple is described by one of the
+        * semi-separated substrings of <code>facts</code>, as per
+        * <code>triple</code> with prefix-mapping <code>Extended</code>.
+        */
+       public static Triple[] tripleArray(String facts) {
+               ArrayList<Triple> al = new ArrayList<Triple>();
+               StringTokenizer semis = new StringTokenizer(facts, ";");
+               while (semis.hasMoreTokens())
+                       al.add(triple(PrefixMapping.Extended, 
semis.nextToken()));
+               return al.toArray(new Triple[al.size()]);
+       }
+
+       /**
+        * Answer a set of triples where the elements are described by the
+        * semi-separated substrings of <code>facts</code>, as per
+        * <code>triple</code>.
+        */
+       public static Set<Triple> tripleSet(String facts) {
+               Set<Triple> result = new HashSet<Triple>();
+               StringTokenizer semis = new StringTokenizer(facts, ";");
+               while (semis.hasMoreTokens())
+                       result.add(triple(semis.nextToken()));
+               return result;
+       }
+
+       /**
+        * Answer a list of nodes, where the nodes are described by the
+        * space-separated substrings of <code>items</code> as per
+        * <code>node()</code>.
+        */
+       public static List<Node> nodeList(String items) {
+               ArrayList<Node> nl = new ArrayList<Node>();
+               StringTokenizer nodes = new StringTokenizer(items);
+               while (nodes.hasMoreTokens())
+                       nl.add(node(nodes.nextToken()));
+               return nl;
+       }
+
+       /**
+        * Answer an array of nodes, where the nodes are described by the
+        * space-separated substrings of <code>items</code> as per
+        */
+       public static Node[] nodeArray(String items) {
+               List<Node> nl = nodeList(items);
+               return nl.toArray(new Node[nl.size()]);
+       }
+
+       /**
+        * Answer the graph <code>g</code> after adding to it every triple 
encoded
+        * in <code>s</code> in the fashion of <code>tripleArray</code>, a
+        * semi-separated sequence of space-separated node descriptions.
+        */
+       public static Graph graphAdd(Graph g, String s) {
+               StringTokenizer semis = new StringTokenizer(s, ";");
+               while (semis.hasMoreTokens())
+                       g.add(triple(PrefixMapping.Extended, 
semis.nextToken()));
+               return g;
+       }
+
+       /**
+        * Like graphAdd but does it within a transaction if supported
+        * 
+        * @param g
+        *            The graph to add to
+        * @param s
+        *            The string describing the graph
+        * @return The populated graph.
+        */
+       public static Graph graphAddTxn(Graph g, String s) {
+               txnBegin(g);
+               StringTokenizer semis = new StringTokenizer(s, ";");
+               while (semis.hasMoreTokens())
+                       g.add(triple(PrefixMapping.Extended, 
semis.nextToken()));
+               txnCommit(g);
+               return g;
+       }
+
+       /**
+        * Used to create a graph with values.
+        * 
+        * @param g
+        *            The newly created graph
+        * @param s
+        *            The string representing the graph data.
+        * @return The populated graph
+        */
+       public static Graph graphWith(Graph g, String s) {
+               return graphAddTxn(g, s);
+       }
+
+       /**
+        * Answer a new memory-based graph with Extended prefixes.
+        */
+       public static Graph memGraph() {
+               Graph result = Factory.createGraphMem();
+               result.getPrefixMapping().setNsPrefixes(PrefixMapping.Extended);
+               return result;
+       }
+
+       /**
+        * Answer a new memory-based graph with initial contents as described by
+        * <code>s</code> in the fashion of <code>graphAdd()</code>. Not
+        * over-ridable; do not use for abstraction.
+        */
+       public static Graph graphWith(String s) {
+               return graphWith(memGraph(), s);
+       }
+
+       /**
+        * Assert that the graph <code>g</code> is isomorphic to the graph 
described
+        * by <code>template</code> in the fashion of <code>graphWith</code>.
+        */
+       public static void assertEqualsTemplate(String title, Graph g,
+                       String template) {
+               assertIsomorphic(title, graphWith(template), g);
+       }
+
+       /**
+        * Assert that the supplied graph <code>got</code> is isomorphic with 
the
+        * the desired graph <code>expected</code>; if not, display a readable
+        * description of both graphs.
+        */
+       public static void assertIsomorphic(String title, Graph expected, Graph 
got) {
+               if (!expected.isIsomorphicWith(got)) {
+                       Map<Node, Object> map = 
CollectionFactory.createHashedMap();
+                       fail(title + ": wanted " + nice(expected, map) + "\nbut 
got "
+                                       + nice(got, map));
+               }
+       }
+
+       /**
+        * Answer a string which is a newline-separated list of triples (as 
produced
+        * by niceTriple) in the graph <code>g</code>. The map 
<code>bnodes</code>
+        * maps already-seen bnodes to their "nice" strings.
+        */
+       public static String nice(Graph g, Map<Node, Object> bnodes) {
+               StringBuffer b = new StringBuffer(g.size() * 100);
+               ExtendedIterator<Triple> it = GraphUtil.findAll(g);
+               while (it.hasNext())
+                       niceTriple(b, bnodes, it.next());
+               return b.toString();
+       }
+
+       /**
+        * Append to the string buffer <code>b</code> a "nice" representation 
of the
+        * triple <code>t</code> on a new line, using (and updating)
+        * <code>bnodes</code> to supply "nice" strings for any blank nodes.
+        */
+       protected static void niceTriple(StringBuffer b, Map<Node, Object> 
bnodes,
+                       Triple t) {
+               b.append("\n    ");
+               appendNode(b, bnodes, t.getSubject());
+               appendNode(b, bnodes, t.getPredicate());
+               appendNode(b, bnodes, t.getObject());
+       }
+
+       /**
+        * Foo counter for new bnode strings; it starts at 1000 so as to make 
the
+        * bnode strings more uniform (at least for the first 9000 bnodes).
+        */
+       protected static int bnc = 1000;
+
+       /**
+        * Append to the string buffer <code>b</code> a space followed by the 
"nice"
+        * representation of the node <code>n</code>. If <code>n</code> is a 
bnode,
+        * re-use any existing string for it from <code>bnodes</code> or make a 
new
+        * one of the form <i>_bNNNN</i> with NNNN a new integer.
+        */
+       protected static void appendNode(StringBuffer b, Map<Node, Object> 
bnodes,
+                       Node n) {
+               b.append(' ');
+               if (n.isBlank()) {
+                       Object already = bnodes.get(n);
+                       if (already == null)
+                               bnodes.put(n, already = "_b" + bnc++);
+                       b.append(already);
+               } else
+                       b.append(nice(n));
+       }
+
+       // protected static Graph graphWithTxn(IProducer<? extends Graph> 
producer,
+       // String s) {
+       // Graph g = producer.newInstance();
+       // txnBegin(g);
+       // try {
+       // graphAdd(g, s);
+       // txnCommit(g);
+       // } catch (Exception e) {
+       // txnRollback(g);
+       // fail(e.getMessage());
+       // }
+       // return g;
+       // }
+
+       /**
+        * Answer the "nice" representation of this node, the string returned by
+        * <code>n.toString(PrefixMapping.Extended,true)</code>.
+        */
+       protected static String nice(Node n) {
+               return n.toString(PrefixMapping.Extended, true);
+       }
+
+       /**
+        * Assert that the computed graph <code>got</code> is isomorphic with 
the
+        * desired graph <code>expected</code>; if not, fail with a default 
message
+        * (and pretty output of the graphs).
+        */
+       public static void assertIsomorphic(Graph expected, Graph got) {
+               assertIsomorphic("graphs must be isomorphic", expected, got);
+       }
+
+       /**
+        * Assert that the graph <code>g</code> must contain the triple 
described by
+        * <code>s</code>; if not, fail with pretty output of both graphs and a
+        * message containing <code>name</code>.
+        */
+       public static void assertContains(String name, String s, Graph g) {
+               assertTrue(name + " must contain " + s, g.contains(triple(s)));
+       }
+
+       /**
+        * Assert that the graph <code>g</code> contains all the triples 
described
+        * by the string <code>s</code; if not, fail with a message containing
+        * <code>name</code>.
+        */
+       public static void assertContainsAll(String name, Graph g, String s) {
+               StringTokenizer semis = new StringTokenizer(s, ";");
+               while (semis.hasMoreTokens())
+                       assertContains(name, semis.nextToken(), g);
+       }
+
+       /**
+        * Assert that the graph <code>g</code> does not contain the triple
+        * described by <code>s<code>; if it does, fail with a message 
containing
+        <code>name</code>.
+        */
+       public static void assertOmits(String name, Graph g, String s) {
+               assertFalse(name + " must not contain " + s, 
g.contains(triple(s)));
+       }
+
+       /**
+        * Assert that the graph <code>g</code> contains none of the triples
+        * described by <code>s</code> in the usual way; otherwise, fail with a
+        * message containing <code>name</code>.
+        */
+       public static void assertOmitsAll(String name, Graph g, String s) {
+               StringTokenizer semis = new StringTokenizer(s, ";");
+               while (semis.hasMoreTokens())
+                       assertOmits(name, g, semis.nextToken());
+       }
+
+       /**
+        * Assert that <code>g</code> contains the triple described by
+        * <code>fact</code> in the usual way.
+        */
+       public static boolean contains(Graph g, String fact) {
+               return g.contains(triple(fact));
+       }
+
+       /**
+        * Assert that <code>g</code> contains every triple in 
<code>triples</code>.
+        */
+       public static void testContains(Graph g, Triple[] triples) {
+               for (int i = 0; i < triples.length; i += 1)
+                       assertTrue("contains " + triples[i], 
g.contains(triples[i]));
+       }
+
+       /**
+        * Assert that <code>g</code> contains every triple in 
<code>triples</code>.
+        */
+       public static void testContains(Graph g, List<Triple> triples) {
+               for (int i = 0; i < triples.size(); i += 1)
+                       assertTrue(g.contains(triples.get(i)));
+       }
+
+       /**
+        * Assert that <code>g</code> contains every triple in <code>it</code>.
+        */
+       public static void testContains(Graph g, Iterator<Triple> it) {
+               while (it.hasNext())
+                       assertTrue(g.contains(it.next()));
+       }
+
+       /**
+        * Assert that <code>g</code> contains every triple in 
<code>other</code>.
+        */
+       public static void testContains(Graph g, Graph other) {
+               testContains(g, GraphUtil.findAll(other));
+       }
+
+       /**
+        * Assert that <code>g</code> contains none of the triples in
+        * <code>triples</code>.
+        */
+       public static void testOmits(Graph g, Triple[] triples) {
+               for (int i = 0; i < triples.length; i += 1)
+                       assertFalse("", g.contains(triples[i]));
+       }
+
+       /**
+        * Assert that <code>g</code> contains none of the triples in
+        * <code>triples</code>.
+        */
+       public static void testOmits(Graph g, List<Triple> triples) {
+               for (int i = 0; i < triples.size(); i += 1)
+                       assertFalse("", g.contains(triples.get(i)));
+       }
+
+       /**
+        * Assert that <code>g</code> contains none of the triples in
+        * <code>it</code>.
+        */
+       public static void testOmits(Graph g, Iterator<Triple> it) {
+               while (it.hasNext())
+                       assertFalse("", g.contains(it.next()));
+       }
+
+       /**
+        * Assert that <code>g</code> contains none of the triples in
+        * <code>other</code>.
+        */
+       public static void testOmits(Graph g, Graph other) {
+               testOmits(g, GraphUtil.findAll(other));
+       }
+
+       /**
+        * Answer an instance of <code>graphClass</code>. If 
<code>graphClass</code>
+        * has a constructor that takes a <code>ReificationStyle</code> 
argument,
+        * then that constructor is run on <code>style</code> to get the 
instance.
+        * Otherwise, if it has a # constructor that takes an argument of
+        * <code>wrap</code>'s class before the <code>ReificationStyle</code>, 
that
+        * constructor is used; this allows non-static inner classes to be used 
for
+        * <code>graphClass</code>, with <code>wrap</code> being the outer class
+        * instance. If no suitable constructor exists, a JenaException is 
thrown.
+        * 
+        * @param wrap
+        *            the outer class instance if graphClass is an inner class
+        * @param graphClass
+        *            a class implementing Graph
+        * @return an instance of graphClass with the given style
+        * @throws RuntimeException
+        *             or JenaException if construction fails
+        */
+       public static Graph getGraph(Object wrap, Class<? extends Graph> 
graphClass) {
+               try {
+                       Constructor<?> cons = getConstructor(graphClass, new 
Class[] {});
+                       if (cons != null)
+                               return (Graph) cons.newInstance(new Object[] 
{});
+                       Constructor<?> cons2 = getConstructor(graphClass,
+                                       new Class[] { wrap.getClass() });
+                       if (cons2 != null)
+                               return (Graph) cons2.newInstance(new Object[] { 
wrap });
+                       throw new JenaException("no suitable graph constructor 
found for "
+                                       + graphClass);
+               } catch (RuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new JenaException(e);
+               }
+       }
+
+       /**
+        * Begin a transaction on the graph if transactions are supported.
+        * 
+        * @param g
+        */
+       public static void txnBegin(Graph g) {
+               if (g.getTransactionHandler().transactionsSupported()) {
+                       g.getTransactionHandler().begin();
+               }
+       }
+
+       /**
+        * Commit the transaction on the graph if transactions are supported.
+        * 
+        * @param g
+        */
+       public static void txnCommit(Graph g) {
+               if (g.getTransactionHandler().transactionsSupported()) {
+                       g.getTransactionHandler().commit();
+               }
+       }
+
+       /**
+        * Rollback (abort) the transaction on the graph if transactions are
+        * supported.
+        * 
+        * @param g
+        */
+       public static void txnRollback(Graph g) {
+               if (g.getTransactionHandler().transactionsSupported()) {
+                       g.getTransactionHandler().abort();
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/GraphProducerInterface.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/GraphProducerInterface.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/GraphProducerInterface.java
new file mode 100644
index 0000000..8c33eb1
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/GraphProducerInterface.java
@@ -0,0 +1,41 @@
+/*
+    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.testing_framework;
+
+/**
+ * Creates the graph for testing. Implementations must track the creation of
+ * graphs created with newGraph and close them when closeGraphs is called.
+ * 
+ */
+//public interface GraphProducerInterface<T> {
+//
+//     /**
+//      * Returns a new Graph to take part in the test.
+//      * 
+//      * @return The graph implementation to test.
+//      */
+//     public abstract Graph newGraph();
+//
+//     /**
+//      * provides a hook to close down graphs. When called all graphs created 
by
+//      * the newGraph() method should be closed. Note that some graphs may 
have
+//      * been closed during the test, so graphs should be tested for being 
closed
+//      * prior to closing.
+//      */
+//     public abstract void closeGraphs();
+// }

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/IContainerProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/IContainerProducer.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IContainerProducer.java
new file mode 100644
index 0000000..41bc6df
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IContainerProducer.java
@@ -0,0 +1,19 @@
+package org.apache.jena.testing_framework;
+
+import org.apache.jena.rdf.model.Container;
+import org.apache.jena.rdf.model.Resource;
+
+public interface IContainerProducer<T extends Container> extends
+               IResourceProducer<T> {
+
+       /**
+        * The Resource identifying the continer type. e.g. RDF.seq
+        */
+       Resource getContainerType();
+
+       /**
+        * The class of the continaer. e.g. Seq.class
+        */
+       Class<? extends Container> getContainerClass();
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/IIteratorProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/IIteratorProducer.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IIteratorProducer.java
new file mode 100644
index 0000000..3178692
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IIteratorProducer.java
@@ -0,0 +1,42 @@
+package org.apache.jena.testing_framework;
+
+import java.util.List;
+
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+public interface IIteratorProducer<T> {
+
+       /**
+        * Get a new instance of the iterator.
+        * 
+        * @return
+        */
+       public ExtendedIterator<T> newInstance();
+
+       /**
+        * Clean up after a test
+        */
+       public void cleanUp();
+
+       /**
+        * The list of items found in the iterator. Does not have to be in 
order.
+        * 
+        * @return
+        */
+       public List<T> getList();
+
+       /**
+        * True if delete is supported by the iterator
+        * 
+        * @return
+        */
+       public boolean supportsDelete();
+
+       /**
+        * True if this is an iterator on a copy so that delete works but 
getting a
+        * new copy for the iterator test will return the original list.
+        * 
+        * @return
+        */
+       public boolean isCopy();
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/INodeProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/INodeProducer.java 
b/jena-core/src/test/java/org/apache/jena/testing_framework/INodeProducer.java
new file mode 100644
index 0000000..2532c32
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/INodeProducer.java
@@ -0,0 +1,39 @@
+/*
+    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.testing_framework;
+
+import org.xenei.junit.contract.IProducer;
+
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.RDFNode;
+
+/**
+ * An abstract implementation of the IProducer<RDFNode> interface.
+ * 
+ * This class handles tracking of the created graphs and closing them. It also
+ * provides a callback for the implementing class to perform extra cleanup when
+ * the graph is closed.
+ * 
+ */
+public interface INodeProducer<T extends RDFNode> extends IProducer<T> {
+
+       abstract public T newInstance(String uri);
+
+       abstract public Model getModel();
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/IResourceProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/IResourceProducer.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IResourceProducer.java
new file mode 100644
index 0000000..ef9eead
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IResourceProducer.java
@@ -0,0 +1,10 @@
+package org.apache.jena.testing_framework;
+
+import org.apache.jena.rdf.model.Resource;
+
+public interface IResourceProducer<X extends Resource> extends 
INodeProducer<X> {
+       /**
+        * Returns true if the Resource implementation supports non URI values
+        */
+       boolean supportsAnonymous();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/IStatementProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/IStatementProducer.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IStatementProducer.java
new file mode 100644
index 0000000..8830948
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/IStatementProducer.java
@@ -0,0 +1,45 @@
+/*
+    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.testing_framework;
+
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+
+/**
+ * An abstract implementation of the IProducer<RDFNode> interface.
+ * 
+ * This class handles tracking of the created graphs and closing them. It also
+ * provides a callback for the implementing class to perform extra cleanup when
+ * the graph is closed.
+ * 
+ */
+public interface IStatementProducer<T extends Statement> {
+
+       abstract public T newInstance(String fact);
+
+       abstract public T newInstance(Resource s, Property p, RDFNode o);
+
+       abstract public Model getModel();
+
+       abstract public void cleanUp();
+
+       abstract public AbstractModelProducer<Model> getModelProducer();
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/ITripleStoreProducer.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/ITripleStoreProducer.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/ITripleStoreProducer.java
new file mode 100644
index 0000000..fe4e25d
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/ITripleStoreProducer.java
@@ -0,0 +1,35 @@
+/*
+    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.testing_framework;
+
+import org.apache.jena.graph.impl.TripleStore;
+
+/**
+ * Creates the graph for testing
+ * 
+ */
+public interface ITripleStoreProducer {
+
+       /**
+        * Returns a TripleStore to take part in the test. Must be overridden 
in a
+        * subclass.
+        * 
+        * @return The TripleStore implementation to test.
+        */
+       public abstract TripleStore newTripleStore();
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/ModelHelper.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/ModelHelper.java 
b/jena-core/src/test/java/org/apache/jena/testing_framework/ModelHelper.java
new file mode 100644
index 0000000..2f35203
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/testing_framework/ModelHelper.java
@@ -0,0 +1,409 @@
+/*
+ * 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.testing_framework;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.rdf.model.Literal;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.jena.rdf.model.StmtIterator;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.util.CollectionFactory;
+
+import static org.junit.Assert.*;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.junit.Assert;
+import org.xenei.junit.contract.IProducer;
+
+/**
+ * provides useful functionality for testing models, eg building small models
+ * from strings, testing equality, etc.
+ * 
+ * Currently this class extends GraphHelper and thus TestCase.
+ */
+public class ModelHelper extends GraphHelper {
+
+       private static Model builderModel;
+
+       static {
+               builderModel = ModelFactory.createDefaultModel();
+               builderModel.setNsPrefixes(PrefixMapping.Extended);
+       }
+
+       protected static final Model empty = ModelFactory.createDefaultModel();
+
+       protected static Model extendedModel(IProducer<Model> producer) {
+               Model result = producer.newInstance();
+               result.setNsPrefixes(PrefixMapping.Extended);
+               return result;
+       }
+
+       protected static String nice(RDFNode n) {
+               return nice(n.asNode());
+       }
+
+       public static Statement statement(String fact) {
+               StringTokenizer st = new StringTokenizer(fact);
+               Resource sub = resource(st.nextToken());
+               Property pred = property(st.nextToken());
+               RDFNode obj = rdfNode(st.nextToken());
+               return builderModel.createStatement(sub, pred, obj);
+       }
+
+       public static Statement statement(Resource s, Property p, RDFNode o) {
+               return builderModel.createStatement(s, p, o);
+       }
+
+       public static RDFNode rdfNode(Model m, String s) {
+               return m.asRDFNode(NodeCreateUtils.create(s));
+       }
+
+       public static RDFNode rdfNode(String s) {
+               return rdfNode(builderModel, s);
+       }
+
+       public static <T extends RDFNode> T rdfNode(String s, Class<T> c) {
+               return rdfNode(s).as(c);
+       }
+
+       public static Resource resource() {
+               return ResourceFactory.createResource();
+       }
+
+       public static Resource resource(String s) {
+               return (Resource) rdfNode(s);
+       }
+
+       // public static Resource resource(Model m, String s) {
+       // return (Resource) rdfNode(m, s);
+       // }
+
+       public static Property property(String s) {
+               return rdfNode(s).as(Property.class);
+       }
+
+       public static Property property(Model m, String s) {
+               return rdfNode(m, s).as(Property.class);
+       }
+
+       public static Literal literal(String s, String lang) {
+               return builderModel.createLiteral(s, lang);
+       }
+
+       public static Literal literal(String s) {
+               return rdfNode(s).as(Literal.class);
+       }
+
+       // /**
+       // * Create an array of Statements parsed from a semi-separated string.
+       // *
+       // * @param lockModel
+       // * a model to serve as a statement factory
+       // * @param facts
+       // * a sequence of semicolon-separated "S P O" facts
+       // * @return a Statement[] of the (S P O) statements from the string
+       // */
+       // public static Statement[] statements(Model m, String facts) {
+       // ArrayList<Statement> sl = new ArrayList<Statement>();
+       // StringTokenizer st = new StringTokenizer(facts, ";");
+       // while (st.hasMoreTokens())
+       // sl.add(statement(m, st.nextToken()));
+       // return sl.toArray(new Statement[sl.size()]);
+       // }
+
+       /**
+        * Create an array of Statements parsed from a semi-separated string.
+        * 
+        * @param lockModel
+        *            a model to serve as a statement factory
+        * @param facts
+        *            a sequence of semicolon-separated "S P O" facts
+        * @return a Statement[] of the (S P O) statements from the string
+        */
+       public static Statement[] statements(String facts) {
+               ArrayList<Statement> sl = new ArrayList<Statement>();
+               StringTokenizer st = new StringTokenizer(facts, ";");
+               while (st.hasMoreTokens())
+                       sl.add(statement(st.nextToken()));
+               return sl.toArray(new Statement[sl.size()]);
+       }
+
+       /**
+        * Create an array of Resources from a whitespace-separated string
+        * 
+        * @param items
+        *            a whitespace-separated sequence to feed to resource
+        * @return a Resource[] of the parsed resources
+        */
+       public static Resource[] resources(String items) {
+               ArrayList<Resource> rl = new ArrayList<Resource>();
+               StringTokenizer st = new StringTokenizer(items);
+               while (st.hasMoreTokens())
+                       rl.add(resource(st.nextToken()));
+               return rl.toArray(new Resource[rl.size()]);
+       }
+
+       /**
+        * Answer the set of resources given by the space-separated
+        * <code>items</code> string. Each resource specification is 
interpreted as
+        * per <code>resource</code>.
+        */
+       public static Set<Resource> resourceSet(String items) {
+               Set<Resource> result = new HashSet<Resource>();
+               StringTokenizer st = new StringTokenizer(items);
+               while (st.hasMoreTokens())
+                       result.add(resource(st.nextToken()));
+               return result;
+       }
+
+       /**
+        * add to a model all the statements expressed by a string.
+        * 
+        * Does not do any transaction manipulation.
+        * 
+        * @param m
+        *            the model to be updated
+        * @param facts
+        *            a sequence of semicolon-separated "S P O" facts
+        * @return the updated model
+        */
+       public static Model modelAdd(Model m, String facts) {
+               StringTokenizer semis = new StringTokenizer(facts, ";");
+
+               while (semis.hasMoreTokens()) {
+                       StringTokenizer st = new 
StringTokenizer(semis.nextToken());
+                       Resource sub = resource(st.nextToken());
+                       Property pred = property(st.nextToken());
+                       RDFNode obj = rdfNode(st.nextToken());
+                       m.add(sub, pred, obj);
+               }
+
+               return m;
+       }
+
+       /**
+        * create a memory based model with extended prefixes and initialises it
+        * with statements parsed from a string.
+        * 
+        * does all insertions in a transaction.
+        * 
+        * @param facts
+        * @return
+        */
+       public static Model memModel(String facts) {
+               Model model = 
ModelFactory.createMemModelMaker().createFreshModel();
+               model.setNsPrefixes(PrefixMapping.Extended);
+               txnBegin(model);
+               modelAdd(model, facts);
+               txnCommit(model);
+               return model;
+       }
+
+       /**
+        * Creates a model with extended prefixes and initialises it with 
statements
+        * parsed from a string.
+        * 
+        * does all insertions in a transaction.
+        * 
+        * @param facts
+        *            a string in semicolon-separated "S P O" format
+        * @return a model containing those facts
+        */
+       public static Model modelWithStatements(
+                       IProducer<? extends Model> producer, String facts) {
+               Model m = createModel(producer);
+               txnBegin(m);
+               modelAdd(m, facts);
+               txnCommit(m);
+               return m;
+       }
+
+       /**
+        * Creates a model with extended prefixes and initialises it with 
statements
+        * parsed from the statement iterator.
+        * 
+        * does all insertions in a transaction.
+        * 
+        * @param facts
+        *            a string in semicolon-separated "S P O" format
+        * @return a model containing those facts
+        */
+       public static Model modelWithStatements(
+                       IProducer<? extends Model> producer, final StmtIterator 
it) {
+               Model m = createModel(producer);
+               txnBegin(m);
+               while (it.hasNext()) {
+                       m.add(it.nextStatement());
+               }
+               txnCommit(m);
+               return m;
+       }
+
+       /**
+        * make a model and give it Extended prefixes
+        */
+       public static Model createModel(IProducer<? extends Model> producer) {
+               Model result = producer.newInstance();
+               result.setNsPrefixes(PrefixMapping.Extended);
+               return result;
+       }
+
+       /**
+        * test that two models are isomorphic and fail if they are not.
+        * 
+        * @param title
+        *            a String appearing at the beginning of the failure message
+        * @param wanted
+        *            the model value that is expected
+        * @param got
+        *            the model value to check
+        * @exception if
+        *                the models are not isomorphic
+        */
+       public static void assertIsoModels(String title, Model wanted, Model 
got) {
+               if (wanted.isIsomorphicWith(got) == false) {
+                       Map<Node, Object> map = 
CollectionFactory.createHashedMap();
+                       fail(title + ": expected " + nice(wanted.getGraph(), 
map)
+                                       + "\n but had " + nice(got.getGraph(), 
map));
+               }
+       }
+
+       public static void assertContainsAll(final Model model, final Model 
model2) {
+               for (final StmtIterator s = model2.listStatements(); 
s.hasNext();) {
+                       Assert.assertTrue(model.contains(s.nextStatement()));
+               }
+       }
+
+       public static void assertSameStatements(final Model model,
+                       final Model model2) {
+               assertContainsAll(model, model2);
+               assertContainsAll(model2, model);
+       }
+
+       public static Property prop(final String uri) {
+               return ResourceFactory.createProperty("eh:/" + uri);
+       }
+
+       public static Resource res(final String uri) {
+               return ResourceFactory.createResource("eh:/" + uri);
+       }
+
+       /**
+        * Fail if the two models are not isomorphic. See
+        * assertIsoModels(String,Model,Model).
+        */
+       public static void assertIsoModels(Model wanted, Model got) {
+               assertIsoModels("models must be isomorphic", wanted, got);
+       }
+
+       public static final boolean tvBoolean = true;
+       public static final byte tvByte = 1;
+       public static final short tvShort = 2;
+       public static final int tvInt = -1;
+       public static final long tvLong = -2;
+       public static final char tvChar = '!';
+       public static final float tvFloat = (float) 123.456;
+       public static final double tvDouble = -123.456;
+       public static final String tvString = "test 12 string";
+       public static final double dDelta = 0.000000005;
+
+       public static final float fDelta = 0.000005f;
+
+       public static final Object tvLitObj = new LitTestObj(1234);
+       public static final LitTestObj tvObject = new LitTestObj(12345);
+
+       public static class LitTestObj {
+               protected long content;
+
+               public LitTestObj(final long l) {
+                       content = l;
+               }
+
+               public LitTestObj(final String s) {
+                       content = Long.parseLong(s.substring(1, s.length() - 
1));
+               }
+
+               @Override
+               public boolean equals(final Object o) {
+                       return (o instanceof LitTestObj)
+                                       && (content == ((LitTestObj) 
o).content);
+               }
+
+               @Override
+               public int hashCode() {
+                       return (int) (content ^ (content >> 32));
+               }
+
+               @Override
+               public String toString() {
+                       return "[" + Long.toString(content) + "]";
+               }
+
+               public long getContent() {
+                       return content;
+               }
+       }
+
+       /**
+        * Begin a transaction on the model if transactions are supported.
+        * 
+        * @param m
+        */
+       public static Model txnBegin(Model m) {
+               if (m.supportsTransactions()) {
+                       return m.begin();
+               }
+               return m;
+       }
+
+       /**
+        * Commit the transaction on the model if transactions are supported.
+        * 
+        * @param m
+        */
+       public static Model txnCommit(Model m) {
+               if (m.supportsTransactions()) {
+                       return m.commit();
+               }
+               return m;
+       }
+
+       /**
+        * Rollback (abort) the transaction on the model if transactions are
+        * supported.
+        * 
+        * @param m
+        */
+       public static Model txnRollback(Model m) {
+               if (m.supportsTransactions()) {
+                       return m.abort();
+               }
+               return m;
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/NodeCreateUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/NodeCreateUtils.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/NodeCreateUtils.java
new file mode 100644
index 0000000..83d42eb
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/NodeCreateUtils.java
@@ -0,0 +1,177 @@
+/*
+ * 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.testing_framework;
+
+import java.util.StringTokenizer;
+
+import org.apache.jena.datatypes.xsd.XSDDatatype;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.graph.impl.LiteralLabelFactory;
+import org.apache.jena.rdf.model.AnonId;
+import org.apache.jena.shared.*;
+
+/**
+ * Creating nodes from string specifications.
+ */
+public class NodeCreateUtils {
+       /**
+        * Returns a Node described by the string, primarily for testing 
purposes.
+        * The string represents a URI, a numeric literal, a string literal, a 
bnode
+        * label, or a variable.
+        * <ul>
+        * <li>'some text' :: a string literal with that text
+        * <li>'some text'someLanguage:: a string literal with that text and
+        * language
+        * <li>'some text'someURI:: a typed literal with that text and datatype
+        * <li>digits :: a literal [OF WHAT TYPE] with that [numeric] value
+        * <li>_XXX :: a bnode with an AnonId built from _XXX
+        * <li>?VVV :: a variable with name VVV
+        * <li>&PPP :: to be done
+        * <li>name:stuff :: the URI; name may be expanded using the Extended 
map
+        * </ul>
+        * 
+        * @param x
+        *            the string describing the node
+        * @return a node of the appropriate type with the appropriate label
+        */
+       public static Node create(String x) {
+               return create(PrefixMapping.Extended, x);
+       }
+
+       /**
+        * Returns a Node described by the string, primarily for testing 
purposes.
+        * The string represents a URI, a numeric literal, a string literal, a 
bnode
+        * label, or a variable.
+        * <ul>
+        * <li>'some text' :: a string literal with that text
+        * <li>'some text'someLanguage:: a string literal with that text and
+        * language
+        * <li>'some text'someURI:: a typed literal with that text and datatype
+        * <li>digits :: a literal [OF WHAT TYPE] with that [numeric] value
+        * <li>_XXX :: a bnode with an AnonId built from _XXX
+        * <li>?VVV :: a variable with name VVV
+        * <li>&PPP :: to be done
+        * <li>name:stuff :: the URI; name may be expanded using the Extended 
map
+        * </ul>
+        * 
+        * @param pm
+        *            the PrefixMapping for translating pre:X strings
+        * @param x
+        *            the string encoding the node to create
+        * @return a node with the appropriate type and label
+        */
+       public static Node create(PrefixMapping pm, String x) {
+               if (x.equals(""))
+                       throw new JenaException(
+                                       "Node.create does not accept an empty 
string as argument");
+               char first = x.charAt(0);
+               if (first == '\'' || first == '\"')
+                       return NodeFactory.createLiteral(newString(pm, first, 
x));
+               if (Character.isDigit(first))
+                       return NodeFactory.createLiteral(x, "", 
XSDDatatype.XSDinteger);
+               if (first == '_')
+                       return NodeFactory.createAnon(new AnonId(x));
+               if (x.equals("??"))
+                       return Node.ANY;
+               if (first == '?')
+                       return NodeFactory.createVariable(x.substring(1));
+               if (first == '&')
+                       return NodeFactory.createURI("q:" + x.substring(1));
+               int colon = x.indexOf(':');
+               String d = pm.getNsPrefixURI("");
+               return colon < 0 ? NodeFactory.createURI((d == null ? "eh:/" : 
d) + x)
+                               : NodeFactory.createURI(pm.expandPrefix(x));
+       }
+
+       public static String unEscape(String spelling) {
+               if (spelling.indexOf('\\') < 0)
+                       return spelling;
+               StringBuffer result = new StringBuffer(spelling.length());
+               int start = 0;
+               while (true) {
+                       int b = spelling.indexOf('\\', start);
+                       if (b < 0)
+                               break;
+                       result.append(spelling.substring(start, b));
+                       result.append(unEscape(spelling.charAt(b + 1)));
+                       start = b + 2;
+               }
+               result.append(spelling.substring(start));
+               return result.toString();
+       }
+
+       public static char unEscape(char ch) {
+               switch (ch) {
+               case '\\':
+               case '\"':
+               case '\'':
+                       return ch;
+               case 'n':
+                       return '\n';
+               case 's':
+                       return ' ';
+               case 't':
+                       return '\t';
+               default:
+                       return 'Z';
+               }
+       }
+
+       public static LiteralLabel literal(PrefixMapping pm, String spelling,
+                       String langOrType) {
+               String content = unEscape(spelling);
+               int colon = langOrType.indexOf(':');
+               return colon < 0 ? LiteralLabelFactory.create(content, 
langOrType,
+                               false) : 
LiteralLabelFactory.createLiteralLabel(content, "",
+                               
NodeFactory.getType(pm.expandPrefix(langOrType)));
+       }
+
+       public static LiteralLabel newString(PrefixMapping pm, char quote,
+                       String nodeString) {
+               int close = nodeString.lastIndexOf(quote);
+               return literal(pm, nodeString.substring(1, close),
+                               nodeString.substring(close + 1));
+       }
+
+       /**
+        * Utility factory as for create(String), but allowing the 
PrefixMapping to
+        * be specified explicitly.
+        */
+       public static Triple createTriple(PrefixMapping pm, String fact) {
+               StringTokenizer st = new StringTokenizer(fact);
+               Node sub = create(pm, st.nextToken());
+               Node pred = create(pm, st.nextToken());
+               Node obj = create(pm, st.nextToken());
+               return Triple.create(sub, pred, obj);
+       }
+
+       /**
+        * Utility factory method for creating a triple based on the content of 
an
+        * "S P O" string. The S, P, O are processed by Node.create, see which 
for
+        * details of the supported syntax. This method exists to support test 
code.
+        * Nodes are interpreted using the Standard prefix mapping.
+        */
+
+       public static Triple createTriple(String fact) {
+               return createTriple(PrefixMapping.Standard, fact);
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/NodeProducerInterface.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/NodeProducerInterface.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/NodeProducerInterface.java
new file mode 100644
index 0000000..c80d3f4
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/NodeProducerInterface.java
@@ -0,0 +1,32 @@
+/*
+    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.testing_framework;
+
+/**
+ * Creates the graph for testing. Implementations must track the creation of
+ * graphs created with newGraph and close them when closeGraphs is called.
+ * 
+ */
+//public interface NodeProducerInterface {
+//
+//     /**
+//      * Returns a new anonymous RDFNode in an model.
+//      */
+//     public abstract RDFNode newRDFNode();
+//
+// }

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/TestFileData.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/TestFileData.java 
b/jena-core/src/test/java/org/apache/jena/testing_framework/TestFileData.java
new file mode 100644
index 0000000..7b46366
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/TestFileData.java
@@ -0,0 +1,380 @@
+package org.apache.jena.testing_framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.rdf.model.AnonId;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.graph.Factory;
+
+/**
+ * Class that produces RDF and TTL data, a Graph and a Model that all contain
+ * the same data. This is used for various tests where files are read/written
+ * 
+ */
+public class TestFileData {
+
+       public static final String NS = "uri:urn:x-rdf:test#";
+
+       private static Map<String, String[]> rdfData = new HashMap<String, 
String[]>();
+       private static Map<String, String[]> ttlData = new HashMap<String, 
String[]>();
+
+       static {
+               rdfData.put(
+                               "", // default set must be equiv to TTL default
+                               new String[] {
+                                               "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>",
+                                               "<rdf:RDF",
+                                               "  xmlns:u=\"uri:\"",
+                                               "  
xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"";,
+                                               String.format("  
xmlns:ex=\"%s\">", NS),
+                                               String.format(
+                                                               "  
<rdf:Description rdf:about=\"%ssubject\">",
+                                                               NS),
+                                               String.format(
+                                                               "    
<ex:predicate rdf:resource=\"%sobject\"/>",
+                                                               NS), "  
</rdf:Description>",
+                                               "  <rdf:Description 
rdf:about=\"uri:e\">",
+                                               "    <u:p5>verify base 
works</u:p5>",
+                                               "  </rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "realtiveURI", // has relative URI in 
description rdf:about
+                               new String[] {
+                                               "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>",
+                                               "<rdf:RDF",
+                                               "  
xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"";,
+                                               "  
xmlns:ex=\"http://example.com/\";>",
+                                               "  <rdf:Description 
rdf:about=\"http://example.com/subject\";>",
+                                               "    <ex:predicate 
rdf:resource=\"http://example.com/object\"/>",
+                                               "  </rdf:Description>",
+                                               "  <rdf:Description 
rdf:about=\"e\">",
+                                               "    <ex:p5>verify base 
works</ex:p5>",
+                                               "  </rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList0",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList1",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">",
+                                               "    <rdf:Description 
rdf:ID=\"a\" />", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList2",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">",
+                                               "    <rdf:Description 
rdf:ID=\"a\" />",
+                                               "    <rdf:Description 
rdf:ID=\"b\" />", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList3",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">",
+                                               "    <rdf:Description 
rdf:ID=\"a\" />",
+                                               "    <rdf:Description 
rdf:ID=\"b\" />",
+                                               "    <rdf:Description 
rdf:ID=\"c\" />", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList4",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">",
+                                               "    <rdf:Description 
rdf:ID=\"a\" />",
+                                               "    <rdf:Description 
rdf:ID=\"b\" />",
+                                               "    <rdf:Description 
rdf:ID=\"c\" />",
+                                               "    <rdf:Description 
rdf:ID=\"d\" />", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+               rdfData.put(
+                               "OntologyList5",
+                               new String[] {
+                                               "<?xml version='1.0' 
encoding='ISO-8859-1'?>",
+                                               "<!DOCTYPE rdf:RDF [",
+                                               "    <!ENTITY rdf   
'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>",
+                                               "    <!ENTITY rdfs  
'http://www.w3.org/2000/01/rdf-schema#'>",
+                                               "]>",
+                                               "<rdf:RDF",
+                                               "    xmlns:rdf   =\"&rdf;\"",
+                                               "    xmlns:rdfs  =\"&rdfs;\"",
+                                               String.format("    xml:base    
=\"%s\"",
+                                                               NS.substring(0, 
NS.length() - 1)),
+                                               String.format("    xmlns       
=\"%s\"", NS), ">",
+                                               "<rdf:Description 
rdf:ID=\"root\">",
+                                               "   <p 
rdf:parseType=\"Collection\">",
+                                               "    <rdf:Description 
rdf:ID=\"a\" />",
+                                               "    <rdf:Description 
rdf:ID=\"b\" />",
+                                               "    <rdf:Description 
rdf:ID=\"c\" />",
+                                               "    <rdf:Description 
rdf:ID=\"d\" />",
+                                               "    <rdf:Description 
rdf:ID=\"e\" />", "   </p>",
+                                               "</rdf:Description>", 
"</rdf:RDF>" });
+
+               ttlData.put(
+                               "", // default set must be equiv to RDF default 
and must be
+                                       // parsable as N-TRIPLE
+                               new String[] {
+                                               String.format("<%ssubject> 
<%spredicate> <%sobject> .",
+                                                               NS, NS, NS), 
"<e> <p5> \"verify base works\" ." });
+
+       }
+
+       private static String toDataString(String[] lines) {
+               String eol = System.getProperty("line.separator");
+               StringBuilder sb = new StringBuilder();
+               for (String l : lines) {
+                       sb.append(l).append(eol);
+               }
+               return sb.toString();
+       }
+
+       public static Graph getGraph() {
+               
+               Graph g = Factory.createGraphMem();
+
+               g.add(new 
Triple(NodeFactory.createURI("http://example.com/subject";),
+                               
NodeFactory.createURI("http://example.com/predicate";),
+                               
NodeFactory.createURI("http://example.com/object";)));
+
+               g.add(new Triple(NodeFactory.createAnon(AnonId.create("a")),
+                               NodeFactory.createURI("http://example.com/p1";), 
NodeFactory
+                                               
.createAnon(AnonId.create("b"))));
+
+               g.add(new Triple(NodeFactory.createAnon(AnonId.create("b")),
+                               NodeFactory.createURI("http://example.com/p2";), 
NodeFactory
+                                               .createLiteral("foo")));
+
+               g.add(new 
Triple(NodeFactory.createURI("http://example.com/ns/e";),
+                               
NodeFactory.createURI("http://example.com/ns/p5";), NodeFactory
+                                               .createLiteral("verify base 
works")));
+
+               return g;
+       }
+
+       public static Model getModel() {
+               return ModelFactory.createModelForGraph(getGraph());
+       }
+
+       public static Model populateRDFModel(Model model, String name)
+                       throws IOException {
+               ModelHelper.txnBegin(model);
+               model.read(getRDFInput(name), "http://example.com/test/";);
+               ModelHelper.txnCommit(model);
+               return model;
+       }
+
+       public static Model getRDFModel(String name) throws IOException {
+               return populateRDFModel(ModelFactory.createDefaultModel(), 
name);
+       }
+
+       private static String[] getRDFData(String name) throws IOException {
+               String[] data = rdfData.get(name);
+               if (data == null) {
+                       throw new IOException("Can not find RDF data " + name);
+               }
+               return data;
+       }
+
+       public static InputStream getRDFInput(String name) throws IOException {
+               return new ByteArrayInputStream(toDataString(getRDFData(name))
+                               .getBytes());
+       }
+
+       public static InputStream getRDFInput() throws IOException {
+               return getRDFInput("");
+       }
+
+       private static String[] getTTLData(String name) throws IOException {
+               String[] data = ttlData.get(name);
+               if (data == null) {
+                       throw new IOException("Can not find TTL data " + name);
+               }
+               return data;
+       }
+
+       public static InputStream getTTLInput(String name) throws IOException {
+               return new ByteArrayInputStream(toDataString(getTTLData(name))
+                               .getBytes());
+       }
+
+       public static InputStream getTTLInput() throws IOException {
+               return getTTLInput("");
+       }
+
+       public static Reader getRDFReader(String name) throws IOException {
+
+               return new StringReader(toDataString(getRDFData(name)));
+       }
+
+       public static Reader getRDFReader() throws IOException {
+
+               return getRDFReader("");
+       }
+
+       public static Reader getTTLReader() throws IOException {
+               return getTTLReader("");
+       }
+
+       public static Reader getTTLReader(String name) throws IOException {
+               return new StringReader(toDataString(getTTLData(name)));
+       }
+
+       public static String getRDFName(String name) throws IOException {
+               return createFile(toDataString(getRDFData(name)), ".rdf");
+       }
+
+       public static String getRDFName() throws IOException {
+               return getRDFName("");
+       }
+
+       private static String createFile(String data, String extension)
+                       throws IOException {
+               File f = File.createTempFile("tfd", extension);
+               f.deleteOnExit();
+               FileOutputStream fos = new FileOutputStream(f);
+               // fos.write("<?xml version=\"1.0\" 
encoding=\"UTF-8\"?>".getBytes());
+               // fos.write(System.getProperty("line.separator").getBytes());
+               fos.write(data.getBytes());
+               fos.close();
+               return f.toURI().toURL().toExternalForm();
+       }
+
+       public static String getTTLName() throws IOException {
+               return getTTLName("");
+       }
+
+       public static String getTTLName(String name) throws IOException {
+               return createFile(toDataString(getTTLData(name)), ".ttl");
+       }
+
+       @Test
+       public void testEquality() throws Exception {
+               Model ttl = 
ModelFactory.createDefaultModel().read(getTTLInput(), NS,
+                               "TTL");
+               Model rdf = 
ModelFactory.createDefaultModel().read(getRDFInput(), NS,
+                               "RDF/XML-ABBREV");
+
+               assertTrue(ttl.isIsomorphicWith(rdf));
+               assertTrue(rdf.isIsomorphicWith(ttl));
+       }
+
+       public static void main(String... argv) throws Exception {
+               // //Model model = ModelFactory.createDefaultModel() ;
+               // //String x = "<s> <p> 'verify it works' ." ;
+               //
+               //
+               // //Reader sr = getTTLReader();
+               // //model.read(sr, "http://example/";, "TTL") ;
+               // //model.read(sr, "", "TTL") ;
+               // //model.read( getRDFInput() );
+               // Model ttl = ModelFactory.createDefaultModel().read( 
getTTLInput(),
+               // "", "TTL");
+               // Model rdf = ModelFactory.createDefaultModel().read( 
getRDFInput(),
+               // "", "RDF/XML-ABBREV");
+               //
+               // ttl.write(System.out, "RDF/XML-ABBREV") ;
+               // System.out.println("-----") ;
+               // // model.setNsPrefix("ex", "http://example/";) ;
+               // rdf.write(System.out, "N-TRIPLES") ;
+               // System.out.println("-----") ;
+               // System.out.println( getTTLName() );
+               // System.out.println( "ttl iso rdf: 
"+ttl.isIsomorphicWith(rdf));
+               //
+               // System.out.println( getRDFName() );
+               // System.out.println( "rdf iso ttl: 
"+rdf.isIsomorphicWith(ttl));
+
+               String[] lines = { "<rdf:RDF",
+                               "  
xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\";>",
+                               "  <rdf:Description rdf:about=\"e\">",
+                               "    <p5>verify base works</p5>", "  
</rdf:Description>",
+                               "</rdf:RDF>" };
+
+               String eol = System.getProperty("line.separator");
+               StringBuilder sb = new StringBuilder();
+               for (String l : lines) {
+                       sb.append(l).append(eol);
+               }
+
+               Model model = ModelFactory.createDefaultModel();
+
+               StringReader sr = new StringReader(sb.toString());
+               model.read(sr, "http://example/";);
+               model.write(System.out, "N-TRIPLES");
+               System.out.println("-----");
+               model.setNsPrefix("ex", "http://example/";);
+               model.write(System.out, "RDF/XML-ABBREV", "http://another/";);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/TestUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/TestUtils.java 
b/jena-core/src/test/java/org/apache/jena/testing_framework/TestUtils.java
new file mode 100644
index 0000000..78d4bab
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/testing_framework/TestUtils.java
@@ -0,0 +1,320 @@
+/*
+ * 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.testing_framework;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.*;
+
+import org.apache.jena.util.CollectionFactory;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.WrappedIterator;
+
+/**
+ * Foo basis for Jena test cases which provides assertFalse and assertDiffer.
+ * Often the logic of the names is clearer than using a negation.
+ */
+public class TestUtils {
+       // do not instantiate
+       protected TestUtils() {
+               throw new UnsupportedOperationException("Do not instantiate 
TestUtils");
+       };
+
+       /**
+        * assert that the two objects must be unequal according to .equals().
+        * 
+        * @param title
+        *            a labelling string for the assertion failure text
+        * @param x
+        *            an object to test; the subject of a .equals()
+        * @param y
+        *            the other object; the argument of the .equals()
+        */
+       public static void assertDiffer(String title, Object x, Object y) {
+               if (x == null ? y == null : x.equals(y))
+                       fail((title == null ? "objects should be different, but 
both were: "
+                                       : title)
+                                       + x);
+       }
+
+       /**
+        * assert that the two objects must be unequal according to .equals().
+        * 
+        * @param x
+        *            an object to test; the subject of a .equals()
+        * @param y
+        *            the other object; the argument of the .equals()
+        */
+       public static void assertDiffer(Object x, Object y) {
+               assertDiffer(null, x, y);
+       }
+
+       /**
+        * assert that the object <code>x</code> must be of the class
+        * <code>expected</code>.
+        */
+       public static void assertInstanceOf(Class<?> expected, Object x) {
+               if (x == null)
+                       fail("expected instance of " + expected + ", but had 
null");
+               if (!expected.isInstance(x))
+                       fail("expected instance of " + expected + ", but had 
instance of "
+                                       + x.getClass());
+       }
+
+       /**
+        * Answer a Set formed from the elements of the List <code>L</code>.
+        */
+       public static <T> Set<T> listToSet(List<T> L) {
+               return CollectionFactory.createHashedSet(L);
+       }
+
+       /**
+        * Answer a List of the substrings of <code>s</code> that are separated 
by
+        * spaces.
+        */
+       public static List<String> listOfStrings(String s) {
+               List<String> result = new ArrayList<String>();
+               StringTokenizer st = new StringTokenizer(s);
+               while (st.hasMoreTokens())
+                       result.add(st.nextToken());
+               return result;
+       }
+
+       /**
+        * Answer a Set of the substrings of <code>s</code> that are separated 
by
+        * spaces.
+        */
+       public static Set<String> setOfStrings(String s) {
+               Set<String> result = new HashSet<String>();
+               StringTokenizer st = new StringTokenizer(s);
+               while (st.hasMoreTokens())
+                       result.add(st.nextToken());
+               return result;
+       }
+
+       /**
+        * Answer a list containing the single object <code>x</code>.
+        */
+       public static <T> List<T> listOfOne(T x) {
+               List<T> result = new ArrayList<T>();
+               result.add(x);
+               return result;
+       }
+
+       /**
+        * Answer a Set containing the single object <code>x</code>.
+        */
+       public static <T> Set<T> setOfOne(T x) {
+               Set<T> result = new HashSet<T>();
+               result.add(x);
+               return result;
+       }
+
+       /**
+        * Answer a fresh list which is the concatenation of <code>L</code> then
+        * <code>R</code>. Neither <code>L</code> nor <code>R</code> is updated.
+        */
+       public static <T> List<T> append(List<? extends T> L, List<? extends T> 
R) {
+               List<T> result = new ArrayList<T>(L);
+               result.addAll(R);
+               return result;
+       }
+
+       /**
+        * Answer an iterator over the space-separated substrings of 
<code>s</code>.
+        */
+       protected static ExtendedIterator<String> iteratorOfStrings(String s) {
+               return WrappedIterator.create(listOfStrings(s).iterator());
+       }
+
+       /**
+        * Answer the constructor of the class <code>c</code> which takes 
arguments
+        * of the type(s) in <code>args</code>, or <code>null</code> if there 
isn't
+        * one.
+        */
+       public static Constructor<?> getConstructor(Class<?> c, Class<?>[] 
args) {
+               try {
+                       return c.getConstructor(args);
+               } catch (NoSuchMethodException e) {
+                       return null;
+               }
+       }
+
+       /**
+        * Answer true iff <code>subClass</code> is the same class as
+        * <code>superClass</code>, if its superclass <i>is</i>
+        * <code>superClass</code>, or if one of its interfaces hasAsInterface 
that
+        * class.
+        */
+       public static boolean hasAsParent(Class<?> subClass, Class<?> 
superClass) {
+               if (subClass == superClass || subClass.getSuperclass() == 
superClass)
+                       return true;
+               Class<?>[] is = subClass.getInterfaces();
+               for (int i = 0; i < is.length; i += 1)
+                       if (hasAsParent(is[i], superClass))
+                               return true;
+               return false;
+       }
+
+       /**
+        * Fail unless <code>subClass</code> has <code>superClass</code> as a
+        * parent, either a superclass or an implemented (directly or not)
+        * interface.
+        */
+       public static void assertHasParent(Class<?> subClass, Class<?> 
superClass) {
+               if (hasAsParent(subClass, superClass) == false)
+                       fail("" + subClass + " should have " + superClass + " 
as a parent");
+       }
+
+       /**
+        * Tests o1.equals( o2 ) && o2.equals(o1) && o1.hashCode() == 
o2.hashCode()
+        * 
+        * @param o1
+        * @param o2
+        */
+       public static void assertEquivalent(Object o1, Object o2) {
+               assertEquals(o1, o2);
+               assertEquals(o2, o1);
+               assertEquals(o1.hashCode(), o2.hashCode());
+       }
+
+       /**
+        * Tests o1.equals( o2 ) && o2.equals(o1) && o1.hashCode() == 
o2.hashCode()
+        * 
+        * @param o1
+        * @param o2
+        */
+       public static void assertEquivalent(String msg, Object o1, Object o2) {
+               assertEquals(msg, o1, o2);
+               assertEquals(msg, o2, o1);
+               assertEquals(msg, o1.hashCode(), o2.hashCode());
+       }
+
+       /**
+        * Tests o1.equals( o2 ) && o2.equals(o1) && o1.hashCode() == 
o2.hashCode()
+        * 
+        * @param o1
+        * @param o2
+        */
+       public static void assertNotEquivalent(String msg, Object o1, Object 
o2) {
+               assertNotEquals(msg, o1, o2);
+               assertNotEquals(msg, o2, o1);
+       }
+
+       private static URL getURL(String fn) {
+               URL u = TestUtils.class.getClassLoader().getResource(fn);
+               if (u == null) {
+                       throw new RuntimeException(new 
FileNotFoundException(fn));
+               }
+               return u;
+       }
+
+       public static String getFileName(String fn) {
+
+               try {
+                       return getURL(fn).toURI().toString();
+               } catch (URISyntaxException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       public static InputStream getInputStream(String fn) throws IOException {
+               return getURL(fn).openStream();
+       }
+
+       // FIXME this is to be removed when testing is complete
+       public static void logAssertEquals(Class<?> clazz, String msg, Object 
obj1,
+                       Object obj2) {
+               if (obj1 == null && obj2 == null) {
+                       return;
+               }
+
+               if (obj1 != null) {
+                       if (obj1 == obj2) {
+                               return;
+                       }
+                       if (obj1.equals(obj2)) {
+                               return;
+                       }
+               }
+               LoggerFactory.getLogger(clazz).warn(
+                               String.format("%s expected: %s got: %s", msg, 
obj1, obj2));
+               System.out.println(String.format("[%sWARNING] %s expected: %s 
got: %s",
+                               clazz, msg, obj1, obj2));
+       }
+
+       // FIXME this is to be removed when testing is complete
+       public static void logAssertTrue(Class<?> clazz, String msg, boolean 
value) {
+               if (value) {
+                       return;
+               }
+
+               LoggerFactory.getLogger(clazz).warn(String.format("%s", msg));
+               System.out.println(String.format("[%s WARNING] %s ", clazz, 
msg));
+       }
+
+       // FIXME this is to be removed when testing is complete
+       public static void logAssertFalse(Class<?> clazz, String msg, boolean 
value) {
+               if (!value) {
+                       return;
+               }
+
+               LoggerFactory.getLogger(clazz).warn(String.format("%s", msg));
+               System.out.println(String.format("[%s WARNING] %s ", clazz, 
msg));
+       }
+
+       // FIXME this is to be removed when testing is complete
+       public static void logAssertSame(Class<?> clazz, String msg, Object 
obj1,
+                       Object obj2) {
+               if (obj1 == null && obj2 == null) {
+                       return;
+               }
+
+               if (obj1 != null) {
+                       if (obj1 == obj2) {
+                               return;
+                       }
+               }
+               LoggerFactory.getLogger(clazz).warn(
+                               String.format("%s expected: %s got: %s", msg, 
obj1, obj2));
+               System.out
+                               .println(String.format("[%s WARNING] %s 
expected: %s got: %s",
+                                               clazz, msg, obj1, obj2));
+       }
+
+       public static String safeName(String s) {
+               // Safe from Eclipse
+               s = s.replace('(', '[');
+               s = s.replace(')', ']');
+               return s;
+
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/Manifest.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/Manifest.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/Manifest.java
new file mode 100644
index 0000000..79d2fce
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/Manifest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.testing_framework.manifest;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.jena.n3.IRIResolver;
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.util.FileManager;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.RDFS;
+import org.apache.jena.vocabulary.TestManifest;
+import org.apache.jena.vocabulary.TestManifestX;
+
+/**
+ * A test manifest for a single manifest file.
+ */
+
+public class Manifest {
+       // This class does not know about JUnit.
+       private static Logger log = LoggerFactory.getLogger(Manifest.class);
+       private Model manifest;
+       private String manifestName;
+       private String filename;
+       private List<String> includedFiles = new ArrayList<String>();
+       private Resource manifestRes = null;
+
+       public Manifest(String fn) {
+               log.debug("Manifest = " + fn);
+               filename = IRIResolver.resolveGlobal(fn);
+               log.debug("         = " + filename);
+               manifest = FileManager.get().loadModel(filename);
+               parseIncludes();
+               parseManifest();
+       }
+
+       public String getName() {
+               return manifestName;
+       }
+
+       public Iterator<String> includedManifests() {
+               return includedFiles.iterator();
+       }
+
+       private void parseManifest() {
+               StmtIterator manifestStmts = manifest.listStatements(null, 
RDF.type,
+                               TestManifest.Manifest);
+               if (!manifestStmts.hasNext()) {
+                       log.warn("No manifest in manifest file: " + filename);
+                       return;
+               }
+
+               Statement manifestItemStmt = manifestStmts.nextStatement();
+               if (manifestStmts.hasNext()) {
+                       log.warn("Multiple manifests in manifest file: " + 
filename);
+                       return;
+               }
+
+               manifestRes = manifestItemStmt.getSubject();
+               manifestName = getLiteral(manifestRes, RDFS.label);
+               if (manifestName == null)
+                       manifestName = getLiteral(manifestRes, RDFS.comment);
+               manifestStmts.close();
+       }
+
+       // For every test item (does not recurse)
+       public void apply(ManifestItemHandler gen) {
+
+               StmtIterator manifestStmts = manifest.listStatements(null, 
RDF.type,
+                               TestManifest.Manifest);
+
+               for (; manifestStmts.hasNext();) {
+                       Statement manifestItemStmt = 
manifestStmts.nextStatement();
+                       Resource manifestRes = manifestItemStmt.getSubject();
+
+                       // For each item in this manifest
+                       StmtIterator listIter = manifestRes
+                                       .listProperties(TestManifest.entries);
+                       for (; listIter.hasNext();) {
+                               // List head
+                               Resource listItem = 
listIter.nextStatement().getResource();
+                               for (; !listItem.equals(RDF.nil);) {
+                                       ManifestItem item = new 
ManifestItem(listItem
+                                                       
.getRequiredProperty(RDF.first).getResource());
+                                       gen.processManifestItem(item);
+                                       // Move to next list item
+                                       listItem = 
listItem.getRequiredProperty(RDF.rest)
+                                                       .getResource();
+                               }
+                       }
+                       listIter.close();
+               }
+               manifestStmts.close();
+       }
+
+       // -------- included manifests
+       private void parseIncludes() {
+               parseIncludes(TestManifest.include);
+               parseIncludes(TestManifestX.include);
+       }
+
+       private void parseIncludes(Property property) {
+               StmtIterator includeStmts = manifest.listStatements(null, 
property,
+                               (RDFNode) null);
+
+               for (; includeStmts.hasNext();) {
+                       Statement s = includeStmts.nextStatement();
+                       if (!(s.getObject() instanceof Resource)) {
+                               log.warn("Include: not a Resource" + s);
+                               continue;
+                       }
+                       Resource r = s.getResource();
+                       parseOneIncludesList(r);
+               }
+               includeStmts.close();
+       }
+
+       private void parseOneIncludesList(Resource r) {
+               if (r == null)
+                       return;
+
+               if (r.equals(RDF.nil))
+                       return;
+
+               if (!r.isAnon()) {
+                       String uri = r.getURI();
+                       if (includedFiles.contains(uri))
+                               return;
+                       includedFiles.add(r.getURI());
+                       return;
+               }
+
+               // BNnode => list
+               Resource listItem = r;
+               while (!listItem.equals(RDF.nil)) {
+                       r = 
listItem.getRequiredProperty(RDF.first).getResource();
+                       parseOneIncludesList(r);
+                       // Move on
+                       listItem = 
listItem.getRequiredProperty(RDF.rest).getResource();
+               }
+       }
+
+       public static Resource getResource(Resource r, Property p) {
+               if (r == null)
+                       return null;
+               if (!r.hasProperty(p))
+                       return null;
+
+               RDFNode n = r.getProperty(p).getObject();
+               if (n instanceof Resource)
+                       return (Resource) n;
+
+               throw new ManifestException("Manifest problem (not a Resource): 
" + n
+                               + " => " + p);
+       }
+
+       public static Collection<Resource> listResources(Resource r, Property 
p) {
+               if (r == null)
+                       return null;
+               List<Resource> x = new ArrayList<Resource>();
+               StmtIterator sIter = r.listProperties(p);
+               for (; sIter.hasNext();) {
+                       RDFNode n = sIter.next().getObject();
+                       if (!(n instanceof Resource))
+                               throw new ManifestException(
+                                               "Manifest problem (not a 
Resource): " + n + " => " + p);
+                       x.add((Resource) n);
+               }
+               return x;
+       }
+
+       public static String getLiteral(Resource r, Property p) {
+               if (r == null)
+                       return null;
+               if (!r.hasProperty(p))
+                       return null;
+
+               RDFNode n = r.getProperty(p).getObject();
+               if (n instanceof Literal)
+                       return ((Literal) n).getLexicalForm();
+
+               throw new ManifestException("Manifest problem (not a Literal): 
" + n
+                               + " => " + p);
+       }
+
+       public static String getLiteralOrURI(Resource r, Property p) {
+               if (r == null)
+                       return null;
+
+               if (!r.hasProperty(p))
+                       return null;
+
+               RDFNode n = r.getProperty(p).getObject();
+               if (n instanceof Literal)
+                       return ((Literal) n).getLexicalForm();
+
+               if (n instanceof Resource) {
+                       Resource r2 = (Resource) n;
+                       if (!r2.isAnon())
+                               return r2.getURI();
+               }
+
+               throw new ManifestException("Manifest problem: " + n + " => " + 
p);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestException.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestException.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestException.java
new file mode 100644
index 0000000..14a3d44
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testing_framework.manifest;
+
+/**
+ * TestException a root exception for all (intentional) exceptions in tests
+ * setup, not a failure of the test itself (e.g. manifest problems)
+ */
+
+public class ManifestException extends RuntimeException {
+
+       /**
+        * 
+        */
+       private static final long serialVersionUID = 5601201233175898449L;
+
+       public ManifestException() {
+               super();
+       }
+
+       public ManifestException(Throwable cause) {
+               super(cause);
+       }
+
+       public ManifestException(String msg) {
+               super(msg);
+       }
+
+       public ManifestException(String msg, Throwable cause) {
+               super(msg, cause);
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestFile.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestFile.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestFile.java
new file mode 100644
index 0000000..d7493bb
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestFile.java
@@ -0,0 +1,38 @@
+/*
+ * 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.testing_framework.manifest;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to declare the manifest file for the test.
+ * 
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ManifestFile {
+       /**
+        * The file that the annotated class should read
+        */
+       String value();
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItem.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItem.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItem.java
new file mode 100644
index 0000000..81999bd
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItem.java
@@ -0,0 +1,53 @@
+package org.apache.jena.testing_framework.manifest;
+
+import org.apache.jena.n3.turtle.TurtleTestVocab;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.vocabulary.RDF;
+import org.apache.jena.vocabulary.TestManifest;
+
+public class ManifestItem {
+       private Resource entry;
+
+       public ManifestItem(Resource entry) {
+               this.entry = entry;
+       }
+
+       public Resource getEntry() {
+               return entry;
+       }
+
+       public String getTestName() {
+               return Manifest.getLiteral(entry, TestManifest.name);
+       }
+
+       public Resource getAction() {
+               return Manifest.getResource(entry, TestManifest.action);
+       }
+
+       public Resource getResult() {
+               return Manifest.getResource(entry, TestManifest.result);
+       }
+
+       public Resource getType() {
+               return Manifest.getResource(entry, RDF.type);
+       }
+
+       public Resource getOutput() {
+               Resource result = getResult();
+               return result == null ? null : Manifest.getResource(result,
+                               TurtleTestVocab.output);
+       }
+
+       public Resource getInput() {
+               Resource action = getAction();
+               return action == null ? null : Manifest.getResource(action,
+                               TurtleTestVocab.input);
+       }
+
+       public String getUriString() {
+               Resource action = getAction();
+               Resource inputIRIr = action == null ? null : 
Manifest.getResource(
+                               action, TurtleTestVocab.inputIRI);
+               return (inputIRIr == null) ? null : inputIRIr.getURI();
+       }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/b293ee8a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItemHandler.java
----------------------------------------------------------------------
diff --git 
a/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItemHandler.java
 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItemHandler.java
new file mode 100644
index 0000000..08ce69f
--- /dev/null
+++ 
b/jena-core/src/test/java/org/apache/jena/testing_framework/manifest/ManifestItemHandler.java
@@ -0,0 +1,35 @@
+/*
+ * 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.testing_framework.manifest;
+
+import java.util.List;
+
+import org.junit.runner.Runner;
+import org.junit.runners.model.InitializationError;
+
+public interface ManifestItemHandler {
+       /**
+        * Handle an item in a manifest
+        * 
+        * @throws InitializationError
+        */
+       public void processManifestItem(ManifestItem item);
+
+       public void setTestRunnerList(List<Runner> runners);
+}

Reply via email to