Skip to content

Commit

Permalink
Add two xfails related to Example 2 from RDF 1.1 TriG specification
Browse files Browse the repository at this point in the history
The first xfail occurs during round tripping, TriG seems to be making
some mistake when encoding blank nodes, as it is encoding that "Bob"
knows someone who does not exist. This was reported by @gjhiggins in
#1796 (comment)

The second xfail seems to be related to hextuple parsing, when comparing
the hextuple parsed result of Example 2 with the TriG parsed
graph of Example 2 the graphs are not isomorphic more than 70% of the time, but
sometimes they are isomorphic. Inoticed this while adding the xfail for
the issue @gjhiggins noticed.

Other changes:
- Added `simple_quad` to variants tests with HexTuple and TriG format.
- Added an additional exact_match assert for variants which can be used
  to sidestep some of the known issues with isomorphic graph detection.
  This is useful for graphs with no BNodes.
- Also added round-tripping for `variants/simple_quad.trig`.
- Various changes to ensure determensitic ordering so that it is easier
  to compare things visually and so that tests always do the exact same
  thing in the exact same order.
  • Loading branch information
aucampia committed Apr 11, 2022
1 parent 7ed86ff commit e8c3a0e
Show file tree
Hide file tree
Showing 22 changed files with 198 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Expand Up @@ -20,7 +20,7 @@ trim_trailing_whitespace = false
[*.{js,py,pyi,toml,yml,yaml}]
charset = utf-8

[*.{yaml,yml,json}]
[*.{yaml,yml,json,jsonld}]
indent_style = space
indent_size = 2

Expand Down
40 changes: 35 additions & 5 deletions test/test_graph/test_variants.py
Expand Up @@ -12,6 +12,7 @@
Iterable,
List,
Optional,
OrderedDict,
Pattern,
Tuple,
Union,
Expand Down Expand Up @@ -45,10 +46,13 @@ class GraphAsserts:
"""

quad_count: Optional[int] = None
exact_match: bool = False

def check(self, graph: ConjunctiveGraph) -> None:
def check(self, first_graph: Optional[ConjunctiveGraph], graph: ConjunctiveGraph) -> None:
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)


@dataclass(order=True)
Expand All @@ -58,7 +62,7 @@ class GraphVariants:
"""

key: str
variants: Dict[str, Path] = field(default_factory=dict)
variants: Dict[str, Path] = field(default_factory=OrderedDict)
asserts: GraphAsserts = field(default_factory=lambda: GraphAsserts())

_variant_regex: ClassVar[Pattern[str]] = re.compile(
Expand Down Expand Up @@ -135,6 +139,29 @@ def for_directory(
reason="Some issue with handling base URI that does not end with a slash",
raises=ValueError,
),
("variants/rdf11trig_eg2"): pytest.mark.xfail(
reason="""
This fails randomly, passing less than 10% of the time, and always failing
with comparing hext against trig. Not clear why, it may be a big with hext
parsing.
AssertionError: checking rdf11trig_eg2.hext against rdf11trig_eg2.trig
in both:
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:bob@oldcorp.example.org'))
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Bob'))
(rdflib.term.URIRef('http://example.org/bob'), rdflib.term.URIRef('http://purl.org/dc/terms/publisher'), rdflib.term.Literal('Bob'))
(rdflib.term.URIRef('http://example.org/alice'), rdflib.term.URIRef('http://purl.org/dc/terms/publisher'), rdflib.term.Literal('Alice'))
only in first:
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/knows'), rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'))
(rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:alice@work.example.org'))
(rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Alice'))
only in second:
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/knows'), rdflib.term.BNode('cbcd41774964510991c01701d8430149bc373e1f23734d9c938c81a40b1429aa33'))
(rdflib.term.BNode('cbcd41774964510991c01701d8430149bc373e1f23734d9c938c81a40b1429aa33'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:alice@work.example.org'))
(rdflib.term.BNode('cbcd41774964510991c01701d8430149bc373e1f23734d9c938c81a40b1429aa33'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Alice'))
""",
raises=AssertionError,
),
}


