diff --git a/qanary_commons/pom.xml b/qanary_commons/pom.xml
index e1142957..a435f93c 100644
--- a/qanary_commons/pom.xml
+++ b/qanary_commons/pom.xml
@@ -3,7 +3,7 @@
4.0.0
eu.wdaqua.qanary
qa.commons
- 3.16.1
+ 3.17.1
org.springframework.boot
spring-boot-starter-parent
diff --git a/qanary_commons/src/test/java/qa/commons/QanaryQueryTest.java b/qanary_commons/src/test/java/qa/commons/QanaryQueryTest.java
new file mode 100644
index 00000000..db4ab127
--- /dev/null
+++ b/qanary_commons/src/test/java/qa/commons/QanaryQueryTest.java
@@ -0,0 +1,210 @@
+package qa.commons;
+
+import static java.util.Map.entry;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jena.graph.impl.LiteralLabel;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryFactory;
+import org.apache.jena.shared.impl.PrefixMappingImpl;
+import org.apache.jena.sparql.algebra.Algebra;
+import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.OpAsQuery;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.syntax.ElementFilter;
+import org.apache.jena.sparql.syntax.ElementPathBlock;
+import org.apache.jena.sparql.syntax.ElementVisitorBase;
+import org.apache.jena.sparql.syntax.ElementWalker;
+
+
+public class QanaryQueryTest {
+
+ // static default prefixMapping that may be used to instantiate these Tests
+ public static Map COMMON_PREFIXES = Map.ofEntries(
+ entry("oa", "http://www.w3.org/ns/openannotation/core/"),
+ entry("wdt", "http://www.wikidata.org/prop/direct/"),
+ entry("rdfs", "http://www.w3.org/2000/01/rdf-schema#"),
+ entry("bd", "http://www.bigdata.com/rdf#"),
+ entry("wikibase", "http://wikiba.se/ontology#")
+ );
+
+ private final PrefixMappingImpl prefixMapping;
+
+ /**
+ * @param prefixMapping use QanaryQueryTest.COMMON_PREFIXES or define your own prefix mappings.
+ */
+ public QanaryQueryTest(Map prefixMapping) {
+ this.prefixMapping = new PrefixMappingImpl();
+ this.prefixMapping.setNsPrefixes(prefixMapping);
+ }
+
+ //
+ // TEST METHODS
+ //
+
+ /**
+ * Assert that two supplied queries have equal string representations.
+ *
+ * @param queryString
+ * @param expectedGraph
+ */
+ public void isSparqlQueryEqual(String expectedQueryString, String actualQueryString) {
+ Query expectedParsedQuery = QueryFactory.create(expectedQueryString);
+ Op expectedOp = Algebra.compile(expectedParsedQuery);
+ Query expectedQueryAlgebra = OpAsQuery.asQuery(expectedOp);
+
+ Query actualParsedQuery = QueryFactory.create(actualQueryString);
+ Op actualOp = Algebra.compile(actualParsedQuery);
+ Query actualQueryAlgebra = OpAsQuery.asQuery(actualOp);
+
+ assertEquals(expectedQueryAlgebra.serialize(), actualQueryAlgebra.serialize(),
+ "The queries do not match");
+ }
+
+ /**
+ * Assert that the supplied query contains a specific graph.
+ *
+ * @param queryString
+ * @param expectedGraph
+ */
+ public void queryContainsGraph(String queryString, String expectedGraph) {
+ ParsedQueryContent parsedQueryContent = this.parseQueryContent(queryString);
+ assertTrue(parsedQueryContent.getGraphs().stream().anyMatch(
+ o -> expectedGraph.equals(o.toString())),
+ "The query does not contain the specified graph");
+ }
+
+ /**
+ * Assert that the supplied query contains a FILTER statement with specific key and value.
+ *
+ * @param queryString
+ * @param expectedKey a variable name
+ * @param expectedValue a URI or literal as String
+ */
+ public void queryContainsFilterKeyValuePair(String queryString, String expectedKey, String expectedValue) {
+ ParsedQueryContent parsedQueryContent = this.parseQueryContent(queryString);
+ assertTrue(parsedQueryContent.getFilters().stream().anyMatch(
+ o -> (o.toString().contains(expectedKey)
+ && o.toString().contains(expectedValue))),
+ "The query does not contain a FILTER statement with the specified key-value pair");
+ }
+
+ /**
+ * Assert that the supplied query contains a specific triple.
+ *
+ * @param queryString
+ * @param subject a URI string or variable name
+ * @param predicate a URI string or variable name
+ * @param object a URI string or variable name
+ */
+ public void queryContainsTriple(String queryString, String subject, String predicate, String object) {
+ ParsedQueryContent parsedQueryContent = this.parseQueryContent(queryString);
+ assertTrue(parsedQueryContent.getTriples().stream().anyMatch(
+ o -> (subject.equals(o.getSubject().toString(this.prefixMapping)))
+ && predicate.equals(o.getPredicate().toString(this.prefixMapping))
+ && object.equals(o.getObject().toString(this.prefixMapping))),
+ "The query does not contain the specified triple");
+ }
+
+ /**
+ * Assert that the supplied query contains a specific triple.
+ *
+ * @param queryString
+ * @param subject a URI string or variable name
+ * @param predicate a URI string or variable name
+ * @param object a literal with lexical form, datatype IRI and optionally a language tag
+ */
+ public void queryContainsTriple(String queryString, String subject, String predicate, LiteralLabel object) {
+ ParsedQueryContent parsedQueryContent = this.parseQueryContent(queryString);
+ assertTrue(parsedQueryContent.getTriples().stream().anyMatch(
+ o -> (subject.equals(o.getSubject().toString(this.prefixMapping)))
+ && predicate.equals(o.getPredicate().toString(this.prefixMapping))
+ && object.equals(o.getObject().getLiteral())),
+ "The query does not contain the specified triple");
+ }
+
+ /**
+ * Assert that the supplied query contains a specific triple.
+ *
+ * @param queryString
+ * @param subject a literal with lexical form, datatype IRI and optionally a language tag
+ * @param predicate a URI string or variable name
+ * @param object a URI string or variable name
+ */
+ public void queryContainsTriple(String queryString, LiteralLabel subject, String predicate, String object) {
+ ParsedQueryContent parsedQueryContent = this.parseQueryContent(queryString);
+ assertTrue(parsedQueryContent.getTriples().stream().anyMatch(
+ o -> (subject.equals(o.getSubject().getLiteral()))
+ && predicate.equals(o.getPredicate().toString(this.prefixMapping))
+ && object.equals(o.getObject().toString(this.prefixMapping))),
+ "The query does not contain the specified triple");
+
+ }
+
+ //
+ // CONTENT PARSING
+ //
+
+ /** Parses the query to extract graphs, filters and triples.
+ * This does not work for INSERT queries (all update queries)
+ *
+ * @param queryString
+ */
+ private ParsedQueryContent parseQueryContent(String queryString) {
+ List graphs = new LinkedList<>();
+ List filters = new LinkedList<>();
+ List triples = new LinkedList<>();
+
+ Query query = QueryFactory.create(queryString);
+ graphs = query.getGraphURIs();
+ ElementWalker.walk(query.getQueryPattern(),
+ new ElementVisitorBase() {
+ // for visiting Filter statements
+ public void visit(ElementFilter filter) {
+ filters.add(filter.getExpr().toString());
+ }
+ // for visiting a block of Triples
+ public void visit(ElementPathBlock el) {
+ // iterate of all Triples in the block
+ Iterator triplePaths = el.patternElts();
+ while (triplePaths.hasNext()) {
+ TriplePath triple = triplePaths.next();
+ triples.add(triple);
+ }
+ }
+ }
+ );
+ return new ParsedQueryContent(graphs, filters, triples);
+ }
+
+ // holds parsed query elements
+ private class ParsedQueryContent {
+ private List graphs = new LinkedList<>();
+ private List filters = new LinkedList<>();
+ private List triples = new LinkedList<>();
+
+ public ParsedQueryContent(List graphs, List filters, List triples) {
+ this.graphs = graphs;
+ this.filters = filters;
+ this.triples = triples;
+ }
+
+ public List getGraphs() {
+ return graphs;
+ }
+
+ public List getFilters() {
+ return filters;
+ }
+
+ public List getTriples() {
+ return triples;
+ }
+ }
+}