Erster Stand der Hintergrundprozesse, die auf der Synology unter /volume1/homes/wagner/SolarManager laufen: der Manager selbst, die Sammler je Geraet, die MQTT-Bruecke, der Wecker und - neu hinzugezogen - der AutoAction-Runner, der als Hintergrundprozess hierher gehoert und nicht ins Web-Verzeichnis. Zugangsdaten stehen nicht mehr im Quelltext, sondern in config.ini, die nicht mit eingecheckt wird. Vorlage ist config.ini.example, gelesen wird sie von konfig.py. Betroffen waren solarManager.py (Datenbank und Wattpilot), zeit.py, gatherWaterData.py, wecker.py und skoda_testdaten.py, das sich das Passwort bisher aus dem Quelltext eines anderen Moduls herausgesucht hat. Die Kia-Anbindung ist mit dem Fahrzeug entfallen: kiaTest.py, gatherCarData.py und hyundai_kia_connect_api sind nicht mehr dabei, ebenso gatherInverterData.py, auf das nur noch eine auskommentierte Zeile zeigte. Die mitgelieferten Bibliotheken bleiben im Repository - die NAS hat kein pip, sie muessen neben den Skripten liegen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1022 lines
36 KiB
Python
1022 lines
36 KiB
Python
import threading
|
|
import time
|
|
import unicodedata
|
|
|
|
import paho.mqtt.client as client
|
|
import pytest
|
|
from paho.mqtt.enums import CallbackAPIVersion, MQTTErrorCode, MQTTProtocolVersion
|
|
from paho.mqtt.packettypes import PacketTypes
|
|
from paho.mqtt.properties import Properties
|
|
from paho.mqtt.reasoncodes import ReasonCode
|
|
|
|
import tests.paho_test as paho_test
|
|
|
|
# Import test fixture
|
|
from tests.testsupport.broker import FakeBroker, fake_broker # noqa: F401
|
|
|
|
|
|
@pytest.mark.parametrize("proto_ver,callback_version", [
|
|
(MQTTProtocolVersion.MQTTv31, CallbackAPIVersion.VERSION1),
|
|
(MQTTProtocolVersion.MQTTv31, CallbackAPIVersion.VERSION2),
|
|
(MQTTProtocolVersion.MQTTv311, CallbackAPIVersion.VERSION1),
|
|
(MQTTProtocolVersion.MQTTv311, CallbackAPIVersion.VERSION2),
|
|
])
|
|
class Test_connect:
|
|
"""
|
|
Tests on connect/disconnect behaviour of the client
|
|
"""
|
|
|
|
def test_01_con_discon_success(self, proto_ver, callback_version, fake_broker):
|
|
mqttc = client.Client(
|
|
callback_version,
|
|
"01-con-discon-success",
|
|
protocol=proto_ver,
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
def on_connect(mqttc, obj, flags, rc_or_reason_code, properties_or_none=None):
|
|
assert rc_or_reason_code == 0
|
|
mqttc.disconnect()
|
|
|
|
mqttc.on_connect = on_connect
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"01-con-discon-success", keepalive=60,
|
|
proto_ver=proto_ver)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
def test_01_con_failure_rc(self, proto_ver, callback_version, fake_broker):
|
|
mqttc = client.Client(
|
|
callback_version, "01-con-failure-rc",
|
|
protocol=proto_ver, transport=fake_broker.transport)
|
|
|
|
def on_connect(mqttc, obj, flags, rc_or_reason_code, properties_or_none=None):
|
|
assert rc_or_reason_code > 0
|
|
assert rc_or_reason_code != 0
|
|
if callback_version == CallbackAPIVersion.VERSION1:
|
|
assert rc_or_reason_code == 1
|
|
else:
|
|
assert rc_or_reason_code == ReasonCode(PacketTypes.CONNACK, "Unsupported protocol version")
|
|
|
|
mqttc.on_connect = on_connect
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"01-con-failure-rc", keepalive=60,
|
|
proto_ver=proto_ver)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=1)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
def test_connection_properties(self, proto_ver, callback_version, fake_broker):
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION2, "client-id",
|
|
protocol=proto_ver, transport=fake_broker.transport)
|
|
mqttc.enable_logger()
|
|
|
|
is_connected = threading.Event()
|
|
is_disconnected = threading.Event()
|
|
|
|
def on_connect(mqttc, obj, flags, rc, properties):
|
|
assert rc == 0
|
|
is_connected.set()
|
|
|
|
def on_disconnect(*args):
|
|
import logging
|
|
logging.info("disco")
|
|
is_disconnected.set()
|
|
|
|
mqttc.on_connect = on_connect
|
|
mqttc.on_disconnect = on_disconnect
|
|
|
|
mqttc.host = "localhost"
|
|
mqttc.connect_timeout = 7
|
|
mqttc.port = fake_broker.port
|
|
mqttc.keepalive = 7
|
|
mqttc.max_inflight_messages = 7
|
|
mqttc.max_queued_messages = 7
|
|
mqttc.transport = fake_broker.transport
|
|
mqttc.username = "username"
|
|
mqttc.password = "password"
|
|
|
|
mqttc.reconnect()
|
|
|
|
# As soon as connection try to be established, no longer accept updates
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.host = "localhost"
|
|
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"client-id",
|
|
keepalive=7,
|
|
username="username",
|
|
password="password",
|
|
proto_ver=proto_ver,
|
|
)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
is_connected.wait()
|
|
|
|
# Check that all connections related properties can't be updated
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.host = "localhost"
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.connect_timeout = 7
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.port = fake_broker.port
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.keepalive = 7
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.max_inflight_messages = 7
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.max_queued_messages = 7
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.transport = fake_broker.transport
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.username = "username"
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.password = "password"
|
|
|
|
# close the connection, but from broker
|
|
fake_broker.finish()
|
|
|
|
is_disconnected.wait()
|
|
assert not mqttc.is_connected()
|
|
|
|
# still not allowed to update, because client try to reconnect in background
|
|
with pytest.raises(RuntimeError):
|
|
mqttc.host = "localhost"
|
|
|
|
mqttc.disconnect()
|
|
|
|
# Now it's allowed, connection is closing AND not trying to reconnect
|
|
mqttc.host = "localhost"
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
|
|
class Test_connect_v5:
|
|
"""
|
|
Tests on connect/disconnect behaviour of the client with MQTTv5
|
|
"""
|
|
|
|
def test_01_broker_no_support(self, fake_broker):
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION2, "01-broker-no-support",
|
|
protocol=MQTTProtocolVersion.MQTTv5, transport=fake_broker.transport)
|
|
|
|
def on_connect(mqttc, obj, flags, reason, properties):
|
|
assert reason == 132
|
|
assert reason == ReasonCode(client.CONNACK >> 4, aName="Unsupported protocol version")
|
|
mqttc.disconnect()
|
|
|
|
mqttc.on_connect = on_connect
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
# Can't test the connect_packet, we can't yet generate MQTTv5 packet.
|
|
# connect_packet = paho_test.gen_connect(
|
|
# "01-con-discon-success", keepalive=60,
|
|
# proto_ver=client.MQTTv311)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
# assert packet_in == connect_packet
|
|
|
|
# The reply packet is a MQTTv3 connack. But that the propose of this test,
|
|
# ensure client convert it to a reason code 132 "Unsupported protocol version"
|
|
connack_packet = paho_test.gen_connack(rc=1)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
|
|
class TestConnectionLost:
|
|
def test_with_loop_start(self, fake_broker: FakeBroker):
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION1,
|
|
"test_with_loop_start",
|
|
protocol=MQTTProtocolVersion.MQTTv311,
|
|
reconnect_on_failure=False,
|
|
transport=fake_broker.transport
|
|
)
|
|
|
|
on_connect_reached = threading.Event()
|
|
on_disconnect_reached = threading.Event()
|
|
|
|
|
|
def on_connect(mqttc, obj, flags, rc):
|
|
assert rc == 0
|
|
on_connect_reached.set()
|
|
|
|
def on_disconnect(*args):
|
|
on_disconnect_reached.set()
|
|
|
|
mqttc.on_connect = on_connect
|
|
mqttc.on_disconnect = on_disconnect
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"test_with_loop_start", keepalive=60,
|
|
proto_ver=MQTTProtocolVersion.MQTTv311)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
assert on_connect_reached.wait(1)
|
|
assert mqttc.is_connected()
|
|
|
|
fake_broker.finish()
|
|
|
|
assert on_disconnect_reached.wait(1)
|
|
assert not mqttc.is_connected()
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
def test_with_loop(self, fake_broker: FakeBroker):
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION1,
|
|
"test_with_loop",
|
|
clean_session=True,
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
on_connect_reached = threading.Event()
|
|
on_disconnect_reached = threading.Event()
|
|
|
|
|
|
def on_connect(mqttc, obj, flags, rc):
|
|
assert rc == 0
|
|
on_connect_reached.set()
|
|
|
|
def on_disconnect(*args):
|
|
on_disconnect_reached.set()
|
|
|
|
mqttc.on_connect = on_connect
|
|
mqttc.on_disconnect = on_disconnect
|
|
|
|
mqttc.connect("localhost", fake_broker.port)
|
|
|
|
fake_broker.start()
|
|
|
|
# not yet connected, packet are not yet processed by loop()
|
|
assert not mqttc.is_connected()
|
|
|
|
# connect packet is sent during connect() call
|
|
connect_packet = paho_test.gen_connect(
|
|
"test_with_loop", keepalive=60,
|
|
proto_ver=MQTTProtocolVersion.MQTTv311)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
# call loop() to process the connack packet
|
|
assert mqttc.loop(timeout=1) == MQTTErrorCode.MQTT_ERR_SUCCESS
|
|
|
|
assert on_connect_reached.wait(1)
|
|
assert mqttc.is_connected()
|
|
|
|
fake_broker.finish()
|
|
|
|
# call loop() to detect the connection lost
|
|
assert mqttc.loop(timeout=1) == MQTTErrorCode.MQTT_ERR_CONN_LOST
|
|
|
|
assert on_disconnect_reached.wait(1)
|
|
assert not mqttc.is_connected()
|
|
|
|
|
|
class TestPublish:
|
|
def test_publish_before_connect(self, fake_broker: FakeBroker) -> None:
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION1,
|
|
"test_publish_before_connect",
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
def on_connect(mqttc, obj, flags, rc):
|
|
assert rc == 0
|
|
|
|
mqttc.on_connect = on_connect
|
|
|
|
mqttc.loop_start()
|
|
mqttc.connect("localhost", fake_broker.port)
|
|
mqttc.enable_logger()
|
|
|
|
try:
|
|
mi = mqttc.publish("test", "testing")
|
|
|
|
fake_broker.start()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
# re-call fake_broker.start() to take the 2nd connection done by client
|
|
# ... this is probably a bug, when using loop_start/loop_forever
|
|
# and doing a connect() before, the TCP connection is opened twice.
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"test_publish_before_connect", keepalive=60,
|
|
proto_ver=client.MQTTv311)
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
with pytest.raises(RuntimeError):
|
|
mi.wait_for_publish(1)
|
|
|
|
mqttc.disconnect()
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
@pytest.mark.parametrize("user_payload,sent_payload", [
|
|
("string", b"string"),
|
|
(b"byte", b"byte"),
|
|
(bytearray(b"bytearray"), b"bytearray"),
|
|
(42, b"42"),
|
|
(4.2, b"4.2"),
|
|
(None, b""),
|
|
])
|
|
def test_publish_various_payload(self, user_payload: client.PayloadType, sent_payload: bytes, fake_broker: FakeBroker) -> None:
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION2,
|
|
"test_publish_various_payload",
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
mqttc.connect("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
mqttc.enable_logger()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"test_publish_various_payload", keepalive=60,
|
|
proto_ver=client.MQTTv311)
|
|
fake_broker.expect_packet("connect", connect_packet)
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
mqttc.publish("test", user_payload)
|
|
|
|
publish_packet = paho_test.gen_publish(
|
|
b"test", payload=sent_payload, qos=0
|
|
)
|
|
fake_broker.expect_packet("publish", publish_packet)
|
|
|
|
mqttc.disconnect()
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(1000)
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
|
|
@pytest.mark.parametrize("callback_version", [
|
|
(CallbackAPIVersion.VERSION1),
|
|
(CallbackAPIVersion.VERSION2),
|
|
])
|
|
class TestPublishBroker2Client:
|
|
def test_invalid_utf8_topic(self, callback_version, fake_broker):
|
|
mqttc = client.Client(callback_version, "client-id", transport=fake_broker.transport)
|
|
|
|
def on_message(client, userdata, msg):
|
|
with pytest.raises(UnicodeDecodeError):
|
|
assert msg.topic
|
|
client.disconnect()
|
|
|
|
mqttc.on_message = on_message
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect("client-id")
|
|
packet_in = fake_broker.receive_packet(len(connect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
publish_packet = paho_test.gen_publish(b"\xff", qos=0)
|
|
count = fake_broker.send_packet(publish_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_packet)
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(len(disconnect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
def test_valid_utf8_topic_recv(self, callback_version, fake_broker):
|
|
mqttc = client.Client(callback_version, "client-id", transport=fake_broker.transport)
|
|
|
|
# It should be non-ascii multi-bytes character
|
|
topic = unicodedata.lookup('SNOWMAN')
|
|
|
|
def on_message(client, userdata, msg):
|
|
assert msg.topic == topic
|
|
client.disconnect()
|
|
|
|
mqttc.on_message = on_message
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect("client-id")
|
|
packet_in = fake_broker.receive_packet(len(connect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
publish_packet = paho_test.gen_publish(
|
|
topic.encode('utf-8'), qos=0
|
|
)
|
|
count = fake_broker.send_packet(publish_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_packet)
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(len(disconnect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
def test_valid_utf8_topic_publish(self, callback_version, fake_broker):
|
|
mqttc = client.Client(callback_version, "client-id", transport=fake_broker.transport)
|
|
|
|
# It should be non-ascii multi-bytes character
|
|
topic = unicodedata.lookup('SNOWMAN')
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect("client-id")
|
|
packet_in = fake_broker.receive_packet(len(connect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
mqttc.publish(topic, None, 0)
|
|
# Small sleep needed to avoid connection reset.
|
|
time.sleep(0.3)
|
|
|
|
publish_packet = paho_test.gen_publish(
|
|
topic.encode('utf-8'), qos=0
|
|
)
|
|
packet_in = fake_broker.receive_packet(len(publish_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == publish_packet
|
|
|
|
mqttc.disconnect()
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(len(disconnect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
def test_message_callback(self, callback_version, fake_broker):
|
|
mqttc = client.Client(callback_version, "client-id", transport=fake_broker.transport)
|
|
userdata = {
|
|
'on_message': 0,
|
|
'callback1': 0,
|
|
'callback2': 0,
|
|
}
|
|
mqttc.user_data_set(userdata)
|
|
|
|
def on_message(client, userdata, msg):
|
|
assert msg.topic == 'topic/value'
|
|
userdata['on_message'] += 1
|
|
|
|
def callback1(client, userdata, msg):
|
|
assert msg.topic == 'topic/callback/1'
|
|
userdata['callback1'] += 1
|
|
|
|
def callback2(client, userdata, msg):
|
|
assert msg.topic in ('topic/callback/3', 'topic/callback/1')
|
|
userdata['callback2'] += 1
|
|
|
|
mqttc.on_message = on_message
|
|
mqttc.message_callback_add('topic/callback/1', callback1)
|
|
mqttc.message_callback_add('topic/callback/+', callback2)
|
|
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect("client-id")
|
|
packet_in = fake_broker.receive_packet(len(connect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == connect_packet
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
publish_packet = paho_test.gen_publish(b"topic/value", qos=1, mid=1)
|
|
count = fake_broker.send_packet(publish_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_packet)
|
|
|
|
publish_packet = paho_test.gen_publish(b"topic/callback/1", qos=1, mid=2)
|
|
count = fake_broker.send_packet(publish_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_packet)
|
|
|
|
publish_packet = paho_test.gen_publish(b"topic/callback/3", qos=1, mid=3)
|
|
count = fake_broker.send_packet(publish_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_packet)
|
|
|
|
|
|
puback_packet = paho_test.gen_puback(mid=1)
|
|
packet_in = fake_broker.receive_packet(len(puback_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == puback_packet
|
|
|
|
puback_packet = paho_test.gen_puback(mid=2)
|
|
packet_in = fake_broker.receive_packet(len(puback_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == puback_packet
|
|
|
|
puback_packet = paho_test.gen_puback(mid=3)
|
|
packet_in = fake_broker.receive_packet(len(puback_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == puback_packet
|
|
|
|
mqttc.disconnect()
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
packet_in = fake_broker.receive_packet(len(disconnect_packet))
|
|
assert packet_in # Check connection was not closed
|
|
assert packet_in == disconnect_packet
|
|
|
|
finally:
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
assert userdata['on_message'] == 1
|
|
assert userdata['callback1'] == 1
|
|
assert userdata['callback2'] == 2
|
|
|
|
|
|
class TestCompatibility:
|
|
"""
|
|
Some tests for backward compatibility
|
|
"""
|
|
|
|
def test_change_error_code_to_enum(self):
|
|
"""Make sure code don't break after MQTTErrorCode enum introduction"""
|
|
rc_ok = client.MQTTErrorCode.MQTT_ERR_SUCCESS
|
|
rc_again = client.MQTTErrorCode.MQTT_ERR_AGAIN
|
|
rc_err = client.MQTTErrorCode.MQTT_ERR_NOMEM
|
|
|
|
# Access using old name still works
|
|
assert rc_ok == client.MQTT_ERR_SUCCESS
|
|
|
|
# User might compare to 0 to check for success
|
|
assert rc_ok == 0
|
|
assert not rc_err == 0
|
|
assert not rc_again == 0
|
|
assert not rc_ok != 0
|
|
assert rc_err != 0
|
|
assert rc_again != 0
|
|
|
|
# User might compare to specific code
|
|
assert rc_again == -1
|
|
assert rc_err == 1
|
|
|
|
# User might just use "if rc:"
|
|
assert not rc_ok
|
|
assert rc_err
|
|
assert rc_again
|
|
|
|
# User might do inequality with 0 (like "if rc > 0")
|
|
assert not (rc_ok > 0)
|
|
assert rc_err > 0
|
|
assert rc_again < 0
|
|
|
|
# This might probably not be done: User might use rc as number in
|
|
# operation
|
|
assert rc_ok + 1 == 1
|
|
|
|
def test_migration_callback_version(self):
|
|
with pytest.raises(ValueError, match="see docs/migrations.rst"):
|
|
_ = client.Client("client-id")
|
|
|
|
def test_callback_v1_mqtt3(self, fake_broker):
|
|
callback_called = []
|
|
with pytest.deprecated_call():
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION1,
|
|
"client-id",
|
|
userdata=callback_called,
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
def on_connect(cl, userdata, flags, rc):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(flags, dict)
|
|
assert isinstance(flags["session present"], int)
|
|
assert isinstance(rc, int)
|
|
userdata.append("on_connect")
|
|
cl.subscribe([("topic", 0)])
|
|
|
|
def on_subscribe(cl, userdata, mid, granted_qos):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
assert isinstance(granted_qos, tuple)
|
|
assert isinstance(granted_qos[0], int)
|
|
userdata.append("on_subscribe")
|
|
cl.publish("topic", "payload", 2)
|
|
|
|
def on_publish(cl, userdata, mid):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
userdata.append("on_publish")
|
|
|
|
def on_message(cl, userdata, message):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(message, client.MQTTMessage)
|
|
userdata.append("on_message")
|
|
cl.unsubscribe("topic")
|
|
|
|
def on_unsubscribe(cl, userdata, mid):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
userdata.append("on_unsubscribe")
|
|
cl.disconnect()
|
|
|
|
def on_disconnect(cl, userdata, rc):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(rc, int)
|
|
userdata.append("on_disconnect")
|
|
|
|
mqttc.on_connect = on_connect
|
|
mqttc.on_subscribe = on_subscribe
|
|
mqttc.on_publish = on_publish
|
|
mqttc.on_message = on_message
|
|
mqttc.on_unsubscribe = on_unsubscribe
|
|
mqttc.on_disconnect = on_disconnect
|
|
|
|
mqttc.enable_logger()
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"client-id", keepalive=60)
|
|
fake_broker.expect_packet("connect", connect_packet)
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
subscribe_packet = paho_test.gen_subscribe(1, "topic", 0)
|
|
fake_broker.expect_packet("subscribe", subscribe_packet)
|
|
|
|
suback_packet = paho_test.gen_suback(1, 0)
|
|
count = fake_broker.send_packet(suback_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(suback_packet)
|
|
|
|
publish_packet = paho_test.gen_publish("topic", 2, "payload", mid=2)
|
|
fake_broker.expect_packet("publish", publish_packet)
|
|
|
|
pubrec_packet = paho_test.gen_pubrec(mid=2)
|
|
count = fake_broker.send_packet(pubrec_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(pubrec_packet)
|
|
|
|
pubrel_packet = paho_test.gen_pubrel(mid=2)
|
|
fake_broker.expect_packet("pubrel", pubrel_packet)
|
|
|
|
pubcomp_packet = paho_test.gen_pubcomp(mid=2)
|
|
count = fake_broker.send_packet(pubcomp_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(pubcomp_packet)
|
|
|
|
publish_from_broker_packet = paho_test.gen_publish("topic", qos=0, payload="payload", mid=99)
|
|
count = fake_broker.send_packet(publish_from_broker_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_from_broker_packet)
|
|
|
|
unsubscribe_packet = paho_test.gen_unsubscribe(mid=3, topic="topic")
|
|
fake_broker.expect_packet("unsubscribe", unsubscribe_packet)
|
|
|
|
suback_packet = paho_test.gen_unsuback(mid=3)
|
|
count = fake_broker.send_packet(suback_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(suback_packet)
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
fake_broker.expect_packet("disconnect", disconnect_packet)
|
|
|
|
assert callback_called == [
|
|
"on_connect",
|
|
"on_subscribe",
|
|
"on_publish",
|
|
"on_message",
|
|
"on_unsubscribe",
|
|
"on_disconnect",
|
|
]
|
|
|
|
finally:
|
|
mqttc.disconnect()
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|
|
|
|
def test_callback_v2_mqtt3(self, fake_broker):
|
|
callback_called = []
|
|
mqttc = client.Client(
|
|
CallbackAPIVersion.VERSION2,
|
|
"client-id",
|
|
userdata=callback_called,
|
|
transport=fake_broker.transport,
|
|
)
|
|
|
|
def on_connect(cl, userdata, flags, reason, properties):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(flags, client.ConnectFlags)
|
|
assert isinstance(reason, ReasonCode)
|
|
assert isinstance(properties, Properties)
|
|
assert reason == 0
|
|
assert properties.isEmpty()
|
|
userdata.append("on_connect")
|
|
cl.subscribe([("topic", 0)])
|
|
|
|
def on_subscribe(cl, userdata, mid, reason_code_list, properties):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
assert isinstance(reason_code_list, list)
|
|
assert isinstance(reason_code_list[0], ReasonCode)
|
|
assert isinstance(properties, Properties)
|
|
assert properties.isEmpty()
|
|
userdata.append("on_subscribe")
|
|
cl.publish("topic", "payload", 2)
|
|
|
|
def on_publish(cl, userdata, mid, reason_code, properties):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
assert isinstance(reason_code, ReasonCode)
|
|
assert isinstance(properties, Properties)
|
|
assert properties.isEmpty()
|
|
userdata.append("on_publish")
|
|
|
|
def on_message(cl, userdata, message):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(message, client.MQTTMessage)
|
|
userdata.append("on_message")
|
|
cl.unsubscribe("topic")
|
|
|
|
def on_unsubscribe(cl, userdata, mid, reason_code_list, properties):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(mid, int)
|
|
assert isinstance(reason_code_list, list)
|
|
assert len(reason_code_list) == 0
|
|
assert isinstance(properties, Properties)
|
|
assert properties.isEmpty()
|
|
userdata.append("on_unsubscribe")
|
|
cl.disconnect()
|
|
|
|
def on_disconnect(cl, userdata, flags, reason_code, properties):
|
|
assert isinstance(cl, client.Client)
|
|
assert isinstance(flags, client.DisconnectFlags)
|
|
assert isinstance(reason_code, ReasonCode)
|
|
assert isinstance(properties, Properties)
|
|
assert properties.isEmpty()
|
|
userdata.append("on_disconnect")
|
|
|
|
mqttc.on_connect = on_connect
|
|
mqttc.on_subscribe = on_subscribe
|
|
mqttc.on_publish = on_publish
|
|
mqttc.on_message = on_message
|
|
mqttc.on_unsubscribe = on_unsubscribe
|
|
mqttc.on_disconnect = on_disconnect
|
|
|
|
mqttc.enable_logger()
|
|
mqttc.connect_async("localhost", fake_broker.port)
|
|
mqttc.loop_start()
|
|
|
|
try:
|
|
fake_broker.start()
|
|
|
|
connect_packet = paho_test.gen_connect(
|
|
"client-id", keepalive=60)
|
|
fake_broker.expect_packet("connect", connect_packet)
|
|
|
|
connack_packet = paho_test.gen_connack(rc=0)
|
|
count = fake_broker.send_packet(connack_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(connack_packet)
|
|
|
|
subscribe_packet = paho_test.gen_subscribe(1, "topic", 0)
|
|
fake_broker.expect_packet("subscribe", subscribe_packet)
|
|
|
|
suback_packet = paho_test.gen_suback(1, 0)
|
|
count = fake_broker.send_packet(suback_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(suback_packet)
|
|
|
|
publish_packet = paho_test.gen_publish("topic", 2, "payload", mid=2)
|
|
fake_broker.expect_packet("publish", publish_packet)
|
|
|
|
pubrec_packet = paho_test.gen_pubrec(mid=2)
|
|
count = fake_broker.send_packet(pubrec_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(pubrec_packet)
|
|
|
|
pubrel_packet = paho_test.gen_pubrel(mid=2)
|
|
fake_broker.expect_packet("pubrel", pubrel_packet)
|
|
|
|
pubcomp_packet = paho_test.gen_pubcomp(mid=2)
|
|
count = fake_broker.send_packet(pubcomp_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(pubcomp_packet)
|
|
|
|
publish_from_broker_packet = paho_test.gen_publish("topic", qos=0, payload="payload", mid=99)
|
|
count = fake_broker.send_packet(publish_from_broker_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(publish_from_broker_packet)
|
|
|
|
unsubscribe_packet = paho_test.gen_unsubscribe(mid=3, topic="topic")
|
|
fake_broker.expect_packet("unsubscribe", unsubscribe_packet)
|
|
|
|
suback_packet = paho_test.gen_unsuback(mid=3)
|
|
count = fake_broker.send_packet(suback_packet)
|
|
assert count # Check connection was not closed
|
|
assert count == len(suback_packet)
|
|
|
|
disconnect_packet = paho_test.gen_disconnect()
|
|
fake_broker.expect_packet("disconnect", disconnect_packet)
|
|
|
|
assert callback_called == [
|
|
"on_connect",
|
|
"on_subscribe",
|
|
"on_publish",
|
|
"on_message",
|
|
"on_unsubscribe",
|
|
"on_disconnect",
|
|
]
|
|
|
|
finally:
|
|
mqttc.disconnect()
|
|
mqttc.loop_stop()
|
|
|
|
packet_in = fake_broker.receive_packet(1)
|
|
assert not packet_in # Check connection is closed
|