aboutsummaryrefslogtreecommitdiffstats
path: root/test/schema/test_schema.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/schema/test_schema.py')
-rw-r--r--test/schema/test_schema.py745
1 files changed, 745 insertions, 0 deletions
diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py
new file mode 100644
index 0000000..888cdca
--- /dev/null
+++ b/test/schema/test_schema.py
@@ -0,0 +1,745 @@
+"""
+
+Part of the tagit test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import operator
+import unittest
+
+# bsfs imports
+from bsfs.namespace import ns
+from bsfs.schema import types
+from bsfs.utils import errors
+
+# objects to test
+from bsfs.schema.schema import Schema
+
+
+## code ##
+
+class TestSchema(unittest.TestCase):
+
+ def setUp(self):
+ self.schema_str = '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+ bsfs:Tag rdfs:subClassOf bsfs:Node .
+ bsfs:Image rdfs:subClassOf bsfs:Entity .
+ bsfs:Unused rdfs:subClassOf bsfs:Node .
+
+ xsd:string rdfs:subClassOf bsfs:Literal .
+ xsd:integer rdfs:subClassOf bsfs:Literal .
+ xsd:boolean rdfs:subClassOf bsfs:Literal .
+
+ bse:tag rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Entity ;
+ rdfs:range bsfs:Tag ;
+ bsfs:unique "false"^^xsd:boolean .
+
+ bse:group rdfs:subClassOf bse:tag ;
+ rdfs:domain bsfs:Image ;
+ rdfs:range bsfs:Tag ;
+ bsfs:unique "false"^^xsd:boolean .
+
+ bse:comment rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Node ;
+ rdfs:range xsd:string ;
+ bsfs:unique "true"^^xsd:boolean .
+
+ '''
+ # nodes
+ self.n_root = types.Node(ns.bsfs.Node, None)
+ self.n_ent = types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Node, None))
+ self.n_img = types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Node, None)))
+ self.n_tag = types.Node(ns.bsfs.Tag, types.Node(ns.bsfs.Node, None))
+ self.n_unused = types.Node(ns.bsfs.Unused, types.Node(ns.bsfs.Node, None))
+ self.nodes = [self.n_root, self.n_ent, self.n_img, self.n_tag, self.n_unused]
+
+ # literals
+ self.l_root = types.Literal(ns.bsfs.Literal, None)
+ self.l_string = types.Literal(ns.xsd.string, types.Literal(ns.bsfs.Literal, None))
+ self.l_integer = types.Literal(ns.xsd.integer, types.Literal(ns.bsfs.Literal, None))
+ self.l_unused = types.Literal(ns.xsd.boolean, types.Literal(ns.bsfs.Literal, None))
+ self.literals = [self.l_root, self.l_string, self.l_integer, self.l_unused]
+
+ # predicates
+ self.p_root = types.Predicate(ns.bsfs.Predicate, None, types.Node(ns.bsfs.Node, None), None, False)
+ self.p_tag = self.p_root.get_child(ns.bse.tag, self.n_ent, self.n_tag, False)
+ self.p_group = self.p_tag.get_child(ns.bse.group, self.n_img, self.n_tag, False)
+ self.p_comment = self.p_root.get_child(ns.bse.comment, self.n_root, self.l_string, True)
+ self.predicates = [self.p_root, self.p_tag, self.p_group, self.p_comment]
+
+ def test_construction(self):
+ # nodes and literals are optional
+ schema = Schema(self.predicates)
+ self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag})
+ self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string})
+ self.assertSetEqual(set(schema.predicates()), set(self.predicates))
+
+ # predicates, nodes, and literals are respected
+ schema = Schema(self.predicates, self.nodes, self.literals)
+ self.assertSetEqual(set(schema.nodes()), set(self.nodes))
+ self.assertSetEqual(set(schema.literals()), set(self.literals))
+ self.assertSetEqual(set(schema.predicates()), set(self.predicates))
+
+ # nodes are complete (w/o unused)
+ schema = Schema(self.predicates, None, self.literals)
+ self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag})
+ schema = Schema(self.predicates, [], self.literals)
+ self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag})
+ schema = Schema(self.predicates, [self.n_img, self.n_tag], self.literals)
+ self.assertSetEqual(set(schema.nodes()), {self.n_root, self.n_ent, self.n_img, self.n_tag})
+ schema = Schema(self.predicates, [self.n_unused], self.literals)
+ self.assertSetEqual(set(schema.nodes()), set(self.nodes))
+
+ # literals are complete
+ schema = Schema(self.predicates, self.nodes, None)
+ self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string})
+ schema = Schema(self.predicates, self.nodes, [])
+ self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string})
+ schema = Schema(self.predicates, self.nodes, [self.l_string])
+ self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string})
+ schema = Schema(self.predicates, self.nodes, [self.l_integer])
+ self.assertSetEqual(set(schema.literals()), {self.l_root, self.l_string, self.l_integer})
+ schema = Schema(self.predicates, self.nodes, [self.l_integer, self.l_unused])
+ self.assertSetEqual(set(schema.literals()), set(self.literals))
+
+ # predicates are complete
+ schema = Schema([], self.nodes, self.literals)
+ self.assertSetEqual(set(schema.predicates()), set())
+ schema = Schema([self.p_group], self.nodes, self.literals)
+ self.assertSetEqual(set(schema.predicates()), {self.p_root, self.p_tag, self.p_group})
+ schema = Schema([self.p_group, self.p_comment], self.nodes, self.literals)
+ self.assertSetEqual(set(schema.predicates()), set(self.predicates))
+
+ # node uris must be unique
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates,
+ self.nodes + [types.Node(ns.bsfs.Entity, None)], self.literals)
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates,
+ self.nodes + [types.Node(ns.bsfs.Entity, types.Node(ns.bsfs.Foo, None))], self.literals)
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates,
+ self.nodes + [types.Node(ns.bsfs.Entity, self.n_img)], self.literals)
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates,
+ [types.Node(ns.bsfs.Entity, self.n_img)], self.literals)
+
+ # literal uris must be unique
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes,
+ self.literals + [types.Literal(ns.xsd.string, None)])
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes,
+ self.literals + [types.Literal(ns.xsd.string, types.Literal(ns.bsfs.Foo, None))])
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes,
+ self.literals + [types.Literal(ns.xsd.string, self.l_integer)])
+ self.assertRaises(errors.ConsistencyError, Schema, self.predicates, self.nodes,
+ [types.Literal(ns.xsd.string, self.l_integer)])
+
+ # predicate uris must be unique
+ self.assertRaises(errors.ConsistencyError, Schema,
+ self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_root, self.n_tag, False)])
+ self.assertRaises(errors.ConsistencyError, Schema,
+ self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_ent, self.n_img, False)])
+ self.assertRaises(errors.ConsistencyError, Schema,
+ self.predicates + [types.Predicate(ns.bse.tag, self.p_root, self.n_ent, self.n_tag, True)])
+ self.assertRaises(errors.ConsistencyError, Schema,
+ self.predicates + [types.Predicate(ns.bse.tag, None, self.n_ent, self.n_tag, False)])
+
+ # uris must be unique across nodes, literals, and predicates
+ self.assertRaises(errors.ConsistencyError, Schema,
+ {}, {types.Node(ns.bsfs.Foo, None)}, {types.Node(ns.bsfs.Foo, None)})
+ self.assertRaises(errors.ConsistencyError, Schema,
+ {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {}, {types.Node(ns.bsfs.Foo, None)})
+ self.assertRaises(errors.ConsistencyError, Schema,
+ {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {types.Node(ns.bsfs.Foo, None)}, {})
+ self.assertRaises(errors.ConsistencyError, Schema,
+ {types.Predicate(ns.bsfs.Foo, None, types.Node(ns.bsfs.Node, None), None, False)}, {types.Node(ns.bsfs.Foo, None)}, {types.Node(ns.bsfs.Foo, None)})
+
+ def test_str(self):
+ self.assertEqual(str(Schema([])), 'Schema()')
+ self.assertEqual(str(Schema([], [], [])), 'Schema()')
+ self.assertEqual(str(Schema(self.predicates, self.nodes, self.literals)), 'Schema()')
+ self.assertEqual(repr(Schema([])), 'Schema([], [], [])')
+ self.assertEqual(repr(Schema([], [], [])), 'Schema([], [], [])')
+ n = [ns.bsfs.Entity, ns.bsfs.Image, ns.bsfs.Node, ns.bsfs.Tag, ns.bsfs.Unused]
+ l = [ns.bsfs.Literal, ns.xsd.boolean, ns.xsd.integer, ns.xsd.string]
+ p = [ns.bse.comment, ns.bse.group, ns.bse.tag, ns.bsfs.Predicate]
+ self.assertEqual(repr(Schema(self.predicates, self.nodes, self.literals)), f'Schema({n}, {l}, {p})')
+
+ def test_equality(self):
+ schema = Schema(self.predicates, self.nodes, self.literals)
+ # instance is equal to itself
+ self.assertEqual(schema, schema)
+ self.assertEqual(hash(schema), hash(schema))
+ # instance is equal to a clone
+ self.assertEqual(schema, Schema(self.predicates, self.nodes, self.literals))
+ self.assertEqual(hash(schema), hash(Schema(self.predicates, self.nodes, self.literals)))
+ # equality respects nodes
+ self.assertNotEqual(schema,
+ Schema(self.predicates, [self.n_root, self.n_ent, self.n_img, self.n_tag], self.literals))
+ self.assertNotEqual(hash(schema),
+ hash(Schema(self.predicates, [self.n_root, self.n_ent, self.n_img, self.n_tag], self.literals)))
+ self.assertNotEqual(schema,
+ Schema(self.predicates, self.nodes + [types.Node(ns.bsfs.Document, self.n_ent)], self.literals))
+ self.assertNotEqual(hash(schema),
+ hash(Schema(self.predicates, self.nodes + [types.Node(ns.bsfs.Document, self.n_ent)], self.literals)))
+ # equality respects literals
+ self.assertNotEqual(schema,
+ Schema(self.predicates, self.nodes, [self.l_root, self.l_string, self.l_integer]))
+ self.assertNotEqual(hash(schema),
+ hash(Schema(self.predicates, self.nodes, [self.l_root, self.l_string, self.l_integer])))
+ self.assertNotEqual(schema,
+ Schema(self.predicates, self.nodes, self.literals + [types.Literal(ns.xsd.number, self.l_root)]))
+ self.assertNotEqual(hash(schema),
+ hash(Schema(self.predicates, self.nodes, self.literals + [types.Literal(ns.xsd.number, self.l_root)])))
+ # equality respects predicates
+ self.assertNotEqual(schema,
+ Schema([self.p_group, self.p_tag, self.p_root], self.nodes, self.literals))
+ self.assertNotEqual(hash(schema),
+ hash(Schema([self.p_group, self.p_tag, self.p_root], self.nodes, self.literals)))
+ self.assertNotEqual(schema,
+ Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals))
+ self.assertNotEqual(hash(schema),
+ hash(Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals)))
+
+ def test_order(self):
+ # setup
+ class Foo(): pass
+ p_foo = self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, True)
+ p_sub = p_foo.get_child(ns.bse.sub, self.n_ent, self.l_string, True)
+ p_bar = self.p_root.get_child(ns.bse.bar, self.n_ent, self.l_string, True)
+
+ # can only compare schema to other schema
+ # <
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), Foo())
+ # <=
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), Foo())
+ # >
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), Foo())
+ # >=
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), Foo())
+
+ # a schema is a subset of itself
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is a superset of itself
+ self.assertTrue(operator.ge(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is not a true subset of itself
+ self.assertFalse(operator.lt(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is not a true superset of itself
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag})))
+
+ # subset considers predicates
+ self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_foo, p_bar})))
+ self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_sub})))
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({p_bar})))
+ # subset considers nodes
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused})))
+ self.assertFalse(operator.lt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag})))
+ # subset considers literals
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused})))
+ self.assertFalse(operator.lt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag})))
+ # subset considers differences in predicates and nodes
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_group})))
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_group})))
+ # subset considers differences in predicates and literals
+ self.assertTrue(operator.lt(Schema.Empty(), Schema({self.p_comment})))
+ # subset considers differences in predicates, nodes, and literals
+ self.assertTrue(operator.lt(Schema({}), Schema.Empty()))
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema.from_string(self.schema_str)))
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema.from_string(self.schema_str)))
+ self.assertFalse(operator.lt(Schema({self.p_comment}), Schema({self.p_tag})))
+ self.assertFalse(operator.le(Schema({self.p_comment}), Schema({self.p_tag})))
+
+ # superset considers predicates
+ self.assertTrue(operator.gt(Schema({p_foo, p_bar}), Schema({p_foo})))
+ self.assertTrue(operator.gt(Schema({p_sub}), Schema({p_foo})))
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({p_bar})))
+ # superset considers nodes
+ self.assertTrue(operator.gt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused})))
+ # superset considers literals
+ self.assertTrue(operator.gt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused})))
+ # superset considers differences in predicates and nodes
+ self.assertTrue(operator.gt(Schema({self.p_group}), Schema({self.p_tag})))
+ self.assertTrue(operator.ge(Schema({self.p_group}), Schema({self.p_tag})))
+ # superset considers differences in predicates and literals
+ self.assertTrue(operator.gt(Schema({self.p_comment}), Schema.Empty()))
+ # superset considers differences in predicates, nodes, and literals
+ self.assertTrue(operator.gt(Schema.Empty(), Schema({})))
+ self.assertTrue(operator.gt(Schema.from_string(self.schema_str), Schema({self.p_tag})))
+ self.assertTrue(operator.ge(Schema.from_string(self.schema_str), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_comment})))
+ self.assertFalse(operator.ge(Schema({self.p_tag}), Schema({self.p_comment})))
+
+ # inconsistent schema cannot be a subset
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.le(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.le(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a true subset
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.lt(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.lt(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a superset
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.ge(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.ge(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a true superset
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.gt(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.gt(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+
+
+
+ def test_diff(self):
+ # difference can be empty
+ diff = Schema({self.p_tag}).diff(Schema({self.p_group}))
+ self.assertSetEqual(set(diff.nodes), set())
+ self.assertSetEqual(set(diff.literals), set())
+ self.assertSetEqual(set(diff.predicates), set())
+
+ # difference contains predicates from the LHS
+ diff = Schema({self.p_group}).diff(Schema({self.p_tag}))
+ self.assertSetEqual(set(diff.nodes), {self.n_img})
+ self.assertSetEqual(set(diff.literals), set())
+ self.assertSetEqual(set(diff.predicates), {self.p_group})
+
+ # difference does not contain predicates from the RHS
+ diff = Schema({self.p_tag, self.p_comment}).diff(Schema({self.p_group}))
+ self.assertSetEqual(set(diff.nodes), set())
+ self.assertSetEqual(set(diff.literals), {self.l_root, self.l_string})
+ self.assertSetEqual(set(diff.predicates), {self.p_comment})
+
+ # difference considers extra nodes and literals
+ diff = Schema({self.p_tag}, {self.n_unused}, {self.l_unused}).diff(Schema({self.p_tag}))
+ self.assertSetEqual(set(diff.nodes), {self.n_unused})
+ self.assertSetEqual(set(diff.literals), {self.l_root, self.l_unused})
+ self.assertSetEqual(set(diff.predicates), set())
+
+ # difference considers inconsistent types
+ diff = Schema({self.p_tag}, {self.n_unused}, {self.l_unused}).diff(
+ Schema({self.p_tag}, {types.Node(ns.bsfs.Unused, None)}, {types.Literal(ns.xsd.boolean, None)}))
+ self.assertSetEqual(set(diff.nodes), {self.n_unused})
+ self.assertSetEqual(set(diff.literals), {self.l_root, self.l_unused})
+ self.assertSetEqual(set(diff.predicates), set())
+
+ # __sub__ is an alias for diff
+ diff = Schema({self.p_comment}, {self.n_unused}, {self.l_unused}) - Schema({self.p_group})
+ self.assertSetEqual(set(diff.nodes), {self.n_unused})
+ self.assertSetEqual(set(diff.literals), {self.l_root, self.l_string, self.l_unused})
+ self.assertSetEqual(set(diff.predicates), {self.p_comment})
+ # __sub__ only accepts Schema instances
+ class Foo(): pass
+ self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), 1234)
+ self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), 'hello world')
+ self.assertRaises(TypeError, operator.sub, Schema({self.p_comment}, {self.n_unused}, {self.l_unused}), Foo())
+
+ def test_consistent_with(self):
+ # argument must be a schema
+ class Foo(): pass
+ self.assertRaises(TypeError, Schema([]).consistent_with, 1234)
+ self.assertRaises(TypeError, Schema([]).consistent_with, 'hello world')
+ self.assertRaises(TypeError, Schema([]).consistent_with, Foo())
+
+ # node consistency
+ self.assertTrue(Schema([], {self.n_ent, self.n_tag, self.n_unused}).consistent_with(
+ Schema(self.predicates)))
+ self.assertFalse(Schema([], {types.Node(ns.bsfs.Entity, None)}).consistent_with(
+ Schema(self.predicates)))
+ # order doesn't matter
+ self.assertTrue(Schema(self.predicates).consistent_with(
+ Schema([], {self.n_ent, self.n_tag, self.n_unused})))
+
+ # literal consistency
+ self.assertTrue(Schema([], [], {self.l_string, self.l_unused}).consistent_with(
+ Schema(self.predicates)))
+ self.assertFalse(Schema([], [], {types.Literal(ns.xsd.string, None)}).consistent_with(
+ Schema(self.predicates)))
+ # order doesn't matter
+ self.assertTrue(Schema(self.predicates).consistent_with(
+ Schema([], [], {self.l_string, self.l_unused})))
+
+ # predicate consistency
+ self.assertTrue(Schema({self.p_tag}).consistent_with(
+ Schema(self.predicates)))
+ self.assertFalse(Schema({types.Predicate(ns.bse.tag, None, self.n_root, self.n_root, False)}).consistent_with(
+ Schema(self.predicates)))
+ # order doesn't matter
+ self.assertTrue(Schema(self.predicates).consistent_with(
+ Schema({self.p_tag})))
+
+ # global consistency
+ self.assertFalse(Schema({types.Predicate(ns.bsfs.Entity, None, self.n_root, self.n_root, False)}).consistent_with(
+ Schema(self.predicates)))
+ self.assertFalse(Schema([], {types.Node(ns.xsd.string, None)}).consistent_with(
+ Schema(self.predicates)))
+ self.assertFalse(Schema([], [], {types.Literal(ns.bsfs.Entity, None)}).consistent_with(
+ Schema(self.predicates)))
+
+
+ def test_union(self):
+ # must provide at least one schema
+ self.assertRaises(TypeError, Schema.Union)
+
+ # can pass schemas as list
+ self.assertEqual(Schema.Union([Schema({self.p_tag})]), Schema({self.p_tag}))
+ self.assertEqual(Schema.Union([Schema({self.p_tag}), Schema({self.p_comment})]),
+ Schema({self.p_tag, self.p_comment}))
+
+ # can pass schemas as arguments
+ self.assertEqual(Schema.Union(Schema({self.p_tag})), Schema({self.p_tag}))
+ self.assertEqual(Schema.Union(Schema({self.p_tag}), Schema({self.p_comment})),
+ Schema({self.p_tag, self.p_comment}))
+
+ # cannot mix the two argument passing styles
+ self.assertRaises(TypeError, Schema.Union, [Schema(self.predicates)], Schema(self.predicates))
+
+ # all arguments must be Schema instances
+ self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 1234)
+ self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 1234, Schema(self.predicates))
+ self.assertRaises(TypeError, Schema.Union, Schema(self.predicates), 'hello world')
+
+ # Union merges predicates, nodes, and literals
+ self.assertEqual(Schema.Union(
+ Schema({self.p_comment}, {self.n_unused}, {}),
+ Schema({self.p_group}, {self.n_img}, {self.l_unused})),
+ Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused}))
+
+ # Union does not accept inconsistent nodes
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema(self.predicates),
+ Schema({}, {types.Node(ns.bsfs.Entity, None)}))
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {self.n_ent}),
+ Schema({}, {types.Node(ns.bsfs.Entity, None)}))
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {self.n_ent}),
+ Schema({}, {}, {types.Literal(ns.bsfs.Entity, None)}))
+
+ # Union does not accept inconsistent literals
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema(self.predicates),
+ Schema({}, {}, {types.Literal(ns.xsd.string, None)}))
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {}, {self.l_string}),
+ Schema({}, {}, {types.Literal(ns.xsd.string, None)}))
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({}, {}, {self.l_string}),
+ Schema({}, {types.Node(ns.xsd.string, None)}))
+
+ # Union does not accept inconsistent predicates
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({self.p_tag}),
+ Schema({types.Predicate(ns.bse.tag, None, self.n_ent, self.n_tag, False)}))
+ self.assertRaises(errors.ConsistencyError, Schema.Union, Schema({self.p_tag}),
+ Schema({}, {types.Node(ns.bse.tag, None)}))
+
+ # union is an alias for Union
+ self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}).union(
+ Schema({self.p_group}, {self.n_img}, {self.l_unused})),
+ Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused}))
+ # union only accepts Schema instances
+ class Foo(): pass
+ self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, 1234)
+ self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, 'hello world')
+ self.assertRaises(TypeError, Schema({self.p_comment}, {self.n_unused}, {}).union, Foo())
+
+ # __add__ is an alias for Union
+ self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}) + Schema({self.p_group}, {self.n_img}, {self.l_unused}),
+ Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused}))
+ # __add__ only accepts Schema instances
+ class Foo(): pass
+ self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), 1234)
+ self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), 'hello world')
+ self.assertRaises(TypeError, operator.add, Schema({self.p_comment}, {self.n_unused}, {}), Foo())
+
+ # __or__ is an alias for Union
+ self.assertEqual(Schema({self.p_comment}, {self.n_unused}, {}) | Schema({self.p_group}, {self.n_img}, {self.l_unused}),
+ Schema({self.p_comment, self.p_group}, {self.n_img, self.n_unused}, {self.l_unused}))
+ # __or__ only accepts Schema instances
+ class Foo(): pass
+ self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), 1234)
+ self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), 'hello world')
+ self.assertRaises(TypeError, operator.or_, Schema({self.p_comment}, {self.n_unused}, {}), Foo())
+
+ def test_type_getters(self):
+ schema = Schema(self.predicates, self.nodes, self.literals)
+ # nodes
+ self.assertEqual(self.n_root, schema.node(ns.bsfs.Node))
+ self.assertEqual(self.n_ent, schema.node(ns.bsfs.Entity))
+ self.assertEqual(self.n_img, schema.node(ns.bsfs.Image))
+ self.assertRaises(KeyError, schema.node, ns.bsfs.Document)
+ self.assertRaises(KeyError, schema.node, self.n_root)
+ # literals
+ self.assertEqual(self.l_root, schema.literal(ns.bsfs.Literal))
+ self.assertEqual(self.l_string, schema.literal(ns.xsd.string))
+ self.assertEqual(self.l_integer, schema.literal(ns.xsd.integer))
+ self.assertRaises(KeyError, schema.literal, ns.xsd.number)
+ self.assertRaises(KeyError, schema.literal, self.l_root)
+ # predicates
+ self.assertEqual(self.p_root, schema.predicate(ns.bsfs.Predicate))
+ self.assertEqual(self.p_tag, schema.predicate(ns.bse.tag))
+ self.assertEqual(self.p_group, schema.predicate(ns.bse.group))
+ self.assertRaises(KeyError, schema.predicate, ns.bse.mimetype)
+ self.assertRaises(KeyError, schema.predicate, self.p_root)
+
+ def test_list_getters(self):
+ schema = Schema(self.predicates, self.nodes, self.literals)
+ self.assertSetEqual(set(self.nodes), set(schema.nodes()))
+ self.assertSetEqual(set(self.literals), set(schema.literals()))
+ self.assertSetEqual(set(self.predicates), set(schema.predicates()))
+
+ def test_has(self):
+ schema = Schema(self.predicates, self.nodes, self.literals)
+ # nodes
+ self.assertTrue(schema.has_node(ns.bsfs.Node))
+ self.assertTrue(schema.has_node(ns.bsfs.Entity))
+ self.assertTrue(schema.has_node(ns.bsfs.Image))
+ self.assertFalse(schema.has_node(ns.bsfs.Document))
+ self.assertFalse(schema.has_node(self.n_root))
+ # literals
+ self.assertTrue(schema.has_literal(ns.bsfs.Literal))
+ self.assertTrue(schema.has_literal(ns.xsd.string))
+ self.assertTrue(schema.has_literal(ns.xsd.integer))
+ self.assertFalse(schema.has_literal(ns.xsd.number))
+ self.assertFalse(schema.has_literal(self.l_root))
+ # predicates
+ self.assertTrue(schema.has_predicate(ns.bsfs.Predicate))
+ self.assertTrue(schema.has_predicate(ns.bse.tag))
+ self.assertTrue(schema.has_predicate(ns.bse.group))
+ self.assertFalse(schema.has_predicate(ns.bse.mimetype))
+ self.assertFalse(schema.has_predicate(self.p_root))
+
+ def test_empty(self):
+ self.assertEqual(Schema.Empty(), Schema(
+ [types.Predicate(ns.bsfs.Predicate, None, types.Node(ns.bsfs.Node, None), None, False)],
+ [types.Node(ns.bsfs.Node, None)],
+ [types.Literal(ns.bsfs.Literal, None)],
+ ))
+
+ def test_from_string(self):
+ # from_string creates a schema
+ self.assertEqual(
+ Schema(self.predicates, self.nodes, self.literals),
+ Schema.from_string(self.schema_str))
+
+ # schema contains at least the root types
+ self.assertEqual(Schema.from_string(''), Schema({self.p_root}, {self.n_root}, {self.l_root}))
+
+ # custom example
+ self.assertEqual(
+ Schema({types.Predicate(ns.bsfs.Predicate, None, self.n_root, None, False).get_child(
+ ns.bse.filename, self.n_ent, self.l_string, False)}),
+ Schema.from_string('''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ 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 .
+ '''))
+
+ # all nodes must be defined
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ 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 .
+ ''')
+
+ # all literals must be defined
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+
+ bse:filename rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Entity ;
+ rdfs:range xsd:string ;
+ bsfs:unique "false"^^xsd:boolean .
+ ''')
+
+ # must not have circular dependencies
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ 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 .
+ ''')
+
+ # range must be a node or literal
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+
+ bse:filename rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Entity ;
+ rdfs:range xsd:string ;
+ bsfs:unique "false"^^xsd:boolean .
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+
+ bse:filename rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Entity ;
+ rdfs:range bsfs:Foo ;
+ bsfs:unique "false"^^xsd:boolean .
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+
+ bse:filename rdfs:subClassOf bsfs:Predicate ;
+ rdfs:domain bsfs:Entity ;
+ rdfs:range bsfs:Predicate ;
+ bsfs:unique "false"^^xsd:boolean .
+ ''')
+
+ # must be consistent
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+
+ bsfs:Entity rdfs:subClassOf bsfs:Node .
+ bsfs:Document rdfs:subClassOf bsfs:Node .
+ bsfs:Document rdfs:subClassOf bsfs:Entity.
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+
+ xsd:string rdfs:subClassOf bsfs:Literal .
+ xsd:name rdfs:subClassOf bsfs:Literal .
+ xsd:name rdfs:subClassOf xsd:string .
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ 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 .
+
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ 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 .
+
+ ''')
+ self.assertRaises(errors.ConsistencyError, Schema.from_string, '''
+ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+ prefix xsd: <http://www.w3.org/2001/XMLSchema#>
+ prefix bsfs: <http://bsfs.ai/schema/>
+ prefix bse: <http://bsfs.ai/schema/Entity#>
+
+ 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 .
+
+ ''')
+
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##