aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/serdi.18
-rw-r--r--meson.build7
-rw-r--r--schemas/owl.ttl614
-rw-r--r--schemas/rdf.ttl127
-rw-r--r--schemas/rdfs.ttl123
-rw-r--r--schemas/xsd.ttl353
-rw-r--r--src/serdi.c15
-rw-r--r--src/validate.c3
-rw-r--r--test/meson.build18
-rwxr-xr-xtest/run_test_suite.py86
-rwxr-xr-xtest/run_validation_test_suite.py268
-rw-r--r--test/serd_test_util/__init__.py63
-rw-r--r--test/validate/bad-all-values-from.ttl18
-rw-r--r--test/validate/bad-cardinality-high.ttl18
-rw-r--r--test/validate/bad-cardinality-low.ttl16
-rw-r--r--test/validate/bad-cardinality.ttl18
-rw-r--r--test/validate/bad-datatype-property.ttl18
-rw-r--r--test/validate/bad-domain.ttl19
-rw-r--r--test/validate/bad-functional-property.ttl13
-rw-r--r--test/validate/bad-inverse-functional-property.ttl15
-rw-r--r--test/validate/bad-literal-pattern.ttl7
-rw-r--r--test/validate/bad-literal-value-high-exclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-high-inclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-low-exclusive.ttl20
-rw-r--r--test/validate/bad-literal-value-low-inclusive.ttl21
-rw-r--r--test/validate/bad-object-property.ttl12
-rw-r--r--test/validate/bad-pattern.ttl23
-rw-r--r--test/validate/bad-plain-literal.ttl12
-rw-r--r--test/validate/bad-range-instance-not-literal.ttl18
-rw-r--r--test/validate/bad-range-instance.ttl21
-rw-r--r--test/validate/bad-range-literal-not-instance.ttl15
-rw-r--r--test/validate/bad-range-literal.ttl27
-rw-r--r--test/validate/bad-some-values-from.ttl18
-rw-r--r--test/validate/bad-string-literal-value-high.ttl21
-rw-r--r--test/validate/bad-string-literal-value-low.ttl21
-rw-r--r--test/validate/bad-unknown-datatype.ttl7
-rw-r--r--test/validate/bad-unknown-property.ttl6
-rw-r--r--test/validate/good-cardinality.ttl17
-rw-r--r--test/validate/good-literal-value-high-inclusive.ttl21
-rw-r--r--test/validate/good-literal-value-low-inclusive.ttl21
-rw-r--r--test/validate/good-pattern.ttl22
-rw-r--r--test/validate/good-some-values-from.ttl17
-rw-r--r--test/validate/good-string-literal-value-low.ttl21
-rw-r--r--test/validate/manifest.ttl205
44 files changed, 2363 insertions, 70 deletions
diff --git a/doc/serdi.1 b/doc/serdi.1
index ce0b1ed4..888dab03 100644
--- a/doc/serdi.1
+++ b/doc/serdi.1
@@ -43,6 +43,14 @@ the URI of the file is automatically used as the base URI.
This option can be used to override that,
or to provide a base URI for input from stdin or a string.
.Pp
+.It Fl V
+Validate inputs.
+All necessary data, including schemas, must be passed as inputs.
+Output will only be written if validation succeeds.
+Combine with
+.Fl o Ar empty
+to suppress output and only show validation errors.
+.Pp
.It Fl a
Write ASCII output.
If this is enabled, all non-ASCII characters will be escaped, even if the output syntax allows them to be written in UTF-8.
diff --git a/meson.build b/meson.build
index a8a45785..bc026f04 100644
--- a/meson.build
+++ b/meson.build
@@ -106,6 +106,7 @@ sources = [
'src/syntax.c',
'src/system.c',
'src/uri.c',
+ 'src/validate.c',
'src/world.c',
'src/writer.c',
'src/zix/btree.c',
@@ -139,6 +140,10 @@ exess_dep = dependency('exess',
version: '>= 0.0.1',
fallback: ['exess', 'exess_dep'])
+rerex_dep = dependency('rerex',
+ version: '>= 0.0.1',
+ fallback: ['rerex', 'rerex_dep'])
+
# Build shared and/or static library/libraries
libserd = build_target(
versioned_name,
@@ -146,7 +151,7 @@ libserd = build_target(
version: meson.project_version(),
include_directories: include_directories('include', 'src'),
c_args: c_warnings + library_args,
- dependencies: [exess_dep, m_dep],
+ dependencies: [exess_dep, m_dep, rerex_dep],
gnu_symbol_visibility: 'hidden',
install: true,
target_type: library_type)
diff --git a/schemas/owl.ttl b/schemas/owl.ttl
new file mode 100644
index 00000000..3057fdd3
--- /dev/null
+++ b/schemas/owl.ttl
@@ -0,0 +1,614 @@
+@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 owl: <http://www.w3.org/2002/07/owl#> .
+
+<http://www.w3.org/2002/07/owl>
+ rdfs:comment "The OWL 2 Schema vocabulary (OWL 2)" ;
+ a owl:Ontology ;
+ rdfs:comment """
+ This ontology partially describes the built-in classes and
+ properties that together form the basis of the RDF/XML syntax of OWL 2.
+ The content of this ontology is based on Tables 6.1 and 6.2
+ in Section 6.4 of the OWL 2 RDF-Based Semantics specification,
+ available at http://www.w3.org/TR/owl2-rdf-based-semantics/.
+ Please note that those tables do not include the different annotations
+ (labels, comments and rdfs:isDefinedBy links) used in this file.
+ Also note that the descriptions provided in this ontology do not
+ provide a complete and correct formal description of either the syntax
+ or the semantics of the introduced terms (please see the OWL 2
+ recommendations for the complete and normative specifications).
+ Furthermore, the information provided by this ontology may be
+ misleading if not used with care. This ontology SHOULD NOT be imported
+ into OWL ontologies. Importing this file into an OWL 2 DL ontology
+ will cause it to become an OWL 2 Full ontology and may have other,
+ unexpected, consequences.
+ """ ;
+ rdfs:isDefinedBy <http://www.w3.org/TR/owl2-mapping-to-rdf/>, <http://www.w3.org/TR/owl2-rdf-based-semantics/>, <http://www.w3.org/TR/owl2-syntax/> ;
+ rdfs:seeAlso <http://www.w3.org/TR/owl2-rdf-based-semantics/#table-axiomatic-classes>, <http://www.w3.org/TR/owl2-rdf-based-semantics/#table-axiomatic-properties> ;
+ owl:imports rdfs: ;
+ owl:versionIRI <http://www.w3.org/2002/07/owl> ;
+ owl:versionInfo "$Date: 2009/11/15 10:54:12 $" .
+
+owl:AllDifferent
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise different individuals." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "AllDifferent" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AllDisjointClasses
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise disjoint classes." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "AllDisjointClasses" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AllDisjointProperties
+ a rdfs:Class ;
+ rdfs:comment "The class of collections of pairwise disjoint properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "AllDisjointProperties" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Annotation
+ a rdfs:Class ;
+ rdfs:comment "The class of annotated annotations for which the RDF serialization consists of an annotated subject, predicate and object." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Annotation" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:AnnotationProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of annotation properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "AnnotationProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:AsymmetricProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of asymmetric properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "AsymmetricProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Axiom
+ a rdfs:Class ;
+ rdfs:comment "The class of annotated axioms for which the RDF serialization consists of an annotated subject, predicate and object." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Axiom" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Class
+ a rdfs:Class ;
+ rdfs:comment "The class of OWL classes." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Class" ;
+ rdfs:subClassOf rdfs:Class .
+
+owl:DatatypeProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of data properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "DatatypeProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:DeprecatedClass
+ a rdfs:Class ;
+ rdfs:comment "The class of deprecated classes." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "DeprecatedClass" ;
+ rdfs:subClassOf rdfs:Class .
+
+owl:DeprecatedProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of deprecated properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "DeprecatedProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:FunctionalProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of functional properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "FunctionalProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:InverseFunctionalProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of inverse-functional properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "InverseFunctionalProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:IrreflexiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of irreflexive properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "IrreflexiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:NamedIndividual
+ a rdfs:Class ;
+ rdfs:comment "The class of named individuals." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "NamedIndividual" ;
+ rdfs:subClassOf owl:Thing .
+
+owl:NegativePropertyAssertion
+ a rdfs:Class ;
+ rdfs:comment "The class of negative property assertions." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "NegativePropertyAssertion" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:Nothing
+ a owl:Class ;
+ rdfs:comment "This is the empty class." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Nothing" ;
+ rdfs:subClassOf owl:Thing .
+
+owl:ObjectProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of object properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "ObjectProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:Ontology
+ a rdfs:Class ;
+ rdfs:comment "The class of ontologies." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Ontology" ;
+ rdfs:subClassOf rdfs:Resource .
+
+owl:OntologyProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of ontology properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "OntologyProperty" ;
+ rdfs:subClassOf rdf:Property .
+
+owl:ReflexiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of reflexive properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "ReflexiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Restriction
+ a rdfs:Class ;
+ rdfs:comment "The class of property restrictions." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Restriction" ;
+ rdfs:subClassOf owl:Class .
+
+owl:SymmetricProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of symmetric properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "SymmetricProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:Thing
+ a owl:Class ;
+ rdfs:comment "The class of OWL individuals." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "Thing" .
+
+owl:TransitiveProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of transitive properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "TransitiveProperty" ;
+ rdfs:subClassOf owl:ObjectProperty .
+
+owl:allValuesFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that a universal property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "all values from" ;
+ rdfs:range rdfs:Class .
+
+owl:annotatedProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the predicate of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "annotated property" ;
+ rdfs:range rdfs:Resource .
+
+owl:annotatedSource
+ a rdf:Property ;
+ rdfs:comment "The property that determines the subject of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "annotated source" ;
+ rdfs:range rdfs:Resource .
+
+owl:annotatedTarget
+ a rdf:Property ;
+ rdfs:comment "The property that determines the object of an annotated axiom or annotated annotation." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "annotated target" ;
+ rdfs:range rdfs:Resource .
+
+owl:assertionProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the predicate of a negative property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "assertion property" ;
+ rdfs:range rdf:Property .
+
+owl:backwardCompatibleWith
+ a owl:AnnotationProperty, owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates that a given ontology is backward compatible with another ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "backward compatible with" ;
+ rdfs:range owl:Ontology .
+
+owl:bottomDataProperty
+ a owl:DatatypeProperty ;
+ rdfs:comment "The data property that does not relate any individual to any data value." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "bottom data property" ;
+ rdfs:range rdfs:Literal .
+
+owl:bottomObjectProperty
+ a owl:ObjectProperty ;
+ rdfs:comment "The object property that does not relate any two individuals." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "bottom object property" ;
+ rdfs:range owl:Thing .
+
+owl:cardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of an exact cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:complementOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given class is the complement of another class." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "complement of" ;
+ rdfs:range owl:Class .
+
+owl:datatypeComplementOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given data range is the complement of another data range with respect to the data domain." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "datatype complement of" ;
+ rdfs:range rdfs:Datatype .
+
+owl:deprecated
+ a owl:AnnotationProperty ;
+ rdfs:comment "The annotation property that indicates that a given entity has been deprecated." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "deprecated" ;
+ rdfs:range rdfs:Resource .
+
+owl:differentFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given individuals are different." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "different from" ;
+ rdfs:range owl:Thing .
+
+owl:disjointUnionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that a given class is equivalent to the disjoint union of a collection of other classes." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "disjoint union of" ;
+ rdfs:range rdf:List .
+
+owl:disjointWith
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given classes are disjoint." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "disjoint with" ;
+ rdfs:range owl:Class .
+
+owl:distinctMembers
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of pairwise different individuals in a owl:AllDifferent axiom." ;
+ rdfs:domain owl:AllDifferent ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "distinct members" ;
+ rdfs:range rdf:List .
+
+owl:equivalentClass
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given classes are equivalent, and that is used to specify datatype definitions." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "equivalent class" ;
+ rdfs:range rdfs:Class .
+
+owl:equivalentProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are equivalent." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "equivalent property" ;
+ rdfs:range rdf:Property .
+
+owl:hasKey
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of properties that jointly build a key." ;
+ rdfs:domain owl:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "has key" ;
+ rdfs:range rdf:List .
+
+owl:hasSelf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the property that a self restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "has self" ;
+ rdfs:range rdfs:Resource .
+
+owl:hasValue
+ a rdf:Property ;
+ rdfs:comment "The property that determines the individual that a has-value restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "has value" ;
+ rdfs:range rdfs:Resource .
+
+owl:imports
+ a owl:OntologyProperty ;
+ rdfs:comment "The property that is used for importing other ontologies into a given ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "imports" ;
+ rdfs:range owl:Ontology .
+
+owl:incompatibleWith
+ a owl:AnnotationProperty, owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates that a given ontology is incompatible with another ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "incompatible with" ;
+ rdfs:range owl:Ontology .
+
+owl:intersectionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of classes or data ranges that build an intersection." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "intersection of" ;
+ rdfs:range rdf:List .
+
+owl:inverseOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are inverse." ;
+ rdfs:domain owl:ObjectProperty ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "inverse of" ;
+ rdfs:range owl:ObjectProperty .
+
+owl:maxCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a maximum cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "max cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:maxQualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a maximum qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "max qualified cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:members
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of members in either a owl:AllDifferent, owl:AllDisjointClasses or owl:AllDisjointProperties axiom." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "members" ;
+ rdfs:range rdf:List .
+
+owl:minCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a minimum cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "min cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:minQualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of a minimum qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "min qualified cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:onClass
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that a qualified object cardinality restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "on class" ;
+ rdfs:range owl:Class .
+
+owl:onDataRange
+ a rdf:Property ;
+ rdfs:comment "The property that determines the data range that a qualified data cardinality restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "on data range" ;
+ rdfs:range rdfs:Datatype .
+
+owl:onDatatype
+ a rdf:Property ;
+ rdfs:comment "The property that determines the datatype that a datatype restriction refers to." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "on datatype" ;
+ rdfs:range rdfs:Datatype .
+
+owl:onProperties
+ a rdf:Property ;
+ rdfs:comment "The property that determines the n-tuple of properties that a property restriction on an n-ary data range refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "on properties" ;
+ rdfs:range rdf:List .
+
+owl:onProperty
+ a rdf:Property ;
+ rdfs:comment "The property that determines the property that a property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "on property" ;
+ rdfs:range rdf:Property .
+
+owl:oneOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of individuals or data values that build an enumeration." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "one of" ;
+ rdfs:range rdf:List .
+
+owl:priorVersion
+ a owl:AnnotationProperty, owl:OntologyProperty ;
+ rdfs:comment "The annotation property that indicates the predecessor ontology of a given ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "prior version" ;
+ rdfs:range owl:Ontology .
+
+owl:propertyChainAxiom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the n-tuple of properties that build a sub property chain of a given property." ;
+ rdfs:domain owl:ObjectProperty ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "property chain axiom" ;
+ rdfs:range rdf:List .
+
+owl:propertyDisjointWith
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given properties are disjoint." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "property disjoint with" ;
+ rdfs:range rdf:Property .
+
+owl:qualifiedCardinality
+ a rdf:Property ;
+ rdfs:comment "The property that determines the cardinality of an exact qualified cardinality restriction." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "qualified cardinality" ;
+ rdfs:range xsd:nonNegativeInteger .
+
+owl:sameAs
+ a rdf:Property ;
+ rdfs:comment "The property that determines that two given individuals are equal." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "same as" ;
+ rdfs:range owl:Thing .
+
+owl:someValuesFrom
+ a rdf:Property ;
+ rdfs:comment "The property that determines the class that an existential property restriction refers to." ;
+ rdfs:domain owl:Restriction ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "some values from" ;
+ rdfs:range rdfs:Class .
+
+owl:sourceIndividual
+ a rdf:Property ;
+ rdfs:comment "The property that determines the subject of a negative property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "source individual" ;
+ rdfs:range owl:Thing .
+
+owl:targetIndividual
+ a rdf:Property ;
+ rdfs:comment "The property that determines the object of a negative object property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "target individual" ;
+ rdfs:range owl:Thing .
+
+owl:targetValue
+ a rdf:Property ;
+ rdfs:comment "The property that determines the value of a negative data property assertion." ;
+ rdfs:domain owl:NegativePropertyAssertion ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "target value" ;
+ rdfs:range rdfs:Literal .
+
+owl:topDataProperty
+ a owl:DatatypeProperty ;
+ rdfs:comment "The data property that relates every individual to every data value." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "top data property" ;
+ rdfs:range rdfs:Literal .
+
+owl:topObjectProperty
+ a owl:ObjectProperty ;
+ rdfs:comment "The object property that relates every two individuals." ;
+ rdfs:domain owl:Thing ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "top object property" ;
+ rdfs:range owl:Thing .
+
+owl:unionOf
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of classes or data ranges that build a union." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "union of" ;
+ rdfs:range rdf:List .
+
+owl:versionIRI
+ a owl:OntologyProperty ;
+ rdfs:comment "The property that identifies the version IRI of an ontology." ;
+ rdfs:domain owl:Ontology ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "version IRI" ;
+ rdfs:range owl:Ontology .
+
+owl:versionInfo
+ a owl:AnnotationProperty ;
+ rdfs:comment "The annotation property that provides version information for an ontology or another OWL construct." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "version info" ;
+ rdfs:range rdfs:Resource .
+
+owl:withRestrictions
+ a rdf:Property ;
+ rdfs:comment "The property that determines the collection of facet-value pairs that define a datatype restriction." ;
+ rdfs:domain rdfs:Datatype ;
+ rdfs:isDefinedBy <http://www.w3.org/2002/07/owl#> ;
+ rdfs:label "with restrictions" ;
+ rdfs:range rdf:List .
+
diff --git a/schemas/rdf.ttl b/schemas/rdf.ttl
new file mode 100644
index 00000000..5ef93450
--- /dev/null
+++ b/schemas/rdf.ttl
@@ -0,0 +1,127 @@
+@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#> .
+
+<http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+ rdfs:comment "The RDF Vocabulary (RDF)" ;
+ a owl:Ontology ;
+ rdfs:seeAlso <http://www.w3.org/2000/01/rdf-schema-more> .
+
+rdf:Alt
+ a rdfs:Class ;
+ rdfs:comment "The class of containers of alternatives." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "Alt" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:Bag
+ a rdfs:Class ;
+ rdfs:comment "The class of unordered containers." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "Bag" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:List
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF Lists." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "List" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:PlainLiteral
+ a rdfs:Datatype ;
+ rdfs:comment "The class of plain (i.e. untyped) literal values." ;
+ rdfs:isDefinedBy <http://www.w3.org/TR/rdf-plain-literal/> ;
+ rdfs:label "Plain Literal" ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:Property
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF properties." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "Property" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:Seq
+ a rdfs:Class ;
+ rdfs:comment "The class of ordered containers." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "Seq" ;
+ rdfs:subClassOf rdfs:Container .
+
+rdf:Statement
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF statements." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "Statement" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdf:XMLLiteral
+ a rdfs:Datatype ;
+ rdfs:comment "The class of XML literal values." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "XML Literal" ;
+ rdfs:subClassOf rdfs:Literal .
+
+rdf:first
+ a rdf:Property ;
+ rdfs:comment "The first item in the subject RDF list." ;
+ rdfs:domain rdf:List ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "first" ;
+ rdfs:range rdfs:Resource .
+
+rdf:nil
+ a rdf:List ;
+ 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." ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "nil" .
+
+rdf:object
+ a rdf:Property ;
+ rdfs:comment "The object of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "object" ;
+ rdfs:range rdfs:Resource .
+
+rdf:predicate
+ a rdf:Property ;
+ rdfs:comment "The predicate of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "predicate" ;
+ rdfs:range rdfs:Resource .
+
+rdf:rest
+ a rdf:Property ;
+ rdfs:comment "The rest of the subject RDF list after the first item." ;
+ rdfs:domain rdf:List ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "rest" ;
+ rdfs:range rdf:List .
+
+rdf:subject
+ a rdf:Property ;
+ rdfs:comment "The subject of the subject RDF statement." ;
+ rdfs:domain rdf:Statement ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "subject" ;
+ rdfs:range rdfs:Resource .
+
+rdf:type
+ a rdf:Property ;
+ rdfs:comment "The subject is an instance of a class." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "type" ;
+ rdfs:range rdfs:Class .
+
+rdf:value
+ a rdf:Property ;
+ rdfs:comment "Idiomatic property used for structured values." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/1999/02/22-rdf-syntax-ns#> ;
+ rdfs:label "value" ;
+ rdfs:range rdfs:Resource .
+
diff --git a/schemas/rdfs.ttl b/schemas/rdfs.ttl
new file mode 100644
index 00000000..43c51e58
--- /dev/null
+++ b/schemas/rdfs.ttl
@@ -0,0 +1,123 @@
+@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#> .
+
+<http://www.w3.org/2000/01/rdf-schema#>
+ rdfs:comment "The RDF Schema vocabulary (RDFS)" ;
+ a owl:Ontology ;
+ rdfs:seeAlso <http://www.w3.org/2000/01/rdf-schema-more> .
+
+rdfs:Class
+ a rdfs:Class ;
+ rdfs:comment "The class of classes." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Class" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:Container
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF containers." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Container" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:ContainerMembershipProperty
+ a rdfs:Class ;
+ rdfs:comment "The class of container membership properties, rdf:_1, rdf:_2, ..., all of which are sub-properties of 'member'." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Container Membership Property" ;
+ rdfs:subClassOf rdf:Property .
+
+rdfs:Datatype
+ a rdfs:Class ;
+ rdfs:comment "The class of RDF datatypes." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Datatype" ;
+ rdfs:subClassOf rdfs:Class .
+
+rdfs:Literal
+ a rdfs:Class ;
+ rdfs:comment "The class of literal values, eg. textual strings and integers." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Literal" ;
+ rdfs:subClassOf rdfs:Resource .
+
+rdfs:Resource
+ a rdfs:Class ;
+ rdfs:comment "The class resource, everything." ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "Resource" .
+
+rdfs:comment
+ a rdf:Property ;
+ rdfs:comment "A description of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "comment" ;
+ rdfs:range rdfs:Literal .
+
+rdfs:domain
+ a rdf:Property ;
+ rdfs:comment "A domain of the subject property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "domain" ;
+ rdfs:range rdfs:Class .
+
+rdfs:isDefinedBy
+ a rdf:Property ;
+ rdfs:comment "The defininition of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "is defined by" ;
+ rdfs:range rdfs:Resource ;
+ rdfs:subPropertyOf rdfs:seeAlso .
+
+rdfs:label
+ a rdf:Property ;
+ rdfs:comment "A human-readable name for the subject." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "label" ;
+ rdfs:range rdfs:Literal .
+
+rdfs:member
+ a rdf:Property ;
+ rdfs:comment "A member of the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "member" ;
+ rdfs:range rdfs:Resource .
+
+rdfs:range
+ a rdf:Property ;
+ rdfs:comment "A range of the subject property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "range" ;
+ rdfs:range rdfs:Class .
+
+rdfs:seeAlso
+ a rdf:Property ;
+ rdfs:comment "Further information about the subject resource." ;
+ rdfs:domain rdfs:Resource ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "see also" ;
+ rdfs:range rdfs:Resource .
+
+rdfs:subClassOf
+ a rdf:Property ;
+ rdfs:comment "The subject is a subclass of a class." ;
+ rdfs:domain rdfs:Class ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "sub-class of" ;
+ rdfs:range rdfs:Class .
+
+rdfs:subPropertyOf
+ a rdf:Property ;
+ rdfs:comment "The subject is a subproperty of a property." ;
+ rdfs:domain rdf:Property ;
+ rdfs:isDefinedBy <http://www.w3.org/2000/01/rdf-schema#> ;
+ rdfs:label "sub-property of" ;
+ rdfs:range rdf:Property .
+
diff --git a/schemas/xsd.ttl b/schemas/xsd.ttl
new file mode 100644
index 00000000..46f6793a
--- /dev/null
+++ b/schemas/xsd.ttl
@@ -0,0 +1,353 @@
+@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#> .
+
+<http://www.w3.org/2001/XMLSchema#>
+ a owl:Ontology ;
+ rdfs:comment "XML Schema Datatypes" .
+
+xsd:ID
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:NCName .
+
+xsd:IDREF
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:NCName .
+
+xsd:ENTITY
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:NCName .
+
+xsd:NCName
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:Name .
+
+xsd:NMTOKEN
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:token .
+
+xsd:Name
+ a rdfs:Datatype ;
+ owl:onDatatype xsd:token .
+
+xsd:totalDigits
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:positiveInteger ;
+ rdfs:label "total digits" ;
+ rdfs:comment "The maximum number of decimal digits required to represent a value." .
+
+xsd:fractionDigits
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:range xsd:nonNegativeInteger ;
+ rdfs:label "fraction digits" ;
+ rdfs:comment "The total number of digits to the right of the decimal point required to represent a value." .
+
+xsd:pattern
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "pattern" ;
+ rdfs:comment "A regular expression that matches complete valid literals." .
+
+xsd:maxInclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "max inclusive" ;
+ rdfs:comment "The inclusive upper bound of an ordered datatype." .
+
+xsd:maxExclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "max exclusive" ;
+ rdfs:comment "The exclusive upper bound of an ordered datatype." .
+
+xsd:minInclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "min inclusive" ;
+ rdfs:comment "The inclusive lower bound of an ordered datatype." .
+
+xsd:minExclusive
+ a rdf:Property ,
+ owl:DatatypeProperty ;
+ rdfs:label "min exclusive" ;
+ rdfs:comment "The exclusive lower bound of an ordered datatype." .
+
+xsd:QName
+ a rdfs:Datatype ;
+ rdfs:label "XML qualified name" .
+
+xsd:anyURI
+ a rdfs:Datatype ;
+ rdfs:label "URI reference" .
+
+xsd:base64Binary
+ a rdfs:Datatype ;
+ rdfs:label "base64 binary" ;
+ rdfs:comment "Base64-encoded arbitrary binary data." ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?"
+ ]
+ ) .
+
+xsd:boolean
+ a rdfs:Datatype ;
+ rdfs:label "boolean" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(true|false)"
+ ]
+ ) .
+
+xsd:byte
+ a rdfs:Datatype ;
+ rdfs:label "byte" ;
+ owl:onDatatype xsd:short ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 127
+ ] [
+ xsd:minInclusive -128
+ ]
+ ) .
+
+xsd:date
+ a rdfs:Datatype ;
+ rdfs:label "date" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "-?[0-9][0-9][0-9][0-9]([0-9]*)?-(0[1-9]|1[0-2])-([0-3][0-9])([+-]([0-1][0-9]|2[0-3]):[0-5][0-9])?"
+ ]
+ ) .
+
+xsd:dateTime
+ a rdfs:Datatype ;
+ rdfs:label "date time" .
+
+xsd:decimal
+ a rdfs:Datatype ;
+ rdfs:label "decimal" ;
+ rdfs:comment "A subset of the real numbers, which can be represented by decimal numerals." ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[+-]?[0-9]*\\.?[0-9]*"
+ ]
+ ) .
+
+xsd:double
+ a rdfs:Datatype ;
+ rdfs:label "double" ;
+ rdfs:comment "IEEE double-precision 64-bit floating point." ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[+-]?[0-9]*\\.?[0-9]*([eE][-+]?[0-9]+)?"
+ ]
+ ) .
+
+xsd:duration
+ a rdfs:Datatype ;
+ rdfs:label "duration" .
+
+xsd:float
+ a rdfs:Datatype ;
+ rdfs:label "float" ;
+ rdfs:comment "IEEE single-precision 32-bit floating point." ;
+ owl:onDatatype xsd:double .
+
+xsd:gDay
+ a rdfs:Datatype .
+
+xsd:gMonth
+ a rdfs:Datatype .
+
+xsd:gMonthDay
+ a rdfs:Datatype .
+
+xsd:gYear
+ a rdfs:Datatype .
+
+xsd:gYearMonth
+ a rdfs:Datatype .
+
+xsd:hexBinary
+ a rdfs:Datatype ;
+ rdfs:label "hex binary" ;
+ rdfs:comment "Hex-encoded arbitrary binary data." ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[0-9A-F]*"
+ ]
+ ) .
+
+xsd:int
+ a rdfs:Datatype ;
+ rdfs:label "int" ;
+ owl:onDatatype xsd:long ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 2147483647
+ ] [
+ xsd:minInclusive -2147483648
+ ]
+ ) .
+
+xsd:integer
+ a rdfs:Datatype ;
+ rdfs:label "integer" ;
+ owl:onDatatype xsd:decimal ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[-+]?[0-9]+"
+ ] [
+ xsd:fractionDigits 0
+ ]
+ ) .
+
+xsd:language
+ a rdfs:Datatype ;
+ rdfs:label "language" ;
+ owl:onDatatype xsd:token ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*"
+ ]
+ ) .
+
+xsd:long
+ a rdfs:Datatype ;
+ rdfs:label "long" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 9223372036854775807
+ ] [
+ xsd:minInclusive -9223372036854775808
+ ]
+ ) .
+
+xsd:negativeInteger
+ a rdfs:Datatype ;
+ rdfs:label "negative integer" ;
+ owl:onDatatype xsd:nonPositiveInteger ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive -1
+ ]
+ ) .
+
+xsd:nonNegativeInteger
+ a rdfs:Datatype ;
+ rdfs:label "non-negative integer" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[0-9]*"
+ ] [
+ xsd:minInclusive 0
+ ]
+ ) .
+
+xsd:nonPositiveInteger
+ a rdfs:Datatype ;
+ rdfs:label "non-positive integer" ;
+ owl:onDatatype xsd:integer ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "(0|-[0-9]+)"
+ ] [
+ xsd:maxInclusive 0
+ ]
+ ) .
+
+xsd:normalizedString
+ a rdfs:Datatype ;
+ rdfs:label "normalized string" ;
+ owl:onDatatype xsd:string ;
+ rdfs:comment "The set of strings that do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters." .
+
+xsd:positiveInteger
+ a rdfs:Datatype ;
+ rdfs:label "positive integer" ;
+ owl:onDatatype xsd:nonNegativeInteger ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[+]?[0-9]+"
+ ] [
+ xsd:minInclusive 1
+ ]
+ ) .
+
+xsd:short
+ a rdfs:Datatype ;
+ rdfs:label "short" ;
+ owl:onDatatype xsd:int ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 32767
+ ] [
+ xsd:minInclusive -32768
+ ]
+ ) .
+
+xsd:string
+ a rdfs:Datatype ;
+ rdfs:label "string" ;
+ rdfs:comment "A character string." .
+
+xsd:time
+ a rdfs:Datatype ;
+ rdfs:label "time" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[1-2][0-9]:[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]"
+ ]
+ ) .
+
+xsd:token
+ a rdfs:Datatype ;
+ rdfs:label "token" ;
+ owl:onDatatype xsd:normalizedString ;
+ rdfs:comment "The set of strings that do not contain the carriage return (#xD), line feed (#xA) nor tab (#x9) characters, that have no leading or trailing spaces (#x20) and that have no internal sequences of two or more spaces." .
+
+xsd:unsignedByte
+ a rdfs:Datatype ;
+ rdfs:label "unsigned byte" ;
+ owl:onDatatype xsd:unsignedShort ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 255
+ ]
+ ) .
+
+xsd:unsignedInt
+ a rdfs:Datatype ;
+ rdfs:label "unsigned int" ;
+ owl:onDatatype xsd:unsignedLong ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 4294967295
+ ]
+ ) .
+
+xsd:unsignedLong
+ a rdfs:Datatype ;
+ rdfs:label "unsigned long" ;
+ owl:onDatatype xsd:nonNegativeInteger ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 18446744073709551615
+ ]
+ ) .
+
+xsd:unsignedShort
+ a rdfs:Datatype ;
+ rdfs:label "unsigned short" ;
+ owl:onDatatype xsd:unsignedInt ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 65535
+ ]
+ ) .
diff --git a/src/serdi.c b/src/serdi.c
index 0955079f..70c52682 100644
--- a/src/serdi.c
+++ b/src/serdi.c
@@ -56,6 +56,7 @@ print_usage(const char* name, bool error)
fprintf(os, "Read and write RDF syntax.\n");
fprintf(os, "Use - for INPUT to read from standard input.\n\n");
fprintf(os, " -I BASE_URI Input base URI.\n");
+ fprintf(os, " -V Validate inputs.\n");
fprintf(os, " -a Write ASCII output if possible.\n");
fprintf(os, " -b Fast bulk output for large serialisations.\n");
fprintf(os, " -c PREFIX Chop PREFIX from matching blank node IDs.\n");
@@ -147,6 +148,7 @@ main(int argc, char** argv)
bool bulk_write = false;
bool no_inline = false;
bool osyntax_set = false;
+ bool validate = false;
bool use_model = false;
bool quiet = false;
size_t stack_size = 4194304;
@@ -167,6 +169,8 @@ main(int argc, char** argv)
}
base = serd_new_uri(SERD_MEASURE_STRING(argv[a]));
+ } else if (argv[a][1] == 'V') {
+ validate = use_model = true;
} else if (argv[a][1] == 'a') {
writer_flags |= SERD_WRITE_ASCII;
} else if (argv[a][1] == 'b') {
@@ -314,9 +318,10 @@ main(int argc, char** argv)
SerdSink* inserter = NULL;
const SerdSink* sink = NULL;
if (use_model) {
- const SerdModelFlags flags = SERD_INDEX_SPO |
- (input_has_graphs ? SERD_INDEX_GRAPHS : 0u) |
- (no_inline ? 0u : SERD_INDEX_OPS);
+ const SerdModelFlags flags =
+ SERD_INDEX_SPO | (input_has_graphs ? SERD_INDEX_GRAPHS : 0u) |
+ (no_inline ? 0u : SERD_INDEX_OPS) | (validate ? SERD_STORE_CURSORS : 0u);
+
model = serd_model_new(world, flags);
inserter = serd_inserter_new(model, env, NULL);
sink = inserter;
@@ -391,6 +396,10 @@ main(int argc, char** argv)
}
free(prefix);
+ if (!st && validate) {
+ st = serd_validate(model);
+ }
+
if (st <= SERD_FAILURE && use_model) {
const SerdSink* writer_sink = serd_writer_sink(writer);
SerdRange* range = serd_model_all(model);
diff --git a/src/validate.c b/src/validate.c
index 2aaae607..46291b64 100644
--- a/src/validate.c
+++ b/src/validate.c
@@ -791,7 +791,8 @@ static void
init_uris(URIs* uris)
{
#define URI(prefix, suffix) \
- uris->prefix##_##suffix = serd_new_uri(NS_##prefix #suffix)
+ uris->prefix##_##suffix = \
+ serd_new_uri(SERD_STATIC_STRING(NS_##prefix #suffix))
URI(owl, Class);
URI(owl, DatatypeProperty);
diff --git a/test/meson.build b/test/meson.build
index 84045225..87b77334 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -1,5 +1,6 @@
autoship = find_program('autoship', required: false)
run_test_suite = find_program('run_test_suite.py')
+run_validation_test_suite = find_program('run_validation_test_suite.py')
wrapper = meson.get_cross_property('wrapper', '')
unit_tests = [
@@ -158,6 +159,8 @@ if get_option('utils')
timeout: 240)
endforeach
+ manifest = files('terse' / 'manifest.ttl')
+ base_uri = serd_base + 'terse' + '/'
test('terse', run_test_suite,
args: script_args + ['--osyntax', 'turtle', manifest, base_uri, '--', '-t'],
suite: ['rdf', 'serd'],
@@ -178,6 +181,21 @@ if get_option('utils')
suite: ['rdf', 'serd'],
timeout: 240)
+ # Validation test suite
+ manifest = files('validate' / 'manifest.ttl')
+ base_uri = serd_base + 'validate' + '/'
+ test('validate', run_validation_test_suite,
+ args: script_args + [
+ manifest,
+ base_uri,
+ meson.current_source_dir() / '../schemas/owl.ttl',
+ meson.current_source_dir() / '../schemas/rdf.ttl',
+ meson.current_source_dir() / '../schemas/rdfs.ttl',
+ meson.current_source_dir() / '../schemas/xsd.ttl',
+ ],
+ suite: ['rdf', 'serd'],
+ timeout: 240)
+
## Standard W3C test suites
w3c_suites = ['Turtle', 'NTriples', 'NQuads', 'TriG']
diff --git a/test/run_test_suite.py b/test/run_test_suite.py
index 05dc81ca..9af3df9e 100755
--- a/test/run_test_suite.py
+++ b/test/run_test_suite.py
@@ -2,6 +2,8 @@
"""Run an RDF test suite with serdi."""
+import serd_test_util
+
import argparse
import datetime
import difflib
@@ -15,31 +17,6 @@ import tempfile
import urllib.parse
-def earl_assertion(test, passed, asserter):
- """Return a Turtle description of an assertion for the test report."""
-
- asserter_str = ""
- if asserter is not None:
- asserter_str = "\n\tearl:assertedBy <%s> ;" % asserter
-
- return """
-[]
-\ta earl:Assertion ;%s
-\tearl:subject <http://drobilla.net/sw/serd> ;
-\tearl:test <%s> ;
-\tearl:result [
-\t\ta earl:TestResult ;
-\t\tearl:outcome %s ;
-\t\tdc:date "%s"^^xsd:dateTime
-\t] .
-""" % (
- asserter_str,
- test,
- "earl:passed" if passed else "earl:failed",
- datetime.datetime.now().replace(microsecond=0).isoformat(),
- )
-
-
def log_error(message):
"""Log an error message to stderr"""
@@ -141,37 +118,6 @@ def _test_output_syntax(test_class):
raise Exception("Unknown test class <{}>".format(test_class))
-def _load_rdf(filename, base_uri, command_prefix):
- """Load an RDF file as dictionaries via serdi (only supports URIs)."""
-
- rdf_type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
- model = {}
- instances = {}
-
- cmd = command_prefix + ["-I", base_uri, filename]
- proc = subprocess.run(cmd, capture_output=True, check=True)
- for line in proc.stdout.splitlines():
- matches = re.match(
- r"<([^ ]*)> <([^ ]*)> <([^ ]*)> \.", line.decode("utf-8")
- )
- if matches:
- s, p, o = (matches.group(1), matches.group(2), matches.group(3))
- if s not in model:
- model[s] = {p: [o]}
- elif p not in model[s]:
- model[s][p] = [o]
- else:
- model[s][p].append(o)
-
- if p == rdf_type:
- if o not in instances:
- instances[o] = set([s])
- else:
- instances[o].update([s])
-
- return model, instances
-
-
def _option_combinations(options):
"""Return an iterator that cycles through all combinations of options."""
@@ -213,7 +159,7 @@ def _file_lines_equal(patha, pathb, subst_from="", subst_to=""):
for path in (patha, pathb):
if not os.access(path, os.F_OK):
- sys.stderr.write("error: missing file %s" % path)
+ log_error("missing file %s\n" % path)
return False
la = sorted(set(io.open(patha, encoding="utf-8").readlines()))
@@ -237,7 +183,9 @@ def test_suite(
mf = "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"
test_dir = os.path.dirname(manifest_path)
- model, instances = _load_rdf(manifest_path, base_uri, command_prefix)
+ model, instances = serd_test_util.load_rdf(
+ manifest_path, base_uri, command_prefix
+ )
top_dir = os.path.commonpath([os.getcwd(), os.path.abspath(test_dir)])
out_test_dir = os.path.relpath(test_dir, top_dir)
@@ -306,7 +254,7 @@ def test_suite(
if not _file_equals(check_path, out_filename):
results.n_failures += 1
log_error(
- "Output {} does not match {}".format(
+ "Output {} does not match {}\n".format(
out_filename, check_path
)
)
@@ -324,14 +272,20 @@ def test_suite(
)
# Run model test for positive test (must succeed)
- out_filename = os.path.join(out_test_dir, test_name + ".model.out")
+ out_filename = os.path.join(
+ out_test_dir, test_name + ".model.out"
+ )
with open(out_filename, "w") as stdout:
- proc = subprocess.run([command[0]] + ['-m'] + command[1:],
- check=True,
- stdout=stdout)
+ proc = subprocess.run(
+ [command[0]] + ["-m"] + command[1:],
+ check=True,
+ stdout=stdout,
+ )
- if proc.returncode == 0 and ((mf + 'result') in model[test]):
+ if proc.returncode == 0 and (
+ (mf + "result") in model[test]
+ ):
if not _file_lines_equal(check_path, out_filename):
results.n_failures += 1
@@ -366,7 +320,9 @@ def test_suite(
# Write test report entry
if report_filename:
with open(report_filename, "a") as report:
- report.write(earl_assertion(test, passed, asserter))
+ report.write(
+ serd_test_util.earl_assertion(test, passed, asserter)
+ )
# Run all test types in the test suite
results = Results()
diff --git a/test/run_validation_test_suite.py b/test/run_validation_test_suite.py
new file mode 100755
index 00000000..a27e55de
--- /dev/null
+++ b/test/run_validation_test_suite.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python3
+
+"""Run the serd RDF validation test suite."""
+
+import serd_test_util
+
+import argparse
+import datetime
+import difflib
+import itertools
+import os
+import re
+import shlex
+import subprocess
+import sys
+import tempfile
+import urllib.parse
+
+
+def _uri_path(uri):
+ path = urllib.parse.urlparse(uri).path
+ drive = os.path.splitdrive(path[1:])[0]
+ return path if not drive else path[1:]
+
+
+def _load_rdf(filename, base_uri, command_prefix):
+ """Load an RDF file as dictionaries via serdi (only supports URIs)."""
+
+ rdf_type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
+ model = {}
+ instances = {}
+
+ cmd = command_prefix + ["-I", base_uri, filename]
+ proc = subprocess.run(cmd, capture_output=True, check=True)
+ for line in proc.stdout.splitlines():
+ matches = re.match(
+ r"<([^ ]*)> <([^ ]*)> <([^ ]*)> \.", line.decode("utf-8")
+ )
+ if matches:
+ s, p, o = (matches.group(1), matches.group(2), matches.group(3))
+ if s not in model:
+ model[s] = {p: [o]}
+ elif p not in model[s]:
+ model[s][p] = [o]
+ else:
+ model[s][p].append(o)
+
+ if p == rdf_type:
+ if o not in instances:
+ instances[o] = set([s])
+ else:
+ instances[o].update([s])
+
+ return model, instances
+
+
+def _option_combinations(options):
+ """Return an iterator that cycles through all combinations of options."""
+
+ combinations = []
+ for count in range(len(options) + 1):
+ combinations += list(itertools.combinations(options, count))
+
+ return itertools.cycle(combinations)
+
+
+def _show_diff(from_lines, to_lines, from_filename, to_filename):
+ same = True
+ for line in difflib.unified_diff(
+ from_lines,
+ to_lines,
+ fromfile=os.path.abspath(from_filename),
+ tofile=os.path.abspath(to_filename),
+ ):
+ sys.stderr.write(line)
+ same = False
+
+ return same
+
+
+def _file_equals(patha, pathb):
+
+ for path in (patha, pathb):
+ if not os.access(path, os.F_OK):
+ sys.stderr.write("error: missing file {}\n".format(path))
+ return False
+
+ with open(patha, "r", encoding="utf-8") as fa:
+ with open(pathb, "r", encoding="utf-8") as fb:
+ return _show_diff(fa.readlines(), fb.readlines(), patha, pathb)
+
+
+def _file_lines_equal(patha, pathb, subst_from="", subst_to=""):
+ import io
+
+ for path in (patha, pathb):
+ if not os.access(path, os.F_OK):
+ sys.stderr.write("error: missing file %s\n" % path)
+ return False
+
+ la = sorted(set(io.open(patha, encoding="utf-8").readlines()))
+ lb = sorted(set(io.open(pathb, encoding="utf-8").readlines()))
+ if la != lb:
+ _show_diff(la, lb, patha, pathb)
+ return False
+
+ return True
+
+
+def validation_test_suite(
+ manifest_path,
+ schemas,
+ base_uri,
+ report_filename,
+ isyntax,
+ command_prefix,
+):
+ """Run all tests in a test suite manifest."""
+
+ mf = "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"
+ test_dir = os.path.dirname(manifest_path)
+ model, instances = serd_test_util.load_rdf(
+ manifest_path, base_uri, command_prefix
+ )
+
+ top_dir = os.path.commonpath([os.getcwd(), os.path.abspath(test_dir)])
+ out_test_dir = os.path.relpath(test_dir, top_dir)
+
+ os.makedirs(out_test_dir, exist_ok=True)
+
+ asserter = ""
+ if os.getenv("USER") == "drobilla":
+ asserter = "http://drobilla.net/drobilla#me"
+
+ class Results:
+ def __init__(self):
+ self.n_tests = 0
+ self.n_failures = 0
+
+ def run_tests(test_class, tests, expected_return, results):
+ for test in sorted(tests):
+ test_uri = model[test][mf + "action"][0]
+ test_uri_path = _uri_path(test_uri)
+ test_name = os.path.basename(test_uri_path)
+ test_path = os.path.join(test_dir, test_name)
+
+ command = (
+ command_prefix
+ + [
+ "-V",
+ "-I",
+ test_uri,
+ test_path,
+ ]
+ + schemas
+ )
+ out_filename = os.path.join(out_test_dir, test_name + ".out")
+
+ results.n_tests += 1
+
+ if expected_return == 0: # Positive test
+
+ with open(out_filename, "w") as stdout:
+ proc = subprocess.run(command, check=False, stdout=stdout)
+ if proc.returncode == 0:
+ passed = True
+ else:
+ results.n_failures += 1
+ sys.stderr.write(
+ "error: Unexpected failure of command: {}\n".format(
+ " ".join(shlex.quote(c) for c in command)
+ )
+ )
+
+ else: # Negative test
+ with open(out_filename, "w") as stdout:
+ with tempfile.TemporaryFile() as stderr:
+ proc = subprocess.run(
+ command, check=False, stdout=stdout, stderr=stderr
+ )
+
+ if proc.returncode != 0:
+ passed = True
+ else:
+ results.n_failures += 1
+ sys.stderr.write(
+ "error: Unexpected success of command: {}\n".format(
+ " ".join(shlex.quote(c) for c in command)
+ )
+ )
+
+ # Check that an error message was printed
+ stderr.seek(0, 2) # Seek to end
+ if stderr.tell() == 0: # Empty
+ sys.stderr.write(
+ "error: No error message printed by command: {}\n".format(
+ " ".join(shlex.quote(c) for c in command)
+ )
+ )
+ result = 1
+
+ # Write test report entry
+ if report_filename:
+ with open(report_filename, "a") as report:
+ report.write(
+ serd_test_util.earl_assertion(test, passed, asserter)
+ )
+
+ # Run all test types in the test suite
+ results = Results()
+ ns_serd = "http://drobilla.net/ns/serd#"
+ for test_class, instances in instances.items():
+ if test_class.startswith(ns_serd):
+ expected = 1 if "Negative" in test_class else 0
+ run_tests(test_class, instances, expected, results)
+
+ # Print result summary
+ if results.n_failures > 0:
+ sys.stderr.write(
+ "error: {}/{} tests failed\n".format(
+ results.n_failures, results.n_tests
+ )
+ )
+ else:
+ sys.stdout.write("All {} tests passed\n".format(results.n_tests))
+
+ return results.n_failures
+
+
+def main():
+ """Run the command line tool."""
+
+ parser = argparse.ArgumentParser(
+ usage="%(prog)s [OPTION]... MANIFEST BASE_URI SCHEMA...",
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+
+ parser.add_argument("--report", help="path to write result report to")
+ parser.add_argument("--serdi", default="serdi", help="path to serdi")
+ parser.add_argument("--syntax", default="turtle", help="input syntax")
+ parser.add_argument("--wrapper", default="", help="executable wrapper")
+ parser.add_argument("manifest", help="test suite manifest.ttl file")
+ parser.add_argument("base_uri", help="base URI for tests")
+ parser.add_argument("schema", nargs="+", help="schema file")
+
+ args = parser.parse_args(sys.argv[1:])
+ command_prefix = shlex.split(args.wrapper) + [args.serdi]
+
+ return validation_test_suite(
+ args.manifest,
+ args.schema,
+ args.base_uri,
+ args.report,
+ args.syntax,
+ command_prefix,
+ )
+
+
+if __name__ == "__main__":
+ try:
+ sys.exit(main())
+ except subprocess.CalledProcessError as e:
+ if e.stderr is not None:
+ sys.stderr.write(e.stderr.decode("utf-8"))
+
+ sys.stderr.write("error: %s\n" % e)
+ sys.exit(e.returncode)
diff --git a/test/serd_test_util/__init__.py b/test/serd_test_util/__init__.py
new file mode 100644
index 00000000..f82ca760
--- /dev/null
+++ b/test/serd_test_util/__init__.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+
+"""Utilities for data-driven tests."""
+
+import datetime
+import re
+import subprocess
+
+
+def earl_assertion(test, passed, asserter):
+ """Return a Turtle description of an assertion for the test report."""
+
+ asserter_str = ""
+ if asserter is not None:
+ asserter_str = "\n\tearl:assertedBy <%s> ;" % asserter
+
+ return """
+[]
+\ta earl:Assertion ;%s
+\tearl:subject <http://drobilla.net/sw/serd> ;
+\tearl:test <%s> ;
+\tearl:result [
+\t\ta earl:TestResult ;
+\t\tearl:outcome %s ;
+\t\tdc:date "%s"^^xsd:dateTime
+\t] .
+""" % (
+ asserter_str,
+ test,
+ "earl:passed" if passed else "earl:failed",
+ datetime.datetime.now().replace(microsecond=0).isoformat(),
+ )
+
+
+def load_rdf(filename, base_uri, command_prefix):
+ """Load an RDF file as dictionaries via serdi (only supports URIs)."""
+
+ rdf_type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
+ model = {}
+ instances = {}
+
+ cmd = command_prefix + ["-I", base_uri, filename]
+ proc = subprocess.run(cmd, capture_output=True, check=True)
+ for line in proc.stdout.splitlines():
+ matches = re.match(
+ r"<([^ ]*)> <([^ ]*)> <([^ ]*)> \.", line.decode("utf-8")
+ )
+ if matches:
+ s, p, o = (matches.group(1), matches.group(2), matches.group(3))
+ if s not in model:
+ model[s] = {p: [o]}
+ elif p not in model[s]:
+ model[s][p] = [o]
+ else:
+ model[s][p].append(o)
+
+ if p == rdf_type:
+ if o not in instances:
+ instances[o] = set([s])
+ else:
+ instances[o].update([s])
+
+ return model, instances
diff --git a/test/validate/bad-all-values-from.ttl b/test/validate/bad-all-values-from.ttl
new file mode 100644
index 00000000..e8243423
--- /dev/null
+++ b/test/validate/bad-all-values-from.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdfs:label ;
+ owl:allValuesFrom rdf:PlainLiteral
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdfs:label "plain" ,
+ "not plain"^^rdf:XMLLiteral .
+
diff --git a/test/validate/bad-cardinality-high.ttl b/test/validate/bad-cardinality-high.ttl
new file mode 100644
index 00000000..7e1605c3
--- /dev/null
+++ b/test/validate/bad-cardinality-high.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:maxCardinality 1
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
+
diff --git a/test/validate/bad-cardinality-low.ttl b/test/validate/bad-cardinality-low.ttl
new file mode 100644
index 00000000..93dd0051
--- /dev/null
+++ b/test/validate/bad-cardinality-low.ttl
@@ -0,0 +1,16 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:minCardinality 1
+ ] .
+
+eg:s
+ a eg:Thing .
+
diff --git a/test/validate/bad-cardinality.ttl b/test/validate/bad-cardinality.ttl
new file mode 100644
index 00000000..481fe456
--- /dev/null
+++ b/test/validate/bad-cardinality.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:cardinality 3
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
+
diff --git a/test/validate/bad-datatype-property.ttl b/test/validate/bad-datatype-property.ttl
new file mode 100644
index 00000000..a3e993f3
--- /dev/null
+++ b/test/validate/bad-datatype-property.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:value
+ rdfs:label "value" ;
+ a owl:DatatypeProperty .
+
+eg:Thing
+ a rdfs:Class .
+
+eg:s1
+ a eg:Thing .
+
+eg:s2
+ eg:value eg:s1 .
+
diff --git a/test/validate/bad-domain.ttl b/test/validate/bad-domain.ttl
new file mode 100644
index 00000000..d36b5652
--- /dev/null
+++ b/test/validate/bad-domain.ttl
@@ -0,0 +1,19 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class .
+
+eg:NonThing
+ a rdfs:Class .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:domain eg:Thing .
+
+eg:nonthing
+ a eg:NonThing ;
+ eg:value 42 .
+
diff --git a/test/validate/bad-functional-property.ttl b/test/validate/bad-functional-property.ttl
new file mode 100644
index 00000000..53a73ccd
--- /dev/null
+++ b/test/validate/bad-functional-property.ttl
@@ -0,0 +1,13 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:identity
+ rdfs:label "identity" ;
+ a owl:FunctionalProperty .
+
+eg:s
+ eg:identity "me" ,
+ "you" .
+
diff --git a/test/validate/bad-inverse-functional-property.ttl b/test/validate/bad-inverse-functional-property.ttl
new file mode 100644
index 00000000..95c0aaea
--- /dev/null
+++ b/test/validate/bad-inverse-functional-property.ttl
@@ -0,0 +1,15 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:identity
+ rdfs:label "identity" ;
+ a owl:InverseFunctionalProperty .
+
+eg:s1
+ eg:identity "me" .
+
+eg:s2
+ eg:identity "me" .
+
diff --git a/test/validate/bad-literal-pattern.ttl b/test/validate/bad-literal-pattern.ttl
new file mode 100644
index 00000000..40f9eec0
--- /dev/null
+++ b/test/validate/bad-literal-pattern.ttl
@@ -0,0 +1,7 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+eg:s
+ rdf:value "no"^^xsd:boolean .
+
diff --git a/test/validate/bad-literal-value-high-exclusive.ttl b/test/validate/bad-literal-value-high-exclusive.ttl
new file mode 100644
index 00000000..f83d2216
--- /dev/null
+++ b/test/validate/bad-literal-value-high-exclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive 1.0
+ ] [
+ xsd:minExclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.0"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-high-inclusive.ttl b/test/validate/bad-literal-value-high-inclusive.ttl
new file mode 100644
index 00000000..c0753250
--- /dev/null
+++ b/test/validate/bad-literal-value-high-inclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ] [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.1"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-low-exclusive.ttl b/test/validate/bad-literal-value-low-exclusive.ttl
new file mode 100644
index 00000000..09ca9f93
--- /dev/null
+++ b/test/validate/bad-literal-value-low-exclusive.ttl
@@ -0,0 +1,20 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive 1.0
+ ] [
+ xsd:minExclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "0.0"^^eg:Normal .
diff --git a/test/validate/bad-literal-value-low-inclusive.ttl b/test/validate/bad-literal-value-low-inclusive.ttl
new file mode 100644
index 00000000..6ae5758b
--- /dev/null
+++ b/test/validate/bad-literal-value-low-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ] [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "-0.1"^^eg:Normal .
+
diff --git a/test/validate/bad-object-property.ttl b/test/validate/bad-object-property.ttl
new file mode 100644
index 00000000..b4a31f9d
--- /dev/null
+++ b/test/validate/bad-object-property.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:value
+ rdfs:label "value" ;
+ a owl:ObjectProperty .
+
+eg:s
+ eg:value "literal" .
+
diff --git a/test/validate/bad-pattern.ttl b/test/validate/bad-pattern.ttl
new file mode 100644
index 00000000..1b764c78
--- /dev/null
+++ b/test/validate/bad-pattern.ttl
@@ -0,0 +1,23 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:BrokenLiteral
+ a rdfs:Datatype ;
+ rdfs:label "broken literal" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "["
+ ]
+ ) .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:BinaryLiteral .
+
+eg:s
+ eg:value "no match"^^eg:BrokenLiteral .
+
diff --git a/test/validate/bad-plain-literal.ttl b/test/validate/bad-plain-literal.ttl
new file mode 100644
index 00000000..116faac0
--- /dev/null
+++ b/test/validate/bad-plain-literal.ttl
@@ -0,0 +1,12 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range rdf:PlainLiteral .
+
+eg:s
+ eg:value "literal"^^rdf:XMLLiteral .
+
diff --git a/test/validate/bad-range-instance-not-literal.ttl b/test/validate/bad-range-instance-not-literal.ttl
new file mode 100644
index 00000000..ea7803f6
--- /dev/null
+++ b/test/validate/bad-range-instance-not-literal.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range rdfs:Literal .
+
+eg:thing
+ a eg:Thing .
+
+eg:s
+ eg:value eg:thing .
+
diff --git a/test/validate/bad-range-instance.ttl b/test/validate/bad-range-instance.ttl
new file mode 100644
index 00000000..a04a5476
--- /dev/null
+++ b/test/validate/bad-range-instance.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class .
+
+eg:NonThing
+ a rdfs:Class .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:Thing .
+
+eg:nonthing
+ a eg:NonThing .
+
+eg:s
+ eg:value eg:nonthing .
+
diff --git a/test/validate/bad-range-literal-not-instance.ttl b/test/validate/bad-range-literal-not-instance.ttl
new file mode 100644
index 00000000..f46de8ce
--- /dev/null
+++ b/test/validate/bad-range-literal-not-instance.ttl
@@ -0,0 +1,15 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:Thing
+ a rdfs:Class .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:Thing .
+
+eg:s
+ eg:value "literal" .
+
diff --git a/test/validate/bad-range-literal.ttl b/test/validate/bad-range-literal.ttl
new file mode 100644
index 00000000..10750391
--- /dev/null
+++ b/test/validate/bad-range-literal.ttl
@@ -0,0 +1,27 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive 1.0
+ ] [
+ xsd:minExclusive 0.0
+ ]
+ ) .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:Normal .
+
+eg:s
+ eg:value 2.0 .
+
+
diff --git a/test/validate/bad-some-values-from.ttl b/test/validate/bad-some-values-from.ttl
new file mode 100644
index 00000000..259bfb88
--- /dev/null
+++ b/test/validate/bad-some-values-from.ttl
@@ -0,0 +1,18 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdfs:label ;
+ owl:someValuesFrom rdf:PlainLiteral
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdfs:label "not plain"^^rdf:XMLLiteral .
+
+
diff --git a/test/validate/bad-string-literal-value-high.ttl b/test/validate/bad-string-literal-value-high.ttl
new file mode 100644
index 00000000..6622c35b
--- /dev/null
+++ b/test/validate/bad-string-literal-value-high.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:startsWithC
+ a rdfs:Datatype ;
+ rdfs:label "starts with C" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxExclusive "D"
+ ] [
+ xsd:minInclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Door"^^eg:startsWithC .
+
diff --git a/test/validate/bad-string-literal-value-low.ttl b/test/validate/bad-string-literal-value-low.ttl
new file mode 100644
index 00000000..06833a46
--- /dev/null
+++ b/test/validate/bad-string-literal-value-low.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:betweenBAndD
+ a rdfs:Datatype ;
+ rdfs:label "between B and D" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive "D"
+ ] [
+ xsd:minExclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Aardvark"^^eg:betweenBAndD .
+
diff --git a/test/validate/bad-unknown-datatype.ttl b/test/validate/bad-unknown-datatype.ttl
new file mode 100644
index 00000000..ed879414
--- /dev/null
+++ b/test/validate/bad-unknown-datatype.ttl
@@ -0,0 +1,7 @@
+@prefix eg: <http://example.org/> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:s
+ rdfs:label "bad datatype"^^rdf:UndefinedLiteral .
+
diff --git a/test/validate/bad-unknown-property.ttl b/test/validate/bad-unknown-property.ttl
new file mode 100644
index 00000000..0db1e85c
--- /dev/null
+++ b/test/validate/bad-unknown-property.ttl
@@ -0,0 +1,6 @@
+@prefix eg: <http://example.org/> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+eg:s
+ eg:undefined 0 .
+
diff --git a/test/validate/good-cardinality.ttl b/test/validate/good-cardinality.ttl
new file mode 100644
index 00000000..6b0b87da
--- /dev/null
+++ b/test/validate/good-cardinality.ttl
@@ -0,0 +1,17 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdf:value ;
+ owl:cardinality 2
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdf:value 1 ,
+ 2 .
diff --git a/test/validate/good-literal-value-high-inclusive.ttl b/test/validate/good-literal-value-high-inclusive.ttl
new file mode 100644
index 00000000..bbaa84a2
--- /dev/null
+++ b/test/validate/good-literal-value-high-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ]
+ [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "1.0"^^eg:Normal .
diff --git a/test/validate/good-literal-value-low-inclusive.ttl b/test/validate/good-literal-value-low-inclusive.ttl
new file mode 100644
index 00000000..61943a36
--- /dev/null
+++ b/test/validate/good-literal-value-low-inclusive.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Normal
+ a rdfs:Datatype ;
+ rdfs:label "normal" ;
+ owl:onDatatype xsd:double ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive 1.0
+ ]
+ [
+ xsd:minInclusive 0.0
+ ]
+ ) .
+
+eg:s
+ rdf:value "0.0"^^eg:Normal .
diff --git a/test/validate/good-pattern.ttl b/test/validate/good-pattern.ttl
new file mode 100644
index 00000000..569cd424
--- /dev/null
+++ b/test/validate/good-pattern.ttl
@@ -0,0 +1,22 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:CapitalLiteral
+ a rdfs:Datatype ;
+ rdfs:label "capital literal" ;
+ owl:withRestrictions (
+ [
+ xsd:pattern "[A-Z][a-z]*"
+ ]
+ ) .
+
+eg:value
+ a rdf:Property ;
+ rdfs:label "value" ;
+ rdfs:range eg:CapitalLiteral .
+
+eg:s
+ eg:value "Uppercase"^^eg:CapitalLiteral .
diff --git a/test/validate/good-some-values-from.ttl b/test/validate/good-some-values-from.ttl
new file mode 100644
index 00000000..1da49270
--- /dev/null
+++ b/test/validate/good-some-values-from.ttl
@@ -0,0 +1,17 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:Thing
+ a rdfs:Class ;
+ rdfs:subClassOf [
+ a owl:Restriction ;
+ owl:onProperty rdfs:label ;
+ owl:someValuesFrom rdf:PlainLiteral
+ ] .
+
+eg:s
+ a eg:Thing ;
+ rdfs:label "not plain"^^rdf:XMLLiteral ,
+ "plain" .
diff --git a/test/validate/good-string-literal-value-low.ttl b/test/validate/good-string-literal-value-low.ttl
new file mode 100644
index 00000000..5bfd6a9e
--- /dev/null
+++ b/test/validate/good-string-literal-value-low.ttl
@@ -0,0 +1,21 @@
+@prefix eg: <http://example.org/> .
+@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#> .
+
+eg:betweenBAndD
+ a rdfs:Datatype ;
+ rdfs:label "between B and D" ;
+ owl:onDatatype xsd:string ;
+ owl:withRestrictions (
+ [
+ xsd:maxInclusive "D"
+ ]
+ [
+ xsd:minExclusive "B"
+ ]
+ ) .
+
+eg:s
+ rdf:value "Cat"^^eg:betweenBAndD .
diff --git a/test/validate/manifest.ttl b/test/validate/manifest.ttl
new file mode 100644
index 00000000..68853073
--- /dev/null
+++ b/test/validate/manifest.ttl
@@ -0,0 +1,205 @@
+@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix rdft: <http://www.w3.org/ns/rdftest#> .
+@prefix serd: <http://drobilla.net/ns/serd#> .
+
+serd:TestTurtleNegativeValidate
+ a rdfs:Class ;
+ rdfs:subClassOf rdft:Test .
+
+serd:TestTurtlePositiveValidate
+ a rdfs:Class ;
+ rdfs:subClassOf rdft:Test .
+
+<>
+ rdf:type mf:Manifest ;
+ rdfs:comment "Serd validation test cases" ;
+ mf:entries (
+ <#bad-all-values-from>
+ <#bad-cardinality-high>
+ <#bad-cardinality-low>
+ <#bad-cardinality>
+ <#bad-datatype-property>
+ <#bad-domain>
+ <#bad-functional-property>
+ <#bad-inverse-functional-property>
+ <#bad-literal-pattern>
+ <#bad-literal-value-high-inclusive>
+ <#bad-literal-value-low-inclusive>
+ <#bad-literal-value-high-exclusive>
+ <#bad-literal-value-low-exclusive>
+ <#bad-string-literal-value-high>
+ <#bad-string-literal-value-low>
+ <#bad-object-property>
+ <#bad-pattern>
+ <#bad-plain-literal>
+ <#bad-range-instance-not-literal>
+ <#bad-range-instance>
+ <#bad-range-literal-not-instance>
+ <#bad-range-literal>
+ <#bad-some-values-from>
+ <#bad-unknown-datatype>
+ <#bad-unknown-property>
+ <#good-cardinality>
+ <#good-literal-value-high-inclusive>
+ <#good-literal-value-low-inclusive>
+ <#good-pattern>
+ <#good-some-values-from>
+ <#good-string-literal-value-low>
+ ) .
+
+<#bad-all-values-from>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-all-values-from" ;
+ mf:action <bad-all-values-from.ttl> .
+
+<#bad-cardinality-low>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-cardinality-low" ;
+ mf:action <bad-cardinality-low.ttl> .
+
+<#bad-cardinality-high>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-cardinality-high" ;
+ mf:action <bad-cardinality-high.ttl> .
+
+<#bad-cardinality>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-cardinality" ;
+ mf:action <bad-cardinality.ttl> .
+
+<#bad-datatype-property>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-datatype-property" ;
+ mf:action <bad-datatype-property.ttl> .
+
+<#bad-domain>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-domain" ;
+ mf:action <bad-domain.ttl> .
+
+<#bad-functional-property>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-functional-property" ;
+ mf:action <bad-functional-property.ttl> .
+
+<#bad-inverse-functional-property>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-inverse-functional-property" ;
+ mf:action <bad-inverse-functional-property.ttl> .
+
+<#bad-literal-pattern>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-literal-pattern" ;
+ mf:action <bad-literal-pattern.ttl> .
+
+<#bad-literal-value-low-inclusive>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-literal-value-low-inclusive" ;
+ mf:action <bad-literal-value-low-inclusive.ttl> .
+
+<#bad-literal-value-high-inclusive>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-literal-value-high-inclusive" ;
+ mf:action <bad-literal-value-high-inclusive.ttl> .
+
+<#bad-literal-value-low-exclusive>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-literal-value-low-exclusive" ;
+ mf:action <bad-literal-value-low-exclusive.ttl> .
+
+<#bad-literal-value-high-exclusive>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-literal-value-high-exclusive" ;
+ mf:action <bad-literal-value-high-exclusive.ttl> .
+
+<#bad-string-literal-value-low>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-string-literal-value-low" ;
+ mf:action <bad-string-literal-value-low.ttl> .
+
+<#bad-string-literal-value-high>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-string-literal-value-high" ;
+ mf:action <bad-string-literal-value-high.ttl> .
+
+<#bad-object-property>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-object-property" ;
+ mf:action <bad-object-property.ttl> .
+
+<#bad-pattern>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-pattern" ;
+ mf:action <bad-pattern.ttl> .
+
+<#bad-plain-literal>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-plain-literal" ;
+ mf:action <bad-plain-literal.ttl> .
+
+<#bad-range-instance-not-literal>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-range-instance-not-literal" ;
+ mf:action <bad-range-instance-not-literal.ttl> .
+
+<#bad-range-instance>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-range-instance" ;
+ mf:action <bad-range-instance.ttl> .
+
+<#bad-range-literal-not-instance>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-range-literal-not-instance" ;
+ mf:action <bad-range-literal-not-instance.ttl> .
+
+<#bad-range-literal>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-range-literal" ;
+ mf:action <bad-range-literal.ttl> .
+
+<#bad-some-values-from>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-some-values-from" ;
+ mf:action <bad-some-values-from.ttl> .
+
+<#bad-unknown-datatype>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-unknown-datatype" ;
+ mf:action <bad-unknown-datatype.ttl> .
+
+<#bad-unknown-property>
+ rdf:type serd:TestTurtleNegativeValidate ;
+ mf:name "bad-unknown-property" ;
+ mf:action <bad-unknown-property.ttl> .
+
+<#good-cardinality>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-cardinality" ;
+ mf:action <good-cardinality.ttl> .
+
+<#good-literal-value-low-inclusive>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-literal-value-low-inclusive" ;
+ mf:action <good-literal-value-low-inclusive.ttl> .
+
+<#good-literal-value-high-inclusive>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-literal-value-high-inclusive" ;
+ mf:action <good-literal-value-high-inclusive.ttl> .
+
+<#good-some-values-from>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-some-values-from" ;
+ mf:action <good-some-values-from.ttl> .
+
+<#good-pattern>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-pattern" ;
+ mf:action <good-pattern.ttl> .
+
+<#good-string-literal-value-low>
+ rdf:type serd:TestTurtlePositiveValidate ;
+ mf:name "good-string-literal-value-low" ;
+ mf:action <good-string-literal-value-low.ttl> .