Skip to content

Commit 6116fe8

Browse files
committed
Test network scanning tool
1 parent 1f4e8f1 commit 6116fe8

File tree

3 files changed

+284
-7
lines changed

3 files changed

+284
-7
lines changed

tests/conftest.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,11 @@ def _flatten_responses(self, request, responses):
210210
yield from self._flatten_responses(request, response)
211211

212212
def reply_once_to(self, request, responses):
213+
future = self.wait_for_response(request)
213214
called_future = asyncio.get_running_loop().create_future()
214215

215-
async def callback(request):
216-
if callback.called:
217-
return
218-
219-
callback.called = request
216+
async def replier():
217+
request = await future
220218

221219
for response in self._flatten_responses(request, responses):
222220
await asyncio.sleep(0.001)
@@ -225,8 +223,7 @@ async def callback(request):
225223

226224
called_future.set_result(request)
227225

228-
callback.called = False
229-
self.callback_for_response(request, lambda r: asyncio.create_task(callback(r)))
226+
asyncio.create_task(replier())
230227

231228
return called_future
232229

tests/tools/test_network_scan.py

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import pytest
2+
3+
import zigpy_znp.types as t
4+
import zigpy_znp.commands as c
5+
from zigpy_znp.exceptions import InvalidCommandResponse
6+
from zigpy_znp.types.nvids import NwkNvIds
7+
from zigpy_znp.tools.network_scan import main as network_scan
8+
9+
from ..conftest import FormedLaunchpadCC26X2R1
10+
11+
pytestmark = [pytest.mark.timeout(1), pytest.mark.asyncio]
12+
13+
14+
@pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
15+
async def test_network_scan(device, make_znp_server, capsys):
16+
znp_server = make_znp_server(server_cls=device)
17+
18+
original_channels = t.Channels.from_channel_list([15, 20, 25]).serialize()
19+
assert znp_server.nvram["nwk"][NwkNvIds.CHANLIST] == original_channels
20+
21+
# Scan 1 results
22+
znp_server.reply_once_to(
23+
c.ZDO.NetworkDiscoveryReq.Req(Channels=t.Channels.ALL_CHANNELS, ScanDuration=2),
24+
responses=[
25+
c.ZDO.NetworkDiscoveryReq.Rsp(Status=t.Status.SUCCESS),
26+
c.ZDO.BeaconNotifyInd.Callback(
27+
Beacons=[
28+
t.Beacon(
29+
Src=0x0000,
30+
PanId=0x7ABE,
31+
Channel=25,
32+
PermitJoining=0,
33+
RouterCapacity=1,
34+
DeviceCapacity=1,
35+
ProtocolVersion=2,
36+
StackProfile=2,
37+
LQI=39,
38+
Depth=0,
39+
UpdateId=0,
40+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
41+
),
42+
t.Beacon(
43+
Src=0xEC9B,
44+
PanId=0x7ABE,
45+
Channel=25,
46+
PermitJoining=0,
47+
RouterCapacity=1,
48+
DeviceCapacity=1,
49+
ProtocolVersion=2,
50+
StackProfile=2,
51+
LQI=66,
52+
Depth=1,
53+
UpdateId=0,
54+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
55+
),
56+
]
57+
),
58+
c.ZDO.BeaconNotifyInd.Callback(
59+
Beacons=[
60+
t.Beacon(
61+
Src=0x74E2,
62+
PanId=0x7ABE,
63+
Channel=25,
64+
PermitJoining=0,
65+
RouterCapacity=1,
66+
DeviceCapacity=1,
67+
ProtocolVersion=2,
68+
StackProfile=2,
69+
LQI=69,
70+
Depth=1,
71+
UpdateId=0,
72+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
73+
),
74+
]
75+
),
76+
c.ZDO.NwkDiscoveryCnf.Callback(Status=t.ZDOStatus.SUCCESS),
77+
],
78+
)
79+
80+
# Scan 2 results
81+
znp_server.reply_once_to(
82+
c.ZDO.NetworkDiscoveryReq.Req(Channels=t.Channels.ALL_CHANNELS, ScanDuration=2),
83+
responses=[
84+
c.ZDO.NetworkDiscoveryReq.Rsp(Status=t.Status.SUCCESS),
85+
c.ZDO.BeaconNotifyInd.Callback(
86+
Beacons=[
87+
# First (and only) other network
88+
t.Beacon(
89+
Src=0x0000,
90+
PanId=0x86E6,
91+
Channel=11,
92+
PermitJoining=1,
93+
RouterCapacity=1,
94+
DeviceCapacity=1,
95+
ProtocolVersion=2,
96+
StackProfile=2,
97+
LQI=0,
98+
Depth=0,
99+
UpdateId=0,
100+
ExtendedPanId=t.EUI64.convert("d9:6d:d1:3b:39:46:6c:36"),
101+
),
102+
t.Beacon(
103+
Src=0x0000,
104+
PanId=0x7ABE,
105+
Channel=25,
106+
PermitJoining=0,
107+
RouterCapacity=1,
108+
DeviceCapacity=1,
109+
ProtocolVersion=2,
110+
StackProfile=2,
111+
LQI=45,
112+
Depth=0,
113+
UpdateId=0,
114+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
115+
),
116+
]
117+
),
118+
c.ZDO.BeaconNotifyInd.Callback(
119+
Beacons=[
120+
t.Beacon(
121+
Src=0x9DC1,
122+
PanId=0x7ABE,
123+
Channel=25,
124+
PermitJoining=0,
125+
RouterCapacity=1,
126+
DeviceCapacity=1,
127+
ProtocolVersion=2,
128+
StackProfile=2,
129+
LQI=57,
130+
Depth=1,
131+
UpdateId=0,
132+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
133+
),
134+
]
135+
),
136+
c.ZDO.NwkDiscoveryCnf.Callback(Status=t.ZDOStatus.SUCCESS),
137+
],
138+
)
139+
140+
# Scan 3 results
141+
znp_server.reply_once_to(
142+
c.ZDO.NetworkDiscoveryReq.Req(Channels=t.Channels.ALL_CHANNELS, ScanDuration=2),
143+
responses=[
144+
c.ZDO.NetworkDiscoveryReq.Rsp(Status=t.Status.SUCCESS),
145+
c.ZDO.BeaconNotifyInd.Callback(
146+
Beacons=[
147+
t.Beacon(
148+
Src=0x74E2,
149+
PanId=0x7ABE,
150+
Channel=25,
151+
PermitJoining=0,
152+
RouterCapacity=1,
153+
DeviceCapacity=1,
154+
ProtocolVersion=2,
155+
StackProfile=2,
156+
LQI=63,
157+
Depth=1,
158+
UpdateId=0,
159+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
160+
),
161+
t.Beacon(
162+
Src=0xEC9B,
163+
PanId=0x7ABE,
164+
Channel=25,
165+
PermitJoining=0,
166+
RouterCapacity=1,
167+
DeviceCapacity=1,
168+
ProtocolVersion=2,
169+
StackProfile=2,
170+
LQI=66,
171+
Depth=1,
172+
UpdateId=0,
173+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
174+
),
175+
]
176+
),
177+
c.ZDO.BeaconNotifyInd.Callback(
178+
Beacons=[
179+
t.Beacon(
180+
Src=0xF8B4,
181+
PanId=0x7ABE,
182+
Channel=25,
183+
PermitJoining=0,
184+
RouterCapacity=1,
185+
DeviceCapacity=1,
186+
ProtocolVersion=2,
187+
StackProfile=2,
188+
LQI=90,
189+
Depth=1,
190+
UpdateId=0,
191+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
192+
),
193+
t.Beacon(
194+
Src=0x43FD,
195+
PanId=0x7ABE,
196+
Channel=25,
197+
PermitJoining=0,
198+
RouterCapacity=1,
199+
DeviceCapacity=1,
200+
ProtocolVersion=2,
201+
StackProfile=2,
202+
LQI=111,
203+
Depth=1,
204+
UpdateId=0,
205+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
206+
),
207+
t.Beacon(
208+
Src=0x4444,
209+
PanId=0x7ABE,
210+
Channel=25,
211+
PermitJoining=0,
212+
RouterCapacity=1,
213+
DeviceCapacity=1,
214+
ProtocolVersion=2,
215+
StackProfile=2,
216+
LQI=126,
217+
Depth=1,
218+
UpdateId=0,
219+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
220+
),
221+
t.Beacon(
222+
Src=0x5014,
223+
PanId=0x7ABE,
224+
Channel=25,
225+
PermitJoining=0,
226+
RouterCapacity=1,
227+
DeviceCapacity=1,
228+
ProtocolVersion=2,
229+
StackProfile=2,
230+
LQI=78,
231+
Depth=1,
232+
UpdateId=0,
233+
ExtendedPanId=t.EUI64.convert("92:6b:f8:1e:df:1b:e8:1c"),
234+
),
235+
]
236+
),
237+
c.ZDO.NwkDiscoveryCnf.Callback(Status=t.ZDOStatus.SUCCESS),
238+
],
239+
)
240+
241+
# Scan 4 results
242+
znp_server.reply_once_to(
243+
c.ZDO.NetworkDiscoveryReq.Req(Channels=t.Channels.ALL_CHANNELS, ScanDuration=2),
244+
responses=[
245+
c.ZDO.NetworkDiscoveryReq.Rsp(Status=t.Status.SUCCESS),
246+
c.ZDO.NwkDiscoveryCnf.Callback(Status=t.ZDOStatus.SUCCESS),
247+
],
248+
)
249+
250+
await network_scan(["-n", "4", znp_server._port_path, "-v", "-v"])
251+
252+
# The channels in NVRAM were restored
253+
assert znp_server.nvram["nwk"][NwkNvIds.CHANLIST] == original_channels
254+
255+
captured = capsys.readouterr()
256+
257+
# Nine unique beacons were detected
258+
assert captured.out.count(", from: ") == 9
259+
assert captured.out.count("0x7ABE, from: ") == 8
260+
assert captured.out.count("0x86E6, from: ") == 1
261+
262+
263+
@pytest.mark.parametrize("device", [FormedLaunchpadCC26X2R1])
264+
async def test_network_scan_failure(device, make_znp_server, capsys):
265+
znp_server = make_znp_server(server_cls=device)
266+
267+
original_channels = t.Channels.from_channel_list([15, 20, 25]).serialize()
268+
assert znp_server.nvram["nwk"][NwkNvIds.CHANLIST] == original_channels
269+
270+
znp_server.reply_once_to(
271+
c.ZDO.NetworkDiscoveryReq.Req(Channels=t.Channels.ALL_CHANNELS, ScanDuration=2),
272+
responses=[c.ZDO.NetworkDiscoveryReq.Rsp(Status=t.Status.FAILURE)],
273+
)
274+
275+
with pytest.raises(InvalidCommandResponse):
276+
await network_scan([znp_server._port_path, "-v", "-v"])
277+
278+
# The channels in NVRAM were restored even when we had a failure
279+
assert znp_server.nvram["nwk"][NwkNvIds.CHANLIST] == original_channels

zigpy_znp/tools/network_scan.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ async def network_scan(
7878
)
7979
finally:
8080
await znp.nvram_write(NwkNvIds.CHANLIST, previous_channels)
81+
znp.close()
8182

8283

8384
async def main(argv):

0 commit comments

Comments
 (0)