# imports import operator import unittest # bsfs imports from bsfs.namespace import ns from bsfs.schema.types import ROOT_PREDICATE, ROOT_VERTEX, ROOT_FEATURE from bsfs.utils import errors # objects to test from bsfs.schema.types import _Type, Vertex, Node, Literal, Predicate, Feature ## code ## ns.bse = ns.bsfs.Entity() class TestType(unittest.TestCase): def test_parents(self): # create some types fst = _Type('First') snd = _Type('Second', fst) trd = _Type('Third', snd) frd = _Type('Fourth', trd) # check parents self.assertListEqual(list(fst.parents()), []) self.assertListEqual(list(snd.parents()), [fst]) self.assertListEqual(list(trd.parents()), [snd, fst]) self.assertListEqual(list(frd.parents()), [trd, snd, fst]) def test_annotations(self): # annotations can be empty self.assertDictEqual(_Type('Foo', None).annotations, {}) # annotations are stored self.assertDictEqual(_Type('Foo', None, foo='bar', bar=123).annotations, { 'foo': 'bar', 'bar': 123}) # comparison ignores annotations self.assertEqual( _Type('Foo', None, foo='bar', bar='foo'), _Type('Foo', None, hello='world', foobar=1234)) self.assertEqual( hash(_Type('Foo', None, foo='bar', bar='foo')), hash(_Type('Foo', None, hello='world', foobar=1234))) # annotations can be passed to child self.assertDictEqual(_Type('First', foo='bar').child('Second', bar='foo').annotations, { 'bar': 'foo'}) def test_string_conversion(self): # type w/o parent self.assertEqual(str(_Type('Foo')), '_Type(Foo)') self.assertEqual(repr(_Type('Foo')), '_Type(Foo, None)') # type w/ parent self.assertEqual(str(_Type('Foo', _Type('Bar'))), '_Type(Foo)') self.assertEqual(repr(_Type('Foo', _Type('Bar'))), '_Type(Foo, _Type(Bar, None))') # subtype w/o parent class SubType(_Type): pass self.assertEqual(str(SubType('Foo')), 'SubType(Foo)') self.assertEqual(repr(SubType('Foo')), 'SubType(Foo, None)') # subtype w/ parent self.assertEqual(str(SubType('Foo', SubType('Bar'))), 'SubType(Foo)') self.assertEqual(repr(SubType('Foo', SubType('Bar'))), 'SubType(Foo, SubType(Bar, None))') # subtype and type mixed self.assertEqual(str(SubType('Foo', _Type('Bar'))), 'SubType(Foo)') self.assertEqual(repr(SubType('Foo', _Type('Bar'))), 'SubType(Foo, _Type(Bar, None))') self.assertEqual(str(_Type('Foo', SubType('Bar'))), '_Type(Foo)') self.assertEqual(repr(_Type('Foo', SubType('Bar'))), '_Type(Foo, SubType(Bar, None))') def test_child(self): # callee is used as parent self.assertEqual(_Type('First').child('Second'), _Type('Second', _Type('First'))) # works with multiple parents self.assertEqual(_Type('First').child('Second').child('Third'), _Type('Third', _Type('Second', _Type('First')))) # type persists class Foo(_Type): pass self.assertEqual(Foo('First').child('Second'), Foo('Second', Foo('First'))) # annotations are respected self.assertDictEqual(_Type('First', foo='bar').child('Second', bar='foo').annotations, { 'bar': 'foo'}) def test_equality(self): # equality depends on uri self.assertEqual(_Type('Foo'), _Type('Foo')) self.assertEqual(hash(_Type('Foo')), hash(_Type('Foo'))) self.assertNotEqual(_Type('Foo'), _Type('Bar')) self.assertNotEqual(hash(_Type('Foo')), hash(_Type('Bar'))) # comparison is case-sensitive self.assertNotEqual(_Type('FOO'), _Type('foo')) self.assertNotEqual(hash(_Type('FOO')), hash(_Type('foo'))) # comparison respects type class Foo(_Type): pass self.assertNotEqual(_Type('Foo'), Foo('Foo')) self.assertNotEqual(hash(_Type('Foo')), hash(Foo('Foo'))) # comparison respects parent self.assertNotEqual(_Type('Foo', _Type('Bar')), _Type('Foo')) self.assertNotEqual(hash(_Type('Foo', _Type('Bar'))), hash(_Type('Foo'))) # comparison ignores annotations self.assertEqual( _Type('Foo', None, foo='bar', bar='foo'), _Type('Foo', None, hello='world', foobar=1234)) self.assertEqual( hash(_Type('Foo', None, foo='bar', bar='foo')), hash(_Type('Foo', None, hello='world', foobar=1234))) def test_order(self): # create some types. vehicle = _Type('Vehicle') twowheel = _Type('Two-wheel', vehicle) bike = _Type('Bike', twowheel) bicycle = _Type('Bicycle', twowheel) # two-wheel is equivalent to itself self.assertFalse(twowheel == vehicle) self.assertTrue(twowheel == twowheel) self.assertFalse(twowheel == bicycle) # two-wheel is a true subclass of Vehicle self.assertTrue(twowheel < vehicle) self.assertFalse(twowheel < twowheel) self.assertFalse(twowheel < bicycle) # two-wheel is a subclass of itself and Vehicle self.assertTrue(twowheel <= vehicle) self.assertTrue(twowheel <= twowheel) self.assertFalse(twowheel <= bicycle) # two-wheel is a true superclass of Bicycle self.assertFalse(twowheel > vehicle) self.assertFalse(twowheel > twowheel) self.assertTrue(twowheel > bicycle) # two-wheel is a superclass of itself and Bicycle self.assertFalse(twowheel >= vehicle) self.assertTrue(twowheel >= twowheel) self.assertTrue(twowheel >= bicycle) # analoguous to sets, this is not a total order self.assertFalse(bike <= bicycle) self.assertFalse(bike < bicycle) self.assertFalse(bike > bicycle) self.assertFalse(bike >= bicycle) self.assertFalse(bike == bicycle) # comparing different classes returns False ... # ... when classes are hierarchically related class Foo(_Type): pass foo = Foo('Foo', bike) self.assertFalse(foo < bike) self.assertFalse(foo <= bike) self.assertFalse(foo > bike) self.assertFalse(foo >= bike) # goes both ways self.assertFalse(bike < foo) self.assertFalse(bike <= foo) self.assertFalse(bike > foo) self.assertFalse(bike >= foo) # ... when classes are unrelated class Bar(_Type): pass bar = Bar('Bar', bike) self.assertFalse(foo < bar) self.assertFalse(foo <= bar) self.assertFalse(foo > bar) self.assertFalse(foo >= bar) # goes both ways self.assertFalse(bar < foo) self.assertFalse(bar <= foo) self.assertFalse(bar > foo) self.assertFalse(bar >= foo) class TestPredicate(unittest.TestCase): def test_construction(self): # domain must be a node self.assertRaises(TypeError, Predicate, ns.bse.foo, 1234, None, True) self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Literal(ns.bsfs.Foo, None), None, True) # range must be a Literal, a Node, or the root Vertex self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), None, True) self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), 1234, True) self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), Vertex(ns.bsfs.Foo, None), True) self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), _Type(ns.bsfs.Foo, None), True) class Foo(): pass self.assertRaises(TypeError, Predicate, ns.bse.foo, None, Node(ns.bsfs.Node, None), Foo(), True) def test_equality(self): n_root = Node(ns.bsfs.Node, None) n_ent = Node(ns.bsfs.Entity, Node(ns.bsfs.Node, None)) n_tag = Node(ns.bsfs.Tag, Node(ns.bsfs.Tag, None)) root = ROOT_PREDICATE tag = Predicate( uri=ns.bse.tag, parent=root, domain=n_root, range=n_tag, unique=False, ) # instance is equal to itself self.assertEqual(tag, tag) self.assertEqual(hash(tag), hash(tag)) # instance is equal to a clone self.assertEqual(tag, Predicate(ns.bse.tag, root, n_root, n_tag, False)) self.assertEqual(hash(tag), hash(Predicate(ns.bse.tag, root, n_root, n_tag, False))) # equality respects uri self.assertNotEqual(tag, Predicate(ns.bsfs.Alternative, root, n_root, n_tag, False)) self.assertNotEqual(hash(tag), hash(Predicate(ns.bsfs.Alternative, root, n_root, n_tag, False))) # equality respects parent self.assertNotEqual(tag, Predicate(ns.bse.tag, n_root, n_root, n_tag, False)) self.assertNotEqual(hash(tag), hash(Predicate(ns.bse.tag, n_root, n_root, n_tag, False))) # equality respects domain self.assertNotEqual(tag, Predicate(ns.bse.tag, root, n_ent, n_tag, False)) self.assertNotEqual(hash(tag), hash(Predicate(ns.bse.tag, root, n_ent, n_tag, False))) # equality respects range self.assertNotEqual(tag, Predicate(ns.bse.tag, root, n_root, n_root, False)) self.assertNotEqual(hash(tag), hash(Predicate(ns.bse.tag, root, n_root, n_root, False))) # equality respects unique self.assertNotEqual(tag, Predicate(ns.bse.tag, root, n_root, n_tag, True)) self.assertNotEqual(hash(tag), hash(Predicate(ns.bse.tag, root, n_root, n_tag, True))) def test_child(self): n_root = Node(ns.bsfs.Node, None) l_root = Literal(ns.bsfs.Literal, None) n_ent = Node(ns.bsfs.Entity, Node(ns.bsfs.Node, None)) n_tag = Node(ns.bsfs.Tag, Node(ns.bsfs.Tag, None)) root = ROOT_PREDICATE tag = Predicate( uri=ns.bse.tag, parent=root, domain=n_ent, range=n_tag, unique=False, ) # child returns Predicate self.assertIsInstance(tag.child(ns.bse.foo), Predicate) # uri is respected self.assertEqual(ns.bse.foo, tag.child(ns.bse.foo).uri) # domain is respected dom = Node(ns.bsfs.Image, n_ent) self.assertEqual(dom, tag.child(ns.bse.foo, domain=dom).domain) # range is respected rng = Node(ns.bsfs.Group, n_tag) self.assertEqual(rng, tag.child(ns.bse.foo, range=rng).range) # cannot set range to None self.assertEqual(n_tag, tag.child(ns.bse.foo, range=None).range) # unique is respected self.assertTrue(tag.child(ns.bse.foo, unique=True).unique) # annotations are respected self.assertDictEqual(tag.child(ns.bse.foo, foo='bar', bar=123).annotations, { 'foo': 'bar', 'bar': 123, }) # domain is inherited from parent self.assertEqual(n_root, root.child(ns.bse.foo).domain) self.assertEqual(n_ent, tag.child(ns.bse.foo).domain) # range is inherited from parent self.assertEqual(ROOT_VERTEX, root.child(ns.bse.foo).range) self.assertEqual(n_tag, tag.child(ns.bse.foo).range) # uniqueness is inherited from parent self.assertFalse(tag.child(ns.bse.foo).unique) # domain must be subtype of parent's domain self.assertRaises(errors.ConsistencyError, tag.child, ns.bse.foo, domain=n_root) self.assertRaises(errors.ConsistencyError, tag.child, ns.bse.foo, domain=Node(ns.bsfs.Image, n_root)) # range must be subtype of parent's range self.assertRaises(errors.ConsistencyError, tag.child, ns.bse.foo, range=n_root) self.assertRaises(errors.ConsistencyError, tag.child, ns.bse.foo, range=Node(ns.bsfs.Image, n_root)) self.assertRaises(errors.ConsistencyError, tag.child, ns.bse.foo, range=Literal(ns.bsfs.Tag, l_root)) # range can be subtyped from ROOT_VERTEX to Node or Literal self.assertEqual(n_root, root.child(ns.bse.foo, range=n_root).range) self.assertEqual(l_root, root.child(ns.bse.foo, range=l_root).range) class TestFeature(unittest.TestCase): def test_construction(self): n_root = Node(ns.bsfs.Node, None) l_root = Literal(ns.bsfs.Literal, None) # dimension, dtype, and distance are respected feat = Feature(ns.bsfs.Feature, None, 1234, ns.bsfs.float, ns.bsfs.euclidean) self.assertEqual(1234, feat.dimension) self.assertEqual(ns.bsfs.float, feat.dtype) self.assertEqual(ns.bsfs.euclidean, feat.distance) def test_equality(self): n_ent = Node(ns.bsfs.Entity, Node(ns.bsfs.Node, None)) colors = Feature( uri=ns.bse.colors, parent=ROOT_FEATURE, dimension=1234, dtype=ns.bsfs.float, distance=ns.bsfs.euclidean, ) # instance is equal to itself self.assertEqual(colors, colors) self.assertEqual(hash(colors), hash(colors)) # instance is equal to a clone self.assertEqual(colors, Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.float, ns.bsfs.euclidean)) self.assertEqual(hash(colors), hash(Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.float, ns.bsfs.euclidean))) # equality respects dimension self.assertNotEqual(colors, Feature(ns.bse.colors, ROOT_FEATURE, 4321, ns.bsfs.float, ns.bsfs.euclidean)) self.assertNotEqual(hash(colors), hash(Feature(ns.bse.colors, ROOT_FEATURE, 4321, ns.bsfs.float, ns.bsfs.euclidean))) # equality respects dtype self.assertNotEqual(colors, Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.integer, ns.bsfs.euclidean)) self.assertNotEqual(hash(colors), hash(Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.integer, ns.bsfs.euclidean))) # equality respects distance self.assertNotEqual(colors, Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.float, ns.bsfs.cosine)) self.assertNotEqual(hash(colors), hash(Feature(ns.bse.colors, ROOT_FEATURE, 1234, ns.bsfs.float, ns.bsfs.cosine))) def test_child(self): n_root = Node(ns.bsfs.Node, None) n_ent = Node(ns.bsfs.Entity, n_root) l_root = Literal(ns.bsfs.Literal, None) colors = Feature( uri=ns.bse.colors, parent=ROOT_FEATURE, dimension=1234, dtype=ns.bsfs.float, distance=ns.bsfs.euclidean, ) # child returns Feature self.assertIsInstance(colors.child(ns.bse.foo), Feature) # uri is respected self.assertEqual(ns.bse.foo, colors.child(ns.bse.foo).uri) # dimension is respected self.assertEqual(4321, colors.child(ns.bse.foo, dimension=4321).dimension) # dtype is respected self.assertEqual(ns.bsfs.integer, colors.child(ns.bse.foo, dtype=ns.bsfs.integer).dtype) # distance is respected self.assertEqual(ns.bsfs.cosine, colors.child(ns.bse.foo, distance=ns.bsfs.cosine).distance) # annotations are respected self.assertDictEqual(colors.child(ns.bse.foo, foo='bar', bar=123).annotations, { 'foo': 'bar', 'bar': 123, }) # dimension is inherited from parent self.assertEqual(1234, colors.child(ns.bse.foo).dimension) # dtype is inherited from parent self.assertEqual(ns.bsfs.float, colors.child(ns.bse.foo).dtype) # distance is inherited from parent self.assertEqual(ns.bsfs.euclidean, colors.child(ns.bse.foo).distance) ## main ## if __name__ == '__main__': unittest.main() ## EOF ##