1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
# -*- coding: utf-8 -*-
#
# This software was developed at the National Institute of Standards
# and Technology by employees of the Federal Government in the course
# of their official duties. Pursuant to title 17 Section 105 of the
# United States Code this software is not subject to copyright
# protection and is in the public domain. NIST assumes no
# responsibility whatsoever for its use by other parties, and makes
# no guarantees, expressed or implied, about its quality,
# reliability, or any other characteristic.
#
# We would appreciate acknowledgement if the software is used.
"""
Test that values declared as the various XSD integer types are confirmed to be in their value spaces.
_PASS and _XFAIL name portions on tests in this script denote whether the input data graph should have a True or False conformance result.
"""
from typing import Set, Tuple
from rdflib import Graph, Literal, Namespace, RDF, SH, URIRef, XSD
from pyshacl import validate
EX_ONT = Namespace("http://example.com/exOnt#")
ontology_file_text = """
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix exOnt: <http://example.com/exOnt#> .
<http://example.com/exOnt> a owl:Ontology ;
rdfs:label "An example extra-ontology file."@en .
exOnt:NumberHolder a owl:Class .
exOnt:propInteger a owl:DatatypeProperty ;
rdfs:domain exOnt:NumberHolder ;
rdfs:range xsd:integer .
exOnt:propNegativeInteger a owl:DatatypeProperty ;
rdfs:domain exOnt:NumberHolder ;
rdfs:range xsd:negativeInteger .
exOnt:propNonNegativeInteger a owl:DatatypeProperty ;
rdfs:domain exOnt:NumberHolder ;
rdfs:range xsd:nonNegativeInteger .
exOnt:propNonPositiveInteger a owl:DatatypeProperty ;
rdfs:domain exOnt:NumberHolder ;
rdfs:range xsd:nonPositiveInteger .
exOnt:propPositiveInteger a owl:DatatypeProperty ;
rdfs:domain exOnt:NumberHolder ;
rdfs:range xsd:positiveInteger .
"""
shacl_file_text = """
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix exShape: <http://example.com/exShape#> .
@prefix exOnt: <http://example.com/exOnt#> .
<http://example.com/exShape> a owl:Ontology ;
rdfs:label "Example Shapes File"@en .
exShape:NumberHolderShape
sh:property
[
sh:datatype xsd:integer ;
sh:path exOnt:propInteger ;
],
[
sh:datatype xsd:negativeInteger ;
sh:path exOnt:propNegativeInteger ;
],
[
sh:datatype xsd:nonNegativeInteger ;
sh:path exOnt:propNonNegativeInteger
],
[
sh:datatype xsd:nonPositiveInteger ;
sh:path exOnt:propNonPositiveInteger ;
],
[
sh:datatype xsd:positiveInteger ;
sh:path exOnt:propPositiveInteger ;
] ;
sh:targetClass exOnt:NumberHolder ;
.
"""
data_file_text_PASS = """
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix exOnt: <http://example.com/exOnt#> .
@prefix ex: <http://example.com/ex#> .
ex:holder0 a exOnt:NumberHolder ;
exOnt:propInteger -1 ;
exOnt:propInteger 0 ;
exOnt:propInteger 1 ;
exOnt:propNegativeInteger "-1"^^xsd:negativeInteger ;
exOnt:propNonNegativeInteger "0"^^xsd:nonNegativeInteger ;
exOnt:propNonNegativeInteger "1"^^xsd:nonNegativeInteger ;
exOnt:propNonPositiveInteger "-1"^^xsd:nonPositiveInteger ;
exOnt:propNonPositiveInteger "0"^^xsd:nonPositiveInteger ;
exOnt:propPositiveInteger "1"^^xsd:positiveInteger ;
.
"""
def test_validate_with_ontology() -> None:
g = Graph().parse(data=data_file_text_PASS, format='turtle')
e = Graph().parse(data=ontology_file_text, format='turtle')
g_len = len(g)
res = validate(
g, shacl_graph=shacl_file_text, shacl_graph_format='turtle', ont_graph=e, inference='both', debug=True
)
conforms, graph, string = res
g_len2 = len(g)
assert conforms
assert g_len2 == g_len
data_file_text_XFAIL_sh_datatypes = """
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix exOnt: <http://example.com/exOnt#> .
@prefix ex: <http://example.com/ex#> .
ex:holder1 a exOnt:NumberHolder ;
rdfs:comment "Each property except propInteger has an error. All properties except propInteger use the wrong datatype, as Turtle infers xsd:integer on unannotated integer lexical values when none is specified."@en ;
rdfs:seeAlso <https://www.w3.org/TR/turtle/#abbrev> ;
exOnt:propInteger -1 ;
exOnt:propInteger 0 ;
exOnt:propInteger 1 ;
exOnt:propNegativeInteger -1 ;
exOnt:propNonNegativeInteger 0 ;
exOnt:propNonNegativeInteger 1 ;
exOnt:propNonPositiveInteger -1 ;
exOnt:propNonPositiveInteger 0 ;
exOnt:propPositiveInteger 1 ;
.
"""
def test_validate_with_ontology_XFAIL_sh_datatypes() -> None:
res = validate(
data_file_text_XFAIL_sh_datatypes,
shacl_graph=shacl_file_text,
data_graph_format='turtle',
shacl_graph_format='turtle',
ont_graph=ontology_file_text,
ont_graph_format="turtle",
inference='both',
debug=True,
)
conforms, graph, string = res
validation_result_tally = 0
for triple in graph.triples((None, RDF.type, SH.ValidationResult)):
validation_result_tally += 1
assert not conforms
assert validation_result_tally == 6
data_file_text_XFAIL_spaces = """
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix exOnt: <http://example.com/exOnt#> .
@prefix ex: <http://example.com/ex#> .
ex:holder2 a exOnt:NumberHolder ;
rdfs:comment "Each property has an error with the object-position node's datatype. propInteger's has a lexical-space error. Each other has a value-space error when considering the datatype declaration in the Literal."@en ;
exOnt:propInteger "zero"^^xsd:integer ;
exOnt:propNegativeInteger "0"^^xsd:negativeInteger ;
exOnt:propNegativeInteger "1"^^xsd:negativeInteger ;
exOnt:propNonNegativeInteger "-1"^^xsd:nonNegativeInteger ;
exOnt:propNonPositiveInteger "1"^^xsd:nonPositiveInteger ;
exOnt:propPositiveInteger "-1"^^xsd:positiveInteger ;
exOnt:propPositiveInteger "0"^^xsd:positiveInteger ;
.
"""
def test_validate_with_ontology_XFAIL_spaces() -> None:
res = validate(
data_file_text_XFAIL_spaces,
shacl_graph=shacl_file_text,
data_graph_format='turtle',
shacl_graph_format='turtle',
ont_graph=ontology_file_text,
ont_graph_format="turtle",
inference='both',
debug=True,
)
conforms, graph, string = res
expected_path_values: Set[Tuple[URIRef, Literal]] = {
(EX_ONT.propInteger, Literal("zero", datatype=XSD.integer)),
(EX_ONT.propNegativeInteger, Literal("0", datatype=XSD.negativeInteger)),
(EX_ONT.propNegativeInteger, Literal("1", datatype=XSD.negativeInteger)),
(EX_ONT.propNonNegativeInteger, Literal("-1", datatype=XSD.nonNegativeInteger)),
(EX_ONT.propNonPositiveInteger, Literal("1", datatype=XSD.nonPositiveInteger)),
(EX_ONT.propPositiveInteger, Literal("-1", datatype=XSD.positiveInteger)),
(EX_ONT.propPositiveInteger, Literal("0", datatype=XSD.positiveInteger)),
}
computed_path_values: Set[Tuple[URIRef, Literal]] = set()
# .triples() is used somewhat redundantly instead of using SPARQL, to avoid the possibility of any extra interpretation steps with SPARQL conversion.
for triple0 in graph.triples((None, RDF.type, SH.ValidationResult)):
n_validation_result = triple0[0]
for triple1 in graph.triples((n_validation_result, SH.resultPath, None)):
n_path = triple1[2]
for triple2 in graph.triples((n_validation_result, SH.value, None)):
l_value: Literal = triple2[2]
assert isinstance(l_value, Literal)
computed_path_values.add((n_path, l_value))
assert not conforms
assert expected_path_values == computed_path_values
|