Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
aucampia committed Jun 23, 2022
1 parent 4f96f46 commit 44e82b9
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 58 deletions.
62 changes: 5 additions & 57 deletions test/README.rst
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
@@ -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
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
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
@@ -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

0 comments on commit 44e82b9

Please sign in to comment.