1
1
import asyncio
2
- from unittest import mock
2
+ import logging
3
3
4
+ from asynctest import CoroutineMock , mock
4
5
import pytest
6
+ import zigpy .exceptions
5
7
6
8
from zigpy_xbee import api as xbee_api , types as t , uart
7
9
from zigpy_xbee .zigbee .application import ControllerApplication
@@ -25,9 +27,10 @@ async def test_connect(monkeypatch):
25
27
26
28
27
29
def test_close (api ):
28
- api . _uart . close = mock . MagicMock ()
30
+ uart = api . _uart
29
31
api .close ()
30
- assert api ._uart .close .call_count == 1
32
+ assert api ._uart is None
33
+ assert uart .close .call_count == 1
31
34
32
35
33
36
def test_commands ():
@@ -84,6 +87,22 @@ def mock_api_frame(name, *args):
84
87
api ._uart .send .reset_mock ()
85
88
86
89
90
+ @pytest .mark .asyncio
91
+ async def test_command_not_connected (api ):
92
+ api ._uart = None
93
+
94
+ def mock_api_frame (name , * args ):
95
+ return mock .sentinel .api_frame_data , api ._seq
96
+
97
+ api ._api_frame = mock .MagicMock (side_effect = mock_api_frame )
98
+
99
+ for cmd , cmd_opts in xbee_api .COMMAND_REQUESTS .items ():
100
+ with pytest .raises (zigpy .exceptions .APIException ):
101
+ await api ._command (cmd , mock .sentinel .cmd_data )
102
+ assert api ._api_frame .call_count == 0
103
+ api ._api_frame .reset_mock ()
104
+
105
+
87
106
async def _test_at_or_queued_at_command (api , cmd , monkeypatch , do_reply = True ):
88
107
monkeypatch .setattr (
89
108
t , "serialize" , mock .MagicMock (return_value = mock .sentinel .serialize )
@@ -518,3 +537,54 @@ def test_handle_many_to_one_rri(api):
518
537
ieee = t .EUI64 ([t .uint8_t (a ) for a in range (0 , 8 )])
519
538
nwk = 0x1234
520
539
api ._handle_many_to_one_rri (ieee , nwk , 0 )
540
+
541
+
542
+ @pytest .mark .asyncio
543
+ async def test_reconnect_multiple_disconnects (monkeypatch , caplog ):
544
+ api = xbee_api .XBee ()
545
+ dev = mock .sentinel .uart
546
+ connect_mock = CoroutineMock ()
547
+ connect_mock .return_value = asyncio .Future ()
548
+ connect_mock .return_value .set_result (True )
549
+ monkeypatch .setattr (uart , "connect" , connect_mock )
550
+
551
+ await api .connect (dev , 115200 )
552
+
553
+ caplog .set_level (logging .DEBUG )
554
+ connected = asyncio .Future ()
555
+ connected .set_result (mock .sentinel .uart_reconnect )
556
+ connect_mock .reset_mock ()
557
+ connect_mock .side_effect = [asyncio .Future (), connected ]
558
+ api .connection_lost ("connection lost" )
559
+ await asyncio .sleep (0.3 )
560
+ api .connection_lost ("connection lost 2" )
561
+ await asyncio .sleep (0.3 )
562
+
563
+ assert "Cancelling reconnection attempt" in caplog .messages
564
+ assert api ._uart is mock .sentinel .uart_reconnect
565
+ assert connect_mock .call_count == 2
566
+
567
+
568
+ @pytest .mark .asyncio
569
+ async def test_reconnect_multiple_attempts (monkeypatch , caplog ):
570
+ api = xbee_api .XBee ()
571
+ dev = mock .sentinel .uart
572
+ connect_mock = CoroutineMock ()
573
+ connect_mock .return_value = asyncio .Future ()
574
+ connect_mock .return_value .set_result (True )
575
+ monkeypatch .setattr (uart , "connect" , connect_mock )
576
+
577
+ await api .connect (dev , 115200 )
578
+
579
+ caplog .set_level (logging .DEBUG )
580
+ connected = asyncio .Future ()
581
+ connected .set_result (mock .sentinel .uart_reconnect )
582
+ connect_mock .reset_mock ()
583
+ connect_mock .side_effect = [asyncio .TimeoutError , OSError , connected ]
584
+
585
+ with mock .patch ("asyncio.sleep" ):
586
+ api .connection_lost ("connection lost" )
587
+ await api ._conn_lost_task
588
+
589
+ assert api ._uart is mock .sentinel .uart_reconnect
590
+ assert connect_mock .call_count == 3
0 commit comments