|
@@ -14,6 +14,7 @@
|
|
|
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
import unittest
|
|
|
+import errno
|
|
|
import os
|
|
|
import signal
|
|
|
|
|
@@ -22,15 +23,47 @@ import isc.config
|
|
|
from isc.server_common.bind10_server import *
|
|
|
from isc.testutils.ccsession_mock import MockModuleCCSession
|
|
|
|
|
|
+TEST_FILENO = 42 # arbitrarily chosen
|
|
|
+
|
|
|
class TestException(Exception):
|
|
|
"""A generic exception class specific in this test module."""
|
|
|
pass
|
|
|
|
|
|
class MyCCSession(MockModuleCCSession, isc.config.ConfigData):
|
|
|
def __init__(self, specfile, config_handler, command_handler):
|
|
|
+ # record parameter for later inspection
|
|
|
+ self.specfile_param = specfile
|
|
|
+ self.config_handler_param = config_handler
|
|
|
+ self.command_handler_param = command_handler
|
|
|
+
|
|
|
+ self.check_command_param = None # used in check_command()
|
|
|
+
|
|
|
+ # Initialize some local attributes of MockModuleCCSession, including
|
|
|
+ # 'stopped'
|
|
|
+ MockModuleCCSession.__init__(self)
|
|
|
+
|
|
|
+ def start(self):
|
|
|
pass
|
|
|
|
|
|
+ def check_command(self, nonblock):
|
|
|
+ """Mock check_command(). Just record the param for later inspection."""
|
|
|
+ self.check_command_param = nonblock
|
|
|
+
|
|
|
+ def get_socket(self):
|
|
|
+ return self
|
|
|
+
|
|
|
+ def fileno(self):
|
|
|
+ """Pretending get_socket().fileno()
|
|
|
+
|
|
|
+ Returing an arbitrarily chosen constant.
|
|
|
+
|
|
|
+ """
|
|
|
+ return TEST_FILENO
|
|
|
+
|
|
|
class MockServer(BIND10Server):
|
|
|
+ def __init__(self):
|
|
|
+ self._select_fn = self.select_wrapper
|
|
|
+
|
|
|
def _setup_ccsession(self):
|
|
|
orig_cls = isc.config.ModuleCCSession
|
|
|
isc.config.ModuleCCSession = MyCCSession
|
|
@@ -41,6 +74,19 @@ class MockServer(BIND10Server):
|
|
|
finally:
|
|
|
isc.config.ModuleCCSession = orig_cls
|
|
|
|
|
|
+ def _config_handler(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def mod_command_handler(self, cmd, args):
|
|
|
+ """A sample _mod_command_handler implementation."""
|
|
|
+ self.command_handler_params = (cmd, args) # for inspection
|
|
|
+ return isc.config.create_answer(0)
|
|
|
+
|
|
|
+ def select_wrapper(self, reads, writes, errors):
|
|
|
+ self._trigger_shutdown() # make sure the loop will stop
|
|
|
+ self.select_params = (reads, writes, errors) # record for inspection
|
|
|
+ return [], [], []
|
|
|
+
|
|
|
class TestBIND10Server(unittest.TestCase):
|
|
|
def setUp(self):
|
|
|
self.__server = MockServer()
|
|
@@ -57,7 +103,7 @@ class TestBIND10Server(unittest.TestCase):
|
|
|
"""Check the signal handler behavior.
|
|
|
|
|
|
SIGTERM and SIGINT should be caught and should call memmgr's
|
|
|
- _trigger_shutdown(). This test also indirectly confirms main() calls
|
|
|
+ _trigger_shutdown(). This test also indirectly confirms run() calls
|
|
|
run_internal().
|
|
|
|
|
|
"""
|
|
@@ -82,10 +128,111 @@ class TestBIND10Server(unittest.TestCase):
|
|
|
raise ex_cls('test')
|
|
|
|
|
|
# Test all possible exceptions that are explicitly caught
|
|
|
- for ex in [TestException]:
|
|
|
+ for ex in [TestException, BIND10ServerFatal]:
|
|
|
self.__server._run_internal = lambda: exception_raiser(ex)
|
|
|
self.assertEqual(1, self.__server.run('test'))
|
|
|
|
|
|
+ def test_run(self):
|
|
|
+ """Check other behavior of run()"""
|
|
|
+ self.__server._run_internal = lambda: None # prevent looping
|
|
|
+ self.assertEqual(0, self.__server.run('test'))
|
|
|
+ # module CC session should have been setup.
|
|
|
+ self.assertEqual(self.__server.mod_ccsession.specfile_param,
|
|
|
+ os.environ['B10_FROM_SOURCE'] +
|
|
|
+ '/src/bin/test/test.spec')
|
|
|
+ self.assertEqual(self.__server.mod_ccsession.config_handler_param,
|
|
|
+ self.__server._config_handler)
|
|
|
+ self.assertEqual(self.__server.mod_ccsession.command_handler_param,
|
|
|
+ self.__server._command_handler)
|
|
|
+
|
|
|
+ def test_shutdown_command(self):
|
|
|
+ answer = self.__server._command_handler('shutdown', None)
|
|
|
+ self.assertTrue(self.__server.shutdown)
|
|
|
+ self.assertEqual((0, None), isc.config.parse_answer(answer))
|
|
|
+
|
|
|
+ def test_other_command(self):
|
|
|
+ self.__server._mod_command_handler = self.__server.mod_command_handler
|
|
|
+ answer = self.__server._command_handler('other command', None)
|
|
|
+ # shouldn't be confused with shutdown
|
|
|
+ self.assertFalse(self.__server.shutdown)
|
|
|
+ self.assertEqual((0, None), isc.config.parse_answer(answer))
|
|
|
+ self.assertEqual(('other command', None),
|
|
|
+ self.__server.command_handler_params)
|
|
|
+
|
|
|
+ def test_other_command_nohandler(self):
|
|
|
+ """Similar to test_other_command, but without explicit handler"""
|
|
|
+ # In this case "unknown command" error should be returned.
|
|
|
+ answer = self.__server._command_handler('other command', None)
|
|
|
+ self.assertEqual(1, isc.config.parse_answer(answer)[0])
|
|
|
+
|
|
|
+ def test_run_internal(self):
|
|
|
+ self.__server._setup_ccsession()
|
|
|
+ self.__server._run_internal()
|
|
|
+ self.assertEqual(([TEST_FILENO], [], []), self.__server.select_params)
|
|
|
+
|
|
|
+ def select_wrapper(self, r, w, e, ex=None, ret=None):
|
|
|
+ """Mock select() function used some of the tests below.
|
|
|
+
|
|
|
+ If ex is not None and it's first call to this method, it raises ex
|
|
|
+ assuming it's an exception.
|
|
|
+
|
|
|
+ If ret is not None, it returns the given value; otherwise it returns
|
|
|
+ all empty lists.
|
|
|
+
|
|
|
+ """
|
|
|
+ self.select_params.append((r, w, e))
|
|
|
+ if ex is not None and len(self.select_params) == 1:
|
|
|
+ raise ex
|
|
|
+ else:
|
|
|
+ self.__server._trigger_shutdown()
|
|
|
+ if ret is not None:
|
|
|
+ return ret
|
|
|
+ return [], [], []
|
|
|
+
|
|
|
+ def test_select_for_command(self):
|
|
|
+ """A normal event iteration, handling one command."""
|
|
|
+ self.select_params = []
|
|
|
+ self.__server._select_fn = \
|
|
|
+ lambda r, w, e: self.select_wrapper(r, w, e,
|
|
|
+ ret=([TEST_FILENO], [], []))
|
|
|
+ self.__server._setup_ccsession()
|
|
|
+ self.__server._run_internal()
|
|
|
+ # select should be called only once.
|
|
|
+ self.assertEqual([([TEST_FILENO], [], [])], self.select_params)
|
|
|
+ # check_command should have been called.
|
|
|
+ self.assertTrue(self.__server.mod_ccsession.check_command_param)
|
|
|
+ # module CC session should have been stopped explicitly.
|
|
|
+ self.assertTrue(self.__server.mod_ccsession.stopped)
|
|
|
+
|
|
|
+ def test_select_interrupted(self):
|
|
|
+ """Emulating case select() raises EINTR."""
|
|
|
+ self.select_params = []
|
|
|
+ self.__server._select_fn = \
|
|
|
+ lambda r, w, e: self.select_wrapper(r, w, e,
|
|
|
+ ex=select.error(errno.EINTR))
|
|
|
+ self.__server._setup_ccsession()
|
|
|
+ self.__server._run_internal()
|
|
|
+ # EINTR will be ignored and select() will be called again.
|
|
|
+ self.assertEqual([([TEST_FILENO], [], []), ([TEST_FILENO], [], [])],
|
|
|
+ self.select_params)
|
|
|
+ # check_command() shouldn't have been called
|
|
|
+ self.assertIsNone(self.__server.mod_ccsession.check_command_param)
|
|
|
+ self.assertTrue(self.__server.mod_ccsession.stopped)
|
|
|
+
|
|
|
+ def test_select_other_exception(self):
|
|
|
+ """Emulating case select() raises other select error."""
|
|
|
+ self.select_params = []
|
|
|
+ self.__server._select_fn = \
|
|
|
+ lambda r, w, e: self.select_wrapper(r, w, e,
|
|
|
+ ex=select.error(errno.EBADF))
|
|
|
+ self.__server._setup_ccsession()
|
|
|
+ # the exception will be propagated.
|
|
|
+ self.assertRaises(select.error, self.__server._run_internal)
|
|
|
+ self.assertEqual([([TEST_FILENO], [], [])], self.select_params)
|
|
|
+ # in this case module CC session hasn't been stopped explicitly
|
|
|
+ # others will notice it due to connection reset.
|
|
|
+ self.assertFalse(self.__server.mod_ccsession.stopped)
|
|
|
+
|
|
|
if __name__== "__main__":
|
|
|
isc.log.init("bind10_server_test")
|
|
|
isc.log.resetUnitTestRootLogger()
|