Skip to content

Commit 06af078

Browse files
committed
Improve unit tests and test coverage
1 parent b0435d4 commit 06af078

File tree

4 files changed

+67
-32
lines changed

4 files changed

+67
-32
lines changed

tests/tools/test_network_backup_restore.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
BaseZStack3CC2531,
2121
FormedZStack1CC2531,
2222
BaseLaunchpadCC26X2R1,
23+
ResetLaunchpadCC26X2R1,
2324
)
2425
from ..application.test_startup import DEV_NETWORK_SETTINGS
2526

@@ -91,13 +92,23 @@ def backup_json():
9192
{
9293
"ieee_address": "aabbccddeeff0011",
9394
"link_key": {
94-
"key": "01234567801234567801234567801234", # Not derived from seed
95+
"key": "4dcc18441d843fe86d8ae1396648a92b", # Derived from seed
9596
"rx_counter": 112233,
9697
"tx_counter": 445566,
9798
},
9899
"nwk_address": "abcd",
99100
"is_child": False,
100101
},
102+
{
103+
"ieee_address": "abcdabcabcabcaaa",
104+
"link_key": {
105+
"key": "76567876567867876718736817112312", # Not derived from seed
106+
"rx_counter": 511223,
107+
"tx_counter": 844556,
108+
},
109+
"nwk_address": None, # No known NWK address
110+
"is_child": True, # but a child
111+
},
101112
],
102113
}
103114

