This is an automated email from the ASF dual-hosted git repository. afs pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/jena.git
commit 2a164f62dbda22a9a14eddc168731e3a4a7501af Author: Andy Seaborne <[email protected]> AuthorDate: Mon May 4 12:21:35 2026 +0100 Tests for G --- .../src/main/java/org/apache/jena/system/G.java | 54 +++--- .../main/java/org/apache/jena/system/GList.java | 16 +- .../java/org/apache/jena/system/TS_System.java | 13 +- .../jena/system/{GTest.java => TestGCopy.java} | 2 +- .../java/org/apache/jena/system/TestG_Basic.java | 123 +++++++++++++ .../java/org/apache/jena/system/TestG_Classes.java | 200 ++++++++++++++++++++ .../test/java/org/apache/jena/system/TestG_PO.java | 201 +++++++++++++++++++++ .../java/org/apache/jena/system/TestG_Quad.java | 72 ++++++++ .../test/java/org/apache/jena/system/TestG_SP.java | 201 +++++++++++++++++++++ .../java/org/apache/jena/system/TestG_Triple.java | 103 +++++++++++ 10 files changed, 943 insertions(+), 42 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/system/G.java b/jena-arq/src/main/java/org/apache/jena/system/G.java index 7392a0b0c2..e9c1cab044 100644 --- a/jena-arq/src/main/java/org/apache/jena/system/G.java +++ b/jena-arq/src/main/java/org/apache/jena/system/G.java @@ -40,8 +40,6 @@ import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.engine.iterator.IterAbortable; import org.apache.jena.sparql.graph.NodeConst; -import org.apache.jena.sparql.util.graph.GNode; -import org.apache.jena.sparql.util.graph.GraphList; import org.apache.jena.util.iterator.ExtendedIterator; import org.apache.jena.util.iterator.WrappedIterator; @@ -270,7 +268,7 @@ public class G { */ public static Node getPO(Graph graph, Node predicate, Node object) { Objects.requireNonNull(graph, "graph"); - return object(first(find(graph, Node.ANY, predicate, object))); + return subject(first(find(graph, Node.ANY, predicate, object))); } /** @@ -287,7 +285,7 @@ public class G { */ public static boolean hasOnePO(Graph graph, Node predicate, Node object) { Objects.requireNonNull(graph, "graph"); - return findUniqueTriple(graph, Node.ANY, predicate, object) != null; + return findZeroOneTriple(graph, Node.ANY, predicate, object) != null; } /** @@ -374,6 +372,12 @@ public class G { return iterSP(graph, subject, predicate).toList(); } + /** Return a set of all objects for subject-predicate */ + public static Set<Node> allSP(Graph graph, Node subject, Node predicate) { + Objects.requireNonNull(graph, "graph"); + return find(graph, subject, predicate, null).mapWith(Triple::getObject).toSet(); + } + /** Count matches of subject-predicate (which can be wildcards). */ public static long countSP(Graph graph, Node subject, Node predicate) { Objects.requireNonNull(graph, "graph"); @@ -399,6 +403,12 @@ public class G { return iterPO(graph, predicate, object).toList(); } + /** Return a set of all subjects for predicate-object */ + public static Set<Node> allPO(Graph graph, Node predicate, Node object) { + Objects.requireNonNull(graph, "graph"); + return find(graph, null, predicate, object).mapWith(Triple::getSubject).toSet(); + } + /** Count matches of predicate-object (which can be wildcards). */ public static long countPO(Graph graph, Node predicate, Node object) { Objects.requireNonNull(graph, "graph"); @@ -499,34 +509,30 @@ public class G { public static List<Node> rdfList(Graph graph, Node node) { Objects.requireNonNull(graph, "graph"); Objects.requireNonNull(node, "node"); - GNode gNode = GNode.create(graph, node); - if ( ! GraphList.isListNode(gNode) ) - return null; - return GraphList.members(gNode); + List<Node> nodes = GList.members(graph, node); + return nodes; } /** Return a the length of an RDF list. */ public static int listLength(Graph graph, Node node) { Objects.requireNonNull(graph, "graph"); Objects.requireNonNull(node, "node"); - GNode gNode = GNode.create(graph, node); - if ( ! GraphList.isListNode(gNode) ) + if ( ! GList.isListNode(graph, node) ) return -1; - return GraphList.length(gNode); + return (int)GList.listLength(graph, node); } /** - * Return a java list where the {@code node} is an RDF list of nodes or a single - * node (returned a singleton list). + * Return a java list where the {@code node} is an RDF list of nodes, + * of if the node is not a list, return the node as a list of one. */ public static List<Node> getOneOrList(Graph graph, Node node) { Objects.requireNonNull(graph, "graph"); Objects.requireNonNull(node, "node"); - GNode gNode = GNode.create(graph, node); // An element on its own is a list of one - if ( ! GraphList.isListNode(gNode) ) + if ( ! GList.isListNode(graph, node) ) return List.of(node); - return GraphList.members(gNode); + return GList.members(graph, node); } // Sub-class / super-class @@ -647,18 +653,6 @@ public class G { ); } - /** Return a set of all objects for subject-predicate */ - public static Set<Node> allSP(Graph graph, Node subject, Node predicate) { - Objects.requireNonNull(graph, "graph"); - return find(graph, subject, predicate, null).mapWith(Triple::getObject).toSet(); - } - - /** Return a set of all subjects for predicate-object */ - public static Set<Node> allPO(Graph graph, Node predicate, Node object) { - Objects.requireNonNull(graph, "graph"); - return find(graph, null, predicate, object).mapWith(Triple::getSubject).toSet(); - } - // --- Graph walking. /** Count the number of in-arc to an object */ @@ -841,15 +835,15 @@ public class G { /** * Creates a copy of the given graph. - * If the graph implements Copyable<Graph> then the copy method is called. + * If the graph implements {@code Copyable<Graph>} then the copy method is called. * Otherwise, a new system default memory-based graph is created and the triples are copied * into it. * @param src the graph to copy * @return a copy of the graph */ - @SuppressWarnings("unchecked") public static Graph copy(Graph src) { if(src instanceof Copyable<?> copyable) { + @SuppressWarnings("unchecked") Copyable<Graph> copyableGraph = (Copyable<Graph>)copyable; return copyableGraph.copy(); } diff --git a/jena-arq/src/main/java/org/apache/jena/system/GList.java b/jena-arq/src/main/java/org/apache/jena/system/GList.java index 6880e2b2f4..a6ac246971 100644 --- a/jena-arq/src/main/java/org/apache/jena/system/GList.java +++ b/jena-arq/src/main/java/org/apache/jena/system/GList.java @@ -316,6 +316,13 @@ public class GList { return elt; } + public static boolean isListNode(Graph graph, Node node) { + if ( node.equals(NIL) ) + return true; + // Well-formedness check. + return isCons(graph, node); + } + /** * Run over a list, checking for cycles and well-formed list cons cells. * Call an action on each element if {@ocde elementAction} is not null. @@ -472,15 +479,6 @@ public class GList { // } } - - - private static boolean isListNode(Graph graph, Node node) { - if ( node.equals(NIL) ) - return true; - // Well-formedness check. - return isCons(graph, node); - } - private static boolean isCons (Graph graph, Node node) { return G.hasOneSP(graph, node, CDR) && G.hasOneSP(graph, node, CAR); } diff --git a/jena-arq/src/test/java/org/apache/jena/system/TS_System.java b/jena-arq/src/test/java/org/apache/jena/system/TS_System.java index 604162c3c9..8e2732d446 100644 --- a/jena-arq/src/test/java/org/apache/jena/system/TS_System.java +++ b/jena-arq/src/test/java/org/apache/jena/system/TS_System.java @@ -29,12 +29,21 @@ import org.junit.platform.suite.api.Suite; TestPrefixes.class , TestPrefixLib.class - , TestTxnCounter.class + , TestG_Basic.class + , TestG_SP .class + , TestG_PO .class + , TestG_Triple.class + , TestG_Quad.class + , TestG_Classes.class + + , TestGCopy.class + + , TestTxn.class , TestThreadAction.class , TestTxnLifecycle.class , TestTxnOp.class - , TestTxn.class , TestTxnThread.class + , TestTxnCounter.class , TestReadXML.class , TestRDFStarTranslation.class , TestFindNamespaces.class diff --git a/jena-arq/src/test/java/org/apache/jena/system/GTest.java b/jena-arq/src/test/java/org/apache/jena/system/TestGCopy.java similarity index 99% rename from jena-arq/src/test/java/org/apache/jena/system/GTest.java rename to jena-arq/src/test/java/org/apache/jena/system/TestGCopy.java index ee3361388f..d877548cb4 100644 --- a/jena-arq/src/test/java/org/apache/jena/system/GTest.java +++ b/jena-arq/src/test/java/org/apache/jena/system/TestGCopy.java @@ -33,7 +33,7 @@ import org.apache.jena.mem.GraphMemFast; import org.apache.jena.memvalue.GraphMemValue; import org.apache.jena.sparql.sse.SSE; -public class GTest { +public class TestGCopy { @Test public void copy() { diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Basic.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_Basic.java new file mode 100644 index 0000000000..9f64b8d799 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Basic.java @@ -0,0 +1,123 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; + +public class TestG_Basic { + + static { JenaSystem.init(); } + + @Test + public void contains_true_and_false() { + Graph g = graph(":s :p :o ."); + Node s = SSE.parseNode(":s"); + Node p = SSE.parseNode(":p"); + Node o = SSE.parseNode(":o"); + + assertTrue(G.contains(g, s, p, o)); + // different predicate -> false + assertFalse(G.contains(g, s, SSE.parseNode(":q"), o)); + } + + @Test + public void containsNode_detects_nodes() { + Graph g = graph(":s :p :o . _:b :p :o ."); + Node s = SSE.parseNode(":s"); + Node o = SSE.parseNode(":o"); + Node missing = SSE.parseNode(":x"); + + assertTrue(G.containsNode(g, s)); + assertTrue(G.containsNode(g, o)); + assertFalse(G.containsNode(g, missing)); + } + + @Test + public void hasProperty_true_false() { + Graph g = graph(":s :p :o ."); + Node s = SSE.parseNode(":s"); + Node p = SSE.parseNode(":p"); + Node q = SSE.parseNode(":q"); + + assertTrue(G.hasProperty(g, s, p)); + assertFalse(G.hasProperty(g, s, q)); + } + + @Test + public void containsOne_single_none_multiple() { + Node s = SSE.parseNode(":s"); + Node p = SSE.parseNode(":p"); + Node o = SSE.parseNode(":o"); + + Graph gSingle = graph(":s :p :o ."); + assertTrue(G.containsOne(gSingle, s, p, o)); + + Graph gNone = graph(":s :q :o ."); + assertFalse(G.containsOne(gNone, s, p, o)); + + Graph gMulti = graph(":s :p :o1 . :s :p :o2 ."); + assertFalse(G.containsOne(gMulti, s, p, Node.ANY)); + } + + @Test + public void hasType_direct() { + Graph g = graph(":x rdf:type :A ."); + Node x = SSE.parseNode(":x"); + Node A = SSE.parseNode(":A"); + assertTrue(G.hasType(g, x, A)); + assertFalse(G.hasType(g, x, SSE.parseNode(":B"))); + } + + @Test + public void isOfType_with_subclass() { + String body = """ + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX : <http://example/> + + :A rdfs:subClassOf :B . + :x rdf:type :A . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node x = SSE.parseNode(":x"); + Node B = SSE.parseNode(":B"); + Node C = SSE.parseNode(":C"); + assertTrue(G.isOfType(g, x, B)); + assertFalse(G.isOfType(g, x, C)); + } + + private static Graph graph(String ttlBody) { + String setup = "PREFIX : <http://example/>\n" + + "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"; + return RDFParser.fromString(setup+ttlBody, Lang.TURTLE).toGraph(); + } +} diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Classes.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_Classes.java new file mode 100644 index 0000000000..89daff8246 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Classes.java @@ -0,0 +1,200 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Set; +import java.util.HashSet; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; + +public class TestG_Classes { + + static { JenaSystem.init(); } + + @Test + public void listSubClasses_includes_transitive_subclasses_and_self() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :A rdfs:subClassOf :B . + :C rdfs:subClassOf :A . + :D rdfs:subClassOf :B . + :E rdfs:subClassOf :C . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + + Node B = SSE.parseNode(":B"); + List<Node> subs = G.listSubClasses(g, B); + Set<Node> set = new HashSet<>(subs); + assertEquals(5, set.size()); + assertTrue(set.contains(SSE.parseNode(":B"))); + assertTrue(set.contains(SSE.parseNode(":A"))); + assertTrue(set.contains(SSE.parseNode(":C"))); + assertTrue(set.contains(SSE.parseNode(":D"))); + assertTrue(set.contains(SSE.parseNode(":E"))); + } + + @Test + public void listSuperClasses_includes_transitive_superclasses_and_self() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :A rdfs:subClassOf :B . + :C rdfs:subClassOf :A . + :E rdfs:subClassOf :C . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + + Node E = SSE.parseNode(":E"); + List<Node> supers = G.listSuperClasses(g, E); + Set<Node> set = new HashSet<>(supers); + assertEquals(4, set.size()); + assertTrue(set.contains(SSE.parseNode(":E"))); + assertTrue(set.contains(SSE.parseNode(":C"))); + assertTrue(set.contains(SSE.parseNode(":A"))); + assertTrue(set.contains(SSE.parseNode(":B"))); + } + + @Test + public void subClasses_return_set_equivalent_to_list() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :A rdfs:subClassOf :B . + :C rdfs:subClassOf :A . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + + Node B = SSE.parseNode(":B"); + List<Node> subsList = G.listSubClasses(g, B); + Set<Node> subsSet = G.subClasses(g, B); + assertEquals(new HashSet<>(subsList), subsSet); + } + + @Test + public void superClasses_return_set_equivalent_to_list() { + String body = "PREFIX : <http://example/>\n" + + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" + + ":A rdfs:subClassOf :B . :C rdfs:subClassOf :A ."; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + + Node C = SSE.parseNode(":C"); + List<Node> supersList = G.listSuperClasses(g, C); + Set<Node> supersSet = G.superClasses(g, C); + assertEquals(new HashSet<>(supersList), supersSet); + } + + @Test + public void listSubClasses_no_relations_contains_self_only() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + :Z a :U . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node Z = SSE.parseNode(":Z"); + List<Node> subs = G.listSubClasses(g, Z); + assertEquals(1, subs.size()); + assertEquals(Z, subs.get(0)); + } + + @Test + public void listTypesOfNodeRDFS_includes_direct_and_superclasses() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :A rdfs:subClassOf :B . + :x rdf:type :A . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node x = SSE.parseNode(":x"); + List<Node> types = G.listTypesOfNodeRDFS(g, x); + Set<Node> set = new HashSet<>(types); + assertTrue(set.contains(SSE.parseNode(":A"))); + assertTrue(set.contains(SSE.parseNode(":B"))); + } + + @Test + public void listNodesOfTypeRDFS_includes_nodes_of_subclasses() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :C rdfs:subClassOf :A . + :x rdf:type :A . + :y rdf:type :C . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node A = SSE.parseNode(":A"); + List<Node> nodes = G.listNodesOfTypeRDFS(g, A); + Set<Node> set = new HashSet<>(nodes); + assertTrue(set.contains(SSE.parseNode(":x"))); + assertTrue(set.contains(SSE.parseNode(":y"))); + } + + @Test + public void allTypesOfNodeRDFS_returns_set_of_types_including_superclasses() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :A rdfs:subClassOf :B . + :x rdf:type :A . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node x = SSE.parseNode(":x"); + Set<Node> types = G.allTypesOfNodeRDFS(g, x); + assertTrue(types.contains(SSE.parseNode(":A"))); + assertTrue(types.contains(SSE.parseNode(":B"))); + } + + @Test + public void allNodesOfTypeRDFS_returns_set_of_nodes_including_subclass_instances() { + String body = """ + PREFIX : <http://example/> + PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + :C rdfs:subClassOf :A . + :x rdf:type :A . + :y rdf:type :C . + """; + Graph g = RDFParser.fromString(body, Lang.TURTLE).toGraph(); + Node A = SSE.parseNode(":A"); + Set<Node> nodes = G.allNodesOfTypeRDFS(g, A); + assertTrue(nodes.contains(SSE.parseNode(":x"))); + assertTrue(nodes.contains(SSE.parseNode(":y"))); + } +} diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_PO.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_PO.java new file mode 100644 index 0000000000..ccba2bb0d1 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_PO.java @@ -0,0 +1,201 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; +import org.apache.jena.util.iterator.ExtendedIterator; + +public class TestG_PO { + + static { JenaSystem.init(); } + + private static final Node s1 = SSE.parseNode(":s1"); + private static final Node s2 = SSE.parseNode(":s2"); + private static final Node x = SSE.parseNode(":x"); + private static final Node p = SSE.parseNode(":p"); + private static final Node o1 = SSE.parseNode(":o1"); + private static final Node o2 = SSE.parseNode(":o2"); + + @Test + public void getPO_many_returns_one_of_subjects() { + Graph g = graph(":s1 :p :o . :s2 :p :o ."); + Node got = G.getPO(g, p, SSE.parseNode(":o")); + assertTrue(s1.sameTermAs(got) || s2.sameTermAs(got)); + } + + @Test + public void getOnePO_single_ok() { + Graph g = graph(":s :p :o1 ."); + Node got = G.getOnePO(g, p, o1); + assertEquals(SSE.parseNode(":s"), got); + } + + @Test + public void getOnePO_none_throws() { + Graph g = graph(":s :q :o1 ."); + assertThrows(RDFDataException.class, ()->G.getOnePO(g, p, o1)); + } + + @Test + public void getOnePO_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o1 ."); + assertThrows(RDFDataException.class, ()->G.getOnePO(g, p, o1)); + } + + @Test + public void getZeroOrOnePO_none_null() { + Graph g = graph(":s :q :o1 ."); + Node got = G.getZeroOrOnePO(g, p, o1); + assertNull(got); + } + + @Test + public void getZeroOrOnePO_one_ok() { + Graph g = graph(":s :p :o1 ."); + Node got = G.getZeroOrOnePO(g, p, o1); + assertEquals(SSE.parseNode(":s"), got); + } + + @Test + public void getZeroOrOnePO_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o1 ."); + assertThrows(RDFDataException.class, ()->G.getZeroOrOnePO(g, p, o1)); + } + + @Test + public void hasOnePO_none_false() { + Graph g = graph(":s :q :o1 ."); + assertFalse(G.hasOnePO(g, p, o1)); + } + + @Test + public void hasOnePO_one_true() { + Graph g = graph(":s :p :o1 ."); + assertTrue(G.hasOnePO(g, p, o1)); + } + + @Test + public void hasOnePO_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o1 ."); + assertThrows(RDFDataException.class, ()->G.hasOnePO(g, p, o1)); + } + + // Wildcard tests similar to TestG_SP + @Test + public void getPO_objectAny_returns_one_of_subjects() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + Node got = G.getPO(g, p, Node.ANY); + assertTrue(SSE.parseNode(":s1").sameTermAs(got) || SSE.parseNode(":s2").sameTermAs(got)); + } + + @Test + public void getOnePO_objectAny_single_ok() { + Graph g = graph(":s1 :p :o1 ."); + Node got = G.getOnePO(g, p, Node.ANY); + assertEquals(SSE.parseNode(":s1"), got); + } + + @Test + public void getOnePO_objectAny_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getOnePO(g, p, Node.ANY)); + } + + @Test + public void getZeroOrOnePO_objectAny_single_ok() { + Graph g = graph(":s1 :p :o1 ."); + Node got = G.getZeroOrOnePO(g, p, Node.ANY); + assertEquals(SSE.parseNode(":s1"), got); + } + + @Test + public void getZeroOrOnePO_objectAny_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getZeroOrOnePO(g, p, Node.ANY)); + } + + @Test + public void hasOnePO_objectAny_true_false_and_throws() { + // none -> false + Graph g0 = graph(":s :q :o1 ."); + assertFalse(G.hasOnePO(g0, p, Node.ANY)); + + // single -> true + Graph g1 = graph(":s1 :p :o1 ."); + assertTrue(G.hasOnePO(g1, p, Node.ANY)); + + // multiple -> throws + Graph g2 = graph(":s1 :p :o1 . :s2 :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.hasOnePO(g2, p, Node.ANY)); + } + + @Test + public void iterPO_returns_subjects() { + Graph g = graph(":s1 :p :o . :s2 :p :o . :s3 :q :o ."); + ExtendedIterator<Node> iter = G.iterPO(g, p, SSE.parseNode(":o")); + try { + List<Node> seen = iter.toList(); + assertEquals(2, seen.size()); + assertTrue(seen.contains(SSE.parseNode(":s1")) && seen.contains(SSE.parseNode(":s2"))); + } finally { iter.close(); } + } + + @Test + public void listPO_returns_list_of_subjects() { + Graph g = graph(":s1 :p :o . :s2 :p :o . :s3 :q :o ."); + List<Node> list = G.listPO(g, p, SSE.parseNode(":o")); + assertEquals(2, list.size()); + assertTrue(list.contains(SSE.parseNode(":s1")) && list.contains(SSE.parseNode(":s2"))); + } + + @Test + public void countPO_counts_matches() { + Graph g = graph(":s1 :p :o . :s2 :p :o . :s3 :q :o ."); + long count = G.countPO(g, p, SSE.parseNode(":o")); + assertEquals(2, count); + } + + @Test + public void allPO_returns_set_of_subjects() { + Graph g = graph(":s1 :p :o . :s2 :p :o . :s3 :q :o ."); + Set<Node> all = G.allPO(g, p, SSE.parseNode(":o")); + assertEquals(2, all.size()); + assertTrue(all.contains(SSE.parseNode(":s1")) && all.contains(SSE.parseNode(":s2"))); + } + + private static Graph graph(String ttlBody) { + String setup = "PREFIX : <http://example/>\n"; + return RDFParser.fromString(setup+ttlBody, Lang.TURTLE).toGraph(); + } +} diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java new file mode 100644 index 0000000000..f68a1935a8 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Quad.java @@ -0,0 +1,72 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Node; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.sparql.core.DatasetGraph; +import org.apache.jena.sparql.core.Quad; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; + +public class TestG_Quad { + + static { JenaSystem.init(); } + + private static final Node s = SSE.parseNode(":s"); + private static final Node p = SSE.parseNode(":p"); + private static final Node o = SSE.parseNode(":o"); + + // DatasetGraph getOne (quads) + @Test + public void getOne_datasetgraph_single_ok() { + DatasetGraph dsg = dataset(":g { :s :p :o } "); + Node gname = SSE.parseNode(":g"); + Quad q = G.getOne(dsg, gname, s, p, o); + assertEquals(Quad.create(gname, s, p, o), q); + } + + @Test + public void getOne_datasetgraph_none_throws() { + DatasetGraph dsg = dataset(""); + Node gname = SSE.parseNode(":g"); + assertThrows(RDFDataException.class, ()->G.getOne(dsg, gname, s, p, o)); + } + + @Test + public void getOne_datasetgraph_multiple_throws_with_wildcard() { + DatasetGraph dsg = dataset(":g1 { :s :p :o } :g2 { :s :p :o } "); + // Use graph wildcard to match multiple quad locations + assertThrows(RDFDataException.class, ()->G.getOne(dsg, Node.ANY, s, p, o)); + } + + private static DatasetGraph dataset(String trigBody) { + String setup = "PREFIX : <http://example/>\n"; + return RDFParser.fromString(setup+trigBody, Lang.TRIG).toDatasetGraph(); + } +} diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_SP.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_SP.java new file mode 100644 index 0000000000..8941072d7c --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_SP.java @@ -0,0 +1,201 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; +import org.apache.jena.util.iterator.ExtendedIterator; + +public class TestG_SP { + + static { JenaSystem.init(); } + + private static final Node s = SSE.parseNode(":s"); + private static final Node x = SSE.parseNode(":x"); + private static final Node p = SSE.parseNode(":p"); + private static final Node o1 = SSE.parseNode(":o1"); + private static final Node o2 = SSE.parseNode(":o2"); + + @Test + public void getSP_many_returns_one_of_objects() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + Node got = G.getSP(g, s, p); + assertTrue(o1.sameTermAs(got) || o2.sameTermAs(got), "getSP should return one of the objects when multiple exist"); + } + + @Test + public void getOneSP_single_ok() { + Graph g = graph(":s :p :o1 ."); + Node got = G.getOneSP(g, s, p); + assertEquals(o1, got); + } + + @Test + public void getOneSP_none_throws() { + Graph g = graph(":s :q :o1 ."); + assertThrows(RDFDataException.class, ()->G.getOneSP(g, s, p)); + } + + @Test + public void getOneSP_multiple_throws() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getOneSP(g, s, p)); + } + + @Test + public void getZeroOrOneSP_none_null() { + Graph g = graph(":s :q :o1 ."); + Node got = G.getZeroOrOneSP(g, s, p); + assertNull(got); + } + + @Test + public void getZeroOrOneSP_one_ok() { + Graph g = graph(":s :p :o1 ."); + Node got = G.getZeroOrOneSP(g, s, p); + assertEquals(o1, got); + } + + @Test + public void getZeroOrOneSP_multiple_throws() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getZeroOrOneSP(g, s, p)); + } + + @Test + public void hasOneSP_none_false() { + Graph g = graph(":s :q :o1 ."); + assertFalse(G.hasOneSP(g, s, p)); + } + + @Test + public void hasOneSP_one_true() { + Graph g = graph(":s :p :o1 ."); + assertTrue(G.hasOneSP(g, s, p)); + } + + @Test + public void hasOneSP_multiple_throws() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.hasOneSP(g, s, p)); + } + + private static Graph graph(String ttlBody) { + String setup = "PREFIX : <http://example/>\n"; + return RDFParser.fromString(setup+ttlBody, Lang.TURTLE).toGraph(); + } + + // --- Wildcard (Node.ANY) handling tests + + @Test + public void getSP_subjectAny_returns_one_of_objects() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + Node got = G.getSP(g, Node.ANY, p); + assertTrue(o1.sameTermAs(got) || o2.sameTermAs(got)); + } + + @Test + public void getOneSP_subjectAny_single_ok() { + Graph g = graph(":s1 :p :o1 ."); + Node got = G.getOneSP(g, Node.ANY, p); + assertEquals(o1, got); + } + + @Test + public void getOneSP_subjectAny_multiple_throws() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getOneSP(g, Node.ANY, p)); + } + + @Test + public void getZeroOrOneSP_predicateAny_single_ok() { + Graph g = graph(":s :p1 :o1 ."); + Node got = G.getZeroOrOneSP(g, s, Node.ANY); + assertEquals(o1, got); + } + + @Test + public void getZeroOrOneSP_predicateAny_multiple_throws() { + Graph g = graph(":s :p1 :o1 . :s :p2 :o2 ."); + assertThrows(RDFDataException.class, ()->G.getZeroOrOneSP(g, s, Node.ANY)); + } + + @Test + public void hasOneSP_predicateAny_true_false_and_throws() { + // none -> false + Graph g0 = graph(":s :q :o1 ."); + assertFalse(G.hasOneSP(g0, x, Node.ANY)); + + // single -> true + Graph g1 = graph(":s :p1 :o1 ."); + assertTrue(G.hasOneSP(g1, s, Node.ANY)); + + // multiple -> throws + Graph g2 = graph(":s :p1 :o1 . :s :p2 :o2 ."); + assertThrows(RDFDataException.class, ()->G.hasOneSP(g2, s, Node.ANY)); + } + + @Test + public void iterSP_returns_objects() { + Graph g = graph(":s :p :o1 . :s :p :o2 . :s2 :p :o2 ."); + ExtendedIterator<Node> iter = G.iterSP(g, s, p); + try { + List<Node> seen = iter.toList(); + assertEquals(2, seen.size()); + assertTrue(seen.contains(o1) && seen.contains(o2)); + } finally { iter.close(); } + } + + @Test + public void listSP_returns_list_of_objects() { + Graph g = graph(":s :p :o1 . :s :p :o2 . :s2 :p :o2 ."); + List<Node> list = G.listSP(g, s, p); + assertEquals(2, list.size()); + assertTrue(list.contains(o1) && list.contains(o2)); + } + + @Test + public void countSP_counts_matches() { + Graph g = graph(":s :p :o1 . :s :p :o2 . :s2 :p :o2 ."); + long count = G.countSP(g, s, p); + assertEquals(2, count); + } + + @Test + public void allSP_returns_set_of_objects() { + Graph g = graph(":s :p :o1 . :s :p :o2 . :s2 :p :o2 ."); + Set<Node> all = G.allSP(g, s, p); + assertEquals(2, all.size()); + assertTrue(all.contains(o1) && all.contains(o2)); + } +} diff --git a/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java b/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java new file mode 100644 index 0000000000..f3a44b1114 --- /dev/null +++ b/jena-arq/src/test/java/org/apache/jena/system/TestG_Triple.java @@ -0,0 +1,103 @@ +/* + * 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 + * + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.apache.jena.system; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.Triple; +import org.apache.jena.riot.Lang; +import org.apache.jena.riot.RDFParser; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.sys.JenaSystem; + +public class TestG_Triple { + + static { JenaSystem.init(); } + + private static final Node s = SSE.parseNode(":s"); + private static final Node p = SSE.parseNode(":p"); + private static final Node o = SSE.parseNode(":o"); + + @Test + public void getOne_single_ok() { + Graph g = graph(":s :p :o ."); + Triple t = G.getOne(g, s, p, o); + assertEquals(SSE.parseTriple("(:s :p :o)"), t); + } + + @Test + public void getOne_none_throws() { + Graph g = graph(":s :q :o ."); + assertThrows(RDFDataException.class, ()->G.getOne(g, s, p, o)); + } + + @Test + public void getOne_multiple_throws_with_wildcard() { + Graph g = graph(":s1 :p :o1 . :s2 :p :o2 ."); + // Use wildcard for object to match multiple objects + assertThrows(RDFDataException.class, ()->G.getOne(g, s, p, Node.ANY)); + } + + @Test + public void getZeroOrOne_none_null() { + Graph g = graph(":s :q :o ."); + Triple t = G.getZeroOrOne(g, s, p, o); + assertNull(t); + } + + @Test + public void getZeroOrOne_one_ok() { + Graph g = graph(":s :p :o ."); + Triple t = G.getZeroOrOne(g, s, p, o); + assertEquals(SSE.parseTriple("(:s :p :o)"), t); + } + + @Test + public void getZeroOrOne_multiple_throws() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + assertThrows(RDFDataException.class, ()->G.getZeroOrOne(g, s, p, Node.ANY)); + } + + @Test + public void getOneOrNull_none_null() { + Graph g = graph(":s :q :o ."); + Triple t = G.getOneOrNull(g, s, p, o); + assertNull(t); + } + + @Test + public void getOneOrNull_multiple_null() { + Graph g = graph(":s :p :o1 . :s :p :o2 ."); + Triple t = G.getOneOrNull(g, s, p, Node.ANY); + assertNull(t); + } + private static Graph graph(String ttlBody) { + String setup = "PREFIX : <http://example/>\n"; + return RDFParser.fromString(setup+ttlBody, Lang.TURTLE).toGraph(); + } +}
