Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT REVIEW] Test Suite Rework #1969

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 5 additions & 57 deletions test/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,60 +46,8 @@ test_conneg - test content negotiation when reading remote graphs
EARL Test Reports
=================

EARL test reports can be generated using the EARL reporter plugin from ``earl.py``.

When this plugin is enabled it will create an ``earl:Assertion`` for every test that has a ``rdf_test_uri`` parameter which can be either a string or an ``URIRef``.

To enable the EARL reporter plugin an output file path must be supplied to pytest with ``--earl-output-file``. The report will be written to this location in turtle format.

Some examples of generating test reports:

.. code-block:: bash

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-jsonld-local.ttl \
test/jsonld/test_localsuite.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-jsonld-v1.1.ttl \
test/jsonld/test_onedotone.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-jsonld-v1.0.ttl \
test/jsonld/test_testsuite.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-sparql.ttl \
test/test_w3c_spec/test_sparql_w3c.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-nquads.ttl \
test/test_w3c_spec/test_nquads_w3c.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-nt.ttl \
test/test_w3c_spec/test_nt_w3c.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-trig.ttl \
test/test_w3c_spec/test_trig_w3c.py

pytest \
--earl-assertor-homepage=http://example.com \
--earl-assertor-name 'Example Name' \
--earl-output-file=/var/tmp/earl/earl-turtle.ttl \
test/test_w3c_spec/test_turtle_w3c.py
EARL test reports are generated using the EARL reporter plugin from ``test/utils/earl.py``.

