/* * Licensed 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. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. */ package org.seaborne.patch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import org.apache.jena.atlas.io.IO; import org.apache.jena.atlas.iterator.Iter; import org.apache.jena.atlas.lib.ListUtils; import org.apache.jena.atlas.lib.StrUtils; import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.graph.TransactionHandler; import org.apache.jena.graph.Triple; import org.apache.jena.riot.Lang; import org.apache.jena.riot.RDFDataMgr; import org.apache.jena.shared.JenaException; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.DatasetGraphFactory; import org.apache.jena.sparql.sse.SSE; import org.junit.Test; import org.seaborne.patch.changes.RDFChangesWriter; import org.seaborne.patch.system.GraphChanges; import org.seaborne.patch.text.TokenWriter; import org.seaborne.patch.text.TokenWriterText; public class TestRDFChangesGraph { private static Graph txnGraph() { DatasetGraph dsg = DatasetGraphFactory.createTxnMem(); Graph g = dsg.getDefaultGraph(); // Jena 3.5.0 and earlier. // g = new GraphWrapper(g) { // @Override public TransactionHandler getTransactionHandler() { return new TransactionHandlerViewX(dsg); } // }; return g; } private static Graph txnGraph(String graphName) { DatasetGraph dsg = DatasetGraphFactory.createTxnMem(); Node gn = NodeFactory.createURI(graphName); Graph g = dsg.getGraph(gn); // Jena 3.5.0 and earlier. // g = new GraphWrapper(g) { // @Override public TransactionHandler getTransactionHandler() { return new TransactionHandlerView(dsg); } // }; return g; } private static Triple triple1 = SSE.parseTriple("(_:sx <p1> 11)"); private static Triple triple2 = SSE.parseTriple("(_:sx <p2> 22)"); // -- RDFPatchOps public static Graph changesGraph(Graph graph, OutputStream out) { TokenWriter tokenWriter = new TokenWriterText(out); RDFChanges changeLog = new RDFChangesWriter(tokenWriter); return changesGraph(graph, changeLog); } public static Graph changesGraph(Graph graph, RDFChanges changes) { return new GraphChanges(graph, changes); } // Bytes for changes private ByteArrayOutputStream bout; // The underlying graph private Graph baseGraph; // The graph with changes wrapper. private Graph graph; // ---- // Replay a changes byte stream into a completely fresh graph private Graph replay() { IO.close(bout); final boolean DEBUG = false; if ( DEBUG ) { System.out.println("== Graph =="); RDFDataMgr.write(System.out, baseGraph, Lang.NQ); System.out.println("== Replay =="); String x = StrUtils.fromUTF8bytes(bout.toByteArray()); System.out.print(x); System.out.println("== =="); } // A completely separate graph (different dataset) Graph graph2 = txnGraph(); try(ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray())) { RDFPatchOps.applyChange(graph2, bin); if ( DEBUG ) { System.out.println("== Graph outcome =="); RDFDataMgr.write(System.out, graph2, Lang.NT); System.out.println("== =="); } return graph2; } catch (IOException ex) { IO.exception(ex); return null; } } private static void check(Graph graph, Triple...quads) { if ( quads.length == 0 ) { assertTrue(graph.isEmpty()); return; } List<Triple> listExpected = Arrays.asList(quads); List<Triple> listActual = Iter.toList(graph.find()); assertEquals(listActual.size(), listExpected.size()); assertTrue(ListUtils.equalsUnordered(listExpected, listActual)); } private static void txn(Graph graph, Runnable action) { graph.getTransactionHandler().execute(action); } // ---- private void setup() { this.bout = new ByteArrayOutputStream(); this.baseGraph = txnGraph(); this.graph = changesGraph(baseGraph, bout); } private void setup(String graphName) { this.bout = new ByteArrayOutputStream(); this.baseGraph = txnGraph(graphName); this.graph = changesGraph(baseGraph, bout); } @Test public void record_00() { setup(); Graph graph2 = replay(); check(graph2); } @Test public void record_add() { setup(); txn(graph, ()->graph.add(triple1)); Graph g2 = replay(); check(g2, triple1); } @Test public void record_add_add_1() { setup(); txn(graph, ()-> { graph.add(triple1); graph.add(triple2); }); Graph g2 = replay(); check(g2, triple1, triple2); } @Test public void record_add_delete_1() { setup(); txn(graph, ()-> { graph.add(triple1); graph.delete(triple1); }); Graph g2 = replay(); check(g2); } @Test public void record_add_abort_2() { setup(); TransactionHandler h = graph.getTransactionHandler(); h.begin(); graph.add(triple1); h.abort(); Graph g2 = replay(); check(g2); } @Test public void record_add_abort_1() { setup(); try { txn(graph, ()->{ graph.add(triple1); throw new JenaException("Abort!"); }); } catch (JenaException ex) {} Graph g2 = replay(); check(g2); } @Test public void record_named_graph_1() { setup("http://example/graph"); txn(graph, ()->graph.add(triple1)); Graph g2 = replay(); check(g2, triple1); } }