diff --git a/test/data/variants/forward_slash-asserts.json b/test/data/variants/forward_slash-asserts.json
new file mode 100644
index 000000000..3df873922
--- /dev/null
+++ b/test/data/variants/forward_slash-asserts.json
@@ -0,0 +1,10 @@
+{
+ "quad_count": 4,
+ "exact_match": true,
+ "has_subject_iris": [
+ "http://example.org/kb/individual-a",
+ "http://example.org/kb/individual-b",
+ "http://example.org/ontology/core/MyClassA",
+ "http://example.org/ontology/core/MyClassB"
+ ]
+}
diff --git a/test/data/variants/forward_slash-variant-prefixed.jsonld b/test/data/variants/forward_slash-variant-prefixed.jsonld
new file mode 100644
index 000000000..1329eac65
--- /dev/null
+++ b/test/data/variants/forward_slash-variant-prefixed.jsonld
@@ -0,0 +1,33 @@
+{
+ "@context": {
+ "ex": "http://example.org/ontology/",
+ "kb": "http://example.org/kb/",
+ "owl": "http://www.w3.org/2002/07/owl#"
+ },
+ "@x-comment": [
+ "The JSON-LD spec does not provide a grammar production rule set in,",
+ "EBNF. However, the section on compact IRIs indicates that an IRI can",
+ "be prefixed at any point that would not result in a suffix starting",
+ "with \"//\". Hence, an unpaired forward slash, as a legal character of",
+ "an IRI, can appear in the suffix component of a compact IRI.",
+ "https://json-ld.org/spec/latest/json-ld/#compact-iris"
+ ],
+ "@graph": [
+ {
+ "@id": "kb:individual-a",
+ "@type": "ex:core/MyClassA"
+ },
+ {
+ "@id": "ex:core/MyClassA",
+ "@type": "owl:Class"
+ },
+ {
+ "@id": "kb:individual-b",
+ "@type": "ex:core/MyClassB"
+ },
+ {
+ "@id": "ex:core/MyClassB",
+ "@type": "owl:Class"
+ }
+ ]
+}
diff --git a/test/data/variants/forward_slash-variant-prefixed.ttl b/test/data/variants/forward_slash-variant-prefixed.ttl
new file mode 100644
index 000000000..633a4e9f6
--- /dev/null
+++ b/test/data/variants/forward_slash-variant-prefixed.ttl
@@ -0,0 +1,18 @@
+@prefix ex: .
+@prefix kb: .
+@prefix owl: .
+
+# Spell a class name with prefixing, but have the prefixing NOT include
+# one of the forward-slashed path components.
+# The forward slash must be escaped, according to Turtle grammar
+# production rules grammar rules including and between PN_LOCAL and
+# PN_LOCAL_ESC.
+# https://www.w3.org/TR/turtle/#sec-grammar-grammar
+
+ex:core\/MyClassA a owl:Class .
+
+kb:individual-a a ex:core\/MyClassA .
+
+ex:core\/MyClassB a owl:Class .
+
+kb:individual-b a ex:core\/MyClassB .
diff --git a/test/data/variants/forward_slash.jsonld b/test/data/variants/forward_slash.jsonld
new file mode 100644
index 000000000..d55faa28f
--- /dev/null
+++ b/test/data/variants/forward_slash.jsonld
@@ -0,0 +1,24 @@
+{
+ "@context": {
+ "kb": "http://example.org/kb/",
+ "owl": "http://www.w3.org/2002/07/owl#"
+ },
+ "@graph": [
+ {
+ "@id": "kb:individual-a",
+ "@type": "http://example.org/ontology/core/MyClassA"
+ },
+ {
+ "@id": "http://example.org/ontology/core/MyClassA",
+ "@type": "owl:Class"
+ },
+ {
+ "@id": "kb:individual-b",
+ "@type": "http://example.org/ontology/core/MyClassB"
+ },
+ {
+ "@id": "http://example.org/ontology/core/MyClassB",
+ "@type": "owl:Class"
+ }
+ ]
+}
diff --git a/test/data/variants/forward_slash.nt b/test/data/variants/forward_slash.nt
new file mode 100644
index 000000000..e46471107
--- /dev/null
+++ b/test/data/variants/forward_slash.nt
@@ -0,0 +1,4 @@
+ .
+ .
+ .
+ .
diff --git a/test/data/variants/forward_slash.ttl b/test/data/variants/forward_slash.ttl
new file mode 100644
index 000000000..67afd7cc2
--- /dev/null
+++ b/test/data/variants/forward_slash.ttl
@@ -0,0 +1,10 @@
+@prefix kb: .
+@prefix owl: .
+
+ a owl:Class .
+
+kb:individual-a a .
+
+ a owl:Class .
+
+kb:individual-b a .
diff --git a/test/test_graph/test_variants.py b/test/test_graph/test_variants.py
index dbb20396b..9025f2e79 100644
--- a/test/test_graph/test_variants.py
+++ b/test/test_graph/test_variants.py
@@ -49,14 +49,26 @@ class GraphAsserts:
quad_count: Optional[int] = None
exact_match: bool = False
+ has_subject_iris: Optional[List[str]] = None
def check(
self, first_graph: Optional[ConjunctiveGraph], graph: ConjunctiveGraph
) -> None:
+ """
+ if `first_graph` is `None` then this is the first check before any
+ other graphs have been processed.
+ """
if self.quad_count is not None:
assert self.quad_count == len(list(graph.quads()))
if first_graph is not None and self.exact_match:
GraphHelper.assert_quad_sets_equals(first_graph, graph)
+ if first_graph is None and self.has_subject_iris is not None:
+ subjects_iris = {
+ f"{subject}"
+ for subject in graph.subjects()
+ if isinstance(subject, URIRef)
+ }
+ assert set(self.has_subject_iris) == subjects_iris
@dataclass(order=True)
@@ -219,6 +231,8 @@ def test_variants(graph_variant: GraphVariants) -> None:
assert len(graph_variant.variants) > 0
first_graph: Optional[ConjunctiveGraph] = None
first_path: Optional[Path] = None
+ logging.debug("graph_variant.asserts = %s", graph_variant.asserts)
+
for variant_key, variant_path in graph_variant.variants.items():
logging.debug("variant_path = %s", variant_path)
format = guess_format(variant_path.name, fmap=SUFFIX_FORMAT_MAP)
diff --git a/test/test_sparql/test_forward_slash_escapes.py b/test/test_sparql/test_forward_slash_escapes.py
index 4f60c9ac6..43e22336c 100644
--- a/test/test_sparql/test_forward_slash_escapes.py
+++ b/test/test_sparql/test_forward_slash_escapes.py
@@ -19,99 +19,15 @@
"mime:application/json".
"""
-from typing import List, Set, Tuple
+from test.data import TEST_DATA_DIR
+from test.utils.graph import cached_graph
+from typing import Set
import pytest
-import rdflib
from rdflib import Graph
from rdflib.plugins.sparql.processor import prepareQuery
from rdflib.plugins.sparql.sparql import Query
-from rdflib.term import Node
-
-# Determine version to delay a test until a version > 6 is being
-# prepared for release. A 7-point-0-anything, alpha or beta designation
-# should enable some further tests backwards-incompatible with version 6.
-# Treat rdflib.__version__ like it can be compiled into a version_info
-# tuple similar to sys.version_info.
-# TODO: During the release of version 7, this set of version-detection
-# code and its corresponding skipifs can be deleted.
-_rdflib_version_info_strs: List[str] = rdflib.__version__.split(".")
-_is_version_7_started = False
-if _rdflib_version_info_strs[0].isdigit():
- if int(_rdflib_version_info_strs[0]) >= 7:
- _is_version_7_started = True
-
-# Note that the data and query strings are Python raw strings, so
-# backslashes won't be escape characters to Python.
-# The "*_expanded" variables eschew prefixing.
-
-# Spell a class name without prefixing.
-turtle_data_expanded = r"""
-@prefix kb: .
-@prefix owl: .
-
- a owl:Class .
-
-kb:individual-a a .
-"""
-
-# Spell a class name with prefixing, but have the prefixing NOT include
-# one of the forward-slashed path components.
-# The forward slash must be escaped, according to Turtle grammar
-# production rules grammar rules including and between PN_LOCAL and
-# PN_LOCAL_ESC.
-# https://www.w3.org/TR/turtle/#sec-grammar-grammar
-turtle_data_prefixed = r"""
-@prefix ex: .
-@prefix kb: .
-@prefix owl: .
-
-ex:core\/MyClassB a owl:Class .
-
-kb:individual-b a ex:core\/MyClassB .
-"""
-
-# This data is an equivalant graph with turtle_data_expanded.
-jsonld_data_expanded = r"""
-{
- "@context": {
- "kb": "http://example.org/kb/",
- "owl": "http://www.w3.org/2002/07/owl#"
- },
- "@graph": {
- "@id": "kb:individual-a",
- "@type": {
- "@id": "http://example.org/ontology/core/MyClassA",
- "@type": "owl:Class"
- }
- }
-}
-"""
-
-# This data is an equivalant graph with turtle_data_prefixed.
-# The JSON-LD spec does not provide a grammar production rule set in
-# EBNF. However, the section on compact IRIs indicates that an IRI can
-# be prefixed at any point that would not result in a suffix starting
-# with "//". Hence, an unpaired forward slash, as a legal character of
-# an IRI, can appear in the suffix component of a compact IRI.
-# https://json-ld.org/spec/latest/json-ld/#compact-iris
-jsonld_data_prefixed = r"""
-{
- "@context": {
- "ex": "http://example.org/ontology/",
- "kb": "http://example.org/kb/",
- "owl": "http://www.w3.org/2002/07/owl#"
- },
- "@graph": {
- "@id": "kb:individual-b",
- "@type": {
- "@id": "ex:core/MyClassB",
- "@type": "owl:Class"
- }
- }
-}
-"""
query_string_expanded = r"""
SELECT ?nIndividual
@@ -132,66 +48,16 @@
?nIndividual a ex:core\/MyClassB .
}"""
+PN_LOCAL_BACKSLASH_XFAIL_REASON = """\
+ Contrary to the ratiried SPARQL 1.1.
+ grammar, the RDFlib SPARQL propcessor accepts backslashes as part of
+ PN_LOCAL which it treats as escape charachters.
-def _test_parse_matches(turtle_data: str, jsonld_data: str) -> None:
- """
- Confirm that the two concrete syntaxes parse to the same set of
- triples.
- """
- g_turtle = Graph()
- g_jsonld = Graph()
+ There should be a way to instruct the SPARQL parser to operate in strict
+ mode, and in strict mode backslashes should not be permitted in PN_LOCAL.
- g_turtle.parse(data=turtle_data_expanded, format="turtle")
- g_jsonld.parse(data=jsonld_data_expanded, format="json-ld")
-
- triples_turtle: Set[Tuple[Node, Node, Node]] = {
- x for x in g_turtle.triples((None, None, None))
- }
- triples_jsonld: Set[Tuple[Node, Node, Node]] = {
- x for x in g_jsonld.triples((None, None, None))
- }
-
- assert triples_turtle == triples_jsonld
-
-
-def test_parse_matches_expanded() -> None:
- _test_parse_matches(turtle_data_expanded, jsonld_data_expanded)
-
-
-def test_parse_matches_prefixed() -> None:
- _test_parse_matches(turtle_data_prefixed, jsonld_data_prefixed)
-
-
-def _test_escapes_to_iri(graph: Graph) -> None:
- """
- Confirm all triple-subjects within the two data graphs are found.
- """
- expected: Set[str] = {
- "http://example.org/kb/individual-a",
- "http://example.org/kb/individual-b",
- "http://example.org/ontology/core/MyClassA",
- "http://example.org/ontology/core/MyClassB",
- }
- computed: Set[str] = set()
-
- for triple in graph.triples((None, None, None)):
- computed.add(str(triple[0]))
-
- assert expected == computed
-
-
-def test_escapes_to_iri_turtle() -> None:
- graph = Graph()
- graph.parse(data=turtle_data_expanded, format="turtle")
- graph.parse(data=turtle_data_prefixed, format="turtle")
- _test_escapes_to_iri(graph)
-
-
-def test_escapes_to_iri_jsonld() -> None:
- graph = Graph()
- graph.parse(data=jsonld_data_expanded, format="json-ld")
- graph.parse(data=jsonld_data_prefixed, format="json-ld")
- _test_escapes_to_iri(graph)
+ See https://github.com/RDFLib/rdflib/issues/1871\
+"""
def _test_query_prepares(query_string: str) -> None:
@@ -215,10 +81,7 @@ def test_query_prepares_expanded() -> None:
_test_query_prepares(query_string_expanded)
-@pytest.mark.skipif(
- not _is_version_7_started,
- reason="query failure detection delayed until rdflib version 7",
-)
+@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON)
def test_query_prepares_prefixed() -> None:
with pytest.raises(ValueError):
_test_query_prepares(query_string_prefixed)
@@ -256,36 +119,22 @@ def _test_escapes_and_query(
def test_escapes_and_query_turtle_expanded() -> None:
- graph = Graph()
- graph.parse(data=turtle_data_expanded, format="turtle")
- graph.parse(data=turtle_data_prefixed, format="turtle")
+ graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.ttl",))
_test_escapes_and_query(graph, query_string_expanded, True)
-@pytest.mark.skipif(
- not _is_version_7_started,
- reason="query failure detection delayed until rdflib version 7",
-)
+@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON, raises=AssertionError)
def test_escapes_and_query_turtle_prefixed() -> None:
- graph = Graph()
- graph.parse(data=turtle_data_expanded, format="turtle")
- graph.parse(data=turtle_data_prefixed, format="turtle")
+ graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.ttl",))
_test_escapes_and_query(graph, query_string_prefixed, False)
def test_escapes_and_query_jsonld_expanded() -> None:
- graph = Graph()
- graph.parse(data=jsonld_data_expanded, format="json-ld")
- graph.parse(data=jsonld_data_prefixed, format="json-ld")
+ graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.jsonld",))
_test_escapes_and_query(graph, query_string_expanded, True)
-@pytest.mark.skipif(
- not _is_version_7_started,
- reason="query failure detection delayed until rdflib version 7",
-)
+@pytest.mark.xfail(reason=PN_LOCAL_BACKSLASH_XFAIL_REASON, raises=AssertionError)
def test_escapes_and_query_jsonld_prefixed() -> None:
- graph = Graph()
- graph.parse(data=jsonld_data_expanded, format="json-ld")
- graph.parse(data=jsonld_data_prefixed, format="json-ld")
+ graph = cached_graph((TEST_DATA_DIR / "variants/forward_slash.jsonld",))
_test_escapes_and_query(graph, query_string_prefixed, False)