@@ -204,6 +215,7 @@ async def test_network_restore_and_backup(
204215
backup_json2["metadata"]["source"] = backup_json["metadata"]["source"]
205216
backup_json2["network_key"]["frame_counter"] -= 2500
206217

218+
# Remove things Z-Stack 1 does not preserve
207219
if issubclass(device, BaseZStack1CC2531):
208220
del backup_json["stack_specific"]["zstack"]["tclk_seed"]
209221

@@ -217,6 +229,31 @@ async def test_network_restore_and_backup(
217229
assert backup_json == backup_json2
218230

219231

232+
@pytest.mark.parametrize("device", [ResetLaunchpadCC26X2R1])
233+
@pytest.mark.asyncio
234+
async def test_network_restore_pick_optimal_tclk(
235+
device, make_znp_server, backup_json, tmp_path
236+
):
237+
# Pick a sub-optimal TCLK
238+
old_tclk_seed = backup_json["stack_specific"]["zstack"]["tclk_seed"]
239+
backup_json["stack_specific"]["zstack"]["tclk_seed"] = "ab" * 16
240+
241+
backup_file = tmp_path / "backup.json"
242+
backup_file.write_text(json.dumps(backup_json))
243+
244+
znp_server = make_znp_server(server_cls=device)
245+
246+
await network_restore([znp_server._port_path, "-i", str(backup_file)])
247+
248+
backup_file2 = tmp_path / "backup2.json"
249+
await network_backup([znp_server._port_path, "-o", str(backup_file2)])
250+
251+
backup_json2 = json.loads(backup_file2.read_text())
252+
253+
# Optimal TCLK is re-derived
254+
assert backup_json2["stack_specific"]["zstack"]["tclk_seed"] == old_tclk_seed
255+
256+
220257
@pytest.mark.asyncio
221258
async def test_tc_frame_counter_zstack1(make_connected_znp):
222259
znp, znp_server = await make_connected_znp(BaseZStack1CC2531)

zigpy_znp/api.py

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@
2626
CallbackResponseListener,
2727
)
2828
from zigpy_znp.frames import GeneralFrame
29-
from zigpy_znp.exceptions import (
30-
SecurityError,
31-
CommandNotRecognized,
32-
InvalidCommandResponse,
33-
)
29+
from zigpy_znp.exceptions import CommandNotRecognized, InvalidCommandResponse
3430
from zigpy_znp.types.nvids import ExNvIds, OsalNvIds
3531

3632
LOGGER = logging.getLogger(__name__)
@@ -113,24 +109,9 @@ async def load_network_info(self, *, load_devices=False):
113109
if not is_on_network:
114110
raise ValueError("Device is not a part of a network")
115111

116-
try:
117-
key_desc = await self.nvram.osal_read(
118-
OsalNvIds.NWK_ACTIVE_KEY_INFO, item_type=t.NwkKeyDesc
119-
)
120-
except SecurityError:
121-
# XXX: Z-Stack 1 has no way to read some key info
122-
key = await self.nvram.osal_read(OsalNvIds.PRECFGKEY, item_type=t.KeyData)
123-
key_desc = t.NwkKeyDesc(
124-
KeySeqNum=0,
125-
Key=key,
126-
)
127-
128-
tclk_seed = None
129-
130-
if self.version > 1.2:
131-
tclk_seed = await self.nvram.osal_read(
132-
OsalNvIds.TCLK_SEED, item_type=t.KeyData
133-
)
112+
key_desc = await self.nvram.osal_read(
113+
OsalNvIds.NWK_ACTIVE_KEY_INFO, item_type=t.NwkKeyDesc
114+
)
134115

135116
tc_frame_counter = await security.read_tc_frame_counter(
136117
self, ext_pan_id=nib.extendedPANID
@@ -157,7 +138,11 @@ async def load_network_info(self, *, load_devices=False):
157138
stack_specific=None,
158139
)
159140

160-
if tclk_seed is not None:
141+
if self.version > 1.2:
142+
tclk_seed = await self.nvram.osal_read(
143+
OsalNvIds.TCLK_SEED, item_type=t.KeyData
144+
)
145+
161146
network_info.stack_specific = {
162147
"zstack": {
163148
"tclk_seed": tclk_seed.serialize().hex(),
@@ -167,9 +152,14 @@ async def load_network_info(self, *, load_devices=False):
167152
# This takes a few seconds
168153
if load_devices:
169154
for dev in await security.read_devices(self):
155+
if dev.node_info.nwk == 0xFFFE:
156+
dev = dev.replace(
157+
node_info=dataclasses.replace(dev.node_info, nwk=None)
158+
)
159+
170160
if dev.is_child:
171161
network_info.children.append(dev.node_info)
172-
else:
162+
elif dev.node_info.nwk is not None:
173163
network_info.nwk_addresses[dev.node_info.ieee] = dev.node_info.nwk
174164

175165
if dev.key is not None:
@@ -306,14 +296,14 @@ async def write_network_info(
306296
while True:
307297
try:
308298
nib = await self.nvram.osal_read(OsalNvIds.NIB, item_type=t.NIB)
309-
299+
except KeyError:
300+
pass
301+
else:
310302
LOGGER.debug("Current NIB is %s", nib)
311303

312304
# Usually this works after the first attempt
313305
if nib.nwkLogicalChannel != 0 and nib.nwkPanId != 0xFFFE:
314306
break
315-
except KeyError:
316-
pass
317307

318308
await asyncio.sleep(1)
319309

zigpy_znp/tools/network_restore.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,19 @@ def json_backup_to_zigpy_state(
5353

5454
for obj in backup["devices"]:
5555
node = zigpy.state.NodeInfo()
56-
node.nwk, _ = t.NWK.deserialize(bytes.fromhex(obj["nwk_address"])[::-1])
56+
57+
if obj["nwk_address"] is not None:
58+
node.nwk, _ = t.NWK.deserialize(bytes.fromhex(obj["nwk_address"])[::-1])
59+
else:
60+
node.nwk = None
61+
5762
node.ieee, _ = t.EUI64.deserialize(bytes.fromhex(obj["ieee_address"])[::-1])
5863
node.logical_type = None
5964

6065
# The `is_child` key is currently optional
6166
if obj.get("is_child", True):
6267
network_info.children.append(node)
63-
else:
68+
elif node.nwk is not None:
6469
network_info.nwk_addresses[node.ieee] = node.nwk
6570

6671
if "link_key" in obj:
@@ -73,6 +78,9 @@ def json_backup_to_zigpy_state(
7378

7479
network_info.key_table.append(key)
7580

81+
# XXX: Devices that are not children, have no NWK address, and have no link key
82+
# are effectively ignored, since there is no place to write them
83+
7684
return network_info, node_info
7785

7886

zigpy_znp/znp/security.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def find_optimal_tclk_seed(
366366
if not keys:
367367
return tclk_seed
368368

369-
best_count, best_seed = max(iter_seed_candidates(keys))
369+
best_count, best_seed = max(sorted(iter_seed_candidates(keys)))
370370
tclk_count = count_seed_matches(keys, tclk_seed)
371371
assert tclk_count <= best_count
372372

0 commit comments

Comments
 (0)