|
1 | 1 | import asyncio |
| 2 | +import logging |
2 | 3 | from unittest import mock |
3 | 4 |
|
| 5 | +import asynctest |
4 | 6 | import pytest |
5 | 7 |
|
6 | 8 | from zigpy_deconz import api as deconz_api, types as t, uart |
@@ -95,6 +97,22 @@ def mock_api_frame(name, *args): |
95 | 97 | api._uart.send.reset_mock() |
96 | 98 |
|
97 | 99 |
|
| 100 | +@pytest.mark.asyncio |
| 101 | +async def test_command_not_connected(api): |
| 102 | + api._uart = None |
| 103 | + |
| 104 | + def mock_api_frame(name, *args): |
| 105 | + return mock.sentinel.api_frame_data, api._seq |
| 106 | + |
| 107 | + api._api_frame = mock.MagicMock(side_effect=mock_api_frame) |
| 108 | + |
| 109 | + for cmd, cmd_opts in deconz_api.TX_COMMANDS.items(): |
| 110 | + with pytest.raises(deconz_api.CommandError): |
| 111 | + await api._command(cmd, mock.sentinel.cmd_data) |
| 112 | + assert api._api_frame.call_count == 0 |
| 113 | + api._api_frame.reset_mock() |
| 114 | + |
| 115 | + |
98 | 116 | def test_api_frame(api): |
99 | 117 | addr = t.DeconzAddressEndpoint() |
100 | 118 | addr.address_mode = t.ADDRESS_MODE.NWK |
@@ -414,3 +432,54 @@ def test_device_state_network_state(data, network_state): |
414 | 432 | assert rest == extra |
415 | 433 | assert state.network_state == deconz_api.NetworkState[network_state] |
416 | 434 | assert state.serialize() == new_data |
| 435 | + |
| 436 | + |
| 437 | +@pytest.mark.asyncio |
| 438 | +async def test_reconnect_multiple_disconnects(monkeypatch, caplog): |
| 439 | + api = deconz_api.Deconz() |
| 440 | + dev = mock.sentinel.uart |
| 441 | + connect_mock = asynctest.CoroutineMock() |
| 442 | + connect_mock.return_value = asyncio.Future() |
| 443 | + connect_mock.return_value.set_result(True) |
| 444 | + monkeypatch.setattr(uart, "connect", connect_mock) |
| 445 | + |
| 446 | + await api.connect(dev, 115200) |
| 447 | + |
| 448 | + caplog.set_level(logging.DEBUG) |
| 449 | + connected = asyncio.Future() |
| 450 | + connected.set_result(mock.sentinel.uart_reconnect) |
| 451 | + connect_mock.reset_mock() |
| 452 | + connect_mock.side_effect = [asyncio.Future(), connected] |
| 453 | + api.connection_lost("connection lost") |
| 454 | + await asyncio.sleep(0.3) |
| 455 | + api.connection_lost("connection lost 2") |
| 456 | + await asyncio.sleep(0.3) |
| 457 | + |
| 458 | + assert "Cancelling reconnection attempt" in caplog.messages |
| 459 | + assert api._uart is mock.sentinel.uart_reconnect |
| 460 | + assert connect_mock.call_count == 2 |
| 461 | + |
| 462 | + |
| 463 | +@pytest.mark.asyncio |
| 464 | +async def test_reconnect_multiple_attempts(monkeypatch, caplog): |
| 465 | + api = deconz_api.Deconz() |
| 466 | + dev = mock.sentinel.uart |
| 467 | + connect_mock = asynctest.CoroutineMock() |
| 468 | + connect_mock.return_value = asyncio.Future() |
| 469 | + connect_mock.return_value.set_result(True) |
| 470 | + monkeypatch.setattr(uart, "connect", connect_mock) |
| 471 | + |
| 472 | + await api.connect(dev, 115200) |
| 473 | + |
| 474 | + caplog.set_level(logging.DEBUG) |
| 475 | + connected = asyncio.Future() |
| 476 | + connected.set_result(mock.sentinel.uart_reconnect) |
| 477 | + connect_mock.reset_mock() |
| 478 | + connect_mock.side_effect = [asyncio.TimeoutError, OSError, connected] |
| 479 | + |
| 480 | + with asynctest.mock.patch("asyncio.sleep"): |
| 481 | + api.connection_lost("connection lost") |
| 482 | + await api._conn_lost_task |
| 483 | + |
| 484 | + assert api._uart is mock.sentinel.uart_reconnect |
| 485 | + assert connect_mock.call_count == 3 |
0 commit comments