1
1
import asyncio
2
+ from unittest .mock import patch
2
3
3
4
import pytest
4
5
5
6
import zigpy_znp .config as conf
6
7
from zigpy_znp .uart import connect as uart_connect
7
8
from zigpy_znp .zigbee .application import ControllerApplication
8
9
9
- from ..conftest import FORMED_DEVICES , FormedLaunchpadCC26X2R1 , swap_attribute
10
+ from ..conftest import FORMED_DEVICES , FormedLaunchpadCC26X2R1
10
11
11
12
12
13
async def test_no_double_connect (make_znp_server , mocker ):
@@ -57,7 +58,7 @@ async def test_probe_unsuccessful():
57
58
58
59
59
60
@pytest .mark .parametrize ("device" , FORMED_DEVICES )
60
- async def test_probe_unsuccessful_slow (device , make_znp_server , mocker ):
61
+ async def test_probe_unsuccessful_slow1 (device , make_znp_server , mocker ):
61
62
znp_server = make_znp_server (server_cls = device , shorten_delays = False )
62
63
63
64
# Don't respond to anything
@@ -74,6 +75,24 @@ async def test_probe_unsuccessful_slow(device, make_znp_server, mocker):
74
75
assert not any ([t ._is_connected for t in znp_server ._transports ])
75
76
76
77
78
+ @pytest .mark .parametrize ("device" , FORMED_DEVICES )
79
+ async def test_probe_unsuccessful_slow2 (device , make_znp_server , mocker ):
80
+ znp_server = make_znp_server (server_cls = device , shorten_delays = False )
81
+
82
+ # Don't respond to anything
83
+ znp_server ._listeners .clear ()
84
+
85
+ mocker .patch ("zigpy_znp.zigbee.application.PROBE_TIMEOUT" , new = 0.1 )
86
+
87
+ assert not (
88
+ await ControllerApplication .probe (
89
+ conf .SCHEMA_DEVICE ({conf .CONF_DEVICE_PATH : znp_server .serial_port })
90
+ )
91
+ )
92
+
93
+ assert not any ([t ._is_connected for t in znp_server ._transports ])
94
+
95
+
77
96
@pytest .mark .parametrize ("device" , FORMED_DEVICES )
78
97
async def test_probe_successful (device , make_znp_server ):
79
98
znp_server = make_znp_server (server_cls = device , shorten_delays = False )
@@ -100,8 +119,8 @@ async def test_probe_multiple(device, make_znp_server):
100
119
101
120
102
121
@pytest .mark .parametrize ("device" , FORMED_DEVICES )
103
- async def test_reconnect (device , event_loop , make_application ):
104
- app , znp_server = make_application (
122
+ async def test_reconnect (device , make_application ):
123
+ app , znp_server = await make_application (
105
124
server_cls = device ,
106
125
client_config = {
107
126
# Make auto-reconnection happen really fast
@@ -118,7 +137,7 @@ async def test_reconnect(device, event_loop, make_application):
118
137
assert app ._znp is not None
119
138
120
139
# Don't reply to anything for a bit
121
- with swap_attribute (znp_server , "frame_received" , lambda _ : None ):
140
+ with patch . object (znp_server , "frame_received" , lambda _ : None ):
122
141
# Now that we're connected, have the server close the connection
123
142
znp_server ._uart ._transport .close ()
124
143
@@ -143,7 +162,7 @@ async def test_reconnect(device, event_loop, make_application):
143
162
144
163
@pytest .mark .parametrize ("device" , FORMED_DEVICES )
145
164
async def test_shutdown_from_app (device , mocker , make_application ):
146
- app , znp_server = make_application (server_cls = device )
165
+ app , znp_server = await make_application (server_cls = device )
147
166
148
167
await app .startup (auto_form = False )
149
168
@@ -159,7 +178,7 @@ async def test_shutdown_from_app(device, mocker, make_application):
159
178
160
179
161
180
async def test_clean_shutdown (make_application ):
162
- app , znp_server = make_application (server_cls = FormedLaunchpadCC26X2R1 )
181
+ app , znp_server = await make_application (server_cls = FormedLaunchpadCC26X2R1 )
163
182
await app .startup (auto_form = False )
164
183
165
184
# This should not throw
@@ -170,7 +189,7 @@ async def test_clean_shutdown(make_application):
170
189
171
190
172
191
async def test_multiple_shutdown (make_application ):
173
- app , znp_server = make_application (server_cls = FormedLaunchpadCC26X2R1 )
192
+ app , znp_server = await make_application (server_cls = FormedLaunchpadCC26X2R1 )
174
193
await app .startup (auto_form = False )
175
194
176
195
await app .shutdown ()
@@ -179,10 +198,10 @@ async def test_multiple_shutdown(make_application):
179
198
180
199
181
200
@pytest .mark .parametrize ("device" , FORMED_DEVICES )
182
- async def test_reconnect_lockup (device , event_loop , make_application , mocker ):
201
+ async def test_reconnect_lockup (device , make_application , mocker ):
183
202
mocker .patch ("zigpy_znp.zigbee.application.WATCHDOG_PERIOD" , 0.1 )
184
203
185
- app , znp_server = make_application (
204
+ app , znp_server = await make_application (
186
205
server_cls = device ,
187
206
client_config = {
188
207
# Make auto-reconnection happen really fast
@@ -197,7 +216,7 @@ async def test_reconnect_lockup(device, event_loop, make_application, mocker):
197
216
await app .startup (auto_form = False )
198
217
199
218
# Stop responding
200
- with swap_attribute (znp_server , "frame_received" , lambda _ : None ):
219
+ with patch . object (znp_server , "frame_received" , lambda _ : None ):
201
220
assert app ._znp is not None
202
221
assert app ._reconnect_task .done ()
203
222
@@ -219,15 +238,16 @@ async def test_reconnect_lockup(device, event_loop, make_application, mocker):
219
238
await app .shutdown ()
220
239
221
240
222
- @pytest .mark .parametrize ("device" , FORMED_DEVICES )
223
- async def test_reconnect_lockup_pyserial (device , event_loop , make_application , mocker ):
241
+ @pytest .mark .parametrize ("device" , [ FormedLaunchpadCC26X2R1 ] )
242
+ async def test_reconnect_lockup_pyserial (device , make_application , mocker ):
224
243
mocker .patch ("zigpy_znp.zigbee.application.WATCHDOG_PERIOD" , 0.1 )
225
244
226
- app , znp_server = make_application (
245
+ app , znp_server = await make_application (
227
246
server_cls = device ,
228
247
client_config = {
229
248
conf .CONF_ZNP_CONFIG : {
230
- conf .CONF_AUTO_RECONNECT_RETRY_DELAY : 0.1 ,
249
+ conf .CONF_AUTO_RECONNECT_RETRY_DELAY : 0.01 ,
250
+ conf .CONF_SREQ_TIMEOUT : 0.1 ,
231
251
}
232
252
},
233
253
)
@@ -242,20 +262,20 @@ async def test_reconnect_lockup_pyserial(device, event_loop, make_application, m
242
262
# We are connected
243
263
assert app ._znp is not None
244
264
245
- did_load_info = asyncio .get_running_loop ().create_future ()
265
+ did_start_network = asyncio .get_running_loop ().create_future ()
246
266
247
- async def patched_load_network_info ( * , old_load = app .load_network_info ):
267
+ async def patched_start_network ( old_start_network = app .start_network , ** kwargs ):
248
268
try :
249
- return await old_load ( )
269
+ return await old_start_network ( ** kwargs )
250
270
finally :
251
- did_load_info .set_result (True )
271
+ did_start_network .set_result (True )
252
272
253
- with swap_attribute (app , "load_network_info " , patched_load_network_info ):
273
+ with patch . object (app , "start_network " , patched_start_network ):
254
274
# "Drop" the connection like PySerial
255
275
app ._znp ._uart .connection_lost (exc = None )
256
276
257
277
# Wait until we are reconnecting
258
- await did_load_info
278
+ await did_start_network
259
279
260
280
# "Drop" the connection like PySerial again, but during connect
261
281
app ._znp ._uart .connection_lost (exc = None )
@@ -269,3 +289,49 @@ async def patched_load_network_info(*, old_load=app.load_network_info):
269
289
assert app ._znp and app ._znp ._uart
270
290
271
291
await app .shutdown ()
292
+
293
+
294
+ @pytest .mark .parametrize ("device" , [FormedLaunchpadCC26X2R1 ])
295
+ async def test_disconnect (device , make_application ):
296
+ app , znp_server = await make_application (
297
+ server_cls = device ,
298
+ client_config = {
299
+ conf .CONF_ZNP_CONFIG : {
300
+ conf .CONF_SREQ_TIMEOUT : 0.1 ,
301
+ }
302
+ },
303
+ )
304
+
305
+ assert app ._znp is None
306
+ await app .connect ()
307
+
308
+ assert app ._znp is not None
309
+
310
+ await app .disconnect ()
311
+ assert app ._znp is None
312
+
313
+ await app .disconnect ()
314
+ await app .disconnect ()
315
+
316
+
317
+ @pytest .mark .parametrize ("device" , [FormedLaunchpadCC26X2R1 ])
318
+ async def test_disconnect_failure (device , make_application ):
319
+ app , znp_server = await make_application (
320
+ server_cls = device ,
321
+ client_config = {
322
+ conf .CONF_ZNP_CONFIG : {
323
+ conf .CONF_SREQ_TIMEOUT : 0.1 ,
324
+ }
325
+ },
326
+ )
327
+
328
+ assert app ._znp is None
329
+ await app .connect ()
330
+
331
+ assert app ._znp is not None
332
+
333
+ with patch .object (app ._znp , "reset" , side_effect = RuntimeError ("An error" )):
334
+ # Runs without error
335
+ await app .disconnect ()
336
+
337
+ assert app ._znp is None
0 commit comments