summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/annotation-vocab/tools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/tests/annotation-vocab/tools
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/annotation-vocab/tools')
-rw-r--r--testing/web-platform/tests/annotation-vocab/tools/README.md15
-rw-r--r--testing/web-platform/tests/annotation-vocab/tools/anno.jsonld128
-rw-r--r--testing/web-platform/tests/annotation-vocab/tools/vocab_tester.py240
3 files changed, 383 insertions, 0 deletions
diff --git a/testing/web-platform/tests/annotation-vocab/tools/README.md b/testing/web-platform/tests/annotation-vocab/tools/README.md
new file mode 100644
index 0000000000..1bc74ec23d
--- /dev/null
+++ b/testing/web-platform/tests/annotation-vocab/tools/README.md
@@ -0,0 +1,15 @@
+Vocabulary Exercising Tools
+===========================
+
+The Web Annotation Vocabulary specification defines an ontology for
+portable annotations. The tools in this directory, along with the
+sample files supplied, can be used to exercise the vocabulary
+"implementation" against various RDF processing engines.
+
+vocab-tester.py
+===============
+
+This python script exercises the vocabulary implementation using rdflib,
+rdflib-jsonld and pyld. Note that this means your python environment must
+have those additional python modules installed in order to run the tests.
+
diff --git a/testing/web-platform/tests/annotation-vocab/tools/anno.jsonld b/testing/web-platform/tests/annotation-vocab/tools/anno.jsonld
new file mode 100644
index 0000000000..58892919c0
--- /dev/null
+++ b/testing/web-platform/tests/annotation-vocab/tools/anno.jsonld
@@ -0,0 +1,128 @@
+{
+ "@context": {
+ "oa": "http://www.w3.org/ns/oa#",
+ "dc": "http://purl.org/dc/elements/1.1/",
+ "dcterms": "http://purl.org/dc/terms/",
+ "dctypes": "http://purl.org/dc/dcmitype/",
+ "foaf": "http://xmlns.com/foaf/0.1/",
+ "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
+ "skos": "http://www.w3.org/2004/02/skos/core#",
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
+ "iana": "http://www.iana.org/assignments/relation/",
+ "owl": "http://www.w3.org/2002/07/owl#",
+ "as": "http://www.w3.org/ns/activitystreams#",
+ "schema": "http://schema.org/",
+
+ "id": {"@type": "@id", "@id": "@id"},
+ "type": {"@type": "@id", "@id": "@type"},
+
+ "Annotation": "oa:Annotation",
+ "Dataset": "dctypes:Dataset",
+ "Image": "dctypes:StillImage",
+ "Video": "dctypes:MovingImage",
+ "Audio": "dctypes:Sound",
+ "Text": "dctypes:Text",
+ "TextualBody": "oa:TextualBody",
+ "ResourceSelection": "oa:ResourceSelection",
+ "SpecificResource": "oa:SpecificResource",
+ "FragmentSelector": "oa:FragmentSelector",
+ "CssSelector": "oa:CssSelector",
+ "XPathSelector": "oa:XPathSelector",
+ "TextQuoteSelector": "oa:TextQuoteSelector",
+ "TextPositionSelector": "oa:TextPositionSelector",
+ "DataPositionSelector": "oa:DataPositionSelector",
+ "SvgSelector": "oa:SvgSelector",
+ "RangeSelector": "oa:RangeSelector",
+ "TimeState": "oa:TimeState",
+ "HttpRequestState": "oa:HttpRequestState",
+ "CssStylesheet": "oa:CssStyle",
+ "Choice": "oa:Choice",
+ "Composite": "oa:Composite",
+ "List": "oa:List",
+ "Independents": "oa:Independents",
+ "Person": "foaf:Person",
+ "Software": "as:Application",
+ "Organization": "foaf:Organization",
+ "AnnotationCollection": "as:OrderedCollection",
+ "AnnotationPage": "as:OrderedCollectionPage",
+ "Audience": "schema:Audience",
+
+ "Motivation": "oa:Motivation",
+ "bookmarking": "oa:bookmarking",
+ "classifying": "oa:classifying",
+ "commenting": "oa:commenting",
+ "describing": "oa:describing",
+ "editing": "oa:editing",
+ "highlighting": "oa:highlighting",
+ "identifying": "oa:identifying",
+ "linking": "oa:linking",
+ "moderating": "oa:moderating",
+ "questioning": "oa:questioning",
+ "replying": "oa:replying",
+ "reviewing": "oa:reviewing",
+ "tagging": "oa:tagging",
+
+ "auto": "oa:autoDirection",
+ "ltr": "oa:ltrDirection",
+ "rtl": "oa:rtlDirection",
+
+ "body": {"@type": "@id", "@id": "oa:hasBody"},
+ "target": {"@type": "@id", "@id": "oa:hasTarget"},
+ "source": {"@type": "@id", "@id": "oa:hasSource"},
+ "selector": {"@type": "@id", "@id": "oa:hasSelector"},
+ "state": {"@type": "@id", "@id": "oa:hasState"},
+ "scope": {"@type": "@id", "@id": "oa:hasScope"},
+ "refinedBy": {"@type": "@id", "@id": "oa:refinedBy"},
+ "startSelector": {"@type": "@id", "@id": "oa:hasStartSelector"},
+ "endSelector": {"@type": "@id", "@id": "oa:hasEndSelector"},
+ "renderedVia": {"@type": "@id", "@id": "oa:renderedVia"},
+ "creator": {"@type": "@id", "@id": "dcterms:creator"},
+ "generator": {"@type": "@id", "@id": "as:generator"},
+ "rights": {"@type": "@id", "@id": "dcterms:rights"},
+ "homepage": {"@type": "@id", "@id": "foaf:homepage"},
+ "via": {"@type": "@id", "@id": "oa:via"},
+ "canonical": {"@type": "@id", "@id": "oa:canonical"},
+ "stylesheet": {"@type": "@id", "@id": "oa:styledBy"},
+ "cached": {"@type": "@id", "@id": "oa:cachedSource"},
+ "conformsTo": {"@type": "@id", "@id": "dcterms:conformsTo"},
+ "items": {"@type": "@id", "@id": "as:items", "@container": "@list"},
+ "partOf": {"@type": "@id", "@id": "as:partOf"},
+ "first": {"@type": "@id", "@id": "as:first"},
+ "last": {"@type": "@id", "@id": "as:last"},
+ "next": {"@type": "@id", "@id": "as:next"},
+ "prev": {"@type": "@id", "@id": "as:prev"},
+ "audience": {"@type": "@id", "@id": "schema:audience"},
+ "motivation": {"@type": "@vocab", "@id": "oa:motivatedBy"},
+ "purpose": {"@type": "@vocab", "@id": "oa:hasPurpose"},
+ "textDirection": {"@type": "@vocab", "@id": "oa:textDirection"},
+
+ "accessibility": "schema:accessibilityFeature",
+ "bodyValue": "oa:bodyValue",
+ "format": "dc:format",
+ "language": "dc:language",
+ "processingLanguage": "oa:processingLanguage",
+ "value": "rdf:value",
+ "exact": "oa:exact",
+ "prefix": "oa:prefix",
+ "suffix": "oa:suffix",
+ "styleClass": "oa:styleClass",
+ "name": "foaf:name",
+ "email": "foaf:mbox",
+ "email_sha1": "foaf:mbox_sha1sum",
+ "nickname": "foaf:nick",
+ "label": "rdfs:label",
+
+ "created": {"@id": "dcterms:created", "@type": "xsd:dateTime"},
+ "modified": {"@id": "dcterms:modified", "@type": "xsd:dateTime"},
+ "generated": {"@id": "dcterms:issued", "@type": "xsd:dateTime"},
+ "sourceDate": {"@id": "oa:sourceDate", "@type": "xsd:dateTime"},
+ "sourceDateStart": {"@id": "oa:sourceDateStart", "@type": "xsd:dateTime"},
+ "sourceDateEnd": {"@id": "oa:sourceDateEnd", "@type": "xsd:dateTime"},
+
+ "start": {"@id": "oa:start", "@type": "xsd:nonNegativeInteger"},
+ "end": {"@id": "oa:end", "@type": "xsd:nonNegativeInteger"},
+ "total": {"@id": "as:totalItems", "@type": "xsd:nonNegativeInteger"},
+ "startIndex": {"@id": "as:startIndex", "@type": "xsd:nonNegativeInteger"}
+ }
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/annotation-vocab/tools/vocab_tester.py b/testing/web-platform/tests/annotation-vocab/tools/vocab_tester.py
new file mode 100644
index 0000000000..86453a7a29
--- /dev/null
+++ b/testing/web-platform/tests/annotation-vocab/tools/vocab_tester.py
@@ -0,0 +1,240 @@
+
+# Author: Rob Sanderson (azaroth42@gmail.com)
+# License: Apache2
+# Last Modified: 2016-09-02
+
+import json
+from rdflib import ConjunctiveGraph, URIRef
+from pyld import jsonld
+from pyld.jsonld import compact, expand, frame, from_rdf, to_rdf, JsonLdProcessor
+import urllib
+
+# Stop code from looking up the contexts online for every operation
+docCache = {}
+
+def fetch(url):
+ fh = urllib.urlopen(url)
+ data = fh.read()
+ fh.close()
+ return data
+
+def load_document_and_cache(url):
+ if docCache.has_key(url):
+ return docCache[url]
+
+ doc = {
+ 'contextUrl': None,
+ 'documentUrl': None,
+ 'document': ''
+ }
+ data = fetch(url)
+ doc['document'] = data;
+ docCache[url] = doc
+ return doc
+
+jsonld.set_document_loader(load_document_and_cache)
+
+class Validator(object):
+
+ def __init__(self):
+
+ self.rdflib_class_map = {
+ "Annotation": "oa:Annotation",
+ "Dataset": "dctypes:Dataset",
+ "Image": "dctypes:StillImage",
+ "Video": "dctypes:MovingImage",
+ "Audio": "dctypes:Sound",
+ "Text": "dctypes:Text",
+ "TextualBody": "oa:TextualBody",
+ "ResourceSelection": "oa:ResourceSelection",
+ "SpecificResource": "oa:SpecificResource",
+ "FragmentSelector": "oa:FragmentSelector",
+ "CssSelector": "oa:CssSelector",
+ "XPathSelector": "oa:XPathSelector",
+ "TextQuoteSelector": "oa:TextQuoteSelector",
+ "TextPositionSelector": "oa:TextPositionSelector",
+ "DataPositionSelector": "oa:DataPositionSelector",
+ "SvgSelector": "oa:SvgSelector",
+ "RangeSelector": "oa:RangeSelector",
+ "TimeState": "oa:TimeState",
+ "HttpState": "oa:HttpRequestState",
+ "CssStylesheet": "oa:CssStyle",
+ "Choice": "oa:Choice",
+ "Composite": "oa:Composite",
+ "List": "oa:List",
+ "Independents": "oa:Independents",
+ "Person": "foaf:Person",
+ "Software": "as:Application",
+ "Organization": "foaf:Organization",
+ "AnnotationCollection": "as:OrderedCollection",
+ "AnnotationPage": "as:OrderedCollectionPage",
+ "Audience": "schema:Audience"
+ }
+
+
+ def _clean_bnode_ids(self, js):
+ new = {}
+ for (k,v) in js.items():
+ if k == 'id' and v.startswith("_:"):
+ continue
+ elif type(v) == dict:
+ # recurse
+ res = self._clean_bnode_ids(v)
+ new[k] = res
+ else:
+ new[k] = v
+ return new
+
+ def _mk_rdflib_jsonld(self, js):
+ # rdflib's json-ld implementation sucks
+ # Pre-process to make it work
+ # recurse the structure looking for types, and replacing them.
+ new = {}
+ for (k,v) in js.items():
+ if k == 'type':
+ if type(v) == list:
+ nl = []
+ for i in v:
+ if self.rdflib_class_map.has_key(i):
+ nl.append(self.rdflib_class_map[i])
+ new['type'] = nl
+ else:
+ if self.rdflib_class_map.has_key(v):
+ new['type'] = self.rdflib_class_map[v]
+ elif type(v) == dict:
+ # recurse
+ res = self._mk_rdflib_jsonld(v)
+ new[k] = res
+ else:
+ new[k] = v
+ return new
+
+ def json_to_rdf(self, js, fmt=None):
+ d2 = self._mk_rdflib_jsonld(js)
+ js = json.dumps(d2)
+ g = ConjunctiveGraph()
+ g.parse(data=js, format='json-ld')
+ if fmt:
+ out = g.serialize(format=fmt)
+ return out
+ else:
+ return g
+
+ def rdf_to_jsonld(self, rdf, fmt):
+
+ g = ConjunctiveGraph()
+ g.parse(data=rdf, format=fmt)
+ out = g.serialize(format='json-ld')
+
+ j2 = json.loads(out)
+ j2 = {"@context": context_js, "@graph": j2}
+ framed = frame(j2, frame_js)
+ out = compact(framed, context_js)
+ # recursively clean blank node ids
+ #out = self._clean_bnode_ids(out)
+ return out
+
+ def compact_and_clean(self, js):
+ newjs = compact(js, context_js)
+ newjs['@context'] = context
+ if newjs.has_key("@graph"):
+ for k,v in newjs['@graph'].items():
+ newjs[k] = v
+ del newjs['@graph']
+ return newjs
+
+validator = Validator()
+
+example = "https://raw.githubusercontent.com/w3c/web-annotation/gh-pages/model/wd2/examples/correct/anno4.json"
+example_ttl = "https://raw.githubusercontent.com/w3c/web-annotation/gh-pages/vocab/wd/examples/correct/anno1.ttl"
+context = "http://www.w3.org/ns/anno.jsonld"
+frameURI = "https://raw.githubusercontent.com/w3c/web-annotation/gh-pages/jsonld/annotation_frame.jsonld"
+# ontology = "https://www.w3.org/ns/oa.ttl"
+ontology = "https://raw.githubusercontent.com/w3c/web-annotation/gh-pages/vocab/wd/ontology/oa.ttl"
+
+data = fetch(context)
+context_js = json.loads(data)
+data = fetch(example)
+example_js = json.loads(data)
+data = fetch(frameURI)
+frame_js = json.loads(data)
+
+# Test1: JSON-LD context document can be parsed without errors by JSON-LD validators
+# Context document is parsable if it can be loaded and used to expand the example
+try:
+ expanded = expand(example_js, context_js)
+except:
+ print("Context is invalid, failed Test 1")
+
+
+# Test2: JSON-LD context document can be used to convert JSON-LD serialized Annotations into RDF triples.
+try:
+ jsonld_nq = to_rdf(example_js, {"base": "http://example.org/", "format": "application/nquads"})
+except:
+ print("Cannot use context to convert JSON-LD to NQuads")
+
+
+# Test3: Graphs produced are isomorphic
+try:
+ rl_g = validator.json_to_rdf(example_js)
+ g = ConjunctiveGraph()
+ js_g = g.parse(data=jsonld_nq, format="nt")
+ rl_g_nq = rl_g.serialize(format="nquads")
+ assert(len(rl_g.store) == len(js_g.store))
+ assert(rl_g.isomorphic(js_g))
+except:
+ print("Different triples from two parsers, or non-isomorphic graphs")
+
+
+# Test4: The graphs produced can be converted back into JSON-LD without loss of information
+try:
+ js = validator.rdf_to_jsonld(jsonld_nq, "nt")
+ js2 = validator.compact_and_clean(js)
+ assert(js2 == example_js)
+except:
+ print("Failed to recompact parsed data")
+ raise
+
+
+# Test5: ontology documents can be parsed without errors by validators
+try:
+ g = ConjunctiveGraph().parse(ontology, format="turtle")
+except:
+ raise
+
+
+# Test6: ontology is internally consistent with respect to domains, ranges, etc
+
+# step 1: find all the classes.
+rdftype = URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
+rdfsdomain = URIRef("http://www.w3.org/2000/01/rdf-schema#domain")
+rdfsrange = URIRef("http://www.w3.org/2000/01/rdf-schema#range")
+rdfsresource = URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#Resource")
+rdfssco = URIRef("http://www.w3.org/2000/01/rdf-schema#subClassOf")
+asColl = URIRef("http://www.w3.org/ns/activitystreams#OrderedCollection")
+skosConcept = URIRef("http://www.w3.org/2004/02/skos/core#Concept")
+
+otherClasses = [asColl, skosConcept]
+classes = list(g.subjects(rdftype, URIRef("http://www.w3.org/2000/01/rdf-schema#Class")))
+props = list(g.subjects(rdftype, URIRef("http://www.w3.org/1999/02/22-rdf-syntax-ns#Property")))
+
+for p in props:
+ domains = list(g.objects(p, rdfsdomain))
+ for d in domains:
+ assert(d in classes)
+
+for p in props:
+ ranges = list(g.objects(p, rdfsrange))
+ for r in ranges:
+ if not r in classes and not str(r).startswith("http://www.w3.org/2001/XMLSchema#") and \
+ not r == rdfsresource:
+ print("Found inconsistent property: %s has unknown range" % p)
+
+for c in classes:
+ parents = list(g.objects(c, rdfssco))
+ for p in parents:
+ if not p in classes and not p in otherClasses:
+ print("Found inconsistent class: %s has unknown superClass" % c)
+
+
+print("Done.")