diff options
-rw-r--r-- | bsfs/schema/schema.py | 35 | ||||
-rw-r--r-- | test/schema/test_schema.py | 129 |
2 files changed, 164 insertions, 0 deletions
diff --git a/bsfs/schema/schema.py b/bsfs/schema/schema.py index 0e053c0..b6f37a7 100644 --- a/bsfs/schema/schema.py +++ b/bsfs/schema/schema.py @@ -103,6 +103,41 @@ class Schema(): SchemaDiff = namedtuple('SchemaDiff', ['nodes', 'literals', 'predicates']) + def _issubset(self, other: 'Schema') -> bool: + # inconsistent schema can't be ordered. + if not self.consistent_with(other): + return False + # since schemas are consistent, it's sufficient to compare their URIs. + # self's sets are fully contained in other's sets + # pylint: disable=protected-access + return set(self._predicates) <= set(other._predicates) \ + and set(self._nodes) <= set(other._nodes) \ + and set(self._literals) <= set(other._literals) + + def __lt__(self, other: typing.Any) -> bool: + """Return True if *other* is a true subset of *self*.""" + if not isinstance(other, Schema): # other is not a Schema + return NotImplemented + return self != other and self._issubset(other) + + def __le__(self, other: typing.Any) -> bool: + """Return True if *other* is a subset of *self*.""" + if not isinstance(other, Schema): # other is not a Schema + return NotImplemented + return self == other or self._issubset(other) + + def __gt__(self, other: typing.Any) -> bool: + """Return True if *other* is a true superset of *self*.""" + if not isinstance(other, Schema): # other is not a Schema + return NotImplemented + return self != other and other._issubset(self) + + def __ge__(self, other: typing.Any) -> bool: + """Return True if *other* is a superset of *self*.""" + if not isinstance(other, Schema): # other is not a Schema + return NotImplemented + return self == other or other._issubset(self) + def diff(self, other: 'Schema') -> SchemaDiff: """Return node, literals, and predicates that are in *self* but not in *other*.""" return self.SchemaDiff( diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py index 2dc26e8..888cdca 100644 --- a/test/schema/test_schema.py +++ b/test/schema/test_schema.py @@ -206,6 +206,135 @@ class TestSchema(unittest.TestCase): 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})) |