Пример #1
0
    def __init__(self, *args, **kwargs):

        # Setup default options
        bundle = kwargs.get("bundle", None)
        connection = kwargs.get("connection", None)
        stop_event = kwargs.get("stop_event", Event())

        if connection is None:
            raise ValueError("Connection is required.")

        # Model-related
        bundle_dir = os.path.dirname(inspect.getfile(self.__class__))
        if bundle is None:
            bundle = Bundle(bundle_dir=bundle_dir)
        self.bundle = bundle
        self.stop_event = stop_event
        self.reg_thread = None
        self.reg_delay = lambda: random() * 5

        # Router-related (Dispatch)
        self.router = Router()
        self.dispatch_thread_count = 3
        self.thread_list = []

        # Response-related (Resolve)
        self._session = Session()
        self.resolve_thread_count = 1

        # Message Bus
        self._conn = connection
        self.is_ready = Event()
        self.req_queue = Queue()
        self.res_queue = Queue()

        # Setup callbacks
        self._conn.set_on_message(self.on_sanji_message)
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_publish(self.on_publish)

        # Publisher
        self.publish = Publish(self._conn, self._session)

        # Register signal to call stop() (only mainthread could do this)
        if threading.current_thread().__class__.__name__ == '_MainThread':
            signal.signal(signal.SIGINT, self.exit)

        # Auto-register routes
        methods = inspect.getmembers(self, predicate=inspect.ismethod)
        self._register_routes(methods)

        # Custom init function
        if hasattr(self, 'init') and \
           hasattr(self.init, '__call__'):
            _logger.debug("Custom init start")
            self.init(*args, **kwargs)
            _logger.debug("Custom init finish")
Пример #2
0
    def __init__(self, bundle=None, connection=None, stop_event=Event()):
        if connection is None:
            raise ValueError("Connection is required.")
        # Model-related
        bundle_dir = os.path.dirname(inspect.getfile(self.__class__))
        if bundle is None:
            bundle = Bundle(bundle_dir=bundle_dir)
        self.bundle = bundle
        self.stop_event = stop_event

        # Router-related (Dispatch)
        self.router = Router()
        self.dispatch_thread_count = 5
        self.dispatch_thread_list = []

        # Response-related (Resolve)
        self._session = Session()
        self.resolve_thread_count = 1
        self.resolve_thread_list = []

        # Message Bus
        self._conn = connection
        self.is_ready = Event()
        self.req_queue = Queue()
        self.res_queue = Queue()

        # Setup callbacks
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_message(self.on_message)
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_publish(self.on_publish)

        # Publisher
        self.publish = Publish(self._conn, self._session)

        # Register signal to call stop()
        signal.signal(signal.SIGINT, self.exit)

        # Auto-register routes
        methods = inspect.getmembers(self, predicate=inspect.ismethod)
        self._register_routes(methods)

        # Custom init function
        logger.debug("Custom init start")
        self.init()
        logger.debug("Custom init finish")
