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 __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")
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
def setUp(self, Thread, sleep): self.thread_mock = Thread.return_value = MagicMock() self.thread_mock.is_alive.return_value = True self.session = Session()
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
def setUp(self, Thread): self.conn = Mockup() self.session = Session() self.publish = Publish(self.conn, self.session)
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")
def setUp(self): self.conn = ConnectionMockup() self.session = Session() self.publish = Publish(self.conn, self.session)
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)
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)
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)
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)
def setUp(self): self.session = Session()
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")