/* * 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.sparql.resultset; import java.io.ByteArrayInputStream ; import java.io.ByteArrayOutputStream ; import java.util.ArrayList ; import java.util.List ; import java.util.NoSuchElementException; import org.apache.jena.atlas.junit.BaseTest ; import org.apache.jena.atlas.lib.StrUtils ; import org.apache.jena.graph.Node ; import org.apache.jena.graph.NodeFactory ; import org.apache.jena.query.ResultSet ; import org.apache.jena.query.ResultSetFactory ; import org.apache.jena.query.ResultSetFormatter ; import org.apache.jena.query.ResultSetRewindable ; import org.apache.jena.rdf.model.Model ; import org.apache.jena.sparql.core.Var ; import org.apache.jena.sparql.engine.QueryIterator ; import org.apache.jena.sparql.engine.ResultSetStream ; import org.apache.jena.sparql.engine.binding.Binding ; import org.apache.jena.sparql.engine.binding.BindingFactory ; import org.apache.jena.sparql.engine.binding.BindingMap ; import org.apache.jena.sparql.engine.iterator.QueryIterPlainWrapper ; import org.apache.jena.sparql.engine.iterator.QueryIterSingleton ; import org.apache.jena.sparql.sse.SSE ; import org.apache.jena.sparql.sse.builders.BuilderResultSet ; import org.apache.jena.sparql.util.NodeFactoryExtra ; import org.apache.jena.sparql.util.ResultSetUtils ; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test ; public class TestResultSet extends BaseTest { @BeforeClass public static void setup() { // Disable warnings these tests will produce ResultSetPeeking.warnOnSyncErrors = false; } @AfterClass public static void teardown() { // Re-enable warnings ResultSetPeeking.warnOnSyncErrors = true; } // Test reading, writing and comparison @Test public void test_RS_1() { ResultSetRewindable rs1 = new ResultSetMem() ; ByteArrayOutputStream arr = new ByteArrayOutputStream() ; ResultSetFormatter.outputAsXML(arr, rs1) ; rs1.reset() ; ByteArrayInputStream ins = new ByteArrayInputStream(arr.toByteArray()) ; ResultSet rs2 = ResultSetFactory.fromXML(ins) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_1_str() { ResultSetRewindable rs1 = new ResultSetMem() ; String x = ResultSetFormatter.asXMLString(rs1) ; rs1.reset() ; ResultSet rs2 = ResultSetFactory.fromXML(x) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_2() { ResultSetRewindable rs1 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ByteArrayOutputStream arr = new ByteArrayOutputStream() ; ResultSetFormatter.outputAsXML(arr, rs1) ; rs1.reset() ; ByteArrayInputStream ins = new ByteArrayInputStream(arr.toByteArray()) ; ResultSet rs2 = ResultSetFactory.fromXML(ins) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_2_str() { ResultSetRewindable rs1 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; String x = ResultSetFormatter.asXMLString(rs1) ; rs1.reset() ; ResultSet rs2 = ResultSetFactory.fromXML(x) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } // RDF @Test public void test_RS_3() { ResultSetRewindable rs1 = new ResultSetMem() ; Model model = RDFOutput.encodeAsModel(rs1) ; rs1.reset() ; ResultSet rs2 = RDFInput.fromRDF(model) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_4() { ResultSetRewindable rs1 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; Model model = RDFOutput.encodeAsModel(rs1) ; rs1.reset() ; ResultSetRewindable rs2 = ResultSetFactory.makeRewindable(RDFInput.fromRDF(model)) ; boolean b = ResultSetCompare.equalsByTerm(rs1, rs2) ; if ( ! b ) { rs1.reset() ; rs2.reset() ; ResultSetFormatter.out(rs1) ; ResultSetFormatter.out(rs2) ; } assertTrue(b) ; } // JSON @Test public void test_RS_5() { ResultSetRewindable rs1 = new ResultSetMem() ; ByteArrayOutputStream arr = new ByteArrayOutputStream() ; ResultSetFormatter.outputAsJSON(arr, rs1) ; rs1.reset() ; ByteArrayInputStream ins = new ByteArrayInputStream(arr.toByteArray()) ; ResultSet rs2 = ResultSetFactory.fromJSON(ins) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_6() { ResultSetRewindable rs1 = make2Rewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ByteArrayOutputStream arr = new ByteArrayOutputStream() ; ResultSetFormatter.outputAsJSON(arr, rs1) ; rs1.reset() ; ByteArrayInputStream ins = new ByteArrayInputStream(arr.toByteArray()) ; ResultSet rs2 = ResultSetFactory.fromJSON(ins) ; // Test using the DAWG examples assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } // Into some format. @Test public void test_RS_7() { ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RS_XML, true) ; } @Test public void test_RS_8() { ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RS_JSON, true) ; } @Test public void test_RS_9() { ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RDF_XML, false) ; } @Test public void test_RS_10() { ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; for ( ; rs.hasNext(); rs.next()) { } // We should be able to call hasNext() as many times as we want! assertFalse(rs.hasNext()); } @Test public void test_RS_union_1() { ResultSet rs1 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs2 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs3 = make2("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; assertTrue(ResultSetCompare.equalsByTerm(rs3, ResultSetUtils.union(rs1, rs2))) ; } @Test(expected = ResultSetException.class) public void test_RS_union_2() { ResultSet rs1 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs2 = make("y", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSetUtils.union(rs1, rs2) ; } private void test_RS_fmt(ResultSet rs, ResultsFormat fmt, boolean ordered) { ResultSetRewindable rs1 = ResultSetFactory.makeRewindable(rs) ; ByteArrayOutputStream arr = new ByteArrayOutputStream() ; ResultSetFormatter.output(arr, rs1, fmt) ; byte bytes[] = arr.toByteArray() ; rs1.reset() ; ByteArrayInputStream ins = new ByteArrayInputStream(bytes) ; ResultSetRewindable rs2 = ResultSetFactory.makeRewindable(ResultSetFactory.load(ins, fmt)) ; // Ordered? Unordered? boolean b = ResultSetCompare.equalsByTerm(rs1, rs2) ; if ( ordered ) { rs1.reset() ; rs2.reset() ; b = b & ResultSetCompare.equalsByTerm(rs1, rs2) ; } if ( !b ) { System.out.println(new String(bytes)) ; rs1.reset() ; rs2.reset() ; ResultSetFormatter.out(rs1) ; ResultSetFormatter.out(rs2) ; } assertTrue(b) ; } // Test comparison @Test public void test_RS_cmp_1() { ResultSetRewindable rs1 = new ResultSetMem() ; ResultSetRewindable rs2 = new ResultSetMem() ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; rs1.reset() ; rs2.reset() ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_cmp_2() { ResultSet rs1 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs2 = new ResultSetMem() ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_cmp_3() { ResultSet rs1 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs2 = new ResultSetMem() ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_cmp_4() { ResultSet rs1 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSet rs2 = make("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_cmp_5() { // Same variable, different values ResultSetRewindable rs1 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local:1")) ; ResultSetRewindable rs2 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local:2")) ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; rs1.reset() ; rs2.reset() ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } @Test public void test_RS_cmp_6() { // Different variable, same values ResultSetRewindable rs1 = makeRewindable("x", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; ResultSetRewindable rs2 = makeRewindable("y", org.apache.jena.graph.NodeFactory.createURI("tag:local")) ; assertFalse(ResultSetCompare.equalsByTermAndOrder(rs1, rs2)) ; rs1.reset() ; rs2.reset() ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; } // Value based @Test public void test_RS_cmp_value_1() { ResultSetRewindable rs1 = makeRewindable("x", NodeFactoryExtra.parseNode("123")) ; ResultSetRewindable rs2 = makeRewindable("x", NodeFactoryExtra.parseNode("0123")) ; assertFalse(ResultSetCompare.equalsByTerm(rs1, rs2)) ; assertTrue(ResultSetCompare.equalsByValue(rs1, rs2)) ; } // Peeking @Test public void test_RS_peeking_1() { ResultSetPeekable rs = makePeekable("x", NodeFactory.createURI("tag:local")); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Peeking should not move the result set onwards so hasNext() should still report true assertTrue(rs.hasNext()); assertNotNull(rs.next()); assertFalse(rs.hasNext()); } @Test(expected=NoSuchElementException.class) public void test_RS_peeking_2() { ResultSetPeekable rs = makePeekable("x", NodeFactory.createURI("tag:local")); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Peeking should not move the result set onwards so hasNext() should still report true assertTrue(rs.hasNext()); assertNotNull(rs.next()); assertFalse(rs.hasNext()); // Peeking beyond end of results throws an error rs.peek(); } @Test public void test_RS_peeking_3() { // Expect that a rewindable result set will be peekable ResultSetPeekable rs = (ResultSetPeekable)makeRewindable("x", NodeFactory.createURI("tag:local")); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Peeking should not move the result set onwards so hasNext() should still report true assertTrue(rs.hasNext()); assertNotNull(rs.next()); assertFalse(rs.hasNext()); } @Test(expected=NoSuchElementException.class) public void test_RS_peeking_4() { // Expect that a rewindable result set will be peekable ResultSetPeekable rs = (ResultSetPeekable) makeRewindable("x", NodeFactory.createURI("tag:local")); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Peeking should not move the result set onwards so hasNext() should still report true assertTrue(rs.hasNext()); assertNotNull(rs.next()); assertFalse(rs.hasNext()); // Peeking beyond end of results throws an error rs.peek(); } @Test public void test_RS_peeking_5() { // Peeking should be able to cope with people moving on the underlying result set independently ResultSet inner = new ResultSetMem(make("x", NodeFactory.createURI("tag:local")), make("x", NodeFactory.createURI("tag:local"))); ResultSetPeekable rs = ResultSetFactory.makePeekable(inner); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Move on the inner result set independently inner.next(); // Since we fiddled with the underlying result set there won't be further elements available anymore assertFalse(rs.hasNext()); } @Test public void test_RS_peeking_6() { // Peeking should be able to cope with people moving on the underlying result set independently ResultSet inner = new ResultSetMem(make("x", NodeFactory.createURI("tag:local")), make("x", NodeFactory.createURI("tag:local")), make("x", NodeFactory.createURI("tag:local"))); ResultSetPeekable rs = ResultSetFactory.makePeekable(inner); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Move on the inner result set independently inner.next(); // Since we fiddled with the underlying result set we'll be out of sync // but there should still be further data available assertTrue(rs.hasNext()); } @Test public void test_RS_peeking_7() { // Peeking may fail if someone moves backwards in the result set // If we hadn't moved pass the first item this should be safe ResultSetRewindable inner = makeRewindable("x", NodeFactory.createURI("tag:local")); ResultSetPeekable rs = ResultSetFactory.makePeekable(inner); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); // Reset the inner result set independently inner.reset(); // Since we moved the underlying result set backwards but we hadn't gone anywhere // we should still be able to safely access the underlying results assertTrue(rs.hasNext()); } @Test(expected=IllegalStateException.class) public void test_RS_peeking_8() { // Peeking may fail if someone moves backwards in the result set // If we had moved past the first item this should be an error ResultSetRewindable inner = new ResultSetMem(make("x", NodeFactory.createURI("tag:local")), make("x", NodeFactory.createURI("tag:local"))); ResultSetPeekable rs = ResultSetFactory.makePeekable(inner); assertTrue(rs.hasNext()); assertNotNull(rs.peek()); assertNotNull(rs.next()); // Reset the inner result set independently inner.reset(); // Since we moved the underlying result set backwards and had moved somewhere we // are now in an illegal state rs.hasNext(); } @Test public void test_RS_peeking_9() { // Check that peeking causes the correct row to be returned when we actually access the rows Node first = NodeFactory.createURI("tag:first"); Node second = NodeFactory.createURI("tag:second"); Var x = Var.alloc("x"); ResultSet inner = new ResultSetMem(make("x", first), make("x", second)); ResultSetPeekable rs = ResultSetFactory.makePeekable(inner); assertTrue(rs.hasNext()); // Peek and check row is as expected Binding peeked = rs.peekBinding(); assertNotNull(peeked); assertTrue(first.equals(peeked.get(x))); // Check first row is as expected Binding next = rs.nextBinding(); assertNotNull(next); assertTrue(first.equals(next.get(x))); // Repeat for second row peeked = rs.peekBinding(); assertNotNull(peeked); assertTrue(second.equals(peeked.get(x))); next = rs.nextBinding(); assertNotNull(next); assertTrue(second.equals(next.get(x))); } // ---- Isomorphism. /* This is from the DAWG test suite. * Result set 1: * --------------- * | x | y | * =============== * | _:b0 | _:b1 | * | _:b2 | _:b3 | * | _:b1 | _:b0 | * --------------- * Result set 2: * --------------- * | x | y | * =============== * | _:b1 | _:b0 | * | _:b3 | _:b2 | * | _:b2 | _:b3 | * --------------- */ // nasty result set. // These are the same but the first row of rs2$ throws in a wrong mapping of b0/c1 // Right mapping is: // b0->c3, b1->c2, b2->c1, b3->c0 // Currently we get, working simply top to bottom, no backtracking: // b0->c1, b1->c0, b2->c3, b3->c2, then last row fails as _:b1 is mapped to c0, b0 to c1 not (c2, c3) private static String[] rs1$ = { "(resultset (?x ?y)", " (row (?x _:b0) (?y _:b1))", " (row (?x _:b2) (?y _:b3))", " (row (?x _:b1) (?y _:b0))", ")"} ; private static String[] rs2$ = { "(resultset (?x ?y)", " (row (?x _:c1) (?y _:c0))", " (row (?x _:c3) (?y _:c2))", " (row (?x _:c2) (?y _:c3))", ")"} ; @Test public void test_RS_iso_1() { isotest(rs1$, rs2$) ; } private void isotest(String[] rs1$2, String[] rs2$2) { ResultSetRewindable rs1 = ResultSetFactory.makeRewindable(BuilderResultSet.build(SSE.parseItem(StrUtils.strjoinNL(rs1$)))) ; ResultSetRewindable rs2 = ResultSetFactory.makeRewindable(BuilderResultSet.build(SSE.parseItem(StrUtils.strjoinNL(rs2$)))) ; assertTrue(ResultSetCompare.isomorphic(rs1, rs2)) ; rs1.reset() ; rs2.reset() ; assertTrue(ResultSetCompare.equalsByTerm(rs1, rs2)) ; assertTrue(ResultSetCompare.equalsByValue(rs1, rs2)) ; } // -------- Support functions private ResultSet make(String var, Node val) { BindingMap b = BindingFactory.create() ; b.add(Var.alloc(var), val) ; List<String> vars = new ArrayList<>() ; vars.add(var) ; QueryIterator qIter = QueryIterSingleton.create(b, null) ; ResultSet rs = new ResultSetStream(vars, null, qIter) ; return rs ; } private ResultSet make2(String var, Node val) { BindingMap b1 = BindingFactory.create() ; b1.add(Var.alloc(var), val) ; BindingMap b2 = BindingFactory.create() ; b2.add(Var.alloc(var), val) ; List<String> vars = new ArrayList<>() ; vars.add(var) ; List<Binding> solutions = new ArrayList<>() ; solutions.add(b1) ; solutions.add(b2) ; QueryIterator qIter = new QueryIterPlainWrapper(solutions.iterator(), null) ; ResultSet rs = new ResultSetStream(vars, null, qIter) ; return rs ; } private ResultSetRewindable makeRewindable(String var, Node val) { ResultSet rs = make(var, val) ; ResultSetRewindable rsw = ResultSetFactory.makeRewindable(rs) ; return rsw ; } private ResultSetRewindable make2Rewindable(String var, Node val) { ResultSet rs = make2(var, val) ; ResultSetRewindable rsw = ResultSetFactory.makeRewindable(rs) ; return rsw ; } private ResultSet make(String var1, Node val1, String var2, Node val2 ) { BindingMap b = BindingFactory.create() ; b.add(Var.alloc(var1), val1) ; b.add(Var.alloc(var2), val2) ; List<String> vars = new ArrayList<>() ; vars.add(var1) ; vars.add(var2) ; QueryIterator qIter = QueryIterSingleton.create(b, null) ; ResultSet rs = new ResultSetStream(vars, null, qIter) ; return rs ; } private ResultSetRewindable makeRewindable(String var1, Node val1, String var2, Node val2 ) { ResultSet rs = make(var1, val1, var2, val2) ; ResultSetRewindable rsw = ResultSetFactory.makeRewindable(rs) ; return rsw ; } private ResultSetPeekable makePeekable(String var, Node val) { ResultSet rs = make(var, val); ResultSetPeekable rsp = ResultSetFactory.makePeekable(rs); return rsp; } private ResultSetPeekable make2Peekable(String var1, Node val1, String var2, Node val2) { ResultSet rs = make(var1, val1, var2, val2); ResultSetPeekable rsp = ResultSetFactory.makePeekable(rs); return rsp; } }