Skip to content

Commit

Permalink
Fail with a descriptive error message if startup loops endlessly (#217)
Browse files Browse the repository at this point in the history
* Fail with a descriptive error message if startup stalls

* Fix CI

* Increase test timeout
  • Loading branch information
puddly committed Mar 30, 2023
1 parent 208d837 commit 677f1e5
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 17 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ jobs:
needs: prepare-base
strategy:
matrix:
python-version: ['3.8.14', '3.9.15', '3.10.8', '3.11.0']
python-version: ['3.8.16', '3.9.15', '3.10.8', '3.11.0']
name: >-
Run tests Python ${{ matrix.python-version }}
steps:
Expand Down Expand Up @@ -311,7 +311,7 @@ jobs:
. venv/bin/activate
pytest \
-qq \
--timeout=9 \
--timeout=15 \
--durations=10 \
--cov zigpy_deconz \
--cov-report=term-missing \
Expand Down
22 changes: 13 additions & 9 deletions tests/test_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,28 +112,32 @@ def addr_nwk_and_ieee(nwk, ieee):
return addr


@patch("zigpy_deconz.zigbee.application.CHANGE_NETWORK_WAIT", 0.001)
@pytest.mark.parametrize(
"proto_ver, nwk_state, error",
"proto_ver, target_state, returned_state",
[
(0x0107, deconz_api.NetworkState.CONNECTED, None),
(0x0106, deconz_api.NetworkState.CONNECTED, None),
(0x0107, deconz_api.NetworkState.OFFLINE, None),
(0x0107, deconz_api.NetworkState.OFFLINE, asyncio.TimeoutError()),
(0x0107, deconz_api.NetworkState.CONNECTED, deconz_api.NetworkState.CONNECTED),
(0x0106, deconz_api.NetworkState.CONNECTED, deconz_api.NetworkState.CONNECTED),
(0x0107, deconz_api.NetworkState.OFFLINE, deconz_api.NetworkState.CONNECTED),
(0x0107, deconz_api.NetworkState.CONNECTED, deconz_api.NetworkState.OFFLINE),
],
)
async def test_start_network(app, proto_ver, nwk_state, error):
async def test_start_network(app, proto_ver, target_state, returned_state):
app.load_network_info = AsyncMock()
app.restore_neighbours = AsyncMock()
app.add_endpoint = AsyncMock()
app._change_network_state = AsyncMock(side_effect=error)

app._api.device_state = AsyncMock(
return_value=(deconz_api.DeviceState(nwk_state), 0, 0)
return_value=(deconz_api.DeviceState(returned_state), 0, 0)
)

app._api._proto_ver = proto_ver
app._api.protocol_version = proto_ver

if nwk_state != deconz_api.NetworkState.CONNECTED and error is not None:
if (
target_state == deconz_api.NetworkState.CONNECTED
and returned_state != deconz_api.NetworkState.CONNECTED
):
with pytest.raises(zigpy.exceptions.FormationFailure):
await app.start_network()

Expand Down
20 changes: 14 additions & 6 deletions zigpy_deconz/zigbee/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,7 @@ async def permit_with_key(self, node: t.EUI64, code: bytes, time_s=60):
async def start_network(self):
await self.register_endpoints()
await self.load_network_info(load_devices=False)

try:
await self._change_network_state(NetworkState.CONNECTED)
except asyncio.TimeoutError as e:
raise FormationFailure() from e
await self._change_network_state(NetworkState.CONNECTED)

coordinator = await DeconzDevice.new(
self,
Expand All @@ -141,7 +137,19 @@ async def change_loop():
await asyncio.sleep(CHANGE_NETWORK_WAIT)

await self._api.change_network_state(target_state)
await asyncio.wait_for(change_loop(), timeout=timeout)

try:
await asyncio.wait_for(change_loop(), timeout=timeout)
except asyncio.TimeoutError:
if target_state != NetworkState.CONNECTED:
raise

raise FormationFailure(
"Network formation refused: there is likely too much RF interference."
" Make sure your coordinator is on a USB 2.0 extension cable and"
" away from any sources of interference, like USB 3.0 ports, SSDs,"
" 2.4GHz routers, motherboards, etc."
)

if self._api.protocol_version < PROTO_VER_WATCHDOG:
return
Expand Down

0 comments on commit 677f1e5

Please sign in to comment.