amqtt/tests/mqtt/protocol/test_handler.py

458 wiersze
21 KiB
Python

# Copyright (c) 2015 Nicolas JOUANIN
#
# See the file license.txt for copying permission.
import unittest
import asyncio
import logging
from hbmqtt.plugins.manager import PluginManager
from hbmqtt.session import Session, OutgoingApplicationMessage, IncomingApplicationMessage
from hbmqtt.mqtt.protocol.handler import ProtocolHandler
from hbmqtt.adapters import StreamWriterAdapter, StreamReaderAdapter
from hbmqtt.mqtt.constants import *
from hbmqtt.mqtt.publish import PublishPacket
from hbmqtt.mqtt.puback import PubackPacket
from hbmqtt.mqtt.pubrec import PubrecPacket
from hbmqtt.mqtt.pubrel import PubrelPacket
from hbmqtt.mqtt.pubcomp import PubcompPacket
formatter = "[%(asctime)s] %(name)s {%(filename)s:%(lineno)d} %(levelname)s - %(message)s"
logging.basicConfig(level=logging.DEBUG, format=formatter)
log = logging.getLogger(__name__)
def adapt(reader, writer):
return StreamReaderAdapter(reader), StreamWriterAdapter(writer)
class ProtocolHandlerTest(unittest.TestCase):
def setUp(self):
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.plugin_manager = PluginManager("hbmqtt.test.plugins", context=None, loop=self.loop)
def tearDown(self):
self.loop.close()
def test_init_handler(self):
s = Session()
handler = ProtocolHandler(s, self.plugin_manager, loop=self.loop)
self.assertIs(handler.session, s)
self.assertIs(handler._loop, self.loop)
self.check_empty_waiters(handler)
def test_start_stop(self):
@asyncio.coroutine
def server_mock(reader, writer):
pass
@asyncio.coroutine
def test_coro():
try:
s = Session()
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888)
reader_adapted, writer_adapted = adapt(reader, writer)
handler = ProtocolHandler(s, self.plugin_manager)
handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(handler, s)
yield from self.stop_handler(handler, s)
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_publish_qos0(self):
@asyncio.coroutine
def server_mock(reader, writer):
try:
packet = yield from PublishPacket.from_stream(reader)
self.assertEquals(packet.topic_name, '/topic')
self.assertEquals(packet.qos, QOS_0)
self.assertIsNone(packet.packet_id)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
s = Session()
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
handler = ProtocolHandler(s, self.plugin_manager, loop=self.loop)
handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(handler, s)
message = yield from handler.mqtt_publish('/topic', b'test_data', QOS_0, False)
self.assertIsInstance(message, OutgoingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNone(message.puback_packet)
self.assertIsNone(message.pubrec_packet)
self.assertIsNone(message.pubrel_packet)
self.assertIsNone(message.pubcomp_packet)
yield from self.stop_handler(handler, s)
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_publish_qos1(self):
@asyncio.coroutine
def server_mock(reader, writer):
packet = yield from PublishPacket.from_stream(reader)
try:
self.assertEquals(packet.topic_name, '/topic')
self.assertEquals(packet.qos, QOS_1)
self.assertIsNotNone(packet.packet_id)
self.assertIn(packet.packet_id, self.session.inflight_out)
self.assertIn(packet.packet_id, self.handler._puback_waiters)
puback = PubackPacket.build(packet.packet_id)
yield from puback.to_stream(writer)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(self.handler, self.session)
message = yield from self.handler.mqtt_publish('/topic', b'test_data', QOS_1, False)
self.assertIsInstance(message, OutgoingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNotNone(message.puback_packet)
self.assertIsNone(message.pubrec_packet)
self.assertIsNone(message.pubrel_packet)
self.assertIsNone(message.pubcomp_packet)
yield from self.stop_handler(self.handler, self.session)
if not future.done():
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_publish_qos2(self):
@asyncio.coroutine
def server_mock(reader, writer):
try:
packet = yield from PublishPacket.from_stream(reader)
self.assertEquals(packet.topic_name, '/topic')
self.assertEquals(packet.qos, QOS_2)
self.assertIsNotNone(packet.packet_id)
self.assertIn(packet.packet_id, self.session.inflight_out)
self.assertIn(packet.packet_id, self.handler._pubrec_waiters)
pubrec = PubrecPacket.build(packet.packet_id)
yield from pubrec.to_stream(writer)
pubrel = yield from PubrelPacket.from_stream(reader)
self.assertIn(packet.packet_id, self.handler._pubcomp_waiters)
pubcomp = PubcompPacket.build(packet.packet_id)
yield from pubcomp.to_stream(writer)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(self.handler, self.session)
message = yield from self.handler.mqtt_publish('/topic', b'test_data', QOS_2, False)
self.assertIsInstance(message, OutgoingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNone(message.puback_packet)
self.assertIsNotNone(message.pubrec_packet)
self.assertIsNotNone(message.pubrel_packet)
self.assertIsNotNone(message.pubcomp_packet)
yield from self.stop_handler(self.handler, self.session)
if not future.done():
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_receive_qos0(self):
@asyncio.coroutine
def server_mock(reader, writer):
packet = PublishPacket.build('/topic', b'test_data', 1, False, QOS_0, False)
yield from packet.to_stream(writer)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(self.handler, self.session)
message = yield from self.handler.mqtt_deliver_next_message()
self.assertIsInstance(message, IncomingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNone(message.puback_packet)
self.assertIsNone(message.pubrec_packet)
self.assertIsNone(message.pubrel_packet)
self.assertIsNone(message.pubcomp_packet)
yield from self.stop_handler(self.handler, self.session)
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_receive_qos1(self):
@asyncio.coroutine
def server_mock(reader, writer):
try:
packet = PublishPacket.build('/topic', b'test_data', 1, False, QOS_1, False)
yield from packet.to_stream(writer)
puback = yield from PubackPacket.from_stream(reader)
self.assertIsNotNone(puback)
self.assertEqual(packet.packet_id, puback.packet_id)
except Exception as ae:
print(ae)
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.start_handler(self.handler, self.session)
message = yield from self.handler.mqtt_deliver_next_message()
self.assertIsInstance(message, IncomingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNotNone(message.puback_packet)
self.assertIsNone(message.pubrec_packet)
self.assertIsNone(message.pubrel_packet)
self.assertIsNone(message.pubcomp_packet)
yield from self.stop_handler(self.handler, self.session)
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
future = asyncio.Future(loop=self.loop)
self.event = asyncio.Event(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
@unittest.skip
def test_receive_qos2(self):
@asyncio.coroutine
def server_mock(reader, writer):
try:
packet = PublishPacket.build('/topic', b'test_data', 2, False, QOS_2, False)
yield from packet.to_stream(writer)
pubrec = yield from PubrecPacket.from_stream(reader)
self.assertIsNotNone(pubrec)
self.assertEqual(packet.packet_id, pubrec.packet_id)
self.assertIn(packet.packet_id, self.handler._pubrel_waiters)
pubrel = PubrelPacket.build(packet.packet_id)
yield from pubrel.to_stream(writer)
pubcomp = yield from PubcompPacket.from_stream(reader)
self.assertIsNotNone(pubcomp)
self.assertEqual(packet.packet_id, pubcomp.packet_id)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
self.session.reader, self.session.writer = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
yield from self.start_handler(self.handler, self.session)
message = yield from self.handler.mqtt_deliver_next_message()
self.assertIsInstance(message, IncomingApplicationMessage)
self.assertIsNotNone(message.publish_packet)
self.assertIsNone(message.puback_packet)
self.assertIsNotNone(message.pubrec_packet)
self.assertIsNotNone(message.pubrel_packet)
self.assertIsNotNone(message.pubcomp_packet)
yield from self.stop_handler(self.handler, self.session)
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
@asyncio.coroutine
def start_handler(self, handler, session):
self.check_empty_waiters(handler)
self.check_no_message(session)
yield from handler.start()
self.assertTrue(handler._reader_ready)
@asyncio.coroutine
def stop_handler(self, handler, session):
yield from handler.stop()
self.assertTrue(handler._reader_stopped)
self.check_empty_waiters(handler)
self.check_no_message(session)
def check_empty_waiters(self, handler):
self.assertFalse(handler._puback_waiters)
self.assertFalse(handler._pubrec_waiters)
self.assertFalse(handler._pubrel_waiters)
self.assertFalse(handler._pubcomp_waiters)
def check_no_message(self, session):
self.assertFalse(session.inflight_out)
self.assertFalse(session.inflight_in)
# self.assertEquals(session.delivered_message_queue.qsize(), 0)
def test_publish_qos1_retry(self):
@asyncio.coroutine
def server_mock(reader, writer):
packet = yield from PublishPacket.from_stream(reader)
try:
self.assertEquals(packet.topic_name, '/topic')
self.assertEquals(packet.qos, QOS_1)
self.assertIsNotNone(packet.packet_id)
self.assertIn(packet.packet_id, self.session.inflight_out)
self.assertIn(packet.packet_id, self.handler._puback_waiters)
puback = PubackPacket.build(packet.packet_id)
yield from puback.to_stream(writer)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.handler.start()
yield from self.stop_handler(self.handler, self.session)
if not future.done():
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
message = OutgoingApplicationMessage(1, '/topic', QOS_1, b'test_data', False)
message.publish_packet = PublishPacket.build('/topic', b'test_data', 1, False, QOS_1, False)
self.session.inflight_out[1] = message
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()
def test_publish_qos2_retry(self):
@asyncio.coroutine
def server_mock(reader, writer):
try:
packet = yield from PublishPacket.from_stream(reader)
self.assertEquals(packet.topic_name, '/topic')
self.assertEquals(packet.qos, QOS_2)
self.assertIsNotNone(packet.packet_id)
self.assertIn(packet.packet_id, self.session.inflight_out)
self.assertIn(packet.packet_id, self.handler._pubrec_waiters)
pubrec = PubrecPacket.build(packet.packet_id)
yield from pubrec.to_stream(writer)
pubrel = yield from PubrelPacket.from_stream(reader)
self.assertIn(packet.packet_id, self.handler._pubcomp_waiters)
pubcomp = PubcompPacket.build(packet.packet_id)
yield from pubcomp.to_stream(writer)
except Exception as ae:
future.set_exception(ae)
@asyncio.coroutine
def test_coro():
try:
reader, writer = yield from asyncio.open_connection('127.0.0.1', 8888, loop=self.loop)
reader_adapted, writer_adapted = adapt(reader, writer)
self.handler = ProtocolHandler(self.session, self.plugin_manager, loop=self.loop)
self.handler.attach_stream(reader_adapted, writer_adapted)
yield from self.handler.start()
yield from self.stop_handler(self.handler, self.session)
if not future.done():
future.set_result(True)
except Exception as ae:
future.set_exception(ae)
self.handler = None
self.session = Session()
message = OutgoingApplicationMessage(1, '/topic', QOS_2, b'test_data', False)
message.publish_packet = PublishPacket.build('/topic', b'test_data', 1, False, QOS_2, False)
self.session.inflight_out[1] = message
future = asyncio.Future(loop=self.loop)
coro = asyncio.start_server(server_mock, '127.0.0.1', 8888, loop=self.loop)
server = self.loop.run_until_complete(coro)
self.loop.run_until_complete(test_coro())
server.close()
self.loop.run_until_complete(server.wait_closed())
if future.exception():
raise future.exception()