Expand Down Expand Up @@ -164,7 +191,8 @@ def test_variants(graph_variant: GraphVariants) -> None:
logging.debug("graph_variant = %s", graph_variant)
public_id = URIRef(f"example:{graph_variant.key}")
assert len(graph_variant.variants) > 0
first_graph: Optional[Graph] = None
first_graph: Optional[ConjunctiveGraph] = None
first_path: Optional[Path] = None
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)
Expand All @@ -175,8 +203,10 @@ def test_variants(graph_variant: GraphVariants) -> None:
# opinions of when a bare string is of datatype XSD.string or not.
# Probably something that needs more investigation.
GraphHelper.strip_literal_datatypes(graph, {XSD.string})
graph_variant.asserts.check(graph)
graph_variant.asserts.check(first_graph, graph)
if first_graph is None:
first_graph = graph
first_path = variant_path
else:
GraphHelper.assert_isomorphic(first_graph, graph)
assert first_path is not None
GraphHelper.assert_isomorphic(first_graph, graph, f"checking {variant_path.relative_to(VARIANTS_DIR)} against {first_path.relative_to(VARIANTS_DIR)}")
24 changes: 24 additions & 0 deletions test/test_roundtrip.py
Expand Up @@ -142,6 +142,28 @@
reason="results in invalid xml element name: <ns1:name(s)/>",
raises=SAXParseException,
),
("trig", "rdf11trig_eg2.trig"): pytest.mark.xfail(
reason="""
Something is going wrong here with blank node serialization. In the second
graph below bob knows someone who does not exist, while in first he knows
someone that does exist and has the name Alice.
AssertionError: in both:
(rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:alice@work.example.org'))
(rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Alice'))
(rdflib.term.URIRef('http://example.org/alice'), rdflib.term.URIRef('http://purl.org/dc/terms/publisher'), rdflib.term.Literal('Alice'))
(rdflib.term.URIRef('http://example.org/bob'), rdflib.term.URIRef('http://purl.org/dc/terms/publisher'), rdflib.term.Literal('Bob'))
only in first:
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/knows'), rdflib.term.BNode('cbb5eb12b5dcf688537b0298cce144c6dd68cf047530d0b4a455a8f31f314244fd'))
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:bob@oldcorp.example.org'))
(rdflib.term.BNode('cb0'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Bob'))
only in second:
(rdflib.term.BNode('cb7be1d0397a49ddd4ae8aa96acc7b6135903c5f3fa5e47bf619c0e4b438aafcc1'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/knows'), rdflib.term.BNode('cb0'))
(rdflib.term.BNode('cb7be1d0397a49ddd4ae8aa96acc7b6135903c5f3fa5e47bf619c0e4b438aafcc1'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/mbox'), rdflib.term.URIRef('mailto:bob@oldcorp.example.org'))
(rdflib.term.BNode('cb7be1d0397a49ddd4ae8aa96acc7b6135903c5f3fa5e47bf619c0e4b438aafcc1'), rdflib.term.URIRef('http://xmlns.com/foaf/0.1/name'), rdflib.term.Literal('Bob'))
""",
raises=AssertionError,
),
}

