@@ -32,26 +32,36 @@ def mqtt_server_fixture(mock_create_connection: None, mock_select: None) -> None
32
32
33
33
@pytest .fixture (autouse = True )
34
34
def mock_client_fixture (event_loop : asyncio .AbstractEventLoop ) -> Generator [None , None , None ]:
35
- """Fixture to patch the MQTT underlying sync client to regularly read from the mock socket."""
35
+ """Fixture to patch the MQTT underlying sync client.
36
+
37
+ The tests use fake sockets, so this ensures that the async mqtt client does not
38
+ attempt to listen on them directly. We instead just poll the socket for
39
+ data ourselves.
40
+ """
36
41
37
42
orig_class = mqtt .Client
38
43
39
- async def ready_loop (client : mqtt .Client ) -> None :
40
- """Run the mqtt read loop."""
44
+ async def poll_sockets (client : mqtt .Client ) -> None :
45
+ """Poll the mqtt client sockets in a loop to pick up new data ."""
41
46
while True :
42
- client .loop_read ( )
43
- # event_loop.call_soon_threadsafe(client.loop_read )
47
+ event_loop . call_soon_threadsafe ( client .loop_read )
48
+ event_loop .call_soon_threadsafe (client .loop_write )
44
49
await asyncio .sleep (0.1 )
45
50
46
51
task : asyncio .Task [None ] | None = None
47
52
48
53
def new_client (* args : Any , ** kwargs : Any ) -> mqtt .Client :
54
+ """Create a new mqtt client and start the socket polling task."""
49
55
nonlocal task
50
56
client = orig_class (* args , ** kwargs )
51
- task = event_loop .create_task (ready_loop (client ))
57
+ task = event_loop .create_task (poll_sockets (client ))
52
58
return client
53
59
54
- with patch ("aiomqtt.client.mqtt.Client" , side_effect = new_client ):
60
+ with patch ("aiomqtt.client.Client._on_socket_open" ), patch ("aiomqtt.client.Client._on_socket_close" ), patch (
61
+ "aiomqtt.client.Client._on_socket_register_write"
62
+ ), patch ("aiomqtt.client.Client._on_socket_unregister_write" ), patch (
63
+ "aiomqtt.client.mqtt.Client" , side_effect = new_client
64
+ ):
55
65
yield
56
66
if task :
57
67
task .cancel ()
0 commit comments