This plugin is enabled by default and writes test reports to ``test_reports/*-latest.ttl`` by default.

For EARL reporter plugin options see the output of ``pytest --help``.
154 changes: 154 additions & 0 deletions test/data/defined_namespaces/rdf.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix dc: <http://purl.org/dc/elements/1.1/> .

<http://www.w3.org/1999/02/22-rdf-syntax-ns#> a owl:Ontology ;
dc:title "The RDF Concepts Vocabulary (RDF)" ;
dc:date "2019-12-16" ;
dc:description "This is the RDF Schema for the RDF vocabulary terms in the RDF Namespace, defined in RDF 1.1 Concepts." .

rdf:HTML a rdfs:Datatype ;
rdfs:subClassOf rdfs:Literal ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <http://www.w3.org/TR/rdf11-concepts/#section-html> ;
rdfs:label "HTML" ;
rdfs:comment "The datatype of RDF literals storing fragments of HTML content" .

rdf:langString a rdfs:Datatype ;
rdfs:subClassOf rdfs:Literal ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <http://www.w3.org/TR/rdf11-concepts/#section-Graph-Literal> ;
rdfs:label "langString" ;
rdfs:comment "The datatype of language-tagged string values" .

rdf:PlainLiteral a rdfs:Datatype ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:subClassOf rdfs:Literal ;
rdfs:seeAlso <http://www.w3.org/TR/rdf-plain-literal/> ;
rdfs:label "PlainLiteral" ;
rdfs:comment "The class of plain (i.e. untyped) literal values, as used in RIF and OWL 2" .

rdf:type a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "type" ;
rdfs:comment "The subject is an instance of a class." ;
rdfs:range rdfs:Class ;
rdfs:domain rdfs:Resource .

rdf:Property a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "Property" ;
rdfs:comment "The class of RDF properties." ;
rdfs:subClassOf rdfs:Resource .

rdf:Statement a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "Statement" ;
rdfs:subClassOf rdfs:Resource ;
rdfs:comment "The class of RDF statements." .

rdf:subject a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "subject" ;
rdfs:comment "The subject of the subject RDF statement." ;
rdfs:domain rdf:Statement ;
rdfs:range rdfs:Resource .

rdf:predicate a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "predicate" ;
rdfs:comment "The predicate of the subject RDF statement." ;
rdfs:domain rdf:Statement ;
rdfs:range rdfs:Resource .

rdf:object a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "object" ;
rdfs:comment "The object of the subject RDF statement." ;
rdfs:domain rdf:Statement ;
rdfs:range rdfs:Resource .

rdf:Bag a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "Bag" ;
rdfs:comment "The class of unordered containers." ;
rdfs:subClassOf rdfs:Container .

rdf:Seq a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "Seq" ;
rdfs:comment "The class of ordered containers." ;
rdfs:subClassOf rdfs:Container .

rdf:Alt a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "Alt" ;
rdfs:comment "The class of containers of alternatives." ;
rdfs:subClassOf rdfs:Container .

rdf:value a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "value" ;
rdfs:comment "Idiomatic property used for structured values." ;
rdfs:domain rdfs:Resource ;
rdfs:range rdfs:Resource .

rdf:List a rdfs:Class ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "List" ;
rdfs:comment "The class of RDF Lists." ;
rdfs:subClassOf rdfs:Resource .

rdf:nil a rdf:List ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "nil" ;
rdfs:comment "The empty list, with no items in it. If the rest of a list is nil then the list has no more items in it." .

rdf:first a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "first" ;
rdfs:comment "The first item in the subject RDF list." ;
rdfs:domain rdf:List ;
rdfs:range rdfs:Resource .

rdf:rest a rdf:Property ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "rest" ;
rdfs:comment "The rest of the subject RDF list after the first item." ;
rdfs:domain rdf:List ;
rdfs:range rdf:List .

rdf:XMLLiteral a rdfs:Datatype ;
rdfs:subClassOf rdfs:Literal ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:label "XMLLiteral" ;
rdfs:comment "The datatype of XML literal values." .

rdf:JSON a rdfs:Datatype ;
rdfs:label "JSON" ;
rdfs:comment "The datatype of RDF literals storing JSON content." ;
rdfs:subClassOf rdfs:Literal ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-json-datatype> .

rdf:CompoundLiteral a rdfs:Class ;
rdfs:label "CompoundLiteral" ;
rdfs:comment "A class representing a compound literal." ;
rdfs:subClassOf rdfs:Resource ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> .

rdf:language a rdf:Property ;
rdfs:label "language" ;
rdfs:comment "The language component of a CompoundLiteral." ;
rdfs:domain rdf:CompoundLiteral ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> .

rdf:direction a rdf:Property ;
rdfs:label "direction" ;
rdfs:comment "The base direction component of a CompoundLiteral." ;
rdfs:domain rdf:CompoundLiteral ;
rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
rdfs:seeAlso <https://www.w3.org/TR/json-ld11/#the-rdf-compoundliteral-class-and-the-rdf-language-and-rdf-direction-properties> .
4 changes: 4 additions & 0 deletions test/data/fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ def _member_io(
remote=Request("https://www.w3.org/2001/sw/DataAccess/tests/test-dawg#"),
local_path=(DATA_PATH / "defined_namespaces/dawgt.ttl"),
),
FileResource(
remote=Request("https://www.w3.org/1999/02/22-rdf-syntax-ns#"),
local_path=(DATA_PATH / "defined_namespaces/rdf.ttl"),
),
FileResource(
remote=Request("https://www.w3.org/2001/sw/DataAccess/tests/test-query#"),
local_path=(DATA_PATH / "defined_namespaces/qt.ttl"),
Expand Down
20 changes: 19 additions & 1 deletion test/utils/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Optional, Tuple, Union
from typing import Optional, Set, Tuple, Union

from rdflib.graph import Graph
from rdflib.namespace import RDFS
from rdflib.term import IdentifiedNode, URIRef
from rdflib.util import guess_format

GraphSourceType = Union["GraphSource", Path]
Expand Down Expand Up @@ -70,3 +72,19 @@ def cached_graph(
sources: Tuple[Union[GraphSource, Path], ...], public_id: Optional[str] = None
) -> Graph:
return load_sources(*sources, public_id=public_id)


def subclasses_of(graph: Graph, node: IdentifiedNode) -> Set[IdentifiedNode]:
return set(graph.transitive_subjects(RDFS.subClassOf, node))


def superclasses_of(graph: Graph, node: IdentifiedNode) -> Set[IdentifiedNode]:
return set(graph.transitive_objects(node, RDFS.subClassOf))


def is_subclass_of(graph: Graph, node: IdentifiedNode, cls: URIRef) -> bool:
return cls in subclasses_of(graph, node)


def is_superclass_of(graph: Graph, node: IdentifiedNode, cls: URIRef) -> bool:
return cls in superclasses_of(graph, node)
118 changes: 118 additions & 0 deletions test/utils/test/test_graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import logging
from contextlib import ExitStack
from pathlib import Path
from test.data import TEST_DATA_DIR
from test.utils.graph import cached_graph, subclasses_of, superclasses_of
from test.utils.namespace import RDFT
from typing import Set, Tuple, Type, Union

import pytest
from pyparsing import Optional

from rdflib.namespace import RDFS
from rdflib.term import IdentifiedNode

RDFT_GRAPHS = (
TEST_DATA_DIR / "defined_namespaces/rdftest.ttl",
TEST_DATA_DIR / "defined_namespaces/rdfs.ttl",
)


@pytest.mark.parametrize(
[
"graph_sources",
"node",
"expected_result",
],
[
(
RDFT_GRAPHS,
RDFS.Resource,
{RDFS.Resource, RDFS.Class, RDFS.Datatype, RDFS.Container, RDFS.Literal},
),
(RDFT_GRAPHS, RDFS.Class, {RDFS.Class, RDFS.Datatype}),
(
RDFT_GRAPHS,
RDFT.Test,
{
RDFT.Test,
RDFT.TestEval,
RDFT.TestNQuadsNegativeSyntax,
RDFT.TestNQuadsPositiveSyntax,
RDFT.TestNTriplesNegativeSyntax,
RDFT.TestNTriplesPositiveSyntax,
RDFT.TestSyntax,
RDFT.TestTriGNegativeSyntax,
RDFT.TestTriGPositiveSyntax,
RDFT.TestTrigNegativeEval,
RDFT.TestTurtleEval,
RDFT.TestTurtleNegativeEval,
RDFT.TestTurtleNegativeSyntax,
RDFT.TestTurtlePositiveSyntax,
RDFT.XMLEval,
},
),
],
)
def test_graph_subclasses_of(
graph_sources: Tuple[Path, ...],
node: IdentifiedNode,
expected_result: Union[Set[IdentifiedNode], Type[Exception]],
) -> None:

catcher: Optional[pytest.ExceptionInfo[Exception]] = None

graph = cached_graph(graph_sources)

with ExitStack() as xstack:
if isinstance(expected_result, type) and issubclass(expected_result, Exception):
catcher = xstack.enter_context(pytest.raises(expected_result))
result = subclasses_of(graph, node)
logging.debug("result = %s", result)
if catcher is not None:
assert catcher is not None
assert catcher.value is not None
else:
assert expected_result == result


@pytest.mark.parametrize(
[
"graph_sources",
"node",
"expected_result",
],
[
(RDFT_GRAPHS, RDFS.Class, {RDFS.Class, RDFS.Resource}),
(RDFT_GRAPHS, RDFS.Literal, {RDFS.Literal, RDFS.Resource}),
(
RDFT_GRAPHS,
RDFT.TestTurtleNegativeSyntax,
{
RDFT.Test,
RDFT.TestSyntax,
RDFT.TestTurtleNegativeSyntax,
},
),
],
)
def test_graph_superclasses_of(
graph_sources: Tuple[Path, ...],
node: IdentifiedNode,
expected_result: Union[Set[IdentifiedNode], Type[Exception]],
) -> None:

catcher: Optional[pytest.ExceptionInfo[Exception]] = None

graph = cached_graph(graph_sources)

with ExitStack() as xstack:
if isinstance(expected_result, type) and issubclass(expected_result, Exception):
catcher = xstack.enter_context(pytest.raises(expected_result))
result = superclasses_of(graph, node)
logging.debug("result = %s", result)
if catcher is not None:
assert catcher is not None
assert catcher.value is not None
else:
assert expected_result == result