Пример #3
0
class Sanji(object):
    """
    This is for sanji framework.
    """
    def __init__(self, bundle=None, connection=None, stop_event=Event()):
        if connection is None:
            raise ValueError("Connection is required.")
        # Model-related
        bundle_dir = os.path.dirname(inspect.getfile(self.__class__))
        if bundle is None:
            bundle = Bundle(bundle_dir=bundle_dir)
        self.bundle = bundle
        self.stop_event = stop_event

        # Router-related (Dispatch)
        self.router = Router()
        self.dispatch_thread_count = 5
        self.dispatch_thread_list = []

        # Response-related (Resolve)
        self._session = Session()
        self.resolve_thread_count = 1
        self.resolve_thread_list = []

        # Message Bus
        self._conn = connection
        self.is_ready = Event()
        self.req_queue = Queue()
        self.res_queue = Queue()

        # Setup callbacks
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_message(self.on_message)
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_publish(self.on_publish)

        # Publisher
        self.publish = Publish(self._conn, self._session)

        # Register signal to call stop()
        signal.signal(signal.SIGINT, self.exit)

        # Auto-register routes
        methods = inspect.getmembers(self, predicate=inspect.ismethod)
        self._register_routes(methods)

        # Custom init function
        logger.debug("Custom init start")
        self.init()
        logger.debug("Custom init finish")

    def _register_routes(self, methods):
        """
        _register_routes
        """
        # setup routes by decorator
        methods = [(n, v) for (n, v) in methods if v.__name__ == "wrapper"]
        methods = sorted(methods, key=lambda x: x[1]._order)
        for name, value in methods:
            value()  # execute setting route

        return methods

    def _dispatch_message(self, stop_event):
        """
        _dispatch_message
        """
        while not stop_event.is_set():
            try:
                message = self.req_queue.get(timeout=0.1)
            except Empty:
                continue

            results = self.router.dispatch(message)
            if len(results) == 0:
                error_msg = "Route '%s' not found." % message.resource
                logger.info(error_msg)
                logger.debug(message.to_json())
                resp = self.publish.create_response(
                    message, self.bundle.profile["name"])
                resp(code=404, data={"message": error_msg})
                continue

            try:
                for result in results:  # same route
                    resp = self.publish.create_response(
                        result["message"], self.bundle.profile["name"])
                    map(lambda cb: cb(self, result["message"], resp),
                        result["callbacks"])
            except Exception as e:
                logger.warning(e)
                resp = self.publish.create_response(
                    message, self.bundle.profile["name"])
                resp(code=500, data={"message": "Internal Error."})

        logger.debug("_dispatch_message thread is terminated")

    def _resolve_responses(self, stop_event):
        """
        _resolve_responses
        """
        while not stop_event.is_set():
            try:
                message = self.res_queue.get(timeout=0.1)
            except Empty:
                continue
            session = self._session.resolve(message.id, message)
            if session is None:
                logger.debug("Unknow response. Not for me.")
        logger.debug("_resolve_responses thread is terminated")

    def on_publish(self, client, userdata, mid):
        with self._session.session_lock:
            self._session.resolve_send(mid)

    def _create_thread_pool(self):
        # create a thread pool
        for _ in range(0, self.dispatch_thread_count):
            stop_event = Event()
            thread = Thread(target=self._dispatch_message,
                            name="thread-%s" % _, args=(stop_event,))
            thread.daemon = True
            thread.start()
            self.dispatch_thread_list.append((thread, stop_event))

        for _ in range(0, 1):
            stop_event = Event()
            thread = Thread(target=self._resolve_responses,
                            name="thread-%s" % _, args=(stop_event,))
            thread.daemon = True
            thread.start()
            self.dispatch_thread_list.append((thread, stop_event))

        logger.debug("Thread pool is created")

    def start(self):
        """
        start
        """
        def main_thread():
            # create resp, req thread pool
            self._create_thread_pool()

            # start connection, this will block until stop()
            self.conn_thread = Thread(target=self._conn.connect)
            self.conn_thread.daemon = True
            self.conn_thread.start()

            # register model to controller...
            self.is_ready.wait()
            self.deregister()
            self.register(self.get_profile())

            if hasattr(self, 'run'):
                logger.debug("Start running...")
                self.run()

        # start main_thread
        self.main_thread = Thread(target=main_thread)
        self.main_thread.daemon = True
        self.main_thread.start()

        # control this bundle stop or not
        while not self.stop_event.wait(0.1):
            pass

        self.stop()
        logger.debug("Shutdown successfully")

    def exit(self, signum=None, frame=None):
        """
        hook ctrl + c to exit program
        """
        self.stop()
        sys.exit(0)

    def stop(self, *args, **kwargs):
        """
        exit
        """
        logger.debug("Bundle [%s] has been shutting down" %
                     self.bundle.profile["name"])
        self._conn.disconnect()
        self._session.stop()
        self.stop_event.set()

        # TODO: shutdown all threads
        for thread, event in self.dispatch_thread_list:
            event.set()
        for thread, event in self.dispatch_thread_list:
            thread.join()
        self.is_ready.clear()

    def init(self):
        """
        This is for user implement
        """
        pass

    def on_message(self, client, userdata, msg):
        """This function will recevie all message from mqtt
        client
            the client instance for this callback
        userdata
            the private user data as set in Client() or userdata_set()
        message
            an instance of MQTTMessage. This is a class with members topic,
            payload, qos, retain.
        """
        try:
            message = Message(msg.payload)
        except (TypeError, ValueError) as e:
            logger.debug(e)
            return

        if message.type() == MessageType.UNKNOWN:
            logger.debug("Got an UNKNOWN message, don't dispatch")
            return

        if message.type() == MessageType.RESPONSE:
            self.res_queue.put(message)

        if message.type() == MessageType.REQUEST or \
           message.type() == MessageType.DIRECT or \
           message.type() == MessageType.HOOK or \
           message.type() == MessageType.EVENT:
            self.req_queue.put(message)

    def on_connect(self, client, userdata, flags, rc):
        """
        on_connect(self, client, obj, flags, rc):
        client
            the client instance for this callback
        userdata
            the private user data as set in Client() or userdata_set()
        flags
            response flags sent by the broker
        rc
            the connection result
        """
        self._conn.set_tunnel(self._conn.tunnel)
        self.is_ready.set()
        logger.debug("Connection established with result code %s" % rc)

    def register(self, reg_data, retry=True, interval=1, timeout=3):
        """
        register function
        retry
            True, infinity retries
            False, no retries
            Number, retries times
        interval
            time period for retry
        return
            False if no success
            Tunnel if success
        """
        resp = Retry(target=self.publish.direct.post,
                     args=("/controller/registration", reg_data,),
                     kwargs={"timeout": timeout},
                     options={"retry": retry, "interval": interval})
        if resp is None:
            logger.error("Can\'t not register to controller")
            sys.exit(1)

        self._conn.set_tunnel(resp.data["tunnel"])
        logger.info("Register successfully tunnel: %s"
                    % (resp.data["tunnel"],))

    def deregister(self, retry=True, interval=1, timeout=3):
        data = {
            "name": self.bundle.profile["name"]
        }

        Retry(target=self.publish.direct.delete,
              args=("/controller/registration", data,),
              kwargs={"timeout": timeout},
              options={"retry": retry, "interval": interval})
        logger.info("Deregister successfully tunnel: %s" %
                    (self._conn.tunnel,))

    def get_profile(self):
        self.bundle.profile["tunnel"] = self._conn.tunnel
        self.bundle.profile["resources"] = [_["resource"] for _ in
                                            self.bundle.profile["resources"]]
        return self.bundle.profile
Пример #4
0
 def setUp(self, Thread, sleep):
     self.thread_mock = Thread.return_value = MagicMock()
     self.thread_mock.is_alive.return_value = True
     self.session = Session()
