diff --git a/src/common/caps.py b/src/common/caps.py index 87c76fbe1..2c91c116b 100644 --- a/src/common/caps.py +++ b/src/common/caps.py @@ -29,45 +29,20 @@ import helpers class CapsCache(object): ''' This object keeps the mapping between caps data and real disco features they represent, and provides simple way to query that info. + It is application-wide, that is there's one object for all connections. + Goals: * handle storing/retrieving info from database * cache info in memory * expose simple interface + Properties: * one object for all connections (move to logger.py?) * store info efficiently (a set() of urls -- we can assume there won't be too much of these, ensure that (X,Y,Z1) and (X,Y,Z2) has different features. - - Connections with other objects: (TODO) - - Interface: - - # object creation - >>> cc=CapsCache(logger_object) - - >>> caps = ('sha-1', '66/0NaeaBKkwk85efJTGmU47vXI=') - >>> muc = 'http://jabber.org/protocol/muc' - >>> chatstates = 'http://jabber.org/protocol/chatstates' - - # setting data - >>> cc[caps].identities = [{'category':'client', 'type':'pc'}] - >>> cc[caps].features = [muc] - - # retrieving data - >>> muc in cc[caps].features - True - >>> chatstates in cc[caps].features - False - >>> cc[caps].identities - [{'category': 'client', 'type': 'pc'}] - >>> x = cc[caps] # more efficient if making several queries for one set of caps - ATypicalBlackBoxObject - >>> muc in x.features - True - ''' def __init__(self, logger=None): ''' Create a cache for entity capabilities. ''' @@ -86,12 +61,14 @@ class CapsCache(object): # TODO: maybe put all known xmpp namespace strings here # (strings given in xmpppy)? __names = {} - def __init__(ciself, hash_method, hash_): + + def __init__(self, hash_method, hash_, logger): # cached into db self.hash_method = hash_method self.hash = hash_ self._features = [] self._identities = [] + self._logger = logger # not cached into db: # have we sent the query? @@ -106,8 +83,7 @@ class CapsCache(object): def _set_features(self, value): self._features = [] for feature in value: - self._features.append(self.__names.setdefault(feature, - feature)) + self._features.append(self.__names.setdefault(feature, feature)) features = property(_get_features, _set_features) def _get_identities(self): @@ -137,7 +113,7 @@ class CapsCache(object): # NOTE: self refers to CapsCache object, not to CacheItem self.identities=identities self.features=features - self.logger.add_caps_entry(self.hash_method, self.hash, + self._logger.add_caps_entry(self.hash_method, self.hash, identities, features) self.__CacheItem = CacheItem @@ -153,7 +129,7 @@ class CapsCache(object): # start logging data from the net self.logger = logger - def load_from_db(self): + def initialize_from_db(self): # get data from logger... if self.logger is not None: for hash_method, hash_, identities, features in \ @@ -169,7 +145,7 @@ class CapsCache(object): hash_method, hash_ = caps - x = self.__CacheItem(hash_method, hash_) + x = self.__CacheItem(hash_method, hash_, self.logger) self.__cache[(hash_method, hash_)] = x return x diff --git a/src/common/optparser.py b/src/common/optparser.py index 05821a2e0..36deaca57 100644 --- a/src/common/optparser.py +++ b/src/common/optparser.py @@ -216,7 +216,7 @@ class OptionsParser: gajim.logger.init_vars() gajim.config.set('version', new_version) - gajim.capscache.load_from_db() + gajim.capscache.initialize_from_db() def update_config_x_to_09(self): # Var name that changed: diff --git a/test/test_caps.py b/test/test_caps.py index 37b24007f..03fa9d9ca 100644 --- a/test/test_caps.py +++ b/test/test_caps.py @@ -6,36 +6,45 @@ import unittest import lib lib.setup_env() -from common import gajim -from common import xmpp from common import helpers - +from common.contacts import Contact from common.caps import CapsCache from mock import Mock -class MockLogger(Mock): - def __init__(self, *args): - Mock.__init__(self, *args) - -class TestCapsCache(unittest.TestCase): + +class CommonCapsTest(unittest.TestCase): + def setUp(self): - self.logger = MockLogger() - self.cc = CapsCache(self.logger) - self.caps_method = 'sha-1' - self.caps_hash = 'zaQfb22o0UCwYDIk8KZOnoZTnrs=' + self.caps_hash = 'RNzJvJnTWqczirzu+YF4V8am9ro=' self.caps = (self.caps_method, self.caps_hash) - self.identity = {'category': 'client', 'type': 'pc'} + + self.node = "http://gajim.org" + self.identity = {'category': 'client', 'type': 'pc', 'name':'Gajim'} self.muc = 'http://jabber.org/protocol/muc' self.chatstates = 'http://jabber.org/protocol/chatstates' self.identities = [self.identity] self.features = [self.muc] + + +class TestCapsCache(CommonCapsTest): + + def setUp(self): + CommonCapsTest.setUp(self) - def test_examples(self): - '''tests the examples given in common/caps.py''' + # Simulate a filled db + db_caps_cache = [ + (self.caps_method, self.caps_hash, self.identities, self.features), + (self.caps_method, self.caps_hash, self.identities, self.features)] + self.logger = Mock(returnValues={"iter_caps_data":db_caps_cache}) + + self.cc = CapsCache(self.logger) + + def test_set_retrieve(self): + ''' Test basic set / retrieve cycle ''' self.cc[self.caps].identities = self.identities self.cc[self.caps].features = self.features @@ -43,20 +52,73 @@ class TestCapsCache(unittest.TestCase): self.assert_(self.muc in self.cc[self.caps].features) self.assert_(self.chatstates not in self.cc[self.caps].features) - id = self.cc[self.caps].identities + identities = self.cc[self.caps].identities - self.assertEqual(1, len(id)) + self.assertEqual(1, len(identities)) - id = id[0] - self.assertEqual('client', id['category']) - self.assertEqual('pc', id['type']) + identity = identities[0] + self.assertEqual('client', identity['category']) + self.assertEqual('pc', identity['type']) + + def test_update(self): + ''' Test caps update gets logged into db ''' + + item = self.cc[self.caps] + item.update(self.identities, self.features) + + self.logger.mockCheckCall(0, "add_caps_entry", self.caps_method, + self.caps_hash, self.identities, self.features) + + def test_initialize_from_db(self): + ''' Read cashed dummy data from db ''' + self.assertEqual(self.cc[self.caps].queried, 0) + self.cc.initialize_from_db() + self.assertEqual(self.cc[self.caps].queried, 2) + def test_preload_triggering_query(self): + ''' Make sure that preload issues a disco ''' + connection = Mock() + + self.cc.preload(connection, "test@gajim.org", self.node, + self.caps_method, self.caps_hash) + + connection.mockCheckCall(0, "discoverInfo", 'test@gajim.org', + 'http://gajim.org#RNzJvJnTWqczirzu+YF4V8am9ro=') + + def test_no_preload_query_if_cashed(self): + ''' Preload must not send a query if the data is already cached ''' + connection = Mock() + + self.cc.initialize_from_db() + self.cc.preload(connection, "test@gajim.org", self.node, + self.caps_method, self.caps_hash) + + self.assertEqual(0, len(connection.mockGetAllCalls())) + + def test_is_supported(self): + contact = Contact(caps_node=self.node, + caps_hash_method=self.caps_method, + caps_hash=self.caps_hash) + + self.assertTrue(self.cc.is_supported(contact, self.chatstates), + msg="Assume everything is supported, if we don't have caps") + + self.cc.initialize_from_db() + + self.assertFalse(self.cc.is_supported(contact, self.chatstates), + msg="Must return false on unsupported feature") + + self.assertTrue(self.cc.is_supported(contact, self.muc), + msg="Must return True on supported feature") + def test_hash(self): '''tests the hash computation''' computed_hash = helpers.compute_caps_hash(self.identities, self.features) - self.assertEqual(self.caps_hash, computed_hash) + + + if __name__ == '__main__': unittest.main()