""" Part of the tagit test suite. A copy of the license is provided with the project. Author: Matthias Baumgartner, 2022 """ # imports import unittest # bsfs imports from bsfs.namespace import ns from bsfs.schema import Schema, types from bsfs.utils import errors, URI # objects to test from bsfs.schema.serialize import from_string, to_string ## code ## class TestFromString(unittest.TestCase): def test_empty(self): # schema contains at least the root types self.assertEqual(from_string(''), Schema()) def test_circular_dependency(self): # must not have circular dependencies self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix bsfs: bsfs:Entity rdfs:subClassOf bsfs:Node . # ah, a nice circular dependency bsfs:Entity rdfs:subClassOf bsfs:Document . bsfs:Document rdfs:subClassOf bsfs:Entity . bsfs:PDF rdfs:subClassOf bsfs:Document . ''') def test_node(self): # all nodes must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: xsd:string rdfs:subClassOf bsfs:Literal . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''') # node definitions must be consistent (cannot re-use a node uri) self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Document rdfs:subClassOf bsfs:Node . bsfs:Document rdfs:subClassOf bsfs:Entity . # conflicting parent ''') # additional nodes can be defined n_unused = types.ROOT_NODE.child(ns.bsfs.unused) self.assertEqual(Schema({}, {n_unused}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:unused rdfs:subClassOf bsfs:Node . # unused symbol ''')) # a node can have multiple children n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) n_tag = types.ROOT_NODE.child(ns.bsfs.Tag) n_doc = n_ent.child(ns.bsfs.Document) n_image = n_ent.child(ns.bsfs.Image) self.assertEqual(Schema({}, {n_ent, n_tag, n_doc, n_image}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: # nodes inherit from same parent bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Tag rdfs:subClassOf bsfs:Node . # nodes inherit from same parent bsfs:Document rdfs:subClassOf bsfs:Entity . bsfs:Image rdfs:subClassOf bsfs:Entity . ''')) # additional nodes can be defined and used n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_filename = types.ROOT_PREDICATE.child(ns.bse.filename, n_ent, l_string, False) self.assertEqual(Schema({p_filename}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''')) # nodes can have annotations self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: bsfs:Entity rdfs:subClassOf bsfs:Node . ''').node(ns.bsfs.Entity).annotations, {}) self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: bsfs:Entity rdfs:subClassOf bsfs:Node ; rdfs:label "hello world"^^xsd:string ; bsfs:foo "1234"^^xsd:integer . ''').node(ns.bsfs.Entity).annotations, { ns.rdfs.label: 'hello world', ns.bsfs.foo: 1234, }) def test_literal(self): # all literals must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; # undefined symbol bsfs:unique "false"^^xsd:boolean . ''') # literal definitions must be consistent (cannot re-use a literal uri) self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: xsd:string rdfs:subClassOf bsfs:Literal . xsd:name rdfs:subClassOf bsfs:Literal . xsd:name rdfs:subClassOf xsd:string . # conflicting parent ''') # additional literals can be defined l_unused = types.ROOT_LITERAL.child(ns.xsd.unused) self.assertEqual(Schema({}, {}, {l_unused}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: xsd:unused rdfs:subClassOf bsfs:Literal . # unused symbol ''')) # a literal can have multiple children l_string = types.ROOT_LITERAL.child(ns.xsd.string) l_integer = types.ROOT_LITERAL.child(ns.xsd.integer) l_unsigned = l_integer.child(ns.xsd.unsigned) l_signed = l_integer.child(ns.xsd.signed) self.assertEqual(Schema({}, {}, {l_string, l_integer, l_unsigned, l_signed}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: # literals inherit from same parent xsd:string rdfs:subClassOf bsfs:Literal . xsd:integer rdfs:subClassOf bsfs:Literal . # literals inherit from same parent xsd:unsigned rdfs:subClassOf xsd:integer . xsd:signed rdfs:subClassOf xsd:integer . ''')) # additional literals can be defined and used n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_filename = types.ROOT_PREDICATE.child(ns.bse.filename, n_ent, l_string, False) self.assertEqual(Schema({p_filename}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''')) # literals can have annotations self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: xsd:string rdfs:subClassOf bsfs:Literal . ''').literal(ns.xsd.string).annotations, {}) self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: xsd:string rdfs:subClassOf bsfs:Literal ; rdfs:label "hello world"^^xsd:string ; bsfs:foo "1234"^^xsd:integer . ''').literal(ns.xsd.string).annotations, { ns.rdfs.label: 'hello world', ns.bsfs.foo: 1234, }) def test_predicate(self): # domain must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: xsd:string rdfs:subClassOf bsfs:Literal . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; # undefined symbol rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''') # domain cannot be a literal self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Literal . xsd:string rdfs:subClassOf bsfs:Literal . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; # literal instead of node rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''') # range must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; # undefined symbol bsfs:unique "false"^^xsd:boolean . ''') # range must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:Foo ; # undefined symbol bsfs:unique "false"^^xsd:boolean . ''') # range must be a node or a literal self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:filename rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:Predicate ; # invalid symbol bsfs:unique "false"^^xsd:boolean . ''') # additional predicates can be defined n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_comment = types.ROOT_PREDICATE.child(ns.bse.comment, domain=n_ent, range=l_string, unique=False) self.assertEqual(Schema({p_comment}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''')) # predicates inherit properties from parents n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_annotation = types.ROOT_PREDICATE.child(ns.bsfs.Annotation, domain=n_ent, range=l_string) p_comment = p_annotation.child(ns.bse.comment, unique=True) self.assertEqual(Schema({p_comment}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bsfs:Annotation rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range xsd:string . bse:comment rdfs:subClassOf bsfs:Annotation ; # inherits domain/range from bsfs:Annotation bsfs:unique "true"^^xsd:boolean . ''')) # we can define partial predicates (w/o specifying a usable range) n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_annotation = types.ROOT_PREDICATE.child(ns.bsfs.Annotation, domain=n_ent) p_comment = p_annotation.child(ns.bse.comment, range=l_string, unique=False) self.assertEqual(Schema({p_comment}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . xsd:string rdfs:subClassOf bsfs:Literal . bsfs:Annotation rdfs:subClassOf bsfs:Predicate ; # derive predicate w/o setting range rdfs:domain bsfs:Entity . bse:comment rdfs:subClassOf bsfs:Annotation ; # derived predicate w/ setting range rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''')) # predicate definition can be split across multiple statements. # statements can be repeated n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) p_foo = types.ROOT_PREDICATE.child(ns.bse.foo, domain=n_ent, range=types.ROOT_NODE, unique=True) self.assertEqual(Schema({p_foo}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Node ; bsfs:unique "true"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity. ''')) # domain must be a subtype of parent's domain n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) n_image = n_ent.child(ns.bsfs.Image) p_foo = types.ROOT_PREDICATE.child(ns.bse.foo, domain=types.ROOT_NODE) p_bar = p_foo.child(ns.bse.bar, domain=n_ent) p_foobar = p_bar.child(ns.bse.foobar, domain=n_image) self.assertEqual(Schema({p_foobar}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Image rdfs:subClassOf bsfs:Entity . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node . bse:bar rdfs:subClassOf bse:foo ; rdfs:domain bsfs:Entity . bse:foobar rdfs:subClassOf bse:bar ; rdfs:domain bsfs:Image . ''')) self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Image rdfs:subClassOf bsfs:Entity . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Image . bse:bar rdfs:subClassOf bse:foo ; rdfs:domain bsfs:Entity . bse:foobar rdfs:subClassOf bse:bar ; rdfs:domain bsfs:Node . ''') # range must be a subtype of parent's range n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) n_image = n_ent.child(ns.bsfs.Image) p_foo = types.ROOT_PREDICATE.child(ns.bse.foo, range=types.ROOT_NODE) p_bar = p_foo.child(ns.bse.bar, range=n_ent) p_foobar = p_bar.child(ns.bse.foobar, range=n_image) self.assertEqual(Schema({p_foobar}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Image rdfs:subClassOf bsfs:Entity . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Node . bse:bar rdfs:subClassOf bse:foo ; rdfs:range bsfs:Entity . bse:foobar rdfs:subClassOf bse:bar ; rdfs:range bsfs:Image . ''')) self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Image rdfs:subClassOf bsfs:Entity . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Image . bse:bar rdfs:subClassOf bse:foo ; rdfs:range bsfs:Entity . bse:foobar rdfs:subClassOf bse:bar ; rdfs:range bsfs:Node . ''') # cannot define the same predicate from multiple parents self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:Annotation rdfs:subClassOf bsfs:Predicate . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Annotation ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . ''') # cannot assign multiple conflicting domains to the same predicate self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity . # conflicting domain ''') # cannot assign multiple conflicting ranges to the same predicate self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Entity . # conflicting range ''') # cannot assign multiple conflicting uniques to the same predicate self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Predicate ; bsfs:unique "true"^^xsd:boolean . # conflicting unique ''') # predicates can have annotations self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Node . ''').predicate(ns.bse.comment).annotations, {}) self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bse:comment rdfs:subClassOf bsfs:Predicate ; rdfs:range bsfs:Node ; rdfs:label "hello world"^^xsd:string ; bsfs:foo "1234"^^xsd:integer . ''').predicate(ns.bse.comment).annotations, { ns.rdfs.label: 'hello world', ns.bsfs.foo: 1234, }) def test_feature(self): # domain must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:array rdfs:subClassOf bsfs:Literal . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; # undefined symbol rdfs:range bsfs:array ; bsfs:unique "false"^^xsd:boolean . ''') # domain cannot be a literal self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Literal . bsfs:array rdfs:subClassOf bsfs:Literal . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; # literal instead of node rdfs:range bsfs:array ; bsfs:unique "false"^^xsd:boolean . ''') # range must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:array ; # undefined symbol bsfs:unique "false"^^xsd:boolean . ''') # range must be defined self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:Foo ; # undefined symbol bsfs:unique "false"^^xsd:boolean . ''') # range must be a node or a literal self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:Predicate ; # invalid symbol bsfs:unique "false"^^xsd:boolean . ''') # additional predicates can be defined n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_array = types.ROOT_LITERAL.child(ns.bsfs.array) p_comment = types.ROOT_FEATURE.child(ns.bse.colors, domain=n_ent, range=l_array, unique=False) self.assertEqual(Schema({p_comment}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:array rdfs:subClassOf bsfs:Literal . bse:colors rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:array ; bsfs:unique "false"^^xsd:boolean . ''')) # features inherit properties from parents n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) l_array = types.ROOT_LITERAL.child(ns.bsfs.array) l_string = types.ROOT_LITERAL.child(ns.xsd.string) p_annotation = types.ROOT_FEATURE.child(ns.bsfs.Annotation, domain=n_ent, range=l_array, dimension=1234, dtype=ns.xsd.string) p_comment = p_annotation.child(ns.bse.colors, unique=True) self.assertEqual(Schema({p_comment}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:array rdfs:subClassOf bsfs:Literal . bsfs:Annotation rdfs:subClassOf bsfs:Feature ; # inherits defaults from bsfs:Feature rdfs:domain bsfs:Entity ; rdfs:range bsfs:array ; bsfs:dimension "1234"^^xsd:integer ; bsfs:dtype xsd:string . bse:colors rdfs:subClassOf bsfs:Annotation ; # inherits domain/range/etc. from bsfs:Annotation bsfs:unique "true"^^xsd:boolean . # overwrites bsfs:Predicate ''')) # feature definition can be split across multiple statements. # statements can be repeated n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) p_foo = types.ROOT_FEATURE.child(ns.bse.foo, domain=n_ent, unique=True, dimension=1234, dtype=ns.bsfs.f32) self.assertEqual(Schema({p_foo}), from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; bsfs:unique "true"^^xsd:boolean ; bsfs:dimension "1234"^^xsd:integer . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; bsfs:dtype bsfs:f32 . ''')) # cannot define the same feature from multiple parents self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Annotation rdfs:subClassOf bsfs:Feature . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Annotation ; rdfs:domain bsfs:Node ; bsfs:unique "false"^^xsd:boolean . ''') # cannot assign multiple conflicting domains to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity . # conflicting domain ''') # cannot assign multiple conflicting ranges to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bsfs:array rdfs:subClassOf bsfs:Literal . bsfs:large_array rdfs:subClassOf bsfs:array . bsfs:small_array rdfs:subClassOf bsfs:array . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; rdfs:range bsfs:large_array ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:range bsfs:small_array . # conflicting range ''') # cannot assign multiple conflicting uniques to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; rdfs:range bsfs:Node ; bsfs:unique "false"^^xsd:boolean . bse:foo rdfs:subClassOf bsfs:Feature ; bsfs:unique "true"^^xsd:boolean . # conflicting unique ''') # cannot assign multiple conflicting dimensions to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; bsfs:dimension "1234"^^xsd:integer . bse:foo rdfs:subClassOf bsfs:Feature ; bsfs:dimension "4321"^^xsd:integer . # conflicting dimension ''') # cannot assign multiple conflicting dtypes to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; bsfs:dtype bsfs:f32 . bse:foo rdfs:subClassOf bsfs:Feature ; bsfs:dtype bsfs:f16 . # conflicting dtype ''') # cannot assign multiple conflicting distance metrics to the same feature self.assertRaises(errors.ConsistencyError, from_string, ''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bsfs:Entity rdfs:subClassOf bsfs:Node . bse:foo rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Node ; bsfs:distance bsfs:euclidean . bse:foo rdfs:subClassOf bsfs:Feature ; bsfs:distance bsfs:cosine . # conflicting distance ''') # features can have annotations self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bse:colors rdfs:subClassOf bsfs:Feature ; bsfs:dimension "1234"^^xsd:integer . ''').predicate(ns.bse.colors).annotations, {}) self.assertDictEqual(from_string(''' prefix rdfs: prefix xsd: prefix bsfs: prefix bse: bsfs:Feature rdfs:subClassOf bsfs:Predicate . bse:colors rdfs:subClassOf bsfs:Feature ; bsfs:dimension "1234"^^xsd:integer ; rdfs:label "hello world"^^xsd:string ; bsfs:foo "1234"^^xsd:integer . ''').predicate(ns.bse.colors).annotations, { ns.rdfs.label: 'hello world', ns.bsfs.foo: 1234, }) def test_integration(self): # nodes n_ent = types.ROOT_NODE.child(ns.bsfs.Entity) n_tag = types.ROOT_NODE.child(ns.bsfs.Tag) n_image = n_ent.child(ns.bsfs.Image) # literals l_string = types.ROOT_LITERAL.child(ns.xsd.string) l_array = types.ROOT_LITERAL.child(ns.bsfs.array) l_integer = types.ROOT_LITERAL.child(ns.xsd.integer) l_boolean = types.ROOT_LITERAL.child(ns.xsd.boolean) # predicates p_annotation = types.ROOT_PREDICATE.child(ns.bsfs.Annotation) p_tag = types.ROOT_PREDICATE.child(ns.bse.tag, domain=n_ent, range=n_tag) p_group = p_tag.child(ns.bse.group, domain=n_image, unique=True) p_comment = p_annotation.child(ns.bse.comment, range=l_string) # features f_colors = types.ROOT_FEATURE.child(URI('http://bsfs.ai/schema/Feature/colors_spatial'), domain=n_ent, range=l_array, unique=True, dtype=ns.bsfs.f16, distance=ns.bsfs.euclidean) f_colors1234 = f_colors.child(URI('http://bsfs.ai/schema/Feature/colors_spatial#1234'), dimension=1024) f_colors4321 = f_colors.child(URI('http://bsfs.ai/schema/Feature/colors_spatial#4321'), dimension=2048) # schema ref = Schema( {p_annotation, p_tag, p_group, p_comment, f_colors, f_colors1234, f_colors4321}, {n_ent, n_tag, n_image}, {l_string, l_integer, l_boolean}) # load from string gen = from_string(''' # generic prefixes prefix rdfs: prefix xsd: # bsfs prefixes prefix bsfs: prefix bse: # nodes bsfs:Entity rdfs:subClassOf bsfs:Node ; rdfs:label "Principal node"^^xsd:string . bsfs:Tag rdfs:subClassOf bsfs:Node ; rdfs:label "Tag"^^xsd:string . bsfs:Image rdfs:subClassOf bsfs:Entity . # literals xsd:string rdfs:subClassOf bsfs:Literal ; rdfs:label "A sequence of characters"^^xsd:string . bsfs:array rdfs:subClassOf bsfs:Literal . xsd:integer rdfs:subClassOf bsfs:Literal . xsd:boolean rdfs:subClassOf bsfs:Literal . # abstract predicates bsfs:Annotation rdfs:subClassOf bsfs:Predicate ; rdfs:label "node annotation"^^xsd:string . bsfs:Feature rdfs:subClassOf bsfs:Predicate . # feature instances rdfs:subClassOf bsfs:Feature ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:array ; bsfs:unique "true"^^xsd:boolean ; bsfs:dtype bsfs:f16 ; bsfs:distance bsfs:euclidean ; # annotations rdfs:label "ColorsSpatial instances. Dimension depends on instance."^^xsd:string ; bsfs:first_arg "1234"^^xsd:integer ; bsfs:second_arg "hello world"^^xsd:string . rdfs:subClassOf ; bsfs:dimension "1024"^^xsd:integer ; rdfs:label "Main colors spatial instance"^^xsd:string . rdfs:subClassOf ; bsfs:dimension "2048"^^xsd:integer . # predicate instances bse:tag rdfs:subClassOf bsfs:Predicate ; rdfs:domain bsfs:Entity ; rdfs:range bsfs:Tag ; bsfs:unique "false"^^xsd:boolean ; # annotations rdfs:label "connect entity to a tag"^^xsd:string . bse:group rdfs:subClassOf bse:tag ; # subtype of another predicate rdfs:domain bsfs:Image ; bsfs:unique "true"^^xsd:boolean . bse:comment rdfs:subClassOf bsfs:Annotation ; # subtype of abstract predicate rdfs:domain bsfs:Node ; rdfs:range xsd:string ; bsfs:unique "false"^^xsd:boolean . ''') # schemas are equal self.assertEqual(ref, gen) # check annotations self.assertDictEqual(gen.node(ns.bsfs.Entity).annotations, {ns.rdfs.label: 'Principal node'}) self.assertDictEqual(gen.node(ns.bsfs.Tag).annotations, {ns.rdfs.label: 'Tag'}) self.assertDictEqual(gen.literal(ns.xsd.string).annotations, {ns.rdfs.label: 'A sequence of characters'}) self.assertDictEqual(gen.predicate(ns.bsfs.Annotation).annotations, {ns.rdfs.label: 'node annotation'}) self.assertDictEqual(gen.predicate(URI('http://bsfs.ai/schema/Feature/colors_spatial')).annotations, { ns.rdfs.label: 'ColorsSpatial instances. Dimension depends on instance.', ns.bsfs.first_arg: 1234, ns.bsfs.second_arg: 'hello world', }) self.assertDictEqual(gen.predicate(URI('http://bsfs.ai/schema/Feature/colors_spatial#1234')).annotations, { ns.rdfs.label: 'Main colors spatial instance'}) self.assertDictEqual(gen.predicate(ns.bse.tag).annotations, {ns.rdfs.label: 'connect entity to a tag'}) class TestToString(unittest.TestCase): def test_stub(self): raise NotImplementedError() ## main ## if __name__ == '__main__': unittest.main() ## EOF ##