Пример #5
0
class Sanji(object):
    """
    This is for sanji framework.
    """
    def __init__(self, *args, **kwargs):

        # Setup default options
        bundle = kwargs.get("bundle", None)
        connection = kwargs.get("connection", None)
        stop_event = kwargs.get("stop_event", Event())

        if connection is None:
            raise ValueError("Connection is required.")

        # Model-related
        bundle_dir = os.path.dirname(inspect.getfile(self.__class__))
        if bundle is None:
            bundle = Bundle(bundle_dir=bundle_dir)
        self.bundle = bundle
        self.stop_event = stop_event
        self.reg_thread = None
        self.reg_delay = lambda: random() * 5

        # Router-related (Dispatch)
        self.router = Router()
        self.dispatch_thread_count = 3
        self.thread_list = []

        # Response-related (Resolve)
        self._session = Session()
        self.resolve_thread_count = 1

        # Message Bus
        self._conn = connection
        self.is_ready = Event()
        self.req_queue = Queue()
        self.res_queue = Queue()

        # Setup callbacks
        self._conn.set_on_message(self.on_sanji_message)
        self._conn.set_on_connect(self.on_connect)
        self._conn.set_on_publish(self.on_publish)

        # Publisher
        self.publish = Publish(self._conn, self._session)

        # Register signal to call stop() (only mainthread could do this)
        if threading.current_thread().__class__.__name__ == '_MainThread':
            signal.signal(signal.SIGINT, self.exit)

        # Auto-register routes
        methods = inspect.getmembers(self, predicate=inspect.ismethod)
        self._register_routes(methods)

        # Custom init function
        if hasattr(self, 'init') and \
           hasattr(self.init, '__call__'):
            _logger.debug("Custom init start")
            self.init(*args, **kwargs)
            _logger.debug("Custom init finish")

    def _register_routes(self, methods):
        """
        _register_routes
        """
        # setup routes by decorator
        methods = [(n, v) for (n, v) in methods if v.__name__ == "wrapper"]
        methods = sorted(methods, key=lambda x: x[1]._order)
        for name, value in methods:
            value()  # execute setting route

        return methods

    def _dispatch_message(self):
        """
        _dispatch_message
        """
        while True:
            message = self.req_queue.get()
            if message is None:
                _logger.debug("_dispatch_message thread is terminated")
                return

            if message._type != MessageType.EVENT:
                self.__dispatch_message(message)
            elif message._type == MessageType.EVENT:
                self.__dispatch_event_message(message)

    def __dispatch_event_message(self, message):
        results = self.router.dispatch(message)

        def ___dispatch(handler, message):
            args_len = len(inspect.getargspec(handler["callback"]).args)
            if args_len == 2:
                handler["callback"](self, result["message"])

        try:
            for result in results:  # same route
                map(lambda handler: ___dispatch(handler, result["message"]),
                    result["handlers"])
        except Exception as e:
            _logger.error(e, exc_info=True)

    def __dispatch_message(self, message):
        results = self.router.dispatch(message)
        # Request not found
        if len(results) == 0:
            error_msg = "Route '%s' not found." % message.resource
            _logger.info(error_msg)
            _logger.debug(message.to_json())
            resp = self.publish.create_response(
                message, self.bundle.profile["name"])
            resp(code=404, data={"message": error_msg})
            return

        def ___dispatch(handler, message, resp):
            if handler["schema"] is not None:
                message.data = handler["schema"](message.data)
            args_len = len(inspect.getargspec(handler["callback"]).args)
            if args_len >= 3:
                handler["callback"](self, result["message"], resp)

        try:
            for result in results:  # same route
                resp = self.publish.create_response(
                    result["message"], self.bundle.profile["name"])
                map(lambda handler: ___dispatch(
                    handler, result["message"], resp
                    ),
                    result["handlers"])
        except Exception as e:
            _logger.error(e, exc_info=True)
            resp_data = {"message": "Internal Error."}

            if os.getenv("SANJI_ENV", 'debug') == 'debug':
                ex_type, ex, tb = sys.exc_info()
                resp_data["traceback"] = "".join(traceback.format_tb(tb))

            resp = self.publish.create_response(
                message, self.bundle.profile["name"])

            # if exception is belongs to schema error
            if isinstance(e, MultipleInvalid):
                return resp(code=400, data={"message": str(e)})

            return resp(code=500, data=resp_data)

    def _resolve_responses(self):
        """
        _resolve_responses
        """
        while True:
            message = self.res_queue.get()
            if message is None:
                _logger.debug("_resolve_responses thread is terminated")
                return
            self.__resolve_responses(message)

    def __resolve_responses(self, message):
        session = self._session.resolve(message.id, message)
        if session is None:
            self.req_queue.put(message.to_event())

    def on_publish(self, client, userdata, mid):
        with self._session.session_lock:
            self._session.resolve_send(mid)

    def _create_thread_pool(self):

        def stop(queue):
            def _stop():
                queue.put(None)
            return _stop

        # create a thread pool
        for _ in range(0, self.dispatch_thread_count):
            thread = Thread(target=self._dispatch_message,
                            name="thread-dispatch-%s" % _)
            thread.daemon = True
            thread.start()
            self.thread_list.append((thread, stop(self.req_queue)))

        for _ in range(0, self.resolve_thread_count):
            thread = Thread(target=self._resolve_responses,
                            name="thread-resolve-%s" % _)
            thread.daemon = True
            thread.start()
            self.thread_list.append((thread, stop(self.res_queue)))

        _logger.debug("Thread pool is created")

    def start(self):
        """
        start
        """
        def main_thread():
            # create resp, req thread pool
            self._create_thread_pool()
            # start connection, this will block until stop()
            self.conn_thread = Thread(target=self._conn.connect)
            self.conn_thread.daemon = True
            self.conn_thread.start()

            # register model to controller...
            self.is_ready.wait()

            if hasattr(self, 'run'):
                _logger.debug("Start running...")
                self.run()

        # start main_thread
        self.main_thread = Thread(target=main_thread)
        self.main_thread.daemon = True
        self.main_thread.start()

        if threading.current_thread().__class__.__name__ == '_MainThread':
            # control this bundle stop or not
            while not self.stop_event.wait(1):
                sleep(1)
        else:
            self.stop_event.wait()

        self.stop()
        _logger.debug("Shutdown successfully")

    def exit(self, signum=None, frame=None):
        """
        hook ctrl + c to exit program
        """
        self.stop()
        sys.exit(0)

    def stop(self, *args, **kwargs):
        """
        exit
        """
        _logger.debug("Bundle [%s] has been shutting down" %
                      self.bundle.profile["name"])

        if hasattr(self, 'before_stop') and \
           hasattr(self.before_stop, '__call__'):
            _logger.debug("Invoking before_stop...")
            self.before_stop()

        self._conn.disconnect()
        self._session.stop()
        self.stop_event.set()

        # TODO: shutdown all threads
        for thread, stop in self.thread_list:
            stop()
        for thread, stop in self.thread_list:
            thread.join()
        self.is_ready.clear()

    def on_sanji_message(self, client, userdata, msg):
        """This function will recevie all message from mqtt
        client
            the client instance for this callback
        userdata
            the private user data as set in Client() or userdata_set()
        message
            an instance of MQTTMessage. This is a class with members topic,
            payload, qos, retain.
        """
        try:
            message = Message(msg.payload)
        except (TypeError, ValueError) as e:
            _logger.error(e, exc_info=True)
            return

        if message.type() == MessageType.UNKNOWN:
            _logger.debug("Got an UNKNOWN message, don't dispatch")
            return

        if message.type() == MessageType.RESPONSE:
            self.res_queue.put(message)

        if message.type() == MessageType.REQUEST or \
           message.type() == MessageType.DIRECT or \
           message.type() == MessageType.HOOK or \
           message.type() == MessageType.EVENT:
            self.req_queue.put(message)

    def on_connect(self, client, userdata, flags, rc):
        """
        on_connect(self, client, obj, flags, rc):
        client
            the client instance for this callback
        userdata
            the private user data as set in Client() or userdata_set()
        flags
            response flags sent by the broker
        rc
            the connection result
        """
        _logger.debug("Connection established with result code %s" % rc)

        if self.reg_thread is not None and self.reg_thread.is_alive():
            _logger.debug("Joining previous reg_thread")
            self.reg_thread.join()

        def reg():
            delay = None
            if hasattr(self.reg_delay, '__call__'):
                delay = self.reg_delay()
            else:
                delay = self.reg_delay

            sleep(delay)
            self._conn.set_tunnels(self._conn.tunnels)
            model_profile = self.get_profile("model")
            view_profile = self.get_profile("view")
            self.deregister(model_profile)
            self.deregister(view_profile)
            self.register(model_profile)
            self.register(view_profile)
            self.is_ready.set()

        self.reg_thread = Thread(target=reg)
        self.reg_thread.daemon = True
        self.reg_thread.start()

    def register(self, reg_data, retry=True, interval=1, timeout=3):
        """
        register function
        retry
            True, infinity retries
            False, no retries
            Number, retries times
        interval
            time period for retry
        return
            False if no success
            Tunnel if success
        """
        if len(reg_data["resources"]) == 0:
            _logger.debug("%s no need to register due to no resources" %
                          (reg_data["name"]))
            return

        def _register():
            try:
                resp = self.publish.direct.post(
                    "/controller/registration", reg_data)
                if resp.code == 200:
                    return resp
            except TimeoutError:
                _logger.debug("Register message is timeout")

            return False

        resp = _register()
        while resp is False:
            _logger.debug("Register failed.")
            self.deregister(reg_data)
            resp = _register()

        if resp is None:
            _logger.error("Can\'t not register to controller")
            self.stop()
            return False

        self._conn.set_tunnel(
            reg_data["role"], resp.data["tunnel"], self.on_sanji_message)
        self.bundle.profile["currentTunnels"] = [
            tunnel for tunnel, callback in self._conn.tunnels.items()]
        self.bundle.profile["regCount"] = \
            self.bundle.profile.get("reg_count", 0) + 1

        _logger.debug("Register successfully %s tunnel: %s"
                      % (reg_data["name"], resp.data["tunnel"],))

    def deregister(self, reg_data, retry=True, interval=1, timeout=3):
        """
        Deregister model/view of this bundle
        """
        Retry(target=self.publish.direct.delete,
              args=("/controller/registration", reg_data,),
              kwargs={"timeout": timeout},
              options={"retry": retry, "interval": interval})
        _logger.debug("Deregister successfully %s tunnel: %s" %
                      (reg_data["name"],
                       self._conn.tunnels[reg_data["role"]][0],))

    def get_profile(self, role="model"):
        profile = dict((k, v) for k, v in self.bundle.profile.items())
        profile["tunnel"] = self._conn.tunnels["internel"][0]
        profile["resources"] = []
        profile["role"] = role
        profile["name"] = "%s%s" % \
            (profile["name"], '' if role == self.bundle.profile["role"]
                              else '-' + role,)

        for _ in self.bundle.profile["resources"]:
            if _.get("role", self.bundle.profile["role"]) != role:
                continue
            profile["resources"].append(re.sub(r":(\w+)", r"+", _["resource"]))

        return profile
