Skip to content

Commit 58bd772

Browse files
committed
Test and fix update_network
1 parent 535f2f6 commit 58bd772

File tree

3 files changed

+137
-6
lines changed

3 files changed

+137
-6
lines changed

tests/test_application.py

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import pytest
55

6+
from asynctest import CoroutineMock
7+
68
import zigpy_znp.types as t
79
import zigpy_znp.commands as c
810
import zigpy_znp.config as conf
@@ -13,6 +15,7 @@
1315

1416
from zigpy_znp.api import ZNP
1517
from zigpy_znp.uart import connect as uart_connect
18+
from zigpy_znp.types.nvids import NwkNvIds
1619
from zigpy_znp.zigbee.application import ControllerApplication
1720

1821

@@ -435,7 +438,7 @@ async def test_reconnect(event_loop, application):
435438
assert app._znp._uart is not None
436439

437440

438-
@pytest_mark_asyncio_timeout()
441+
@pytest_mark_asyncio_timeout(seconds=3)
439442
async def test_auto_connect(mocker, application):
440443
AUTO_DETECTED_PORT = "/dev/ttyFAKE0"
441444

@@ -525,3 +528,122 @@ async def test_zdo_request_interception(application, mocker):
525528
await active_ep_req
526529

527530
assert status == t.Status.Success
531+
532+
533+
@pytest_mark_asyncio_timeout()
534+
async def test_update_network_noop(mocker, application):
535+
app, znp_server = application
536+
537+
await app.startup(auto_form=False)
538+
539+
app._znp = mocker.NonCallableMock()
540+
541+
# Nothing should be called
542+
await app.update_network(reset=False)
543+
544+
# This will call _znp.request and fail
545+
with pytest.raises(TypeError):
546+
await app.update_network(reset=True)
547+
548+
549+
@pytest_mark_asyncio_timeout(seconds=5)
550+
async def test_update_network(mocker, caplog, application):
551+
app, znp_server = application
552+
553+
await app.startup(auto_form=False)
554+
mocker.patch.object(app, "_reset", new=CoroutineMock())
555+
556+
channel = t.uint8_t(15)
557+
pan_id = t.PanId(0x1234)
558+
extended_pan_id = t.ExtendedPanId(range(8))
559+
channels = t.Channels.from_channel_list([11, 15, 20])
560+
network_key = t.KeyData(range(16))
561+
562+
channels_updated = znp_server.reply_once_to(
563+
request=c.UtilCommands.SetChannels.Req(Channels=channels),
564+
responses=[c.UtilCommands.SetChannels.Rsp(Status=t.Status.Success)],
565+
)
566+
567+
bdb_set_primary_channel = znp_server.reply_once_to(
568+
request=c.APPConfigCommands.BDBSetChannel.Req(IsPrimary=True, Channel=channels),
569+
responses=[c.APPConfigCommands.BDBSetChannel.Rsp(Status=t.Status.Success)],
570+
)
571+
572+
bdb_set_secondary_channel = znp_server.reply_once_to(
573+
request=c.APPConfigCommands.BDBSetChannel.Req(
574+
IsPrimary=False, Channel=t.Channels.NO_CHANNELS
575+
),
576+
responses=[c.APPConfigCommands.BDBSetChannel.Rsp(Status=t.Status.Success)],
577+
)
578+
579+
set_pan_id = znp_server.reply_once_to(
580+
request=c.UtilCommands.SetPanId.Req(PanId=pan_id),
581+
responses=[c.UtilCommands.SetPanId.Rsp(Status=t.Status.Success)],
582+
)
583+
584+
set_extended_pan_id = znp_server.reply_once_to(
585+
request=c.SysCommands.OSALNVWrite.Req(
586+
Id=NwkNvIds.EXTENDED_PAN_ID, Offset=0, Value=extended_pan_id.serialize()
587+
),
588+
responses=[c.SysCommands.OSALNVWrite.Rsp(Status=t.Status.Success)],
589+
)
590+
591+
set_network_key_util = znp_server.reply_once_to(
592+
request=c.UtilCommands.SetPreConfigKey.Req(PreConfigKey=network_key),
593+
responses=[c.UtilCommands.SetPreConfigKey.Rsp(Status=t.Status.Success)],
594+
)
595+
596+
set_network_key_nvram = znp_server.reply_once_to(
597+
request=c.SysCommands.OSALNVWrite.Req(
598+
Id=NwkNvIds.PRECFGKEYS_ENABLE, Offset=0, Value=t.Bool(True).serialize()
599+
),
600+
responses=[c.SysCommands.OSALNVWrite.Rsp(Status=t.Status.Success)],
601+
)
602+
603+
# But it does succeed with a warning if you explicitly allow it
604+
with caplog.at_level(logging.WARNING):
605+
await app.update_network(
606+
channel=channel,
607+
channels=channels,
608+
extended_pan_id=extended_pan_id,
609+
network_key=network_key,
610+
pan_id=pan_id,
611+
tc_address=t.EUI64(range(8)),
612+
tc_link_key=t.KeyData(range(8)),
613+
update_id=0,
614+
reset=True,
615+
)
616+
617+
# We should receive a warning about setting a specific channel
618+
assert len(caplog.records) >= 1
619+
assert any(
620+
"Cannot set a specific channel in config" in r.message for r in caplog.records
621+
)
622+
623+
await channels_updated
624+
await bdb_set_primary_channel
625+
await bdb_set_secondary_channel
626+
await set_pan_id
627+
await set_extended_pan_id
628+
await set_network_key_util
629+
await set_network_key_nvram
630+
631+
app._reset.assert_called_once_with()
632+
633+
# Ensure we set everything we could
634+
assert app.channel is None # We can't set it
635+
assert app.nwk_update_id is None # We can't use it
636+
assert app.channels == channels
637+
assert app.pan_id == pan_id
638+
assert app.extended_pan_id == extended_pan_id
639+
640+
641+
@pytest_mark_asyncio_timeout(seconds=5)
642+
async def test_update_network_bad_channel(mocker, caplog, application):
643+
app, znp_server = application
644+
645+
with pytest.raises(ValueError):
646+
# 12 is not in the mask
647+
await app.update_network(
648+
channel=t.uint8_t(12), channels=t.Channels.from_channel_list([11, 15, 20]),
649+
)

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ deps =
1818
pytest-asyncio==0.10.0
1919
pytest-mock
2020
asyncmock
21+
asynctest
2122

