Skip to content

Commit 50fcd48

Browse files
committed
Beginning of manifest validations using SHACL
Only validates some fields of RDF tests for now
1 parent 7ad71f6 commit 50fcd48

5 files changed

Lines changed: 112 additions & 5 deletions

File tree

.github/workflows/validate.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: validate
2+
on:
3+
push:
4+
branches: [ main ]
5+
pull_request:
6+
branches: [ main ]
7+
8+
jobs:
9+
validate_manifest:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v5
14+
- uses: astral-sh/setup-uv@v7
15+
- run: uv run validate_manifest.py

ns/shapes.ttl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SHACL shapes for tests
2+
3+
PREFIX mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#>
4+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
5+
PREFIX rdft: <http://www.w3.org/ns/rdftest#>
6+
PREFIX sh: <http://www.w3.org/ns/shacl#>
7+
8+
[
9+
a sh:NodeShape ;
10+
sh:targetClass mf:Manifest ;
11+
sh:property [
12+
sh:path mf:include ;
13+
sh:maxCount 1
14+
] ;
15+
sh:property [
16+
sh:path mf:entries ;
17+
sh:maxCount 1
18+
]
19+
] .
20+
21+
[
22+
a sh:NodeShape ;
23+
sh:targetClass rdft:Test ;
24+
sh:property [
25+
sh:path mf:action ;
26+
sh:nodeKind sh:IRI ;
27+
sh:maxCount 1
28+
] ;
29+
sh:property [
30+
sh:path mf:result ;
31+
sh:or ( [ sh:nodeKind sh:IRI ] [ sh:hasValue false ] ) ;
32+
sh:maxCount 1
33+
] ;
34+
sh:property [
35+
sh:path rdft:approval ;
36+
sh:nodeKind sh:IRI ;
37+
sh:in (rdft:Approved rdft:Proposed rdft:Rejected rdft:NotClassified) ; # TODO: rdft:NotClassified is not in the vocabulary but used
38+
sh:maxCount 1
39+
]
40+
] .

rdf/rdf11/rdf-mt/manifest-az.ttl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<#ill-formed-string> a mf:PositiveEntailmentTest;
4040
mf:name "ill-formed-string";
4141
rdfs:comment """
42-
XSD Strings do not allow the null code point \0000.
42+
XSD Strings do not allow the null code point \\0000.
4343
""";
4444
mf:entailmentRegime "Simple" ;
4545
mf:recognizedDatatypes ( xsd:string ) ;
@@ -50,7 +50,7 @@
5050
<#well-formed-html> a mf:NegativeEntailmentTest;
5151
mf:name "well-formed-html";
5252
rdfs:comment """
53-
rdf:HTML does allow the null code point \0000.
53+
rdf:HTML does allow the null code point \\0000.
5454
""";
5555
mf:entailmentRegime "Simple" ;
5656
mf:recognizedDatatypes ( rdf:HTML ) ;
@@ -87,7 +87,7 @@
8787
""";
8888
mf:entailmentRegime "RDFS" ;
8989
mf:recognizedDatatypes ( xsd:integer ) ;
90-
mf:unrecognizedDatatypes ( ex:dt ) ;
90+
mf:unrecognizedDatatypes ( <http://example.org/dt> ) ;
9191
mf:action <az-tests/unrecognized-datatype001.ttl>;
9292
mf:result false .
9393

@@ -100,7 +100,7 @@
100100
""";
101101
mf:entailmentRegime "RDFS" ;
102102
mf:recognizedDatatypes ( ) ;
103-
mf:unrecognizedDatatypes ( ex:dt ) ;
103+
mf:unrecognizedDatatypes ( <http://example.org/dt> ) ;
104104
mf:action <az-tests/unrecognized-datatype002.ttl>;
105105
mf:result <az-tests/unrecognized-datatype003.ttl> .
106106

rdf/rdf12/rdf-semantics/manifest.ttl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ trs:literal-type a mf:PositiveEntailmentTest;
244244
mf:recognizedDatatypes (xsd:integer);
245245
mf:result <literal-type.ttl>;
246246
mf:unrecognizedDatatypes ();
247-
test:approval test:NotClassified .
247+
rdft:approval rdft:NotClassified .
248248

249249
trs:malformed-literal a mf:PositiveEntailmentTest;
250250
rdfs:comment "Malformed literals are allowed in triple terms, but cause inconsistency.";

validate_manifest.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# /// script
2+
# dependencies = [
3+
# "pyshacl~=0.30.0",
4+
# ]
5+
# ///
6+
"""
7+
Small Python script to validate manifest files
8+
"""
9+
10+
import os
11+
from pathlib import Path
12+
from pyshacl import validate
13+
14+
repository_root = Path(__file__).parent
15+
shape_path = repository_root / "ns" / "shapes.ttl"
16+
vocab_path = repository_root / "ns" / "rdftest.ttl"
17+
18+
19+
def log_line(message: str, level: str, file: Path | None = None) -> None:
20+
if "GITHUB_ACTIONS" in os.environ:
21+
print(
22+
f"::{level}{f' file={file.relative_to(repository_root)}' if file else ''}::{message}"
23+
)
24+
else:
25+
print(message)
26+
27+
28+
failure_counter = 0
29+
total_counter = 0
30+
for manifest_path in repository_root.rglob("manifest*.ttl"):
31+
try:
32+
(conforms, _, results_text) = validate(
33+
str(manifest_path),
34+
shacl_graph=str(shape_path),
35+
ont_graph=str(vocab_path),
36+
inference="rdfs",
37+
)
38+
except SyntaxError as e:
39+
conforms = False
40+
results_text = str(e)
41+
if not conforms:
42+
log_line(results_text, "error", manifest_path)
43+
failure_counter += 1
44+
total_counter += 1
45+
46+
log_line(
47+
f"Found {failure_counter} files with error"
48+
if failure_counter > 0
49+
else f"Validated {total_counter} files without errors",
50+
"notice",
51+
)
52+
exit(int(failure_counter > 0))

0 commit comments

Comments
 (0)