Пример #6
0
 def setUp(self, Thread):
     self.conn = Mockup()
     self.session = Session()
     self.publish = Publish(self.conn, self.session)
Пример #7
0
class TestSessionClass(unittest.TestCase):

    @patch("sanji.session.sleep")
    @patch("sanji.session.Thread")
    def setUp(self, Thread, sleep):
        self.thread_mock = Thread.return_value = MagicMock()
        self.thread_mock.is_alive.return_value = True
        self.session = Session()

    def tearDown(self):
        self.session.stop()
        self.session = None

    def test_init(self):
        self.assertIsInstance(self.session.session_list, dict)
        self.assertIsInstance(self.session.timeout_queue, deque)

    def test_resolve(self):
        message1 = Message({}, generate_id=True)
        self.session.create(message1)
        self.assertEqual(self.session.resolve(message1.id)["message"],
                         message1)

        # resolve unknow message
        self.session.resolve(1234)

    def test_resolve_send(self):
        # normal
        message1 = Message({}, generate_id=True)
        self.session.create(message1, mid=1)
        session = self.session.resolve_send(1)
        self.assertEqual(session["message"], message1)
        self.assertEqual(session["status"], Status.SENT)
        # not found
        self.assertEqual(self.session.resolve_send(1234), None)

    def test_create(self):
        message1 = Message({}, generate_id=True)
        message2 = Message({}, generate_id=True)
        message3 = Message({}, generate_id=True)

        # create session as normal
        self.session.create(message1)
        self.assertEqual(self.session.session_list[message1.id]["message"],
                         message1)
        # id duplicate
        message2.id = message1.id
        session = self.session.create(message2)
        self.assertNotEqual(session, None)

        message3.id = message1.id
        with self.assertRaises(SessionError):
            self.session.create(message3, force=False)

    def test_stop_is_alive_false(self):
        self.thread_mock.is_alive.return_value = False
        self.session.stop()
        self.assertFalse(self.session.thread_aging.is_alive())
        self.assertFalse(self.thread_mock.join.called)

    def test_stop_is_alive_true(self):
        self.thread_mock.is_alive.return_value = True
        self.session.stop()
        self.thread_mock.join.assert_called_once_with()

    @patch("sanji.session.sleep")
    def test_aging(self, sleep):
        self.session.stop()

        message1 = Message({}, generate_id=True)

        # timeout (response)
        self.session.create(message1, age=0)
        m = self.session.stop_event = Mock()
        conf = {"is_set.side_effect": [False, True, False, True]}
        m.configure_mock(**conf)
        self.session.aging()

        try:
            self.session.timeout_queue.pop()
        except Exception:
            self.fail("timeout_queue is empty")

        # timeout (send)
        self.session.create(message1, age=0)
        self.session.aging()

        for session in self.session.session_list.itervalues():
            session["is_published"].set()

        try:
            self.session.timeout_queue.pop()
        except Exception:
            self.fail("timeout_queue is empty")
