|
@@ -0,0 +1,396 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+import unittest
|
|
|
+import isc.log
|
|
|
+import isc.bind10.socket_cache
|
|
|
+import isc.bind10.sockcreator
|
|
|
+from isc.net.addr import IPAddr
|
|
|
+import os
|
|
|
+
|
|
|
+class Test(unittest.TestCase):
|
|
|
+ """
|
|
|
+ Base for the tests here. It replaces the os.close method.
|
|
|
+ """
|
|
|
+ def setUp(self):
|
|
|
+ self._closes = []
|
|
|
+ isc.bind10.socket_cache.os.close = self.__close
|
|
|
+
|
|
|
+ def tearDown(self):
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ isc.bind10.socket_cache.os.close = lambda fd: None
|
|
|
+
|
|
|
+ def __close(self, fd):
|
|
|
+ """
|
|
|
+ Just log a close was called.
|
|
|
+ """
|
|
|
+ self._closes.append(fd)
|
|
|
+
|
|
|
+class SocketTest(Test):
|
|
|
+ """
|
|
|
+ Test for the Socket class.
|
|
|
+ """
|
|
|
+ def setUp(self):
|
|
|
+ """
|
|
|
+ Creates the socket to be tested.
|
|
|
+
|
|
|
+ It also creates other useful test variables.
|
|
|
+ """
|
|
|
+ Test.setUp(self)
|
|
|
+ self.__address = IPAddr("192.0.2.1")
|
|
|
+ self.__socket = isc.bind10.socket_cache.Socket('UDP', self.__address,
|
|
|
+ 1024, 42)
|
|
|
+
|
|
|
+ def test_init(self):
|
|
|
+ """
|
|
|
+ Checks the intrnals of the cache just after the creation.
|
|
|
+ """
|
|
|
+ self.assertEqual('UDP', self.__socket.protocol)
|
|
|
+ self.assertEqual(self.__address, self.__socket.address)
|
|
|
+ self.assertEqual(1024, self.__socket.port)
|
|
|
+ self.assertEqual(42, self.__socket.fileno)
|
|
|
+ self.assertEqual({}, self.__socket.active_tokens)
|
|
|
+ self.assertEqual({}, self.__socket.shares)
|
|
|
+ self.assertEqual(set(), self.__socket.waiting_tokens)
|
|
|
+
|
|
|
+ def test_del(self):
|
|
|
+ """
|
|
|
+ Check it closes the socket when removed.
|
|
|
+ """
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket = None
|
|
|
+ self.assertEqual([42], self._closes)
|
|
|
+
|
|
|
+ def test_share_modes(self):
|
|
|
+ """
|
|
|
+ Test the share mode compatibility check function.
|
|
|
+ """
|
|
|
+ modes = ['NO', 'SAMEAPP', 'ANY']
|
|
|
+
|
|
|
+ for mode in modes:
|
|
|
+ self.assertTrue(self.__socket.share_compatible(mode, 'anything'))
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket.shares = {'token': ('NO', 'anything')}
|
|
|
+ for mode in modes:
|
|
|
+ self.assertFalse(self.__socket.share_compatible(mode, 'anything'))
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket.shares = {'token': ('SAMEAPP', 'app')}
|
|
|
+ self.assertFalse(self.__socket.share_compatible('NO', 'app'))
|
|
|
+ self.assertFalse(self.__socket.share_compatible('SAMEAPP',
|
|
|
+ 'something'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('SAMEAPP', 'app'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('ANY', 'app'))
|
|
|
+ self.assertFalse(self.__socket.share_compatible('ANY', 'something'))
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket.shares = {'token': ('ANY', 'app')}
|
|
|
+ self.assertFalse(self.__socket.share_compatible('NO', 'app'))
|
|
|
+ self.assertFalse(self.__socket.share_compatible('SAMEAPP',
|
|
|
+ 'something'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('SAMEAPP', 'app'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('ANY', 'something'))
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket.shares = {
|
|
|
+ 'token': ('ANY', 'app'),
|
|
|
+ 'another': ('SAMEAPP', 'app')
|
|
|
+ }
|
|
|
+ self.assertFalse(self.__socket.share_compatible('NO', 'app'))
|
|
|
+ self.assertFalse(self.__socket.share_compatible('SAMEAPP',
|
|
|
+ 'something'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('SAMEAPP', 'app'))
|
|
|
+ self.assertFalse(self.__socket.share_compatible('ANY', 'something'))
|
|
|
+ self.assertTrue(self.__socket.share_compatible('ANY', 'app'))
|
|
|
+
|
|
|
+
|
|
|
+ self.assertRaises(ValueError, self.__socket.share_compatible, 'bad',
|
|
|
+ 'bad')
|
|
|
+
|
|
|
+class SocketCacheTest(Test):
|
|
|
+ """
|
|
|
+ Some tests for the isc.bind10.socket_cache.Cache.
|
|
|
+
|
|
|
+ This class, as well as being the testcase, pretends to be the
|
|
|
+ socket creator so it can hijack all the requests for sockets.
|
|
|
+ """
|
|
|
+ def setUp(self):
|
|
|
+ """
|
|
|
+ Creates the cache for tests with us being the socket creator.
|
|
|
+
|
|
|
+ Also creates some more variables for testing.
|
|
|
+ """
|
|
|
+ Test.setUp(self)
|
|
|
+ self.__cache = isc.bind10.socket_cache.Cache(self)
|
|
|
+ self.__address = IPAddr("192.0.2.1")
|
|
|
+ self.__socket = isc.bind10.socket_cache.Socket('UDP', self.__address,
|
|
|
+ 1024, 42)
|
|
|
+ self.__get_socket_called = False
|
|
|
+
|
|
|
+ def test_init(self):
|
|
|
+ """
|
|
|
+ Checks the internals of the cache just after the creation.
|
|
|
+ """
|
|
|
+ self.assertEqual(self, self.__cache._creator)
|
|
|
+ self.assertEqual({}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._active_apps)
|
|
|
+ self.assertEqual({}, self.__cache._sockets)
|
|
|
+ self.assertEqual(set(), self.__cache._live_tokens)
|
|
|
+
|
|
|
+ def get_socket(self, address, port, socktype):
|
|
|
+ """
|
|
|
+ Pretend to be a socket creator.
|
|
|
+
|
|
|
+ This expects to be called with the _address, port 1024 and 'UDP'.
|
|
|
+
|
|
|
+ Returns 42 and notes down it was called.
|
|
|
+ """
|
|
|
+ self.assertEqual(self.__address, address)
|
|
|
+ self.assertEqual(1024, port)
|
|
|
+ self.assertEqual('UDP', socktype)
|
|
|
+ self.__get_socket_called = True
|
|
|
+ return 42
|
|
|
+
|
|
|
+ def test_get_token_cached(self):
|
|
|
+ """
|
|
|
+ Check the behaviour of get_token when the requested socket is already
|
|
|
+ cached inside.
|
|
|
+ """
|
|
|
+ self.__cache._sockets = {
|
|
|
+ 'UDP': {'192.0.2.1': {1024: self.__socket}}
|
|
|
+ }
|
|
|
+ token = self.__cache.get_token('UDP', self.__address, 1024, 'ANY',
|
|
|
+ 'test')
|
|
|
+
|
|
|
+ self.assertFalse(self.__get_socket_called)
|
|
|
+
|
|
|
+ self.assertIsNotNone(token)
|
|
|
+
|
|
|
+ self.assertEqual({token: self.__socket}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual(set([token]), self.__cache._live_tokens)
|
|
|
+
|
|
|
+ self.assertEqual({token: ('ANY', 'test')}, self.__socket.shares)
|
|
|
+
|
|
|
+ self.assertEqual(set([token]), self.__socket.waiting_tokens)
|
|
|
+
|
|
|
+
|
|
|
+ self.assertRaises(isc.bind10.socket_cache.ShareError,
|
|
|
+ self.__cache.get_token, 'UDP', self.__address, 1024,
|
|
|
+ 'NO', 'test')
|
|
|
+
|
|
|
+ self.assertEqual({token: self.__socket}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual(set([token]), self.__cache._live_tokens)
|
|
|
+ self.assertEqual({token: ('ANY', 'test')}, self.__socket.shares)
|
|
|
+ self.assertEqual(set([token]), self.__socket.waiting_tokens)
|
|
|
+
|
|
|
+ def test_get_token_uncached(self):
|
|
|
+ """
|
|
|
+ Check a new socket is created when a corresponding one is missing.
|
|
|
+ """
|
|
|
+ token = self.__cache.get_token('UDP', self.__address, 1024, 'ANY',
|
|
|
+ 'test')
|
|
|
+
|
|
|
+ self.assertTrue(self.__get_socket_called)
|
|
|
+
|
|
|
+ self.assertIsNotNone(token)
|
|
|
+
|
|
|
+ socket = self.__cache._waiting_tokens[token]
|
|
|
+ self.assertEqual(self.__address, socket.address)
|
|
|
+ self.assertEqual(1024, socket.port)
|
|
|
+ self.assertEqual(42, socket.fileno)
|
|
|
+ self.assertEqual('UDP', socket.protocol)
|
|
|
+
|
|
|
+ self.assertEqual({
|
|
|
+ 'UDP': {'192.0.2.1': {1024: socket}}
|
|
|
+ }, self.__cache._sockets)
|
|
|
+
|
|
|
+ self.assertEqual({token: socket}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual(set([token]), self.__cache._live_tokens)
|
|
|
+
|
|
|
+ self.assertEqual({token: ('ANY', 'test')}, socket.shares)
|
|
|
+
|
|
|
+ self.assertEqual(set([token]), socket.waiting_tokens)
|
|
|
+
|
|
|
+ def test_get_token_excs(self):
|
|
|
+ """
|
|
|
+ Test that it is handled properly if the socket creator raises
|
|
|
+ some exceptions.
|
|
|
+ """
|
|
|
+ def raiseCreatorError(fatal):
|
|
|
+ raise isc.bind10.sockcreator.CreatorError('test error', fatal)
|
|
|
+
|
|
|
+ self.get_socket = lambda addr, port, proto: raiseCreatorError(True)
|
|
|
+ self.assertRaises(isc.bind10.sockcreator.CreatorError,
|
|
|
+ self.__cache.get_token, 'UDP', self.__address, 1024,
|
|
|
+ 'NO', 'test')
|
|
|
+
|
|
|
+ self.get_socket = lambda addr, port, proto: raiseCreatorError(False)
|
|
|
+ self.assertRaises(isc.bind10.socket_cache.SocketError,
|
|
|
+ self.__cache.get_token, 'UDP', self.__address, 1024,
|
|
|
+ 'NO', 'test')
|
|
|
+
|
|
|
+ def test_get_socket(self):
|
|
|
+ """
|
|
|
+ Test that we can pickup a socket if we know a token.
|
|
|
+ """
|
|
|
+ token = "token"
|
|
|
+ app = 13
|
|
|
+
|
|
|
+ self.assertRaises(ValueError, self.__cache.get_socket, token, app)
|
|
|
+
|
|
|
+ self.assertEqual({}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._active_apps)
|
|
|
+ self.assertEqual({}, self.__cache._sockets)
|
|
|
+ self.assertEqual(set(), self.__cache._live_tokens)
|
|
|
+
|
|
|
+ self.__socket.waiting_tokens = set([token])
|
|
|
+ self.__socket.shares = {token: ('ANY', 'app')}
|
|
|
+ self.__cache._waiting_tokens = {token: self.__socket}
|
|
|
+ self.__cache._sockets = {'UDP': {'192.0.2.1': {1024: self.__socket}}}
|
|
|
+ self.__cache._live_tokens = set([token])
|
|
|
+ socket = self.__cache.get_socket(token, app)
|
|
|
+
|
|
|
+ self.assertEqual(42, socket)
|
|
|
+
|
|
|
+ self.assertEqual({}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual({token: self.__socket}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({13: set([token])}, self.__cache._active_apps)
|
|
|
+ self.assertEqual(set([token]), self.__cache._live_tokens)
|
|
|
+ self.assertEqual(set(), self.__socket.waiting_tokens)
|
|
|
+ self.assertEqual({token: 13}, self.__socket.active_tokens)
|
|
|
+
|
|
|
+ self.assertRaises(ValueError, self.__cache.get_socket, token, app)
|
|
|
+
|
|
|
+ def test_drop_application(self):
|
|
|
+ """
|
|
|
+ Test that a drop_application calls drop_socket on all the sockets
|
|
|
+ held by the application.
|
|
|
+ """
|
|
|
+ sockets = set()
|
|
|
+ def drop_socket(token):
|
|
|
+ sockets.add(token)
|
|
|
+
|
|
|
+ self.__cache.drop_socket = drop_socket
|
|
|
+ self.assertRaises(ValueError, self.__cache.drop_application,
|
|
|
+ 13)
|
|
|
+ self.assertEqual(set(), sockets)
|
|
|
+
|
|
|
+
|
|
|
+ self.__cache._active_apps = {
|
|
|
+ 1: set(['t1', 't2']),
|
|
|
+ 2: set(['t3'])
|
|
|
+ }
|
|
|
+ self.__cache.drop_application(1)
|
|
|
+
|
|
|
+
|
|
|
+ self.assertEqual(set(['t1', 't2']), sockets)
|
|
|
+
|
|
|
+ def test_drop_socket(self):
|
|
|
+ """
|
|
|
+ Test the drop_socket call. It tests:
|
|
|
+ * That a socket that still has something to keep it alive is left alive
|
|
|
+ (both waiting and active).
|
|
|
+ * If not, it is deleted.
|
|
|
+ * All bookkeeping data around are properly removed.
|
|
|
+ * Of course the exception.
|
|
|
+ """
|
|
|
+ self.assertRaises(ValueError, self.__cache.drop_socket, "bad token")
|
|
|
+ self.__socket.active_tokens = {'t1': 1}
|
|
|
+ self.__socket.waiting_tokens = set(['t2'])
|
|
|
+ self.__socket.shares = {'t1': ('ANY', 'app1'), 't2': ('ANY', 'app2')}
|
|
|
+ self.__cache._waiting_tokens = {'t2': self.__socket}
|
|
|
+ self.__cache._active_tokens = {'t1': self.__socket}
|
|
|
+ self.__cache._sockets = {'UDP': {'192.0.2.1': {1024: self.__socket}}}
|
|
|
+ self.__cache._live_tokens = set(['t1', 't2'])
|
|
|
+ self.__cache._active_apps = {1: set(['t1'])}
|
|
|
+
|
|
|
+ self.assertRaises(ValueError, self.__cache.drop_socket, 't2')
|
|
|
+ self.assertEqual({'t1': 1}, self.__socket.active_tokens)
|
|
|
+ self.assertEqual(set(['t2']), self.__socket.waiting_tokens)
|
|
|
+ self.assertEqual({'t1': ('ANY', 'app1'), 't2': ('ANY', 'app2')},
|
|
|
+ self.__socket.shares)
|
|
|
+ self.assertEqual({'t2': self.__socket}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual({'t1': self.__socket}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({'UDP': {'192.0.2.1': {1024: self.__socket}}},
|
|
|
+ self.__cache._sockets)
|
|
|
+ self.assertEqual(set(['t1', 't2']), self.__cache._live_tokens)
|
|
|
+ self.assertEqual({1: set(['t1'])}, self.__cache._active_apps)
|
|
|
+ self.assertEqual([], self._closes)
|
|
|
+
|
|
|
+ self.__cache.drop_socket('t1')
|
|
|
+ self.assertEqual({}, self.__socket.active_tokens)
|
|
|
+ self.assertEqual(set(['t2']), self.__socket.waiting_tokens)
|
|
|
+ self.assertEqual({'t2': ('ANY', 'app2')}, self.__socket.shares)
|
|
|
+ self.assertEqual({}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({'UDP': {'192.0.2.1': {1024: self.__socket}}},
|
|
|
+ self.__cache._sockets)
|
|
|
+ self.assertEqual(set(['t2']), self.__cache._live_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._active_apps)
|
|
|
+ self.assertEqual([], self._closes)
|
|
|
+
|
|
|
+ self.__socket.active_tokens = {'t1': 1, 't2': 2}
|
|
|
+ self.__socket.waiting_tokens = set()
|
|
|
+ self.__socket.shares = {'t1': ('ANY', 'app1'), 't2': ('ANY', 'app2')}
|
|
|
+ self.__cache._waiting_tokens = {}
|
|
|
+ self.__cache._active_tokens = {
|
|
|
+ 't1': self.__socket,
|
|
|
+ 't2': self.__socket
|
|
|
+ }
|
|
|
+ self.__cache._live_tokens = set(['t1', 't2', 't3'])
|
|
|
+ self.assertEqual([], self._closes)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ self.__cache._active_apps = {1: set(['t1', 't3']), 2: set(['t2'])}
|
|
|
+
|
|
|
+ self.__cache.drop_socket('t1')
|
|
|
+ self.assertEqual({'t2': 2}, self.__socket.active_tokens)
|
|
|
+ self.assertEqual(set(), self.__socket.waiting_tokens)
|
|
|
+ self.assertEqual({'t2': ('ANY', 'app2')}, self.__socket.shares)
|
|
|
+ self.assertEqual({}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual({'t2': self.__socket}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({'UDP': {'192.0.2.1': {1024: self.__socket}}},
|
|
|
+ self.__cache._sockets)
|
|
|
+ self.assertEqual(set(['t3', 't2']), self.__cache._live_tokens)
|
|
|
+ self.assertEqual({1: set(['t3']), 2: set(['t2'])},
|
|
|
+ self.__cache._active_apps)
|
|
|
+ self.assertEqual([], self._closes)
|
|
|
+
|
|
|
+
|
|
|
+ self.__cache.drop_socket('t2')
|
|
|
+ self.assertEqual({}, self.__socket.active_tokens)
|
|
|
+ self.assertEqual(set(), self.__socket.waiting_tokens)
|
|
|
+ self.assertEqual({}, self.__socket.shares)
|
|
|
+ self.assertEqual({}, self.__cache._waiting_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._active_tokens)
|
|
|
+ self.assertEqual({}, self.__cache._sockets)
|
|
|
+ self.assertEqual(set(['t3']), self.__cache._live_tokens)
|
|
|
+ self.assertEqual({1: set(['t3'])}, self.__cache._active_apps)
|
|
|
+
|
|
|
+
|
|
|
+ self.__socket = None
|
|
|
+ self.assertEqual([42], self._closes)
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ isc.log.init("bind10")
|
|
|
+ isc.log.resetUnitTestRootLogger()
|
|
|
+ unittest.main()
|