2223
[testenv:lint]
2324
basepython = python3

zigpy_znp/zigbee/application.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,17 +318,27 @@ async def update_network(
318318
extended_pan_id: typing.Optional[t.ExtendedPanId] = None,
319319
network_key: typing.Optional[t.KeyData] = None,
320320
pan_id: typing.Optional[t.PanId] = None,
321-
tc_address: typing.Optional[t.KeyData] = None,
321+
tc_address: typing.Optional[t.EUI64] = None,
322322
tc_link_key: typing.Optional[t.KeyData] = None,
323323
update_id: int = 0,
324324
reset: bool = True,
325325
):
326+
if (
327+
channel is not None
328+
and channels is not None
329+
and not t.Channels.from_channel_list([channel]) & channels
330+
):
331+
raise ValueError("Channel does not overlap with channel mask")
332+
326333
if channel is not None:
327334
LOGGER.warning("Cannot set a specific channel in config: %d", channel)
328335

329336
if tc_link_key is not None:
330337
LOGGER.warning("Trust center link key in config is not yet supported")
331338

339+
if tc_address is not None:
340+
LOGGER.warning("Trust center address in config is not yet supported")
341+
332342
if channels is not None:
333343
await self._znp.request(
334344
c.UtilCommands.SetChannels.Req(Channels=channels),
@@ -358,7 +368,7 @@ async def update_network(
358368
# There is no Util request to do this
359369
await self._znp.nvram_write(NwkNvIds.EXTENDED_PAN_ID, extended_pan_id)
360370

361-
self._extended_pan_id = extended_pan_id
371+
self._ext_pan_id = extended_pan_id
362372

363373
if network_key is not None:
364374
await self._znp.request(
@@ -367,9 +377,7 @@ async def update_network(
367377
)
368378

369379
# XXX: The Util request does not actually write to this NV address
370-
await self._znp.nvram_write(
371-
NwkNvIds.PRECFGKEYS_ENABLE, zigpy.types.bool(True)
372-
)
380+
await self._znp.nvram_write(NwkNvIds.PRECFGKEYS_ENABLE, t.Bool(True))
373381

374382
if reset:
375383
# We have to reset afterwards

0 commit comments

Comments
 (0)