Пример #8
0
 def setUp(self):
     self.conn = ConnectionMockup()
     self.session = Session()
     self.publish = Publish(self.conn, self.session)
Пример #9
0
class TestPublishClass(unittest.TestCase):
    @patch("sanji.session.Thread")
    def setUp(self, Thread):
        self.conn = Mockup()
        self.session = Session()
        self.publish = Publish(self.conn, self.session)

    def tearDown(self):
        self.session.stop()
        self.conn = None
        self.session = None
        self.publish = None

    def test_crud(self):  # noqa
        self.conn.publish = Mock(return_value=1)
        self.session.create = Mock(return_value={})

        # CRUD: block
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            for method in ["get", "put", "post", "delete"]:
                self.publish.__getattribute__(method)("/test/resource", {
                    "test": method
                }, False)
                self.conn.publish.assert_called_once_with(topic="/controller",
                                                          qos=2,
                                                          payload=ANY)
                self.conn.publish.reset_mock()
                self.session.create.assert_called_once_with(ANY, mid=1, age=60)
                self.session.create.reset_mock()

        # CRUD: non-block
        with patch("sanji.publish.Publish._wait_resolved") as _wait_resolved:
            # Normal case
            _wait_resolved.return_value = None
            for method in ["get", "put", "post", "delete"]:
                self.publish.__getattribute__(method)("/test/resource", {
                    "test": method
                }, True)

            # Timeout
            _wait_resolved.side_effect = TimeoutError
            for method in ["get", "put", "post", "delete"]:
                with self.assertRaises(TimeoutError):
                    self.publish.__getattribute__(method)("/test/resource", {
                        "test": method
                    }, True, 0)

            # StatusError
            _wait_resolved.side_effect = StatusError
            for method in ["get", "put", "post", "delete"]:
                with self.assertRaises(StatusError):
                    self.publish.__getattribute__(method)("/test/resource", {
                        "test": method
                    }, True)

    def test_event(self):
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            self.publish._conn.publish = Mock()
            self.publish.event.get("/test/event2", {
                "type": "notify2",
                "message": "hi"
            })
            self.publish._conn.publish.assert_called_once_with(
                topic="/controller", qos=2, payload=ANY)

    def test_direct(self):
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            self.publish.direct.get("/test/direct1", {
                "type": "direct1",
                "message": "hi"
            },
                                    block=False)
            _wait_published.assert_called_once_with(ANY)

    def test_create_response(self):

        messages = [
            Message({"test": "block"}, generate_id=True),
            Message({
                "query": {},
                "param": {},
                "sign": ["controller"]
            },
                    generate_id=True)
        ]

        def check_message(topic, qos, payload):
            self.assertNotIn("query", payload)
            self.assertNotIn("param", payload)
            self.assertIn("sign", payload)
            self.assertIn("code", payload)
            self.assertIn("this is sign", payload["sign"])

        self.publish._wait_published = Mock(return_value=None)
        self.conn.publish = check_message
        for message in messages:
            resp = self.publish.create_response(message, "this is sign")
            resp(500, {"ccc": "moxa best"})
            self.publish._wait_published.assert_called_once_with(
                ANY, no_response=True)
            self.publish._wait_published.reset_mock()

    def test__create_message(self):
        # input dict
        msg = self.publish._create_message({}, None)
        self.assertIsInstance(msg, Message)
        msg = self.publish._create_message(
            {
                'method': 'get',
                'sign': ['aaa', 'bbb']
            }, {'test': 1234})
        self.assertEqual(msg.method, 'get')
        self.assertEqual(msg.data['test'], 1234)
        self.assertEqual(msg.sign, ['aaa', 'bbb'])

        # input Messgae
        in_msg = Message({'method': 'post', 'resource': '/test'})
        out_msg = self.publish._create_message(data=in_msg)
        self.assertDictEqual(in_msg.__dict__, out_msg.__dict__)

    def test__wait_resolved(self):
        # RESPONSE_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESPONSE_TIMEOUT
        with self.assertRaises(TimeoutError):
            self.publish._wait_resolved(session)

        # RESOLVED
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESOLVED
        session["resolve_message"] = True
        self.assertTrue(self.publish._wait_resolved(session))

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = 999
        with self.assertRaises(StatusError):
            self.publish._wait_resolved(session)

    def test__wait_published(self):
        # SEND_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SEND_TIMEOUT
        session["is_published"].set()
        with self.assertRaises(TimeoutError):
            self.publish._wait_published(session)

        # SENT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SENT
        session["is_published"].set()
        self.assertDictEqual(self.publish._wait_published(session), session)

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = 999
        session["is_published"].set()
        with self.assertRaises(StatusError):
            self.publish._wait_published(session)
Пример #10
0
 def setUp(self, Thread):
     self.conn = Mockup()
     self.session = Session()
     self.publish = Publish(self.conn, self.session)
