msg_sequence_test.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #!/usr/bin/env python3
  2. # Test whether a valid CONNECT results in the correct CONNACK packet.
  3. from mosq_test_helper import *
  4. import importlib
  5. from os import walk
  6. import socket
  7. import json
  8. from collections import deque
  9. import mosq_test
  10. send = 1
  11. recv = 2
  12. disconnected_check = 3
  13. connected_check = 4
  14. publish = 5
  15. class SingleMsg(object):
  16. __slots__ = 'action', 'message', 'comment'
  17. def __init__(self, action, message, comment=''):
  18. self.action = action
  19. self.message = message
  20. self.comment = comment
  21. class MsgSequence(object):
  22. __slots__ = 'name', 'msgs', 'expect_disconnect'
  23. def __init__(self, name, default_connect=True, proto_ver=4, expect_disconnect=True):
  24. self.name = name
  25. self.msgs = deque()
  26. self.expect_disconnect = expect_disconnect
  27. if default_connect:
  28. self.add_default_connect(proto_ver=proto_ver)
  29. def add_default_connect(self, proto_ver):
  30. self.add_send(mosq_test.gen_connect(self.name, keepalive=60, proto_ver=proto_ver))
  31. self.add_recv(mosq_test.gen_connack(rc=0, proto_ver=proto_ver), "default connack")
  32. def add_send(self, message):
  33. self._add(send, message)
  34. def add_recv(self, message, comment):
  35. self._add(recv, message, comment)
  36. def add_publish(self, message, comment):
  37. self._add(publish, message, comment)
  38. def add_connected_check(self):
  39. self._add(connected_check, b"")
  40. def add_disconnected_check(self):
  41. self._add(disconnected_check, b"")
  42. def _add(self, action, message, comment=""):
  43. msg = SingleMsg(action, message, comment)
  44. self.msgs.append(msg)
  45. def _connected_check(self, sock):
  46. try:
  47. mosq_test.do_ping(sock)
  48. except mosq_test.TestError:
  49. raise ValueError("connection failed")
  50. def _send_message(self, sock, msg):
  51. sock.send(msg.message)
  52. def _publish_message(self, msg):
  53. sock = mosq_test.client_connect_only(hostname="localhost", port=1888, timeout=2)
  54. sock.send(mosq_test.gen_connect("helper", keepalive=60))
  55. mosq_test.expect_packet(sock, "connack", mosq_test.gen_connack(rc=0))
  56. m = msg.message
  57. if m['qos'] == 0:
  58. sock.send(mosq_test.gen_publish(topic=m['topic'], payload=m['payload']))
  59. elif m['qos'] == 1:
  60. sock.send(mosq_test.gen_publish(mid=1, qos=1, topic=m['topic'], payload=m['payload']))
  61. mosq_test.expect_packet(sock, "helper puback", mosq_test.gen_puback(mid=1))
  62. elif m['qos'] == 2:
  63. sock.send(mosq_test.gen_publish(mid=1, qos=2, topic=m['topic'], payload=m['payload']))
  64. mosq_test.expect_packet(sock, "helper pubrec", mosq_test.gen_pubrec(mid=1))
  65. sock.send(mosq_test.gen_pubrel(mid=1))
  66. mosq_test.expect_packet(sock, "helper pubcomp", mosq_test.gen_pubcomp(mid=1))
  67. sock.close()
  68. def _recv_message(self, sock, msg):
  69. data = sock.recv(len(msg.message))
  70. if data != msg.message:
  71. raise ValueError("Receive message %s | %s | %s" % (msg.comment, data, msg.message))
  72. def _disconnected_check(self, sock):
  73. try:
  74. data = sock.recv(1)
  75. if len(data) == 1 and self.expect_disconnect:
  76. raise ValueError("Still connected")
  77. except ConnectionResetError:
  78. if self.expect_disconnect:
  79. pass
  80. else:
  81. raise
  82. def _process_message(self, sock, msg):
  83. if msg.action == send:
  84. self._send_message(sock, msg)
  85. elif msg.action == recv:
  86. self._recv_message(sock, msg)
  87. elif msg.action == publish:
  88. self._publish_message(msg)
  89. elif msg.action == disconnected_check:
  90. self._disconnected_check(sock)
  91. elif msg.action == connected_check:
  92. self._connected_check(sock)
  93. def process_next(self, sock):
  94. msg = self.msgs.popleft()
  95. self._process_message(sock, msg)
  96. def process_all(self, sock):
  97. while len(self.msgs):
  98. self.process_next(sock)
  99. if self.expect_disconnect:
  100. self._disconnected_check(sock)
  101. else:
  102. self._connected_check(sock)
  103. def do_test(hostname, port):
  104. rc = 0
  105. sequences = []
  106. for (_, _, filenames) in walk("data"):
  107. sequences.extend(filenames)
  108. break
  109. total = 0
  110. succeeded = 0
  111. test = None
  112. for seq in sorted(sequences):
  113. if seq[-5:] != ".json":
  114. continue
  115. with open("data/"+seq, "r") as f:
  116. test_file = json.load(f)
  117. for g in test_file:
  118. group_name = g["group"]
  119. try:
  120. disabled = g["disable"]
  121. if disabled:
  122. continue
  123. except KeyError:
  124. pass
  125. tests = g["tests"]
  126. for t in tests:
  127. tname = group_name + " " + t["name"]
  128. try:
  129. proto_ver = t["ver"]
  130. except KeyError:
  131. proto_ver = 4
  132. try:
  133. connect = t["connect"]
  134. except KeyError:
  135. connect = True
  136. try:
  137. expect_disconnect = t["expect_disconnect"]
  138. except KeyError:
  139. expect_disconnect = True
  140. this_test = MsgSequence(tname,
  141. proto_ver=proto_ver,
  142. expect_disconnect=expect_disconnect,
  143. default_connect=connect)
  144. for m in t["msgs"]:
  145. try:
  146. c = m["comment"]
  147. except KeyError:
  148. c = ""
  149. if m["type"] == "send":
  150. this_test.add_send(bytes.fromhex(m["payload"].replace(" ", "")))
  151. elif m["type"] == "recv":
  152. this_test.add_recv(bytes.fromhex(m["payload"].replace(" ", "")), c)
  153. elif m["type"] == "publish":
  154. this_test.add_publish(m, c)
  155. total += 1
  156. try:
  157. sock = mosq_test.client_connect_only(hostname=hostname, port=port, timeout=2)
  158. this_test.process_all(sock)
  159. print("\033[32m" + tname + "\033[0m")
  160. succeeded += 1
  161. except ValueError as e:
  162. print("\033[31m" + tname + " failed: " + str(e) + "\033[0m")
  163. rc = 1
  164. except ConnectionResetError as e:
  165. print("\033[31m" + tname + " failed: " + str(e) + "\033[0m")
  166. rc = 1
  167. except socket.timeout as e:
  168. print("\033[31m" + tname + " failed: " + str(e) + "\033[0m")
  169. rc = 1
  170. except mosq_test.TestError as e:
  171. print("\033[31m" + tname + " failed: " + str(e) + "\033[0m")
  172. rc = 1
  173. print("%d tests total\n%d tests succeeded" % (total, succeeded))
  174. return rc
  175. hostname = "localhost"
  176. port = mosq_test.get_port()
  177. broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, nolog=True)
  178. rc = 0
  179. try:
  180. rc = do_test(hostname=hostname, port=port)
  181. finally:
  182. broker.terminate()
  183. broker.wait()
  184. (stdo, stde) = broker.communicate()
  185. if rc:
  186. #print(stde.decode('utf-8'))
  187. exit(rc)