TupleExprIRRendererTest.java
/*******************************************************************************
* Copyright (c) 2025 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************/
package org.eclipse.rdf4j.queryrender;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.rdf4j.query.MalformedQueryException;
import org.eclipse.rdf4j.query.QueryLanguage;
import org.eclipse.rdf4j.query.algebra.TupleExpr;
import org.eclipse.rdf4j.query.parser.ParsedQuery;
import org.eclipse.rdf4j.query.parser.QueryParserUtil;
import org.eclipse.rdf4j.queryrender.sparql.TupleExprIRRenderer;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
@Execution(ExecutionMode.SAME_THREAD)
public class TupleExprIRRendererTest {
private static final String EX = "http://ex/";
private static final String SPARQL_PREFIX = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" +
"PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" +
"PREFIX ex: <http://ex/>\n" +
"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n";
private TestInfo testInfo;
// Shared renderer config with canonical whitespace and useful prefixes.
private static TupleExprIRRenderer.Config cfg() {
TupleExprIRRenderer.Config style = new TupleExprIRRenderer.Config();
style.prefixes.put("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
style.prefixes.put("rdfs", "http://www.w3.org/2000/01/rdf-schema#");
style.prefixes.put("foaf", "http://xmlns.com/foaf/0.1/");
style.prefixes.put("ex", "http://ex/");
style.prefixes.put("xsd", "http://www.w3.org/2001/XMLSchema#");
style.valuesPreserveOrder = true;
return style;
}
// @RepeatedTest10
// void render_throws_when_round_trip_differs() {
// String q = "SELECT * WHERE { ?s ?p ?o . }";
// TupleExpr tupleExpr = parseAlgebra(SPARQL_PREFIX + q);
//
// TupleExprIRRenderer tamperingRenderer = new TupleExprIRRenderer() {
// @Override
// public IrSelect toIRSelect(TupleExpr original) {
// IrSelect ir = super.toIRSelect(original);
// // Strip the WHERE body to force a semantic mismatch after rendering.
// ir.setWhere(new IrBGP(false));
// return ir;
// }
// };
//
// assertThrows(IllegalStateException.class, () -> tamperingRenderer.render(tupleExpr));
// }
@BeforeEach
void _captureTestInfo(TestInfo info) {
this.testInfo = info;
purgeReportFilesForCurrentTest();
}
private static void writeReportFile(String base, String label, String content) {
Path dir = Paths.get("target", "surefire-reports");
try {
Files.createDirectories(dir);
Path file = dir.resolve(base + "_" + label + ".txt");
Files.writeString(file, content == null ? "" : content, StandardCharsets.UTF_8);
// Optional: surface where things went
System.out.println("[debug] wrote " + file.toAbsolutePath());
} catch (IOException ioe) {
// Don't mask the real assertion failure if file I/O borks
System.err.println("������ Failed to write " + label + " to surefire-reports: " + ioe);
}
}
// ---------- Helpers ----------
// --- compute full-class-name#test-method-name (same as your writer uses) ---
private String currentTestBaseName() {
String cls = testInfo != null && testInfo.getTestClass().isPresent()
? testInfo.getTestClass().get().getName()
: "UnknownClass";
String method = testInfo != null && testInfo.getTestMethod().isPresent()
? testInfo.getTestMethod().get().getName()
: "UnknownMethod";
return cls + "#" + method;
}
// --- delete the four files if they exist ---
private static final Path SUREFIRE_DIR = Paths.get("target", "surefire-reports");
private static final String[] REPORT_LABELS = new String[] {
"SPARQL_expected",
"SPARQL_actual",
"TupleExpr_expected",
"TupleExpr_actual"
};
private static Set<String> extractBnodeLabels(String rendered) {
Set<String> labels = new HashSet<>();
Matcher labelMatcher = Pattern.compile("_:[A-Za-z][A-Za-z0-9]*").matcher(rendered);
while (labelMatcher.find()) {
labels.add(labelMatcher.group());
}
return labels;
}
private static long countAnonPlaceholders(String rendered) {
Matcher bracketMatcher = Pattern.compile("\\[\\]").matcher(rendered);
long count = 0;
while (bracketMatcher.find()) {
count++;
}
return count;
}
private void purgeReportFilesForCurrentTest() {
String base = currentTestBaseName();
for (String label : REPORT_LABELS) {
Path file = SUREFIRE_DIR.resolve(base + "_" + label + ".txt");
try {
Files.deleteIfExists(file);
} catch (IOException e) {
// Don���t block the test on cleanup trouble; just log
System.err.println("������ Unable to delete old report file: " + file.toAbsolutePath() + " :: " + e);
}
}
}
private TupleExpr parseAlgebra(String sparql) {
try {
ParsedQuery pq = QueryParserUtil.parseQuery(QueryLanguage.SPARQL, sparql, null);
return pq.getTupleExpr();
} catch (MalformedQueryException e) {
throw new MalformedQueryException(
"Failed to parse SPARQL query.\n###### QUERY ######\n" + sparql + "\n\n######################",
e);
}
}
private String render(String sparql, TupleExprIRRenderer.Config cfg) {
TupleExpr algebra = parseAlgebra(sparql);
if (sparql.contains("ASK")) {
return new TupleExprIRRenderer(cfg).renderAsk(algebra, null).trim();
}
if (sparql.contains("DESCRIBE")) {
return new TupleExprIRRenderer(cfg).renderAsk(algebra, null).trim();
}
return new TupleExprIRRenderer(cfg).render(algebra, null).trim();
}
/** Round-trip twice and assert the renderer is a fixed point (idempotent). */
private String assertFixedPoint(String sparql, TupleExprIRRenderer.Config cfg) {
// System.out.println("# Original SPARQL query\n" + sparql + "\n");
TupleExpr tupleExpr = parseAlgebra(SPARQL_PREFIX + sparql);
// System.out.println("# Original TupleExpr\n" + tupleExpr + "\n");
String r1 = render(SPARQL_PREFIX + sparql, cfg);
String r2;
try {
r2 = render(r1, cfg);
} catch (MalformedQueryException e) {
throw new RuntimeException("Failed to parse SPARQL query after rendering.\n### Original query ###\n"
+ sparql + "\n\n### Rendered query ###\n" + r1 + "\n", e);
}
assertEquals(r1, r2, "Renderer must be idempotent after one round-trip");
String r3 = render(r2, cfg);
assertEquals(r2, r3, "Renderer must be idempotent after two round-trips");
return r2;
}
// private String currentTestBaseName() {
// String cls = testInfo != null && testInfo.getTestClass().isPresent()
// ? testInfo.getTestClass().get().getName()
// : "UnknownClass";
// String method = testInfo != null && testInfo.getTestMethod().isPresent()
// ? testInfo.getTestMethod().get().getName()
// : "UnknownMethod";
// return cls + "#" + method;
// }
/** Assert semantic equivalence by comparing result rows (order-insensitive). */
/** Assert semantic equivalence by comparing result rows (order-insensitive). */
private void assertSameSparqlQuery(String sparql, TupleExprIRRenderer.Config cfg, boolean requireStringEquality) {
// cfg.debugIR = true;
sparql = sparql.trim();
TupleExpr expected = parseAlgebra(SPARQL_PREFIX + sparql);
// System.out.println("# Original SPARQL query\n" + SparqlFormatter.format(sparql) + "\n");
// System.out.println("# Original TupleExpr\n" + expected + "\n");
String rendered = render(SPARQL_PREFIX + sparql, cfg);
// System.out.println("# Actual SPARQL query\n" + SparqlFormatter.format(rendered) + "\n");
TupleExpr actual = parseAlgebra(rendered);
try {
assertThat(VarNameNormalizer.normalizeVars(actual.toString()))
.as("Algebra after rendering must be identical to original")
.isEqualTo(VarNameNormalizer.normalizeVars(expected.toString()));
if (requireStringEquality) {
assertThat(rendered).isEqualToNormalizingNewlines(SPARQL_PREFIX + sparql);
}
} catch (Throwable t) {
// assertThat(VarNameNormalizer.normalizeVars(actual.toString()))
// .as("Algebra after rendering must be identical to original")
// .isEqualTo(VarNameNormalizer.normalizeVars(expected.toString()));
// Gather as much as we can without throwing during diagnostics
String base = currentTestBaseName();
String expectedSparql = SPARQL_PREFIX + sparql;
TupleExpr expectedTe = null;
try {
expectedTe = parseAlgebra(expectedSparql);
} catch (Throwable parseExpectedFail) {
// Extremely unlikely, but don't let this hide the original failure
}
TupleExpr actualTe = null;
System.out.println("\n\n\n");
System.out.println("# Original SPARQL query\n" + SparqlFormatter.format(sparql) + "\n");
if (expectedTe != null) {
System.out.println("# Original TupleExpr\n" + expectedTe + "\n");
}
try {
cfg.debugIR = true;
System.out.println("\n# Re-rendering with IR debug enabled for this failing test\n");
String rendered2 = render(expectedSparql, cfg);
System.out.println("\n# Rendered SPARQL query\n" + rendered + "\n");
} catch (Throwable renderFail) {
rendered = "<render failed: " + renderFail + ">";
} finally {
cfg.debugIR = false;
}
try {
if (!rendered.startsWith("<render failed")) {
actualTe = parseAlgebra(rendered);
if (!VarNameNormalizer.normalizeVars(actual.toString())
.equals(VarNameNormalizer.normalizeVars(actualTe.toString()))) {
System.out.println("# actual TupleExpr \n" + actual + "\n");
System.out.println("# actualTe TupleExpr\n" + actualTe);
throw new IllegalStateException(
"`actualTe` TupleExpr differs from original `actual` TupleExpr");
}
System.out.println("# Actual TupleExpr\n" + actualTe + "\n");
}
} catch (Throwable parseActualFail) {
System.out.println("# Actual TupleExpr\n<parse failed: " + parseActualFail + ">\n");
// Keep actualTe as null; we'll record a placeholder
}
// --- Write the four artifacts ---
writeReportFile(base, "SPARQL_expected", expectedSparql);
writeReportFile(base, "SPARQL_actual", rendered);
writeReportFile(base, "TupleExpr_expected",
expectedTe != null ? VarNameNormalizer.normalizeVars(expectedTe.toString())
: "<expected TupleExpr unavailable: parse failed>");
writeReportFile(base, "TupleExpr_actual",
actualTe != null ? VarNameNormalizer.normalizeVars(actualTe.toString())
: "<actual TupleExpr unavailable: " +
"parse failed" + ">");
String rendered2 = render(expectedSparql, cfg);
// Fail (again) with the original comparison so the test result is correct
assertThat(rendered).isEqualToNormalizingNewlines(SPARQL_PREFIX + sparql);
}
}
// ---------- Tests: fixed point + semantic equivalence where applicable ----------
@RepeatedTest(10)
void basic_select_bgp() {
String q = "SELECT ?s ?name WHERE {\n" +
" ?s a foaf:Person ; foaf:name ?name .\n" +
"}";
assertFixedPoint(q, cfg());
}
@RepeatedTest(10)
void filter_compare_and_regex() {
String q = "SELECT ?s ?name WHERE {\n" +
" ?s foaf:name ?name .\n" +
" FILTER ((?name != \"Zed\") && REGEX(?name, \"a\", \"i\"))\n" +
"}";
assertFixedPoint(q, cfg());
}
@RepeatedTest(10)
void optional_with_condition() {
String q = "SELECT ?s ?age WHERE {\n" +
" ?s foaf:name ?n .\n" +
" OPTIONAL {\n" +
" ?s ex:age ?age .\n" +
" FILTER (?age >= 18)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void union_of_groups() {
String q = "SELECT ?who WHERE {\n" +
" {\n" +
" ?who foaf:name \"Alice\" .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?who foaf:name \"Bob\" .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void order_by_limit_offset() {
String q = "SELECT ?name WHERE {\n" +
" ?s foaf:name ?name .\n" +
"}\n" +
"ORDER BY DESC(?name)\n" +
"LIMIT 2\n" +
"OFFSET 0";
// Semantic equivalence depends on ordering; still fine since we run the same query
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_single_var_and_undef() {
String q = "SELECT ?x WHERE {\n" +
" VALUES (?x) {\n" +
" (ex:alice)\n" +
" (UNDEF)\n" +
" (ex:bob)\n" +
" }\n" +
" ?x foaf:name ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_multi_column() {
String q = "SELECT ?s ?n WHERE {\n" +
" VALUES (?n ?s) {\n" +
" (\"Alice\" ex:alice)\n" +
" (\"Bob\" ex:bob)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void bind_inside_where() {
String q = "SELECT ?s ?sn WHERE {\n" +
" ?s foaf:name ?n .\n" +
" BIND(STR(?n) AS ?sn)\n" +
" FILTER (STRSTARTS(?sn, \"A\"))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void aggregates_count_star_and_group_by() {
String q = "SELECT (COUNT(*) AS ?c) WHERE {\n" +
" ?s ?p ?o .\n" +
"}";
// No dataset dependency issues; simple count
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void aggregates_count_distinct_group_by() {
String q = "SELECT (COUNT(DISTINCT ?o) AS ?c) ?s WHERE {\n" +
" ?s ?p ?o .\n" +
"}\n" +
"GROUP BY ?s";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void group_concat_with_separator_literal() {
String q = "SELECT (GROUP_CONCAT(?name; SEPARATOR=\", \") AS ?names) WHERE {\n" +
" ?s foaf:name ?name .\n" +
"}";
// Semantic equivalence: both queries run in the same engine; comparing string results
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void service_silent_block() {
String q = "SELECT ?s ?p ?o WHERE {\n" +
" SERVICE SILENT <http://example.org/sparql> {\n" +
" ?s ?p ?o .\n" +
" }\n" +
"}";
// We do not execute against remote SERVICE; check fixed point only:
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void property_paths_star_plus_question() {
// These rely on RDF4J producing ArbitraryLengthPath for +/*/?.
String qStar = "SELECT ?x ?y WHERE {\n" +
" ?x ex:knows*/foaf:name ?y .\n" +
"}";
String qPlus = "SELECT ?x ?y WHERE {\n" +
" ?x ex:knows+/foaf:name ?y .\n" +
"}";
String qOpt = "SELECT ?x ?y WHERE {\n" +
" ?x ex:knows?/foaf:name ?y .\n" +
"}";
assertSameSparqlQuery(qStar, cfg(), false);
assertSameSparqlQuery(qPlus, cfg(), false);
assertSameSparqlQuery(qOpt, cfg(), false);
}
@RepeatedTest(10)
void rdf_star_triple_terms_render_verbatim() {
String q = "SELECT * WHERE {\n" +
" <<ex:s ex:p ex:o>> ex:q ?x .\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
// assertTrue(rendered.contains("<<ex:s ex:p ex:o>>"), "RDF-star triple term must render as <<...>>");
// Round-trip to ensure algebra equivalence once triple text is correct.
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void blank_node_square_brackets_render_as_empty_bnode() {
String q = "SELECT ?s1 ?s2 WHERE {\n" +
" ?s1 ex:p [] .\n" +
" _:bnode1 ex:p [] .\n" +
" ?s2 ex:p [] .\n" +
" [] ex:p _:bnode1 .\n" +
" [] ex:p _:bnode1 .\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), true);
}
@RepeatedTest(10)
void rdf_type_renders_as_a_keyword() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s a ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), true);
}
@RepeatedTest(10)
void regex_flags_and_lang_filters() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s foaf:name ?n .\n" +
" FILTER (REGEX(?n, \"^a\", \"i\") || LANGMATCHES(LANG(?n), \"en\"))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void datatype_filter_and_is_tests() {
String q = "SELECT ?s ?age WHERE {\n" +
" ?s ex:age ?age .\n" +
" FILTER ((DATATYPE(?age) = xsd:integer) && isLiteral(?age))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void distinct_projection_and_reduced_shell() {
String q = "SELECT DISTINCT ?s WHERE {\n" +
" ?s ?p ?o .\n" +
"}\n" +
"LIMIT 10\n" +
"OFFSET 1";
assertSameSparqlQuery(q, cfg(), false);
}
// ----------- Edge/robustness cases ------------
@RepeatedTest(10)
void empty_where_is_not_produced_and_triple_format_stable() {
String q = "SELECT * WHERE { ?s ?p ?o . }";
String rendered = assertFixedPoint(q, cfg());
// Ensure one triple per line and trailing dot
assertTrue(rendered.contains("?s ?p ?o ."), "Triple should be printed with trailing dot");
assertTrue(rendered.contains("WHERE {\n"), "Block should open with newline");
}
@RepeatedTest(10)
void values_undef_matrix() {
String q = "SELECT ?a ?b WHERE {\n" +
" VALUES (?a ?b) {\n" +
" (\"x\" UNDEF)\n" +
" (UNDEF \"y\")\n" +
" (\"x\" \"y\")\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void count_and_sum_in_select_with_group_by() {
String q = "SELECT ?s (COUNT(?o) AS ?c) (SUM(?age) AS ?sumAge) WHERE {\n" +
" {\n" +
" ?s ?p ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ex:age ?age .\n" +
" }\n" +
"}\n" +
"GROUP BY ?s";
// Semantic equivalence: engine evaluates both sides consistently
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void order_by_multiple_keys() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s foaf:name ?n .\n" +
"}\n" +
"ORDER BY ?n DESC(?s)";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void list_member_in_and_not_in() {
String q = "SELECT ?s WHERE {\n" +
" VALUES (?s) {\n" +
" (ex:alice)\n" +
" (ex:bob)\n" +
" (ex:carol)\n" +
" }\n" +
" FILTER (?s IN (ex:alice, ex:bob))\n" +
" FILTER (?s != ex:bob)\n" +
" FILTER (!(?s = ex:bob))\n" +
"}";
String r = assertFixedPoint(q, cfg());
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void exists_in_filter_and_bind() {
String q = "SELECT ?hasX WHERE {\n" +
" OPTIONAL {\n" +
" BIND(EXISTS { ?s ?p ?o . } AS ?hasX)\n" +
" }\n" +
" FILTER (EXISTS { ?s ?p ?o . })\n" +
"}";
String r = assertFixedPoint(q, cfg());
assertTrue(r.contains("EXISTS {"), "should render EXISTS");
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void strlen_alias_for_fn_string_length() {
String q = "SELECT ?s ?p ?o WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER (STRLEN(STR(?o)) > 1)\n" +
"}";
String r = assertFixedPoint(q, cfg());
assertTrue(r.contains("STRLEN("), "fn:string-length should render as STRLEN");
assertSameSparqlQuery(q, cfg(), false);
}
// =========================
// ===== New test cases ====
// =========================
// --- Negation: NOT EXISTS & MINUS ---
@RepeatedTest(10)
void filter_not_exists() {
String q = "SELECT ?s WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER (NOT EXISTS { ?s foaf:name ?n . })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void minus_set_difference() {
String q = "SELECT ?s WHERE {\n" +
" ?s ?p ?o .\n" +
" MINUS {\n" +
" ?s foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// --- Property paths (sequence, alternation, inverse, NPS, grouping) ---
@RepeatedTest(10)
void property_paths_sequence_and_alternation() {
String q = "SELECT ?x ?name WHERE { ?x (ex:knows/foaf:knows)|(foaf:knows/ex:knows) ?y . ?y foaf:name ?name }";
assertFixedPoint(q, cfg());
}
@RepeatedTest(10)
void property_paths_inverse() {
String q = "SELECT ?x ?y WHERE { ?x ^foaf:knows ?y }";
assertFixedPoint(q, cfg());
}
@RepeatedTest(10)
void property_paths_negated_property_set() {
String q = "SELECT ?x ?y WHERE {\n" +
" ?x !(rdf:type|^rdf:type) ?y .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void property_paths_grouping_precedence() {
String q = "SELECT ?x ?y WHERE { ?x (ex:knows/ (foaf:knows|^foaf:knows)) ?y }";
assertFixedPoint(q, cfg());
}
// --- Assignment forms: SELECT (expr AS ?v), GROUP BY (expr AS ?v) ---
@RepeatedTest(10)
void select_projection_expression_alias() {
String q = "SELECT ((?age + 1) AS ?age1) WHERE {\n" +
" ?s ex:age ?age .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void group_by_with_alias_and_having() {
String q = "SELECT ?name (COUNT(?s) AS ?c) WHERE {\n" +
" ?s foaf:name ?n .\n" +
" BIND(STR(?n) AS ?name)\n" +
"}\n" +
"GROUP BY (?n AS ?name)\n" +
"HAVING (COUNT(?s) > 1)\n" +
"ORDER BY DESC(?c)";
assertSameSparqlQuery(q, cfg(), true);
}
// --- Aggregates: MIN/MAX/AVG/SAMPLE + HAVING ---
@RepeatedTest(10)
void aggregates_min_max_avg_sample_having() {
String q = "SELECT ?s (MIN(?o) AS ?minO) (MAX(?o) AS ?maxO) (AVG(?o) AS ?avgO) (SAMPLE(?o) AS ?anyO)\n" +
"WHERE { ?s ?p ?o . }\n" +
"GROUP BY ?s\n" +
"HAVING (COUNT(?o) >= 1)";
assertSameSparqlQuery(q, cfg(), false);
}
// --- Subquery with aggregate and scope ---
@RepeatedTest(10)
void subquery_with_aggregate_and_having() {
String q = "SELECT ?y ?minName WHERE {\n" +
" ex:alice foaf:knows ?y .\n" +
" {\n" +
" SELECT ?y (MIN(?name) AS ?minName)\n" +
" WHERE { ?y foaf:name ?name . }\n" +
" GROUP BY ?y\n" +
" HAVING (MIN(?name) >= \"A\")\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// --- GRAPH with IRI and variable ---
@RepeatedTest(10)
void graph_iri_and_variable() {
String q = "SELECT ?g ?s WHERE {\n" +
" GRAPH ex:g1 { ?s ?p ?o }\n" +
" GRAPH ?g { ?s ?p ?o }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// --- Federation: SERVICE (no SILENT) and variable endpoint ---
@RepeatedTest(10)
void service_without_silent() {
String q = "SELECT * WHERE { SERVICE <http://example.org/sparql> { ?s ?p ?o } }";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void service_variable_endpoint() {
String q = "SELECT * WHERE { SERVICE ?svc { ?s ?p ?o } }";
assertSameSparqlQuery(q, cfg(), false);
}
// --- Solution modifiers: REDUCED; ORDER BY expression; OFFSET-only; LIMIT-only ---
@RepeatedTest(10)
void select_reduced_modifier() {
String q = "SELECT REDUCED ?s WHERE {\n" +
" ?s ?p ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void order_by_expression_and_by_aggregate_alias() {
String q = "SELECT ?n (COUNT(?s) AS ?c)\n" +
"WHERE { ?s foaf:name ?n }\n" +
"GROUP BY ?n\n" +
"ORDER BY LCASE(?n) DESC(?c)";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void offset_only() {
String q = "SELECT ?s ?p ?o WHERE {\n" +
" ?s ?p ?o .\n" +
"}\n" +
"OFFSET 5";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void limit_only_zero_and_positive() {
String q1 = "SELECT ?s ?p ?o WHERE {\n" +
" ?s ?p ?o .\n" +
"}\n" +
"LIMIT 0";
String q2 = "SELECT ?s ?p ?o WHERE {\n" +
" ?s ?p ?o .\n" +
"}\n" +
"LIMIT 3";
assertSameSparqlQuery(q1, cfg(), false);
assertSameSparqlQuery(q2, cfg(), false);
}
// --- Expressions & built-ins ---
@RepeatedTest(10)
void functional_forms_and_rdf_term_tests() {
String q = "SELECT ?ok1 ?ok2 ?ok3 ?ok4 WHERE {\n" +
" VALUES (?x) { (1) }\n" +
" BIND(IRI(CONCAT(\"http://ex/\", \"alice\")) AS ?iri)\n" +
" BIND(BNODE() AS ?b)\n" +
" BIND(STRDT(\"2020-01-01\", xsd:date) AS ?d)\n" +
" BIND(STRLANG(\"hi\", \"en\") AS ?l)\n" +
" BIND(IF(BOUND(?iri), true, false) AS ?ok1)\n" +
" BIND(COALESCE(?missing, ?x) AS ?ok2)\n" +
" BIND(sameTerm(?iri, IRI(\"http://ex/alice\")) AS ?ok3)\n" +
" BIND((isIRI(?iri) && isBlank(?b) && isLiteral(?l) && isNumeric(?x)) AS ?ok4)\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void string_functions_concat_substr_replace_encode() {
String q = "SELECT ?a ?b ?c ?d WHERE {\n" +
" VALUES (?n) { (\"Alice\") }\n" +
" BIND(CONCAT(?n, \" \", \"Doe\") AS ?a)\n" +
" BIND(SUBSTR(?n, 2) AS ?b)\n" +
" BIND(REPLACE(?n, \"A\", \"a\") AS ?c)\n" +
" BIND(ENCODE_FOR_URI(?n) AS ?d)\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void numeric_datetime_hash_and_random() {
String q = "SELECT ?r ?now ?y ?tz ?abs ?ceil ?floor ?round ?md5 WHERE {\n" +
" VALUES (?x) { (\"abc\") }\n" +
" BIND(RAND() AS ?r)\n" +
" BIND(NOW() AS ?now)\n" +
" BIND(YEAR(?now) AS ?y)\n" +
" BIND(TZ(?now) AS ?tz)\n" +
" BIND(ABS(-2.5) AS ?abs)\n" +
" BIND(CEIL(2.1) AS ?ceil)\n" +
" BIND(FLOOR(2.9) AS ?floor)\n" +
" BIND(ROUND(2.5) AS ?round)\n" +
" BIND(MD5(?x) AS ?md5)\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void uuid_and_struuid() {
String q = "SELECT (UUID() AS ?u) (STRUUID() AS ?su) WHERE {\n" +
"}";
assertFixedPoint(q, cfg());
}
@RepeatedTest(10)
void not_in_and_bound() {
String q = "SELECT ?s WHERE {\n" +
" VALUES ?s { ex:alice ex:bob ex:carol }\n" +
" OPTIONAL { ?s foaf:nick ?nick }\n" +
" FILTER(BOUND(?nick) || (?s NOT IN (ex:bob)))\n" +
"}";
assertFixedPoint(q, cfg());
}
// --- VALUES short form and empty edge case ---
@RepeatedTest(10)
void values_single_var_short_form() {
String q = "SELECT ?s WHERE {\n" +
" VALUES (?s) {\n" +
" (ex:alice)\n" +
" (ex:bob)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_empty_block() {
String q = "SELECT ?s WHERE {\n" +
" VALUES (?s) {\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// --- Syntactic sugar: blank node property list and collections ---
@RepeatedTest(10)
void blank_node_property_list() {
String q = "SELECT ?n WHERE {\n" +
" [] foaf:name ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void collections() {
String q = "SELECT ?el WHERE {\n" +
" (1 2 3) rdf:rest*/rdf:first ?el .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// ==========================================
// ===== Complex integration-style tests ====
// ==========================================
@RepeatedTest(10)
void complex_kitchen_sink_paths_graphs_subqueries() {
String q = "SELECT REDUCED ?g ?y (?cnt AS ?count) (COALESCE(?avgAge, -1) AS ?ageOrMinus1) WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" (ex:g2)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?x (foaf:knows|ex:knows)/^foaf:knows ?y .\n" +
" ?y foaf:name ?name .\n" +
" }\n" +
" OPTIONAL {\n" +
" GRAPH ?g {\n" +
" ?y ex:age ?age .\n" +
" }\n" +
" FILTER (?age >= 21)\n" +
" }\n" +
" MINUS {\n" +
" ?y a ex:Robot .\n" +
" }\n" +
" FILTER (NOT EXISTS { ?y foaf:nick ?nick . FILTER (STRLEN(?nick) > 0) })\n" +
" {\n" +
" SELECT ?y (COUNT(DISTINCT ?name) AS ?cnt) (AVG(?age) AS ?avgAge)\n" +
" WHERE {\n" +
" ?y foaf:name ?name .\n" +
" OPTIONAL {\n" +
" ?y ex:age ?age .\n" +
" }\n" +
" }\n" +
" GROUP BY ?y\n" +
" }\n" +
"}\n" +
"ORDER BY DESC(?cnt) LCASE(?name)\n" +
"LIMIT 10\n" +
"OFFSET 5";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testMoreGraph1() {
String q = "SELECT REDUCED ?g ?y (?cnt AS ?count) (COALESCE(?avgAge, -1) AS ?ageOrMinus1) WHERE {\n" +
" VALUES ?g { ex:g1 ex:g2 }\n" +
" GRAPH ?g {\n" +
" ?x (foaf:knows|ex:knows)/^foaf:knows ?y .\n" +
" ?y foaf:name ?name .\n" +
" }\n" +
" OPTIONAL {\n" +
" GRAPH ?g {\n" +
" ?y ex:age ?age .\n" +
" }\n" +
" FILTER (?age >= 21)\n" +
" }\n" +
" MINUS {\n" +
" ?y a ex:Robot .\n" +
" }\n" +
" FILTER NOT EXISTS {\n" +
" ?y foaf:nick ?nick .\n" +
" FILTER (STRLEN(?nick) > 0)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testMoreGraph2() {
String q = "SELECT REDUCED ?g ?y (?cnt AS ?count) (COALESCE(?avgAge, -1) AS ?ageOrMinus1) WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" (ex:g2)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?x (foaf:knows|ex:knows)/^foaf:knows ?y .\n" +
" ?y foaf:name ?name .\n" +
" }\n" +
" OPTIONAL {\n" +
" GRAPH ?g {\n" +
" ?y ex:age ?age .\n" +
" }\n" +
" FILTER (?age >= 21)\n" +
" }\n" +
" MINUS {\n" +
" ?y a ex:Robot .\n" +
" }\n" +
" FILTER (NOT EXISTS { ?y foaf:nick ?nick . FILTER (STRLEN(?nick) > 0) })\n" +
" {\n" +
" SELECT ?y ?name\n" +
" WHERE {\n" +
" ?y foaf:name ?name .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void morePathInGraph() {
String q = "SELECT REDUCED ?g ?y (?cnt AS ?count) (COALESCE(?avgAge, -1) AS ?ageOrMinus1) WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" (ex:g2)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?x (foaf:knows|ex:knows)/^foaf:knows ?y .\n" +
" ?y foaf:name ?name .\n" +
" }\n" +
" OPTIONAL {\n" +
" ?y ex:age ?age .\n" +
" FILTER (?age >= 21)\n" +
" }\n" +
"}\n" +
"ORDER BY DESC(?cnt) LCASE(?name)\n" +
"LIMIT 10\n" +
"OFFSET 5";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_deep_union_optional_with_grouping() {
String q = "SELECT ?s ?label ?src (SUM(?innerC) AS ?c) WHERE {\n" +
" VALUES ?src { \"A\" \"B\" }\n" +
" {\n" +
" ?s a foaf:Person .\n" +
" OPTIONAL {\n" +
" ?s rdfs:label ?label .\n" +
" FILTER (LANGMATCHES(LANG(?label), \"en\"))\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?anon1 foaf:name ?label .\n" +
" BIND( \"B\" AS ?src)\n" +
" BIND( BNODE() AS ?s)\n" +
" }\n" +
" {\n" +
" SELECT ?s (COUNT(?o) AS ?innerC)\n" +
" WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER (?p != rdf:type)\n" +
" }\n" +
" GROUP BY ?s\n" +
" HAVING (COUNT(?o) >= 0)\n" +
" }\n" +
"}\n" +
"GROUP BY ?s ?label ?src\n" +
"HAVING (SUM(?innerC) >= 1)\n" +
"ORDER BY DESC( ?c) STRLEN( COALESCE(?label, \"\"))\n" +
"LIMIT 20";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_federated_service_subselect_and_graph() {
String q = "SELECT ?u ?g (COUNT(DISTINCT ?p) AS ?pc) WHERE {\n" +
" SERVICE <http://example.org/sparql> {\n" +
" {\n" +
" SELECT ?u ?p WHERE {\n" +
" ?u ?p ?o .\n" +
" FILTER (?p != rdf:type)\n" +
" }\n" +
" }\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?u !(ex:age|foaf:knows) ?any .\n" +
" }\n" +
" FILTER (EXISTS { GRAPH ?g { ?u foaf:name ?n . } })\n" +
"}\n" +
"GROUP BY ?u ?g\n" +
"ORDER BY DESC(?pc)\n" +
"LIMIT 7\n" +
"OFFSET 3";
collections();
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_ask_with_subselect_exists_and_not_exists() {
String q = "SELECT ?g ?s ?n WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?s foaf:name ?n .\n" +
" }\n" +
" FILTER (EXISTS { { SELECT ?s WHERE { ?s foaf:knows ?t . } GROUP BY ?s HAVING (COUNT(?t) > 1) } })\n"
+
" FILTER (NOT EXISTS { ?s ex:blockedBy ?b . })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_expressions_aggregation_and_ordering() {
String q = "SELECT ?s (CONCAT(LCASE(STR(?n)), \"-\", STRUUID()) AS ?tag) (MAX(?age) AS ?maxAge) WHERE {\n" +
" ?s foaf:name ?n .\n" +
" OPTIONAL {\n" +
" ?s ex:age ?age .\n" +
" }\n" +
" FILTER ((STRLEN(?n) > 1) && (isLiteral(?n) || BOUND(?n)))\n" +
" FILTER ((REPLACE(?n, \"A\", \"a\") != ?n) || (?s IN (ex:alice, ex:bob)))\n" +
" FILTER ((DATATYPE(?age) = xsd:integer) || !(BOUND(?age)))\n" +
"}\n" +
"GROUP BY ?s ?n\n" +
"ORDER BY STRLEN(?n) DESC(?maxAge)\n" +
"LIMIT 50";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_mutual_knows_with_degree_subqueries() {
String q = "SELECT ?a ?b ?aC ?bC WHERE {\n" +
" {\n" +
" SELECT ?a (COUNT(?ka) AS ?aC)\n" +
" WHERE {\n" +
" ?a foaf:knows ?ka .\n" +
" }\n" +
" GROUP BY ?a\n" +
" }\n" +
" {\n" +
" SELECT ?b (COUNT(?kb) AS ?bC)\n" +
" WHERE {\n" +
" ?b foaf:knows ?kb .\n" +
" }\n" +
" GROUP BY ?b\n" +
" }\n" +
" ?a foaf:knows ?b .\n" +
" FILTER (EXISTS { ?b foaf:knows ?a . })\n" +
"}\n" +
"ORDER BY DESC(?aC + ?bC)\n" +
"LIMIT 10";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_path_inverse_and_negated_set_mix() {
String q = "SELECT ?a ?n WHERE {\n" +
" ?a (^foaf:knows/!(ex:helps|ex:knows|rdf:subject|rdf:type)/foaf:name) ?n .\n" +
" FILTER ((LANG(?n) = \"\") || LANGMATCHES(LANG(?n), \"en\"))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_service_variable_and_nested_subqueries() {
String q = "SELECT ?svc ?s (SUM(?c) AS ?total) WHERE {\n" +
" BIND(<http://example.org/sparql> AS ?svc)\n" +
" SERVICE ?svc {\n" +
" {\n" +
" SELECT ?s (COUNT(?p) AS ?c)\n" +
" WHERE {\n" +
" ?s ?p ?o .\n" +
" }\n" +
" GROUP BY ?s\n" +
" }\n" +
" }\n" +
" OPTIONAL {\n" +
" GRAPH ?g {\n" +
" ?s foaf:name ?n .\n" +
" }\n" +
" }\n" +
" MINUS {\n" +
" ?s a ex:Robot .\n" +
" }\n" +
"}\n" +
"GROUP BY ?svc ?s\n" +
"HAVING (SUM(?c) >= 0)\n" +
"ORDER BY DESC(?total)";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complex_values_matrix_paths_and_groupby_alias() {
String q = "SELECT ?key ?person (COUNT(?o) AS ?c) WHERE {\n" +
" {\n" +
" VALUES ?k { \"foaf\" }\n" +
" ?person foaf:knows/foaf:knows* ?other .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" VALUES ?k { \"foaf\" }\n" +
" ?person ex:knows/foaf:knows* ?other .\n" +
" }\n" +
" ?person ?p ?o .\n" +
" FILTER (?p != rdf:type)\n" +
"}\n" +
"GROUP BY (?k AS ?key) ?person\n" +
"ORDER BY ?key DESC(?c)\n" +
"LIMIT 100";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void groupByAlias() {
String q = "SELECT ?predicate WHERE {\n" +
" ?a ?b ?c .\n" +
"}\n" +
"GROUP BY (?b AS ?predicate)\n" +
"ORDER BY ?predicate\n" +
"LIMIT 100";
assertSameSparqlQuery(q, cfg(), false);
}
// ================================================
// ===== Ultra-heavy, limit-stretching tests ======
// ================================================
@RepeatedTest(10)
void mega_monster_deep_nesting_everything() {
String q = "SELECT REDUCED ?g ?x ?y (?cnt AS ?count) (IF(BOUND(?avgAge), (xsd:decimal(?cnt) + xsd:decimal(?avgAge)), xsd:decimal(?cnt)) AS ?score)\n"
+
"WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" (ex:g2)\n" +
" (ex:g3)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?x (foaf:knows/(^foaf:knows|ex:knows)*) ?y .\n" +
" OPTIONAL { ?y rdfs:label ?label FILTER (LANGMATCHES(LANG(?label), \"en\")) }\n" +
" }\n" +
" FILTER (NOT EXISTS { ?y ex:blockedBy ?b } && !EXISTS { ?y ex:status \"blocked\"@en })\n" +
" MINUS { ?y rdf:type ex:Robot }\n" +
" {\n" +
" SELECT ?y (COUNT(DISTINCT ?name) AS ?cnt) (AVG(?age) AS ?avgAge)\n" +
" WHERE {\n" +
" ?y foaf:name ?name .\n" +
" OPTIONAL { ?y ex:age ?age FILTER (DATATYPE(?age) = xsd:integer) }\n" +
" }\n" +
" GROUP BY ?y\n" +
" }\n" +
" OPTIONAL {\n" +
" {\n" +
" SELECT ?x (COUNT(?k) AS ?deg)\n" +
" WHERE { ?x foaf:knows ?k }\n" +
" GROUP BY ?x\n" +
" }\n" +
" FILTER (?deg >= 0)\n" +
" }\n" +
"}\n" +
"ORDER BY DESC(?cnt) LCASE(COALESCE(?label, \"\"))\n" +
"LIMIT 50\n" +
"OFFSET 10";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_monster_deep_nesting_everything_simple() {
String q = "SELECT REDUCED ?g ?x ?y (?cnt AS ?count) (IF(BOUND(?avgAge), (xsd:decimal(?cnt) + xsd:decimal(?avgAge)), xsd:decimal(?cnt)) AS ?score)\n"
+
"WHERE {\n" +
" VALUES (?g) {\n" +
" (ex:g1)\n" +
" (ex:g2)\n" +
" (ex:g3)\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?x foaf:knows/(^foaf:knows|ex:knows)* ?y .\n" +
" OPTIONAL {\n" +
" ?y rdfs:label ?label .\n" +
" }\n" +
" }\n" +
" FILTER (LANGMATCHES(LANG(?label), \"en\"))\n" +
" FILTER (NOT EXISTS { ?y ex:blockedBy ?b . } && NOT EXISTS { ?y ex:status \"blocked\"@en . })\n" +
"}\n" +
"ORDER BY DESC(?cnt) LCASE(COALESCE(?label, \"\"))\n" +
"LIMIT 50\n" +
"OFFSET 10";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_massive_union_chain_with_mixed_paths() {
String q = "SELECT ?s ?kind WHERE {\n" +
" {\n" +
" BIND(\"knows\" AS ?kind)\n" +
" ?s foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"knows2\" AS ?kind)\n" +
" ?s foaf:knows/foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"alt\" AS ?kind)\n" +
" ?s (foaf:knows|ex:knows) ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"inv\" AS ?kind)\n" +
" ?s ^foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"nps\" AS ?kind)\n" +
" ?o !(ex:age|rdf:type) ?s .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"zeroOrOne\" AS ?kind)\n" +
" ?s (foaf:knows)? ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"zeroOrMore\" AS ?kind)\n" +
" ?s foaf:knows* ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" BIND(\"oneOrMore\" AS ?kind)\n" +
" ?s foaf:knows+ ?o .\n" +
" }\n" +
"}\n" +
"ORDER BY ?kind\n" +
"LIMIT 1000";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_wide_values_matrix_typed_and_undef() {
String q = "SELECT ?s ?p ?o ?tag ?n (IF(BOUND(?o), STRLEN(STR(?o)), -1) AS ?len) WHERE {\n" +
" VALUES (?s ?p ?o ?tag ?n) {\n" +
" (ex:a foaf:name \"Ann\"@en \"A\" 1)\n" +
" (ex:b foaf:name \"B��b\"@de \"B\" 2)\n" +
" (ex:c foaf:name \"Carol\"@en-US \"C\" 3)\n" +
" (ex:d ex:age 42 \"D\" 4)\n" +
" (ex:e ex:age 3.14 \"E\" 5)\n" +
" (ex:f foaf:name \"����������������\"@el \"F\" 6)\n" +
" (ex:g foaf:name \"��������\"@ru \"G\" 7)\n" +
" (ex:h foaf:name \"���\"@zh \"H\" 8)\n" +
" (ex:i foaf:name \"������\"@ar \"I\" 9)\n" +
" (ex:j foaf:name \"Ren��e\"@fr \"J\" 10)\n" +
" (UNDEF ex:age UNDEF \"U\" UNDEF)\n" +
" (ex:k foaf:name \"multi\\nline\" \"M\" 11)\n" +
" (ex:l foaf:name \"quote\\\"test\" \"Q\" 12)\n" +
" (ex:m foaf:name \"smile\uD83D\uDE42\" \"S\" 13)\n" +
" (ex:n foaf:name \"emoji\uD83D\uDE00\" \"E\" 14)\n" +
" }\n" +
" OPTIONAL {\n" +
" ?s ?p ?o .\n" +
" }\n" +
"}\n" +
"ORDER BY ?tag ?n\n" +
"LIMIT 500";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_parentheses_precedence() {
String q = "SELECT ?s ?o (?score AS ?score2) WHERE {\n" +
" ?s foaf:knows/((^foaf:knows)|ex:knows) ?o .\n" +
" BIND(((IF(BOUND(?o), 1, 0) + 0) * 1) AS ?score)\n" +
" FILTER ((BOUND(?s) && BOUND(?o)) && REGEX(STR(?o), \"^.+$\", \"i\"))\n" +
"}\n" +
"ORDER BY ?score\n" +
"LIMIT 100";
assertSameSparqlQuery(q, cfg(), false);
}
// ==========================
// ===== New unit tests =====
// ==========================
@RepeatedTest(10)
void filter_before_trailing_subselect_movable() {
String q = "SELECT ?s WHERE {\n" +
" ?s a foaf:Person .\n" +
" FILTER (BOUND(?s))\n" +
" {\n" +
" SELECT ?x\n" +
" WHERE {\n" +
" ?x a ex:Thing .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void filter_after_trailing_subselect_depends_on_subselect() {
String q = "SELECT ?x WHERE {\n" +
" ?s a foaf:Person .\n" +
" {\n" +
" SELECT ?x\n" +
" WHERE {\n" +
" ?x a ex:Thing .\n" +
" }\n" +
" }\n" +
" FILTER (?x = ?x)\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void graph_optional_merge_plain_body_expected_shape() {
String q = "SELECT ?g ?s ?label WHERE {\n" +
" GRAPH ?g {\n" +
" ?s a foaf:Person .\n" +
" OPTIONAL {\n" +
" ?s rdfs:label ?label .\n" +
" }\n" +
" FILTER (LANGMATCHES(LANG(?label), \"en\"))\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void graph_optional_inner_graph_same_expected_shape() {
String q = "SELECT ?g ?s ?label WHERE {\n" +
" GRAPH ?g {\n" +
" ?s a foaf:Person .\n" +
" OPTIONAL {\n" +
" ?s rdfs:label ?label .\n" +
" }\n" +
" FILTER (LANGMATCHES(LANG(?label), \"en\"))\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void graph_optional_inner_graph_mismatch_no_merge_expected_shape() {
String q = "SELECT ?g ?h ?s ?label WHERE {\n" +
" GRAPH ?g {\n" +
" ?s a foaf:Person .\n" +
" }\n" +
" OPTIONAL {\n" +
" GRAPH ?h {\n" +
" ?s rdfs:label ?label .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_empty_parentheses_rows() {
String q = "SELECT ?s WHERE {\n" +
" VALUES () {\n" +
" ()\n" +
" ()\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void function_fallback_decimal_prefix_compaction() {
String q = "SELECT (?cnt AS ?c) (xsd:decimal(?cnt) AS ?d) WHERE {\n" +
" VALUES (?cnt) {\n" +
" (1)\n" +
" (2)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void function_fallback_unknown_prefixed_kept() {
String q = "SELECT (ex:score(?x, ?y) AS ?s) WHERE {\n" +
" ?x ex:knows ?y .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void inverse_triple_heuristic_print_caret() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ^ex:knows ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void property_list_with_a_and_multiple_preds() {
String q = "SELECT ?s ?name ?age WHERE {\n" +
" ?s a ex:Person ; foaf:name ?name ; ex:age ?age .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void union_branches_to_path_alternation() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s foaf:knows|ex:knows ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nps_via_not_in() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER (?p NOT IN (rdf:type, ex:age))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nps_via_inequalities() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER (?p NOT IN (rdf:type, ex:age))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void service_silent_block_layout() {
String q = "SELECT ?s ?o WHERE {\n" +
" SERVICE SILENT ?svc {\n" +
" ?s ?p ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void ask_basic_bgp() {
String q = "ASK WHERE {\n" +
" ?s a foaf:Person .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void order_by_mixed_vars_and_exprs() {
String q = "SELECT ?x ?name WHERE {\n" +
" ?x foaf:name ?name .\n" +
"}\n" +
"ORDER BY ?x DESC(?name)";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void graph_merge_with_following_filter_inside_group() {
String q = "SELECT ?g ?s ?label WHERE {\n" +
" GRAPH ?g {\n" +
" ?s a foaf:Person .\n" +
" OPTIONAL {\n" +
" ?s rdfs:label ?label .\n" +
" }\n" +
" FILTER (STRLEN(STR(?label)) >= 0)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_with_undef_mixed() {
String q = "SELECT ?s ?p ?o WHERE {\n" +
" VALUES (?s ?p ?o) {\n" +
" (ex:a ex:age 42)\n" +
" (UNDEF ex:age UNDEF)\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void optional_outside_graph_when_complex_body() {
String q = "SELECT ?g ?s ?label ?nick WHERE {\n" +
" GRAPH ?g {\n" +
" ?s a foaf:Person .\n" +
" }\n" +
" OPTIONAL {\n" +
" ?s rdfs:label ?label .\n" +
" FILTER (?label != \"\")\n" +
" OPTIONAL {\n" +
" ?s foaf:nick ?nick .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// -----------------------------
// Deeply nested path scenarios
// -----------------------------
@RepeatedTest(10)
void deep_path_in_optional_in_graph() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" OPTIONAL {\n" +
" GRAPH ?g {\n" +
" ?s foaf:knows/(^foaf:knows|ex:knows)* ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_path_in_minus() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s a ex:Person .\n" +
" MINUS {\n" +
" ?s foaf:knows/foaf:knows? ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void pathExample() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s a ex:Person .\n" +
" MINUS {\n" +
" ?s foaf:knows/foaf:knows? ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_path_in_filter_not_exists() {
String q = "SELECT ?s WHERE {\n" +
" FILTER (NOT EXISTS { ?s (foaf:knows|ex:knows)/^foaf:knows ?o . })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_path_in_union_branch_with_graph() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g {\n" +
" ?s (foaf:knows|ex:knows)* ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ^ex:knows ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void zero_or_more_then_inverse_then_alt_in_graph() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" GRAPH ?g {\n" +
" ?s (foaf:knows*/^(foaf:knows|ex:knows)) ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void optional_with_values_and_bind_inside_graph() {
String q = "SELECT ?g ?s ?n ?name WHERE {\n" +
" GRAPH ?g {\n" +
" OPTIONAL {\n" +
" VALUES (?s ?n) { (ex:a 1) (ex:b 2) }\n" +
" BIND(STR(?n) AS ?name)\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void exists_with_path_and_aggregate_in_subselect() {
String q = "SELECT ?s WHERE {\n" +
" FILTER (EXISTS { { SELECT (COUNT(?x) AS ?c) WHERE { ?s foaf:knows+ ?x . } } FILTER (?c >= 0) })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_union_optional_with_path_and_filter() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" OPTIONAL { ?s foaf:knows/foaf:knows ?o . FILTER (BOUND(?o)) }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (ex:knows|foaf:knows)+ ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void minus_with_graph_and_optional_path() {
String q = "SELECT ?s WHERE {\n" +
" MINUS {\n" +
" OPTIONAL {\n" +
" ?s foaf:knows?/^ex:knows ?o . \n" +
" } \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void service_with_graph_and_path() {
String q = "SELECT ?s ?o WHERE {\n" +
" SERVICE ?svc { GRAPH ?g { ?s (foaf:knows|ex:knows) ?o . } }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void group_by_filter_with_path_in_where() {
String q = "SELECT ?s (COUNT(?o) AS ?c) WHERE {\n" +
" ?s foaf:knows/foaf:knows? ?o .\n" +
" FILTER (?c >= 0)\n" +
"}\n" +
"GROUP BY ?s";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_subselect_with_path_and_order() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s foaf:knows+ ?o .\n" +
"}\n" +
"ORDER BY ?o";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void optional_chain_then_graph_path() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" OPTIONAL {\n" +
" ?s foaf:knows ?mid .\n" +
" OPTIONAL {\n" +
" ?mid foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" GRAPH ?g {\n" +
" ?s ex:knows/^foaf:knows ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void values_then_graph_then_minus_with_path() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" VALUES (?g) { (ex:g1) (ex:g2) }\n" +
" GRAPH ?g { ?s foaf:knows ?o . }\n" +
" MINUS { ?s (ex:knows|foaf:knows) ?o . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nps_path_followed_by_constant_step_in_graph() {
String q = "SELECT ?s ?x WHERE {\n" +
" GRAPH ?g {\n" +
" ?s !(ex:age|rdf:type)/foaf:name ?x .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_nested_union_optional_minus_mix_with_paths() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" OPTIONAL {\n" +
" ?s foaf:knows/foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" MINUS {\n" +
" ?s (ex:knows/foaf:knows)? ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_exists_with_path_and_inner_filter() {
String q = "SELECT ?s WHERE {\n" +
" FILTER (EXISTS { ?s foaf:knows+/^ex:knows ?o . FILTER (BOUND(?o)) })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_zero_or_one_path_in_union() {
String q = "SELECT ?o ?s WHERE {\n" +
" {\n" +
" ?s foaf:knows? ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ex:knows? ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_path_chain_with_graph_and_filter() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" GRAPH ?g {\n" +
" ?s (foaf:knows)/(((^ex:knows)|^foaf:knows)) ?o .\n" +
" }\n" +
" FILTER (BOUND(?s) && BOUND(?o))\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_ask_deep_exists_notexists_filters() {
String q = "ASK WHERE {\n" +
" { ?a foaf:knows ?b } UNION { ?b foaf:knows ?a }\n" +
" FILTER (EXISTS { ?a foaf:name ?n . FILTER (REGEX(?n, \"^A\", \"i\")) })\n" +
" FILTER (NOT EXISTS { ?a ex:blockedBy ?b . })" +
" GRAPH ?g { ?a !(rdf:type|ex:age)/foaf:name ?x }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_ask_deep_exists_notexists_filters2() {
String q = "ASK WHERE {\n" +
" {\n" +
" ?a foaf:knows ?b .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?b foaf:knows ?a .\n" +
" }\n" +
" FILTER (EXISTS {\n" +
" ?a foaf:name ?n .\n" +
" FILTER (REGEX(?n, \"^A\", \"i\"))\n" +
" })\n" +
" FILTER (NOT EXISTS {\n" +
" ?a ex:blockedBy ?b .\n" +
" })\n" +
" GRAPH ?g {\n" +
" ?a !(ex:age|rdf:type)/foaf:name ?x .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void path_in_graph() {
String q = "SELECT ?g ?a ?x WHERE {\n" +
" GRAPH ?g {\n" +
" ?a !(ex:age|rdf:type)/foaf:name ?x .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nps_fusion_graph_filter_graph_not_in_forward() {
String expanded = "SELECT ?g ?a ?x WHERE {\n" +
" GRAPH ?g {\n" +
" ?a ?p ?m .\n" +
" }\n" +
" FILTER (?p NOT IN (rdf:type, ex:age))\n" +
" GRAPH ?g {\n" +
" ?m foaf:name ?x .\n" +
" }\n" +
"}";
assertSameSparqlQuery(expanded, cfg(), false);
}
@RepeatedTest(10)
void nps_fusion_graph_filter_graph_ineq_chain_inverse() {
String expanded = "SELECT ?g ?a ?x WHERE {\n" +
" GRAPH ?g {\n" +
" ?a ?p ?m .\n" +
" }\n" +
" FILTER ((?p != rdf:type) && (?p != ex:age))\n" +
" GRAPH ?g {\n" +
" ?x foaf:name ?m .\n" +
" }\n" +
"}";
assertSameSparqlQuery(expanded, cfg(), false);
}
@RepeatedTest(10)
void nps_fusion_graph_filter_only() {
String expanded = "SELECT ?g ?a ?m WHERE {\n" +
" GRAPH ?g {\n" +
" ?a ?p ?m .\n" +
" }\n" +
" FILTER (?p NOT IN (rdf:type, ex:age))\n" +
"}";
assertSameSparqlQuery(expanded, cfg(), false);
}
@RepeatedTest(10)
void nps_fusion_graph_filter_only2() {
String expanded = "SELECT ?g ?a ?m ?n WHERE {\n" +
" GRAPH ?g {\n" +
" ?a !(ex:age|^rdf:type) ?m .\n" +
" ?a !(^ex:age|rdf:type) ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(expanded, cfg(), false);
}
@RepeatedTest(10)
void mega_service_graph_interleaved_with_subselects() {
String q = "SELECT ?s ?g (SUM(?c) AS ?total) WHERE {\n" +
" VALUES (?svc) {\n" +
" (<http://example.org/sparql>)\n" +
" }\n" +
" SERVICE ?svc {\n" +
" {\n" +
" SELECT ?s (COUNT(?p) AS ?c)\n" +
" WHERE {\n" +
" GRAPH ?g {\n" +
" ?s ?p ?o .\n" +
" }\n" +
" FILTER (?p NOT IN (rdf:type, ex:type))\n" +
" }\n" +
" GROUP BY ?s\n" +
" }\n" +
" }\n" +
" OPTIONAL {\n" +
" ?s foaf:name ?n .\n" +
" FILTER (LANGMATCHES(LANG(?n), \"en\"))\n" +
" }\n" +
" MINUS {\n" +
" ?s a ex:Robot .\n" +
" }\n" +
"}\n" +
"GROUP BY ?s ?g\n" +
"HAVING (SUM(?c) >= 0)\n" +
"ORDER BY DESC(?total) LCASE(COALESCE(?n, \"\"))\n" +
"LIMIT 25";
assertSameSparqlQuery(q, cfg(), false);
}
// @RepeatedTest(10)
// void mega_long_string_literals_and_escaping() {
// String q = "SELECT ?txt ?repl WHERE {\n" +
// " BIND(\"\"\"Line1\\nLine2 \\\"quotes\\\" and backslash \\\\ and \\t tab and unicode \\u03B1 \\U0001F642\"\"\" AS ?txt)\n"
// +
// " BIND(REPLACE(?txt, \"Line\", \"Ln\") AS ?repl)\n" +
// " FILTER(REGEX(?txt, \"Line\", \"im\"))\n" +
// "}";
// assertSameSparqlQuery(q, cfg());
// }
@RepeatedTest(10)
void mega_order_by_on_expression_over_aliases() {
String q = "SELECT ?s ?bestName ?avgAge WHERE {\n" +
" {\n" +
" SELECT ?s (MIN(?n) AS ?bestName) (AVG(?age) AS ?avgAge)\n" +
" WHERE {\n" +
" ?s foaf:name ?n .\n" +
" OPTIONAL {\n" +
" ?s ex:age ?age .\n" +
" }\n" +
" }\n" +
" GROUP BY ?s\n" +
" }\n" +
" FILTER (BOUND(?bestName))\n" +
"}\n" +
"ORDER BY DESC(COALESCE(?avgAge, -999)) LCASE(?bestName)\n" +
"LIMIT 200";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_optional_minus_nested() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ?p ?o .\n" +
" OPTIONAL {\n" +
" ?s foaf:knows ?k .\n" +
" OPTIONAL {\n" +
" ?k foaf:name ?kn .\n" +
" MINUS {\n" +
" ?k ex:blockedBy ?s .\n" +
" }\n" +
" FILTER (!(BOUND(?kn)) || (STRLEN(?kn) >= 0))\n" +
" }\n" +
" }\n" +
" FILTER ((?s IN (ex:a, ex:b, ex:c)) || EXISTS { ?s foaf:name ?nn . })\n" +
"}\n" +
"ORDER BY ?s ?o";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_scoped_variables_and_aliasing_across_subqueries() {
String q = "SELECT ?s ?bestName ?deg WHERE {\n" +
" {\n" +
" SELECT ?s (MIN(?n) AS ?bestName)\n" +
" WHERE {\n" +
" ?s foaf:name ?n .\n" +
" }\n" +
" GROUP BY ?s\n" +
" }\n" +
" OPTIONAL {\n" +
" {\n" +
" SELECT ?s (COUNT(?o) AS ?deg)\n" +
" WHERE {\n" +
" ?s foaf:knows ?o .\n" +
" }\n" +
" GROUP BY ?s\n" +
" }\n" +
" }\n" +
" FILTER (BOUND(?bestName))\n" +
"}\n" +
"ORDER BY ?bestName ?s";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_type_shorthand_and_mixed_sugar() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s a foaf:Person ; foaf:name ?n .\n" +
" [] foaf:knows ?s .\n" +
" (ex:alice ex:bob ex:carol) rdf:rest*/rdf:first ?x .\n" +
" FILTER (STRLEN(?n) > 0)\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void mega_exists_union_inside_exists_and_notexists() {
String q = "SELECT ?s WHERE {\n" +
" ?s ?p ?o .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s foaf:knows ?t .\n" +
" } \n" +
" UNION\n" +
" {\n" +
" ?t foaf:knows ?s .\n" +
" } \n" +
"\n" +
" FILTER NOT EXISTS {\n" +
" ?t ex:blockedBy ?s . \n" +
" } \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// -------- New deep nested OPTIONAL path tests --------
@RepeatedTest(10)
void deep_optional_path_1() {
String q = "SELECT ?s ?n WHERE {\n" +
" OPTIONAL {\n" +
" OPTIONAL {\n" +
" OPTIONAL {\n" +
" ?s (^foaf:knows)/(foaf:knows|ex:knows)/foaf:name ?n .\n" +
" FILTER (LANGMATCHES(LANG(?n), \"en\"))\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_optional_path_2() {
String q = "SELECT ?x ?y WHERE {\n" +
" OPTIONAL {\n" +
" ?x ^foaf:knows|ex:knows/^foaf:knows ?y .\n" +
" FILTER (?x != ?y)\n" +
" OPTIONAL {\n" +
" ?y (foaf:knows|ex:knows)/foaf:knows ?x .\n" +
" FILTER (BOUND(?x))\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_optional_path_3() {
String q = "SELECT ?a ?n WHERE {\n" +
" OPTIONAL {\n" +
" ?a (^foaf:knows/!(ex:helps|ex:knows|rdf:subject|rdf:type)/foaf:name) ?n .\n" +
" FILTER ((LANG(?n) = \"\") || LANGMATCHES(LANG(?n), \"en\"))\n" +
" OPTIONAL {\n" +
" ?a foaf:knows+ ?anon1 .\n" +
" FILTER (BOUND(?anon1))\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_optional_path_4() {
String q = "SELECT ?s ?o WHERE {\n" +
" OPTIONAL {\n" +
" OPTIONAL {\n" +
" ?s (foaf:knows/foaf:knows|ex:knows/^ex:knows) ?o .\n" +
" FILTER (?s != ?o)\n" +
" }\n" +
" FILTER (BOUND(?s))\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_optional_path_5() {
String q = "SELECT ?g ?s ?n WHERE {\n" +
" OPTIONAL {\n" +
" OPTIONAL {\n" +
" ?s (foaf:knows|ex:knows)/^foaf:knows/(foaf:name|^foaf:name) ?n .\n" +
" FILTER (STRLEN(STR(?n)) >= 0)\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complexPath() {
String q = "SELECT ?g ?s ?n WHERE {\n" +
" ?s ex:path1/ex:path2/(ex:alt1|ex:alt2) ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void complexPathUnionOptionalScope() {
String q = "SELECT ?g ?s ?n WHERE {\n" +
" {\n" +
" ?s ex:path1/ex:path2 ?o .\n" +
" OPTIONAL {\n" +
" ?s (ex:alt1|ex:alt2) ?n .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ex:path1/ex:path2 ?o .\n" +
" OPTIONAL {\n" +
" ?s (ex:alt3|ex:alt4) ?n .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// -------- New deep nested UNION path tests --------
@RepeatedTest(10)
void deep_union_path_1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s (foaf:knows|ex:knows)/^foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ^foaf:knows/((foaf:knows|ex:knows)) ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" OPTIONAL {\n" +
" ?s foaf:knows ?x .\n" +
" ?x foaf:name ?_n .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_union_path_2() {
String q = "SELECT ?a ?n WHERE {\n" +
" {\n" +
" ?a ^foaf:knows/foaf:knows/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" {\n" +
" ?a foaf:knows|ex:knows ?_x .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?a foaf:knows ?_x .\n" +
" }\n" +
" OPTIONAL {\n" +
" ?_x foaf:name ?n .\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_union_path_3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" ?s foaf:knows/foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (ex:knows1|^ex:knows2) ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" {\n" +
" ?s ^foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !(ex:age|rdf:type) ?s .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void simpleOrInversePath() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s (ex:knows1|^ex:knows2) ?o . " +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void simpleOrInversePathGraph() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH ?g { ?s (ex:knows1|^ex:knows2) ?o . }" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void simpleOrNonInversePath() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s (ex:knows1|ex:knows2) ?o . " +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_union_path_4() {
String q = "SELECT ?g ?s ?o WHERE {\n" +
" {\n" +
" ?s (foaf:knows|ex:knows)/^foaf:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" OPTIONAL {\n" +
" ?s foaf:knows+ ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" OPTIONAL {\n" +
" ?s !(ex:age|rdf:type)/foaf:name ?_n .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_union_path_5() {
String q = "SELECT ?o ?s WHERE {\n" +
" {\n" +
" {\n" +
" ?s foaf:knows/foaf:knows|ex:knows/^ex:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ^foaf:knows/(foaf:knows|ex:knows) ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" {\n" +
" ?o !(ex:age|rdf:type) ?s .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s foaf:knows? ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void deep_union_path_5_curly_braces() {
String q = "SELECT ?o ?s WHERE {\n" +
" {\n" +
" {\n" +
" ?s foaf:knows/foaf:knows|ex:knows/^ex:knows ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ^foaf:knows/(foaf:knows|ex:knows) ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" {\n" +
" ?o !(ex:age|rdf:type) ?s .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s foaf:knows? ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), true);
}
// -------- Additional SELECT tests with deeper, more nested paths --------
@RepeatedTest(10)
void nested_paths_extreme_1() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s ((foaf:knows/^foaf:knows | !(rdf:type|^rdf:type)/ex:knows?)\n" +
" /((ex:colleagueOf|^ex:colleagueOf)/(ex:knows/foaf:knows)?)*\n" +
" /(^ex:knows/(ex:knows|^ex:knows)+))/foaf:name ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_1_simple() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s foaf:knows/^foaf:knows | !(rdf:type|^rdf:type)/ex:knows? ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_1_simple2() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s (ex:knows1/ex:knows2)* ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_1_simple2_1() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s (ex:knows1|ex:knows2)* ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_1_simple3() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s (ex:knows1/ex:knows2)+ ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_1_simpleGraph() {
String q = "SELECT ?s ?n WHERE {\n" +
" GRAPH ?g {\n" +
" ?s foaf:knows/^foaf:knows | !(rdf:type|^rdf:type)/ex:knows? ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_2_optional_and_graph() {
String q = "SELECT ?g ?s ?n WHERE {\n" +
" GRAPH ?g {\n" +
" ?s ((ex:p1|^ex:p2)+/(!(^ex:p4|ex:p3))? /((ex:p5|^ex:p6)/(foaf:knows|^foaf:knows))*) ?y .\n" +
" }\n" +
" OPTIONAL {\n" +
" ?y (^foaf:knows/(ex:p7|^ex:p8)?/((ex:p9/foaf:knows)|(^ex:p10/ex:p11))) ?z .\n" +
" }\n" +
" ?z foaf:name ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_3_subquery_exists() {
String q = "SELECT ?s WHERE {\n" +
" FILTER (EXISTS {\n" +
" {\n" +
" SELECT ?s\n" +
" WHERE {\n" +
" ?s (ex:p1|^ex:p2)/(!(rdf:type|^rdf:type))*/ex:p3? ?o .\n" +
" }\n" +
" GROUP BY ?s\n" +
" HAVING (COUNT(?o) >= 0)\n" +
" }\n" +
" })\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods() {
String q = "SELECT ?s ?n WHERE {\n" +
" {\n" +
" ?s (((ex:a|^ex:b)/(ex:c/foaf:knows)?)*)/(^ex:d/(ex:e|^ex:f)+)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (((!(ex:g|^ex:h))/(((ex:i|^ex:j))?))/((ex:k/foaf:knows)|(^ex:l/ex:m)))/foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods2() {
String q = "SELECT ?s ?n WHERE {\n" +
" {\n" +
" ?s (((ex:a|^ex:b)/(ex:c/foaf:knows)?)*)/(^ex:d/(ex:e|^ex:f)+)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (((!(^ex:h|ex:g))/(((ex:i|^ex:j))?))/((ex:k/foaf:knows)|(^ex:l/ex:m)))/foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods3() {
String q = "SELECT ?s ?n WHERE {\n" +
" {\n" +
" ?s (((ex:a|^ex:b)/(ex:c/foaf:knows)?)*)/(^ex:d/(ex:e|^ex:f)+)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (((!(ex:h|^ex:g))/(((ex:i|^ex:j))?))/((ex:k/foaf:knows)|(^ex:l/ex:m)))/foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods4() {
String q = "SELECT ?s ?n WHERE {\n" +
" {\n" +
" ?s (((ex:a|^ex:b)/(ex:c/foaf:knows)?)*)/(^ex:d/(ex:e|^ex:f)+)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (((!(^ex:g|ex:h))/(((ex:i|^ex:j))?))/((ex:k/foaf:knows)|(^ex:l/ex:m)))/foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods5() {
String q = "SELECT ?s ?n WHERE {\n" +
" {\n" +
" ?s (^ex:g|ex:h)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s !(^ex:g|ex:h)/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (^ex:g|ex:h)*/foaf:name ?n .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s (^ex:g|ex:h)+/foaf:name ?n .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_4_union_mixed_mods6() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s !(^ex:g|ex:h)/foaf:name ?n .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nested_paths_extreme_5_grouped_repetition() {
String q = "SELECT ?s ?n WHERE {\n" +
" ?s (((ex:pA|^ex:pB)/(ex:pC|^ex:pD))*/(^ex:pE/(ex:pF|^ex:pG)+)/(ex:pH/foaf:knows)?)/foaf:name ?n .\n"
+
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void invertedPathInUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !^<http://example.org/p/I01> ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !^<http://example.org/p/I02> ?s .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void invertedPathInUnion2() {
String q = "SELECT ?s ?o WHERE {\n" +
" { ?s !^<http://example.org/p/I01> ?o . }\n" +
" UNION\n" +
" { ?s !<http://example.org/p/I02> ?o . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNegatedPathUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" { ?o !<http://example.org/p/I01> ?s . }\n" +
" UNION\n" +
" { ?s !<http://example.org/p/I02> ?o . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void negatedPath() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s !ex:pA ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void negatedInvertedPath() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s !^ex:pA ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testInvertedPathUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" { ?s ^<http://example.org/p/I0> ?o . }\n" +
" UNION\n" +
" { ?o ^<http://example.org/p/I0> ?s . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testUnionOrdering() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !(ex:pA|^ex:pB) ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !(ex:pC|^ex:pD) ?s .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testBnodes() {
String q = "SELECT ?s ?x WHERE {\n" +
" [] ex:pA ?s ;\n" +
" ex:pB [ ex:pC ?x ] .\n" +
" ?s ex:pD (ex:Person ex:Thing) .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testBnodes2() {
String q = "SELECT ?s ?x WHERE {\n" +
" _:bnode1 ex:pA ?s ;\n" +
" ex:pB [ ex:pC ?x ] .\n" +
" ?s ex:pD (ex:Person ex:Thing) .\n" +
" [] ex:pE _:bnode1 .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testBnodes3() {
String q = "SELECT ?s ?x WHERE {\n" +
" _:bnode1 ex:pA ?s ;\n" +
" ex:pB [\n" +
" ex:pC ?x;\n" +
" ex:pB [ ex:pF _:bnode1 ] \n" +
" ] .\n" +
" ?s ex:pD (ex:Person ex:Thing) .\n" +
" [] !(ex:pE |^ex:pE) _:bnode1 .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void anonymous_and_named_bnodes_across_optional_union_values_minus_notexists() {
String q = "SELECT ?o ?y WHERE {\n" +
" OPTIONAL {\n" +
" [] ex:p ?o .\n" +
" FILTER(isBlank(?o))\n" +
" }\n" +
" {\n" +
" [] ex:q ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" _:branch ex:q ?o .\n" +
" ?s ex:q [] .\n" +
" MINUS { [] ex:q ?s }\n" +
" }\n" +
" FILTER NOT EXISTS { _:keep ex:r [] }\n" +
" VALUES (?o ?y) {\n" +
" (UNDEF \"v1\")\n" +
" (\"v2\" UNDEF)\n" +
" }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
Matcher bracketMatcher = Pattern.compile("\\[\\]").matcher(rendered);
int bracketCount = 0;
while (bracketMatcher.find()) {
bracketCount++;
}
assertThat(bracketCount).as("[] should remain visible for anonymous blank nodes").isGreaterThanOrEqualTo(2);
Set<String> labels = new HashSet<>();
Matcher labelMatcher = Pattern.compile("_:[A-Za-z][A-Za-z0-9]*").matcher(rendered);
while (labelMatcher.find()) {
labels.add(labelMatcher.group());
}
assertThat(labels.size()).as("named blank nodes should keep distinct labels").isGreaterThanOrEqualTo(2);
assertThat(rendered)
.contains("OPTIONAL")
.contains("UNION")
.contains("MINUS")
.contains("NOT EXISTS")
.contains("VALUES");
}
@RepeatedTest(10)
void distinct_named_bnodes_in_nested_subselects() {
String q = "SELECT ?x ?y WHERE {\n" +
" OPTIONAL { _:outerA ex:p [] . }\n" +
" { SELECT ?x WHERE { _:inner1 ex:p ?x . } }\n" +
" { SELECT ?y WHERE { OPTIONAL { _:inner2 ex:q ?y . } } }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
Set<String> labels = new HashSet<>();
Matcher labelMatcher = Pattern.compile("_:[A-Za-z][A-Za-z0-9]*").matcher(rendered);
while (labelMatcher.find()) {
labels.add(labelMatcher.group());
}
assertThat(labels.size()).as("distinct subselect bnodes must not be reused").isGreaterThanOrEqualTo(3);
Matcher bracketMatcher = Pattern.compile("\\[\\]").matcher(rendered);
assertThat(bracketMatcher.find()).as("anonymous [] must survive rendering").isTrue();
assertThat(rendered).contains("SELECT ?x WHERE").contains("SELECT ?y WHERE").contains("OPTIONAL");
}
@RepeatedTest(10)
void bnodes_survive_filters_and_bind() {
String q = "SELECT ?b ?o WHERE {\n" +
" BIND(BNODE() AS ?b)\n" +
" OPTIONAL { _:filterNode ex:p ?o . }\n" +
" FILTER(isBlank(?b))\n" +
" FILTER EXISTS { [] ex:p ?b }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(rendered).contains("BIND(BNODE()");
assertThat(rendered).contains("_:").contains("FILTER EXISTS {");
assertThat(countAnonPlaceholders(rendered)).as("anonymous [] inside EXISTS must remain")
.isGreaterThanOrEqualTo(1);
}
// -------- Additional blank node coverage --------
@RepeatedTest(10)
void optional_named_bnode_label_preserved() {
String q = "SELECT ?o WHERE { OPTIONAL { _:opt ex:p ?o . } }";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void optional_anonymous_bnode_keeps_brackets() {
String q = "SELECT ?o WHERE { OPTIONAL { [] ex:p ?o . } }";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void union_branches_keep_separate_bnodes() {
String q = "SELECT ?o WHERE {\n" +
" { _:u1 ex:p ?o . }\n" +
" UNION\n" +
" { _:u2 ex:q ?o . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(2);
}
@RepeatedTest(10)
void minus_clause_keeps_named_bnode() {
String q = "SELECT ?o WHERE {\n" +
" _:keepL ex:p ?o .\n" +
" MINUS { _:keepR ex:q ?o }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(2);
}
@RepeatedTest(10)
void not_exists_preserves_anonymous_property_list() {
String q = "SELECT * WHERE {\n" +
" FILTER NOT EXISTS { [] ex:p [ ex:q ?o ] }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void values_alongside_bnodes_do_not_change_labels() {
String q = "SELECT ?o WHERE {\n" +
" [] ex:p ?o .\n" +
" VALUES ?o { \"a\" \"b\" }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void filter_isblank_on_named_bnode() {
String q = "SELECT ?b WHERE {\n" +
" [] ex:p ?b .\n" +
" FILTER(isBlank(?b))\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertThat(rendered).isNotEmpty();
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void graph_clause_named_bnode_subject() {
String q = "SELECT * WHERE {\n" +
" GRAPH <http://g> { _:gsub ex:p ?o . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void graph_clause_anonymous_bnode_object() {
String q = "SELECT * WHERE {\n" +
" GRAPH <http://g> { ?s ex:p [] . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void service_clause_with_anonymous_property_list() {
String q = "SELECT * WHERE {\n" +
" SERVICE <http://svc> { [] ex:p [ ex:q ?o ] . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void subselect_named_bnodes_not_reused() {
String q = "SELECT ?x ?y WHERE {\n" +
" { SELECT ?x WHERE { _:innerA ex:p ?x . } }\n" +
" OPTIONAL { _:outer ex:p ?y . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(2);
}
@RepeatedTest(10)
void subselect_anonymous_bnode_remains_brackets() {
String q = "SELECT ?x WHERE {\n" +
" { SELECT ?x WHERE { [] ex:p ?x . } }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void property_list_nested_bnodes_keep_labels() {
String q = "SELECT * WHERE {\n" +
" _:root ex:p [ ex:q _:leaf ; ex:r [] ] .\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
parseAlgebra(rendered); // ensure round-trip parseable
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(2);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void exists_with_named_bnode_in_pattern() {
String q = "SELECT ?s WHERE {\n" +
" ?s ex:p ?o .\n" +
" FILTER EXISTS { _:exists ex:q ?s }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void not_exists_with_named_bnode_different_scope() {
String q = "SELECT ?s WHERE {\n" +
" ?s ex:p ?o .\n" +
" FILTER NOT EXISTS { _:nex ex:q ?o }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void minus_with_property_list_anonymous() {
String q = "SELECT ?s WHERE {\n" +
" ?s ex:p ?o .\n" +
" MINUS { [] ex:p [ ex:q ?o ] }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
parseAlgebra(rendered);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void filter_sameTerm_on_named_bnode() {
String q = "SELECT * WHERE {\n" +
" [] ex:p ?o .\n" +
" FILTER(sameTerm(?o, ?o))\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void path_with_named_bnode_object() {
String q = "SELECT * WHERE {\n" +
" ?s ex:p+/ex:q _:pnode .\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void union_with_property_list_bnodes_preserves_counts() {
String q = "SELECT * WHERE {\n" +
" { [] ex:p [ ex:q ?o ] . }\n" +
" UNION\n" +
" { _:u ex:p [ ex:q [] ] . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
parseAlgebra(rendered);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(2);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void bind_and_optional_do_not_rename_bnode_labels() {
String q = "SELECT ?b WHERE {\n" +
" BIND(BNODE() AS ?b)\n" +
" OPTIONAL { _:keep ex:p ?b . }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
assertSameSparqlQuery(q, cfg(), false);
assertThat(extractBnodeLabels(rendered).size()).isGreaterThanOrEqualTo(1);
}
@RepeatedTest(10)
void nested_optional_anonymous_property_list() {
String q = "SELECT * WHERE {\n" +
" OPTIONAL { OPTIONAL { [] ex:p [ ex:q [] ] . } }\n" +
"}";
String rendered = render(SPARQL_PREFIX + q, cfg());
parseAlgebra(rendered);
assertThat(countAnonPlaceholders(rendered)).isGreaterThanOrEqualTo(2);
}
@RepeatedTest(10)
void nestedSelectDistinct() {
String q = "SELECT ?s WHERE {\n" +
" { SELECT DISTINCT ?s WHERE { ?s ex:pA ?o } ORDER BY ?s LIMIT 10 }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testPathGraphFilterExists() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s !(ex:pA|^ex:pD) ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsForceNewScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" { FILTER EXISTS {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s ?b ?o .\n" +
" }\n" +
" } }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testPathFilterExistsForceNewScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" { \n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s !(ex:pA|^ex:pD) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesPathUnionScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" { \n" +
" {\n" +
" VALUES (?s) {\n" +
" (ex:s1)\n" +
" (ex:s2)\n" +
" }\n" +
" ?s !^foaf:knows ?o .\n" +
" } \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesPathUnionScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
"{\n" +
" VALUES (?s) {\n" +
" (ex:s1)\n" +
" (ex:s2)\n" +
" }\n" +
" ?o !(foaf:knows) ?s .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// New tests to validate new-scope behavior and single-predicate inversion
@RepeatedTest(10)
void testValuesPrefersSubjectAndCaretForInverse() {
// VALUES binds ?s; inverse single predicate should render with caret keeping ?s as subject
String q = "SELECT ?s ?o WHERE {\n" +
" { {\n" +
" VALUES (?s) { (ex:s1) }\n" +
" ?s !^foaf:knows ?o .\n" +
" } }\n" +
" UNION\n" +
" { ?u1 ex:pD ?v1 . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesAllowsForwardSwappedVariant() {
// VALUES binds ?s; swapped forward form should be preserved when written that way
String q = "SELECT ?s ?o WHERE {\n" +
" { {\n" +
" VALUES (?s) { (ex:s1) }\n" +
" ?o !(foaf:knows) ?s .\n" +
" } }\n" +
" UNION\n" +
" { ?u1 ex:pD ?v1 . }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsPrecedingTripleIsGrouped() {
// Preceding triple + FILTER EXISTS with inner group must retain grouping braces
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS { { \n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS { ?s !(ex:pA|^<http://example.org/p/I0>) ?o . }\n" +
" } } \n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !( ex:pA|^<http://example.org/p/I0>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testComplexPath1() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" ?s !( ex:pA|^<http://example.org/p/I0>) ?o .\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested2_1() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
"{\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested3() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" { \n" +
" ?s ex:pC ?u0 .\n" +
" {\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" } \n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested4() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" ?s ex:pC ?u0 .\n" +
" {\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsNested5() {
String q = "SELECT ?s ?o WHERE {\n" +
"{\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" { \n" +
" ?s ex:pC ?u0 .\n" +
" {\n" +
" FILTER(?s != ?u1) " +
" }\n" +
" }\n" +
" } \n" +
"}\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedSelect() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" { \n" +
" SELECT ?s WHERE {\n" +
" ?s !^<http://example.org/p/I2> ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphOptionalPath() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" { \n" +
" ?s ex:pA ?o . \n" +
" OPTIONAL {\n" +
" ?s !(ex:pA|foaf:knows) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void scopeMinusTest() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" ?s ex:pB ?v0 .\n" +
" MINUS {\n" +
" ?s foaf:knows ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testPathUnionAndServiceAndScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" ?s ^ex:pD ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testPathUnionAndServiceAndScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" {\n" +
" ?s ^ex:pD ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pA ?o . \n" +
" OPTIONAL {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pQ ?ok .\n" +
" {\n" +
" ?s ex:pA ?o .\n" +
" ?s ex:pA ?f .\n" +
" OPTIONAL {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope4() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pQ ?ok .\n" +
" ?s ex:pA ?o .\n" +
" ?s ex:pA ?f .\n" +
" OPTIONAL {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope5() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pQ ?ok .\n" +
" ?s ex:pA ?o .\n" +
" ?s ex:pA ?f .\n" +
" OPTIONAL { {\n" +
" ?o ex:pX ?vX . \n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" } }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope6() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pQ ?ok . \n" +
" ?s ex:pA ?o . \n" +
" ?s ex:pA ?f. \n" +
" OPTIONAL { {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" } }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalServicePathScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pA ?o . \n" +
" OPTIONAL {\n" +
" {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I0>) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testOptionalPathScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
"{ ?s ex:pA ?o . OPTIONAL { { ?s ^<http://example.org/p/I1> ?o . } } }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraph1() {
String q = "SELECT ?s ?o WHERE {\n" +
" VALUES ?s { ex:s1 ex:s2 }\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s a ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraph2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" VALUES ?s { ex:s1 ex:s2 }\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s a ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsGraphScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" { \n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsGraphScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsGraphScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" { \n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsGraphScope4() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows2 ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterExistsGraphScope5() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" ?s !foaf:knows ?o .\n" +
" }\n" +
" }\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s !foaf:knows2 ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" GRAPH ?g0 {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphValuesPathScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g1 {\n" +
" {\n" +
" VALUES ?s {\n" +
" ex:s1 ex:s2 \n" +
" }\n" +
" ?s !^<http://example.org/p/I0> ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphValuesPathScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH ?g1 {\n" +
" {\n" +
" VALUES ?s {\n" +
" ex:s1 ex:s2 \n" +
" }\n" +
" ?s !^<http://example.org/p/I0> ?o . \n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphValuesPathScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g1 {\n" +
" VALUES ?s {\n" +
" ex:s1 ex:s2 \n" +
" }\n" +
" ?s !^<http://example.org/p/I0> ?o . \n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void bgpScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s a ?o . \n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void bgpScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s a ?o . \n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" ?s ^<http://example.org/p/I2> ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectScope4() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" ?s ^<http://example.org/p/I2> ?o . \n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" ?s ^<http://example.org/p/I2> ?o . \n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" SELECT ?s WHERE {\n" +
" ?s ^<http://example.org/p/I2> ?o . \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void filterExistsNestedScopeTest() {
String q = "SELECT ?s ?o WHERE {\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:p ?o .\n" +
" FILTER EXISTS {\n" +
" ?s ex:q ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectGraph() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s ^ex:pB ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectGraph2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
" ?s ex:pC ?u0 . \nFILTER EXISTS {\n" +
" ?s !(ex:pB|^ex:pA) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectGraph3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" ?s <http://example.org/p/I0> ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void scopeGraphFilterExistsPathTest() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" ?s ex:pC ?u0 . \nFILTER EXISTS {\n" +
" ?s ^ex:pC ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedServiceGraphPath() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedServiceGraphPath2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testServiceValuesPathMinus() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" VALUES ?s {\n" +
" ex:s1 ex:s2 \n" +
" }\n" +
" {\n" +
" ?s ex:pB ?v0 . MINUS {\n" +
" ?s !(ex:pA|^foaf:knows) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testServiceGraphGraphPath() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testServiceGraphGraphPath2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" GRAPH <http://graphs.example/g0> {\n" +
" {\n" +
" ?s !(ex:pA|^<http://example.org/p/I1>) ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectServiceUnionPathTest() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" {\n" +
" ?s ^ex:pD ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
// ---- Additional generalization tests to ensure robustness of SERVICE + UNION + SUBSELECT grouping ----
@RepeatedTest(10)
void nestedSelectServiceUnionSimpleTriples_bracedUnionInsideService() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" { ?s ex:pA ?o . } UNION { ?u0 ex:pA ?v0 . }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectServiceUnionWithGraphBranches_bracedUnionInsideService() {
String q = "SELECT ?s WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" GRAPH ?g {\n" +
" {\n" +
" ?s ex:pB ?t . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s ex:pC ?t . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectServiceSinglePath_noExtraUnionGroup() {
String q = "SELECT ?s WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" ?s ex:pZ ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void nestedSelectServiceUnionInversePath_bracedUnionInsideService() {
String q = "SELECT ?s WHERE {\n" +
" {\n" +
" SELECT ?s WHERE {\n" +
" {\n" +
" SERVICE SILENT <http://federation.example/ep> {\n" +
" {\n" +
" {\n" +
" ?s ^ex:pD ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void yetAnotherTest() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
" ?s ex:pC ?u1 . FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pA ?o . OPTIONAL {\n" +
" ?s !<http://example.org/p/I0> ?o . \n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void yetAnotherTest2() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pA ?o .\n" +
" OPTIONAL {\n" +
" ?s !<http://example.org/p/I0> ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void pathUnionTest1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !(ex:pA|ex:pB|^ex:pA) ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !(ex:pA|ex:pB|^ex:pA) ?s . \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void pathUnionTest2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !(<http://example.org/p/I0>|ex:pA|^ex:pA) ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !(<http://example.org/p/I0>|ex:pA|^ex:pA) ?s . \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void pathUnionTest3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !(<http://example.org/p/I0>|ex:pA|^ex:pA|ex:Pb|^ex:Pb|ex:Pc|^ex:Pc|ex:Pd|^ex:Pd|ex:Pe|^ex:Pe|ex:Pf|^ex:Pf) ?o . \n"
+
" }\n" +
" UNION\n" +
" {\n" +
" ?s !(<http://example.org/p/I0>|ex:pA|ex:Pb|ex:Pc|ex:Pd|ex:Pe|ex:Pf) ?o . \n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s !(<http://example.org/p/I0>|ex:pA1|ex:Pb2|ex:Pc3|ex:Pd4|ex:Pe5|ex:Pf6) ?o . \n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void pathUnionTest4() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s !(ex:P1|ex:pA) ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?s !(ex:P1|ex:pA|ex:pA) ?o .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphFilterValuesPathAndScoping() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g2 {\n" +
" {\n" +
" ?s ex:pC ?u1 . FILTER EXISTS {\n" +
" {\n" +
" VALUES ?s { ex:s1 ex:s2 }\n" +
" ?s !( ex:pA|^ex:pC) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testScopeGraphUnionUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
" ?s !ex:pC ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testMinusGraphUnion1() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pB ?v2 .\n" +
" MINUS {\n" +
// " {\n" +
" {\n" +
// " {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s !( ex:pA|foaf:name) ?o .\n" +
" }\n" +
// " }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
// " }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testMinusGraphUnionScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pB ?v2 .\n" +
" MINUS {\n" +
" {\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?s !( ex:pA|foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionUnionScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" {\n" +
" ?s ^ex:pC ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionUnionScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u2 . FILTER EXISTS {\n" +
" {\n" +
" {\n" +
" {\n" +
" ?s ^ex:pC ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u0 ex:pD ?v0 .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pB|foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionScope2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pB|foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionScope3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
// " {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pB|foaf:name) ?o .\n" +
" }\n" +
// " }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionScope4() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s !( ex:pB|foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testFilterUnionScope5() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" ?s ex:pC ?u2 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ex:pC ?u0 .\n" +
" FILTER EXISTS {\n" +
" ?s !(ex:pB|foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScopeUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s ^foaf:name ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScopeUnion2() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
// " {\n" +
" GRAPH ?g0 {\n" +
" ?s ^foaf:name ?o .\n" +
" }\n" +
// " }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testNestedGraphScopeUnion3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?o foaf:name ?s .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s !( ex:pA|^foaf:name) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion2() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" {\n" +
" ?s !ex:pA ?o .\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?o !foaf:name ?s .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion3() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" VALUES ?s { ex:s1 ex:s2 }\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s ex:pA|^foaf:name ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion4() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" VALUES ?s {\n" +
" ex:s1 ex:s2\n" +
" }\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s !( ex:pA|^foaf:name|ex:pB) ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion5() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" VALUES ?s { ex:s1 ex:s2 }\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s ex:pA|!(foaf:knows|^foaf:name)|ex:pB ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u2 ex:pD ?v2 .\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion6() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s ex:pA|!(foaf:knows|^foaf:name)|ex:pB ?o .\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testValuesGraphUnion7() {
String q = "SELECT ?s ?o WHERE {\n" +
" {\n" +
" GRAPH ?g0 {\n" +
" ?s ex:pA|!foaf:knows ?o .\n" +
" }\n" +
" }\n" +
"}\n";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testGraphUnionScope1() {
String q = "SELECT ?s ?o WHERE {\n" +
" GRAPH <http://graphs.example/g1> {\n" +
" {\n" +
" {\n" +
" ?s <http://example.org/p/I2> ?o .\n" +
" }\n" +
" }\n" +
" UNION\n" +
" {\n" +
" ?u1 ex:pD ?v1 .\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
@RepeatedTest(10)
void testServiceFilterExistsAndScope() {
String q = "SELECT ?s ?o WHERE {\n" +
" SERVICE SILENT <http://services.example/sparql> {\n" +
" {\n" +
" ?s ex:pC ?u1 .\n" +
" FILTER EXISTS {\n" +
" {\n" +
" ?s ^ex:pB ?o .\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
assertSameSparqlQuery(q, cfg(), false);
}
}