Пример #11
0
class TestPublishClass(unittest.TestCase):
    def setUp(self):
        self.conn = ConnectionMockup()
        self.session = Session()
        self.publish = Publish(self.conn, self.session)

    def tearDown(self):
        self.session.stop()
        self.conn = None
        self.session = None
        self.publish = None

    def test_crud(self):  # noqa
        this = self
        mids = []
        this.index = 0

        def send(method, resouce, data, block):
            mids.append(
                this.publish.__getattribute__(method)(resouce,
                                                      data,
                                                      block=block))

        threads = []
        for method in ["get", "put", "post", "delete"]:
            thread = Thread(target=send,
                            args=(method, "/test/resource", {
                                "test": method
                            }, False))
            thread.daemon = True
            thread.start()
            threads.append(thread)
        sleep(0.5)
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_resolved"].set()
            session["is_published"].set()

        for thread in threads:
            thread.join(1)
            self.assertFalse(thread.is_alive())

        # CRUD - block
        def send_block(message, data):
            self.assertEqual(self.publish.get("/test/resource", message), data)

        def send_timeout(message, data):
            with self.assertRaises(TimeoutError):
                print self.publish.put("/test/resource", message, timeout=0)
            with self.assertRaises(TimeoutError):
                self.publish.delete("/test/resource", message, timeout=-1)

        def resolve_statuserror(message):
            with self.assertRaises(StatusError):
                self.publish.put("/", message, timeout=10)

        message = Message({"test": "block"}, generate_id=True)
        thread = Thread(target=send_block, args=(
            message,
            "block",
        ))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        self.session.resolve(message.id, "block")
        thread.join(1)
        self.assertFalse(thread.is_alive())

        # CRUD - block timeout
        message = Message({"test": "timeout"}, generate_id=True)
        thread = Thread(target=send_timeout, args=(
            message,
            "timeout",
        ))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        # self.session.resolve(message.id, 1)
        thread.join(1)
        self.assertFalse(thread.is_alive())

        # Resolve StatusError
        message = Message({"test": "StatusError"}, generate_id=True)
        thread = Thread(target=resolve_statuserror, args=(message, ))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        self.session.resolve(message.id, 1, 123)
        thread.join(1)
        self.assertFalse(thread.is_alive())

    def test_event(self):
        # Resolve StatusError
        def send_block():
            self.publish.event("/test/event2", {
                "type": "notify2",
                "message": "hi"
            })

        thread = Thread(target=send_block, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        for session in self.session.session_list.itervalues():
            self.session.resolve_send(session["mid"])
        thread.join(0.5)
        self.assertFalse(thread.is_alive())
        self.assertEqual(len(self.session.session_list), 0)

    def test_direct(self):
        def send_nonblock():
            session = self.publish.direct.get("/test/direct1", {
                "type": "direct1",
                "message": "hi"
            },
                                              block=False)
            self.session.resolve(session["message"].id, None)

        thread = Thread(target=send_nonblock, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_published"].set()
        thread.join(0.5)
        self.assertFalse(thread.is_alive())

        def send_block():
            self.publish.direct.get("/test/direct2", {
                "type": "direct2",
                "message": "hi"
            },
                                    block=True)

        thread = Thread(target=send_block, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        session = self.session.session_list.values()[0]
        self.session.resolve(session["message"].id, session["mid"])
        thread.join(0.5)
        self.assertFalse(thread.is_alive())

    def test_create_response(self):

        messages = [
            Message({"test": "block"}, generate_id=True),
            Message({
                "query": {},
                "param": {},
                "sign": ["controller"]
            },
                    generate_id=True)
        ]

        def send_block(msg):
            response = self.publish.create_response(msg, "this is sign")
            response(500, {"ccc": "moxa best"})

        threads = []
        for message in messages:
            thread = Thread(target=send_block, args=(message, ))
            thread.daemon = True
            thread.start()
            threads.append(thread)
        map(lambda t: t.join(0.1), threads)

        self.assertEqual(len(self.session.session_list), len(messages))
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_published"].set()

        for thread in threads:
            thread.join(0.5)
            self.assertFalse(thread.is_alive())

    def test__create_message(self):
        # input dict
        msg = self.publish._create_message({}, None)
        self.assertIsInstance(msg, Message)
        msg = self.publish._create_message(
            {
                'method': 'get',
                'sign': ['aaa', 'bbb']
            }, {'test': 1234})
        self.assertEqual(msg.method, 'get')
        self.assertEqual(msg.data['test'], 1234)
        self.assertEqual(msg.sign, ['aaa', 'bbb'])

        # input Messgae
        in_msg = Message({'method': 'post', 'resource': '/test'})
        out_msg = self.publish._create_message(data=in_msg)
        self.assertDictEqual(in_msg.__dict__, out_msg.__dict__)

    def test__wait_resolved(self):
        # RESPONSE_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESPONSE_TIMEOUT
        with self.assertRaises(TimeoutError):
            self.publish._wait_resolved(session)

        # RESOLVED
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESOLVED
        session["resolve_message"] = True
        self.assertTrue(self.publish._wait_resolved(session))

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = 999
        with self.assertRaises(StatusError):
            self.publish._wait_resolved(session)

    def test__wait_published(self):
        # SEND_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SEND_TIMEOUT
        session["is_published"].set()
        with self.assertRaises(TimeoutError):
            self.publish._wait_published(session)

        # SENT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SENT
        session["is_published"].set()
        self.assertDictEqual(self.publish._wait_published(session), session)

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = 999
        session["is_published"].set()
        with self.assertRaises(StatusError):
            self.publish._wait_published(session)
Пример #12
0
class TestPublishClass(unittest.TestCase):

    @patch("sanji.session.Thread")
    def setUp(self, Thread):
        self.conn = Mockup()
        self.session = Session()
        self.publish = Publish(self.conn, self.session)

    def tearDown(self):
        self.session.stop()
        self.conn = None
        self.session = None
        self.publish = None

    def test_crud(self):  # noqa
        self.conn.publish = Mock(return_value=1)
        self.session.create = Mock(return_value={})

        # CRUD: block
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            for method in ["get", "put", "post", "delete"]:
                self.publish.__getattribute__(method)("/test/resource",
                                                      {"test": method}, False)
                self.conn.publish.assert_called_once_with(topic="/controller",
                                                          qos=2,
                                                          payload=ANY)
                self.conn.publish.reset_mock()
                self.session.create.assert_called_once_with(ANY, mid=1, age=60)
                self.session.create.reset_mock()

        # CRUD: non-block
        with patch("sanji.publish.Publish._wait_resolved") as _wait_resolved:
            # Normal case
            _wait_resolved.return_value = None
            for method in ["get", "put", "post", "delete"]:
                self.publish.__getattribute__(method)("/test/resource",
                                                      {"test": method}, True)

            # Timeout
            _wait_resolved.side_effect = TimeoutError
            for method in ["get", "put", "post", "delete"]:
                with self.assertRaises(TimeoutError):
                    self.publish.__getattribute__(method)("/test/resource",
                                                          {"test": method},
                                                          True, 0)

            # StatusError
            _wait_resolved.side_effect = StatusError
            for method in ["get", "put", "post", "delete"]:
                with self.assertRaises(StatusError):
                    self.publish.__getattribute__(method)("/test/resource",
                                                          {"test": method},
                                                          True)

    def test_event(self):
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            self.publish._conn.publish = Mock()
            self.publish.event.get("/test/event2",
                                   {"type": "notify2", "message": "hi"})
            self.publish._conn.publish.assert_called_once_with(
                topic="/controller", qos=2, payload=ANY)

    def test_direct(self):
        with patch("sanji.publish.Publish._wait_published") as _wait_published:
            _wait_published.return_value = None
            self.publish.direct.get("/test/direct1", {
                                    "type": "direct1",
                                    "message": "hi"},
                                    block=False)
            _wait_published.assert_called_once_with(ANY)

    def test_create_response(self):

        messages = [
            Message({"test": "block"}, generate_id=True),
            Message({"query": {}, "param": {}, "sign": ["controller"]},
                    generate_id=True)
        ]

        def check_message(topic, qos, payload):
            self.assertNotIn("query", payload)
            self.assertNotIn("param", payload)
            self.assertIn("sign", payload)
            self.assertIn("code", payload)
            self.assertIn("this is sign", payload["sign"])

        self.publish._wait_published = Mock(return_value=None)
        self.conn.publish = check_message
        for message in messages:
            resp = self.publish.create_response(message, "this is sign")
            resp(500, {"ccc": "moxa best"})
            self.publish._wait_published.assert_called_once_with(
                ANY, no_response=True)
            self.publish._wait_published.reset_mock()

    def test__create_message(self):
        # input dict
        msg = self.publish._create_message({}, None)
        self.assertIsInstance(msg, Message)
        msg = self.publish._create_message(
            {'method': 'get', 'sign': ['aaa', 'bbb']}, {'test': 1234}
        )
        self.assertEqual(msg.method, 'get')
        self.assertEqual(msg.data['test'], 1234)
        self.assertEqual(msg.sign, ['aaa', 'bbb'])

        # input Messgae
        in_msg = Message({'method': 'post', 'resource': '/test'})
        out_msg = self.publish._create_message(data=in_msg)
        self.assertDictEqual(in_msg.__dict__, out_msg.__dict__)

    def test__wait_resolved(self):
        # RESPONSE_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESPONSE_TIMEOUT
        with self.assertRaises(TimeoutError):
            self.publish._wait_resolved(session)

        # RESOLVED
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESOLVED
        session["resolve_message"] = True
        self.assertTrue(self.publish._wait_resolved(session))

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = 999
        with self.assertRaises(StatusError):
            self.publish._wait_resolved(session)

    def test__wait_published(self):
        # SEND_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SEND_TIMEOUT
        session["is_published"].set()
        with self.assertRaises(TimeoutError):
            self.publish._wait_published(session)

        # SENT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SENT
        session["is_published"].set()
        self.assertDictEqual(self.publish._wait_published(session),
                             session)

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = 999
        session["is_published"].set()
        with self.assertRaises(StatusError):
            self.publish._wait_published(session)
Пример #13
0
 def setUp(self):
     self.conn = ConnectionMockup()
     self.session = Session()
     self.publish = Publish(self.conn, self.session)
Пример #14
0
class TestPublishClass(unittest.TestCase):

    def setUp(self):
        self.conn = ConnectionMockup()
        self.session = Session()
        self.publish = Publish(self.conn, self.session)

    def tearDown(self):
        self.session.stop()
        self.conn = None
        self.session = None
        self.publish = None

    def test_crud(self):  # noqa
        this = self
        mids = []
        this.index = 0

        def send(method, resouce, data, block):
            mids.append(this.publish.__getattribute__(method)
                        (resouce, data, block=block))

        threads = []
        for method in ["get", "put", "post", "delete"]:
            thread = Thread(target=send, args=(
                method, "/test/resource", {"test": method}, False))
            thread.daemon = True
            thread.start()
            threads.append(thread)
        sleep(0.5)
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_resolved"].set()
            session["is_published"].set()

        for thread in threads:
            thread.join(1)
            self.assertFalse(thread.is_alive())

        # CRUD - block
        def send_block(message, data):
            self.assertEqual(self.publish.get("/test/resource", message),
                             data)

        def send_timeout(message, data):
            with self.assertRaises(TimeoutError):
                print self.publish.put("/test/resource", message, timeout=0)
            with self.assertRaises(TimeoutError):
                self.publish.delete("/test/resource", message, timeout=-1)

        def resolve_statuserror(message):
            with self.assertRaises(StatusError):
                self.publish.put("/", message, timeout=10)

        message = Message({"test": "block"}, generate_id=True)
        thread = Thread(target=send_block, args=(message, "block",))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        self.session.resolve(message.id, "block")
        thread.join(1)
        self.assertFalse(thread.is_alive())

        # CRUD - block timeout
        message = Message({"test": "timeout"}, generate_id=True)
        thread = Thread(target=send_timeout, args=(message, "timeout",))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        # self.session.resolve(message.id, 1)
        thread.join(1)
        self.assertFalse(thread.is_alive())

        # Resolve StatusError
        message = Message({"test": "StatusError"}, generate_id=True)
        thread = Thread(target=resolve_statuserror, args=(message,))
        thread.daemon = True
        thread.start()
        sleep(0.1)
        self.session.resolve(message.id, 1, 123)
        thread.join(1)
        self.assertFalse(thread.is_alive())

    def test_event(self):
        # Resolve StatusError
        def send_block():
            self.publish.event("/test/event2",
                               {"type": "notify2", "message": "hi"})
        thread = Thread(target=send_block, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        for session in self.session.session_list.itervalues():
            self.session.resolve_send(session["mid"])
        thread.join(0.5)
        self.assertFalse(thread.is_alive())
        self.assertEqual(len(self.session.session_list), 0)

    def test_direct(self):

        def send_nonblock():
            session = self.publish.direct.get("/test/direct1", {
                                              "type": "direct1",
                                              "message": "hi"},
                                              block=False)
            self.session.resolve(session["message"].id, None)
        thread = Thread(target=send_nonblock, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_published"].set()
        thread.join(0.5)
        self.assertFalse(thread.is_alive())

        def send_block():
            self.publish.direct.get("/test/direct2",
                                    {"type": "direct2", "message": "hi"},
                                    block=True)
        thread = Thread(target=send_block, args=())
        thread.daemon = True
        thread.start()
        sleep(0.5)
        self.assertEqual(len(self.session.session_list), 1)
        session = self.session.session_list.values()[0]
        self.session.resolve(session["message"].id, session["mid"])
        thread.join(0.5)
        self.assertFalse(thread.is_alive())

    def test_create_response(self):

        messages = [
            Message({"test": "block"}, generate_id=True),
            Message({"query": {}, "param": {}, "sign": ["controller"]},
                    generate_id=True)
        ]

        def send_block(msg):
            response = self.publish.create_response(msg, "this is sign")
            response(500, {"ccc": "moxa best"})

        threads = []
        for message in messages:
            thread = Thread(target=send_block, args=(message,))
            thread.daemon = True
            thread.start()
            threads.append(thread)
        map(lambda t: t.join(0.1), threads)

        self.assertEqual(len(self.session.session_list), len(messages))
        for session in self.session.session_list.itervalues():
            session["status"] = Status.SENT
            session["is_published"].set()

        for thread in threads:
            thread.join(0.5)
            self.assertFalse(thread.is_alive())

    def test__create_message(self):
        # input dict
        msg = self.publish._create_message({}, None)
        self.assertIsInstance(msg, Message)
        msg = self.publish._create_message(
            {'method': 'get', 'sign': ['aaa', 'bbb']}, {'test': 1234}
        )
        self.assertEqual(msg.method, 'get')
        self.assertEqual(msg.data['test'], 1234)
        self.assertEqual(msg.sign, ['aaa', 'bbb'])

        # input Messgae
        in_msg = Message({'method': 'post', 'resource': '/test'})
        out_msg = self.publish._create_message(data=in_msg)
        self.assertDictEqual(in_msg.__dict__, out_msg.__dict__)

    def test__wait_resolved(self):
        # RESPONSE_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESPONSE_TIMEOUT
        with self.assertRaises(TimeoutError):
            self.publish._wait_resolved(session)

        # RESOLVED
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = Status.RESOLVED
        session["resolve_message"] = True
        self.assertTrue(self.publish._wait_resolved(session))

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["is_resolved"].set()
        session["status"] = 999
        with self.assertRaises(StatusError):
            self.publish._wait_resolved(session)

    def test__wait_published(self):
        # SEND_TIMEOUT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SEND_TIMEOUT
        session["is_published"].set()
        with self.assertRaises(TimeoutError):
            self.publish._wait_published(session)

        # SENT
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = Status.SENT
        session["is_published"].set()
        self.assertDictEqual(self.publish._wait_published(session),
                             session)

        # UNKNOWN
        session = self.session.create(Message({}, generate_id=True))
        session["status"] = 999
        session["is_published"].set()
        with self.assertRaises(StatusError):
            self.publish._wait_published(session)
Пример #15
0
 def setUp(self):
     self.session = Session()
Пример #16
0
class TestSessionClass(unittest.TestCase):

    def setUp(self):
        self.session = Session()

    def tearDown(self):
        self.session.stop()
        self.session = None

    def test_init(self):
        self.assertIsInstance(self.session.session_list, dict)
        # self.assertIsInstance(self.session.session_lock, Lock)
        self.assertIsInstance(self.session.timeout_queue, deque)
        self.assertIsInstance(self.session.thread_aging, Thread)

    def test_resolve(self):
        message1 = Message({}, generate_id=True)
        self.session.create(message1)
        self.assertEqual(self.session.resolve(message1.id)["message"],
                         message1)

        # resolve unknow message
        self.session.resolve(1234)

    def test_resolve_send(self):
        # normal
        message1 = Message({}, generate_id=True)
        self.session.create(message1, mid=1)
        session = self.session.resolve_send(1)
        self.assertEqual(session["message"], message1)
        self.assertEqual(session["status"], Status.SENT)
        # not found
        self.assertEqual(self.session.resolve_send(1234), None)

    def test_create(self):
        message1 = Message({}, generate_id=True)
        message2 = Message({}, generate_id=True)
        message3 = Message({}, generate_id=True)

        # create session as normal
        self.session.create(message1)
        self.assertEqual(self.session.session_list[message1.id]["message"],
                         message1)
        # id duplicate
        message2.id = message1.id
        session = self.session.create(message2)
        self.assertNotEqual(session, None)

        message3.id = message1.id
        with self.assertRaises(SessionError):
            self.session.create(message3, force=False)

        # aging should be test too
        sleep(1)
        self.assertLess(self.session.session_list[message1.id]["age"], 60)

    def test_stop(self):
        self.session.stop()
        self.assertFalse(self.session.thread_aging.is_alive())

    def test_aging(self):
        message1 = Message({}, generate_id=True)

        # timeout (response)
        self.session.create(message1, age=0)
        sleep(1)
        try:
            self.session.timeout_queue.pop()
        except Exception:
            self.fail("timeout_queue is not empty")

        # timeout (send)
        self.session.create(message1, age=1)
        for session in self.session.session_list.itervalues():
            session["is_published"].set()
        sleep(1)
        try:
            self.session.timeout_queue.pop()
        except Exception:
            self.fail("timeout_queue is empty")