# This is for files which can only be represented properly in one format
Expand Down Expand Up @@ -267,6 +289,8 @@ def test_n3(checker: Callable[[str, str, Path], None], args: Tuple[str, str, Pat
(TEST_DIR / "variants" / "special_chars.nt", "ntriples"),
(TEST_DIR / "variants" / "xml_literal.rdf", "xml"),
(TEST_DIR / "variants" / "rdf_prefix.jsonld", "json-ld"),
(TEST_DIR / "variants" / "simple_quad.trig", "trig"),
(TEST_DIR / "variants" / "rdf11trig_eg2.trig", "trig"),
]


Expand Down
22 changes: 12 additions & 10 deletions test/testutils.py
Expand Up @@ -191,36 +191,38 @@ def assert_sets_equals(

@classmethod
def format_set(
cls, item_set: Union[IdentifierQuadSet, IdentifierTripleSet], prefix: str = " "
cls, item_set: Union[IdentifierQuadSet, IdentifierTripleSet], prefix: str = " ", sort: bool = False
) -> str:
items = []
for item in item_set:
use_item_set = sorted(item_set) if sort else item_set
for item in use_item_set:
items.append(f"{prefix}{item}")
return "\n".join(items)

@classmethod
def format_graph_set(cls, graph: Graph, prefix: str = " ") -> str:
return cls.format_set(cls.triple_or_quad_set(graph), prefix)
def format_graph_set(cls, graph: Graph, prefix: str = " ", sort: bool = False) -> str:
return cls.format_set(cls.triple_or_quad_set(graph), prefix, sort)

@classmethod
def assert_isomorphic(cls, lhs: Graph, rhs: Graph) -> None:
def assert_isomorphic(cls, lhs: Graph, rhs: Graph, message: Optional[str] = None) -> None:
"""
This asserts that the two graphs are isomorphic, providing a nicely
formatted error message if they are not.
"""

def format_report() -> str:
def format_report(message: Optional[str] = None) -> str:
in_both, in_lhs, in_rhs = rdflib.compare.graph_diff(lhs, rhs)
preamle = "" if message is None else f"{message}\n"
return (
"in both:\n"
f"{preamle}in both:\n"
f"{cls.format_graph_set(in_both)}"
"\nonly in first:\n"
f"{cls.format_graph_set(in_lhs)}"
f"{cls.format_graph_set(in_lhs, sort = True)}"
"\nonly in second:\n"
f"{cls.format_graph_set(in_rhs)}"
f"{cls.format_graph_set(in_rhs, sort = True)}"
)

assert rdflib.compare.isomorphic(lhs, rhs), format_report()
assert rdflib.compare.isomorphic(lhs, rhs), format_report(message)

@classmethod
def strip_literal_datatypes(cls, graph: Graph, datatypes: Set[URIRef]) -> None:
Expand Down
2 changes: 1 addition & 1 deletion test/variants/README.md
Expand Up @@ -18,4 +18,4 @@ test/variants/literal_with_lang.ttl

Some additional assertions on graphs can be specified in file names that end
with `-asserts.json`, for details on supported asserts see
`test/test_variants.py`.
`test/test_graph/test_variants.py`.
3 changes: 3 additions & 0 deletions test/variants/rdf11trig_eg2-asserts.json
@@ -0,0 +1,3 @@
{
"quad_count": 7
}
7 changes: 7 additions & 0 deletions test/variants/rdf11trig_eg2.hext
@@ -0,0 +1,7 @@
["_:b", "http://xmlns.com/foaf/0.1/mbox", "mailto:alice@work.example.org", "globalId", "", "http://example.org/alice"]
["_:b", "http://xmlns.com/foaf/0.1/name", "Alice", "http://www.w3.org/2001/XMLSchema#string", "", "http://example.org/alice"]
["_:a", "http://xmlns.com/foaf/0.1/mbox", "mailto:bob@oldcorp.example.org", "globalId", "", "http://example.org/bob"]
["_:a", "http://xmlns.com/foaf/0.1/name", "Bob", "http://www.w3.org/2001/XMLSchema#string", "", "http://example.org/bob"]
["_:a", "http://xmlns.com/foaf/0.1/knows", "_:b", "localId", "", "http://example.org/bob"]
["http://example.org/bob", "http://purl.org/dc/terms/publisher", "Bob", "http://www.w3.org/2001/XMLSchema#string", "", ""]
["http://example.org/alice", "http://purl.org/dc/terms/publisher", "Alice", "http://www.w3.org/2001/XMLSchema#string", "", ""]
43 changes: 43 additions & 0 deletions test/variants/rdf11trig_eg2.jsonld
@@ -0,0 +1,43 @@
{
"@graph": [
{
"@graph": [
{
"@id": "http://example.org/bob",
"http://purl.org/dc/terms/publisher": "Bob"
},
{
"@id": "http://example.org/alice",
"http://purl.org/dc/terms/publisher": "Alice"
}
]
},
{
"@graph": [
{
"@id": "_:a",
"http://xmlns.com/foaf/0.1/knows": {
"@id": "_:b"
},
"http://xmlns.com/foaf/0.1/mbox": {
"@id": "mailto:bob@oldcorp.example.org"
},
"http://xmlns.com/foaf/0.1/name": "Bob"
}
],
"@id": "http://example.org/bob"
},
{
"@graph": [
{
"@id": "_:b",
"http://xmlns.com/foaf/0.1/mbox": {
"@id": "mailto:alice@work.example.org"
},
"http://xmlns.com/foaf/0.1/name": "Alice"
}
],
"@id": "http://example.org/alice"
}
]
}
7 changes: 7 additions & 0 deletions test/variants/rdf11trig_eg2.nq
@@ -0,0 +1,7 @@
_:a <http://xmlns.com/foaf/0.1/knows> _:b <http://example.org/bob> .
_:a <http://xmlns.com/foaf/0.1/mbox> <mailto:bob@oldcorp.example.org> <http://example.org/bob> .
_:a <http://xmlns.com/foaf/0.1/name> "Bob" <http://example.org/bob> .
_:b <http://xmlns.com/foaf/0.1/mbox> <mailto:alice@work.example.org> <http://example.org/alice> .
_:b <http://xmlns.com/foaf/0.1/name> "Alice" <http://example.org/alice> .
<http://example.org/alice> <http://purl.org/dc/terms/publisher> "Alice" .
<http://example.org/bob> <http://purl.org/dc/terms/publisher> "Bob" .
26 changes: 26 additions & 0 deletions test/variants/rdf11trig_eg2.trig
@@ -0,0 +1,26 @@
# from example 2 in https://www.w3.org/TR/trig/#sec-graph-statements

# This document contains a default graph and two named graphs.

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix dc: <http://purl.org/dc/terms/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .

# default graph
{
<http://example.org/bob> dc:publisher "Bob" .
<http://example.org/alice> dc:publisher "Alice" .
}

<http://example.org/bob>
{
_:a foaf:name "Bob" .
_:a foaf:mbox <mailto:bob@oldcorp.example.org> .
_:a foaf:knows _:b .
}

<http://example.org/alice>
{
_:b foaf:name "Alice" .
_:b foaf:mbox <mailto:alice@work.example.org> .
}
3 changes: 3 additions & 0 deletions test/variants/rdf_prefix-asserts.json
@@ -0,0 +1,3 @@
{
"quad_count": 3
}
3 changes: 2 additions & 1 deletion test/variants/schema_only_base-asserts.json
@@ -1,3 +1,4 @@
{
"quad_count": 4
"quad_count": 4,
"exact_match": true
}
4 changes: 4 additions & 0 deletions test/variants/simple_quad-asserts.json
@@ -0,0 +1,4 @@
{
"quad_count": 1,
"exact_match": true
}
1 change: 1 addition & 0 deletions test/variants/simple_quad.hext
@@ -0,0 +1 @@
["http://example.org/subject", "http://example.org/predicate", "http://example.org/object", "globalId", "", "http://example.org/graph"]
15 changes: 15 additions & 0 deletions test/variants/simple_quad.jsonld
@@ -0,0 +1,15 @@
[
{
"@graph": [
{
"@id": "http://example.org/subject",
"http://example.org/predicate": [
{
"@id": "http://example.org/object"
}
]
}
],
"@id": "http://example.org/graph"
}
]
1 change: 1 addition & 0 deletions test/variants/simple_quad.nq
@@ -0,0 +1 @@
<http://example.org/subject> <http://example.org/predicate> <http://example.org/object> <http://example.org/graph> .
5 changes: 5 additions & 0 deletions test/variants/simple_quad.trig
@@ -0,0 +1,5 @@
@prefix example: <http://example.org/>.

example:graph {
example:subject example:predicate example:object .
}
3 changes: 2 additions & 1 deletion test/variants/simple_triple-asserts.json
@@ -1,3 +1,4 @@
{
"quad_count": 1
"quad_count": 1,
"exact_match": true
}
1 change: 1 addition & 0 deletions test/variants/simple_triple.nq
@@ -0,0 +1 @@
<http://example.org/subject> <http://example.org/predicate> <http://example.org/object> .
1 change: 1 addition & 0 deletions test/variants/simple_triple.nt
@@ -0,0 +1 @@
<http://example.org/subject> <http://example.org/predicate> <http://example.org/object> .
3 changes: 2 additions & 1 deletion test/variants/special_chars-asserts.json
@@ -1,3 +1,4 @@
{
"quad_count": 7
"quad_count": 7,
"exact_match": true
}
3 changes: 2 additions & 1 deletion test/variants/xml_literal-asserts.json
@@ -1,3 +1,4 @@
{
"quad_count": 1
"quad_count": 1,
"exact_match": true
}

0 comments on commit e8c3a0e

Please sign in to comment.