Skip to content

Commit 5dfe760

Browse files
committed
Add tests
1 parent de5571e commit 5dfe760

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

tests/test_application.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
FlowControlType,
2323
GetChipInfoRsp,
2424
GetRouteTableEntryRsp,
25+
GetTxPowerInfoRsp,
2526
)
2627
import bellows.types
2728
import bellows.types as t
@@ -2639,3 +2640,46 @@ async def test_migration_failure_eui64_overwrite_confirmation(
26392640
assert app._ezsp.write_custom_eui64.mock_calls == [
26402641
call(t.EUI64.convert("aa:aa:aa:aa:aa:aa:aa:aa"), burn_into_userdata=True)
26412642
]
2643+
2644+
2645+
async def test_tx_power_with_xncp_feature(app: ControllerApplication) -> None:
2646+
"""Test TX power methods with XNCP TX_POWER_INFO feature."""
2647+
app._ezsp._xncp_features |= FirmwareFeatures.TX_POWER_INFO
2648+
app._ezsp.xncp_get_tx_power_info = AsyncMock(
2649+
return_value=GetTxPowerInfoRsp(recommended_power_dbm=10, max_power_dbm=20)
2650+
)
2651+
2652+
assert await app.get_recommended_tx_power("US") == 10.0
2653+
assert await app.get_maximum_tx_power("US") == 20.0
2654+
2655+
2656+
async def test_tx_power_without_xncp_feature(app: ControllerApplication) -> None:
2657+
"""Test TX power methods fall back to parent class without XNCP feature."""
2658+
app._ezsp._xncp_features = FirmwareFeatures.NONE
2659+
app._ezsp.xncp_get_tx_power_info = AsyncMock()
2660+
2661+
app_cls = zigpy.application.ControllerApplication
2662+
2663+
ezsp_rec_tx_power = await app.get_recommended_tx_power("US")
2664+
base_rec_tx_power = await app_cls.get_recommended_tx_power(app, "US")
2665+
assert ezsp_rec_tx_power == base_rec_tx_power
2666+
2667+
ezsp_max_tx_power = await app.get_maximum_tx_power("US")
2668+
base_max_tx_power = await app_cls.get_maximum_tx_power(app, "US")
2669+
assert ezsp_max_tx_power == base_max_tx_power
2670+
2671+
assert len(app._ezsp.xncp_get_tx_power_info.mock_calls) == 0
2672+
2673+
2674+
async def test_set_tx_power(app: ControllerApplication) -> None:
2675+
"""Test set_tx_power with float-to-int conversion and NVRAM persistence."""
2676+
app._ezsp.setRadioPower = AsyncMock()
2677+
2678+
with patch(
2679+
"bellows.zigbee.repairs.update_tx_power", return_value=True
2680+
) as mock_update:
2681+
result = await app.set_tx_power(12.7)
2682+
2683+
assert result == 12.0
2684+
assert app._ezsp.setRadioPower.mock_calls == [call(power=12)]
2685+
assert mock_update.mock_calls == [call(app._ezsp, tx_power=12)]

tests/test_xncp.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,26 @@ async def test_xncp_route_table_operations(ezsp_f: EZSP) -> None:
291291
).serialize()
292292
)
293293
]
294+
295+
296+
async def test_xncp_get_tx_power_info(ezsp_f: EZSP) -> None:
297+
"""Test XNCP get_tx_power_info."""
298+
ezsp_f._mock_commands["customFrame"] = customFrame = AsyncMock(
299+
return_value=[
300+
t.EmberStatus.SUCCESS,
301+
xncp.XncpCommand.from_payload(
302+
xncp.GetTxPowerInfoRsp(recommended_power_dbm=10, max_power_dbm=20)
303+
).serialize(),
304+
]
305+
)
306+
307+
rsp = await ezsp_f.xncp_get_tx_power_info("us")
308+
assert rsp.recommended_power_dbm == 10
309+
assert rsp.max_power_dbm == 20
310+
assert customFrame.mock_calls == [
311+
call(
312+
xncp.XncpCommand.from_payload(
313+
xncp.GetTxPowerInfoReq(country_code=b"US")
314+
).serialize()
315+
)
316+
]

tests/test_zigbee_repairs.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,69 @@ async def test_fix_invalid_tclk_all_versions(
174174
]
175175
else:
176176
assert "NV3 interface not available in this firmware" in caplog.text
177+
178+
179+
async def test_update_tx_power(ezsp_f: EZSP, caplog) -> None:
180+
"""Test update_tx_power behavior in various scenarios."""
181+
token_data = t.NV3StackNodeData(
182+
panId=t.EmberPanId(0x1234),
183+
radioTxPower=t.int8s(5),
184+
radioFreqChannel=t.uint8_t(15),
185+
stackProfile=t.uint8_t(0x02),
186+
nodeType=t.EmberNodeType.COORDINATOR,
187+
zigbeeNodeId=t.EmberNodeId(0x0000),
188+
extendedPanId=t.ExtendedPanId.convert("AA:BB:CC:DD:EE:FF:00:11"),
189+
)
190+
191+
# Test 1: NV3 interface unavailable
192+
ezsp_f.getTokenData = AsyncMock(side_effect=InvalidCommandError())
193+
with caplog.at_level(logging.WARNING):
194+
assert await repairs.update_tx_power(ezsp_f, tx_power=10) is False
195+
assert "NV3 interface not available in this firmware" in caplog.text
196+
197+
# Test 2: TX power already correct (no write needed)
198+
ezsp_f.getTokenData = AsyncMock(
199+
return_value=GetTokenDataRsp(
200+
status=t.EmberStatus.SUCCESS,
201+
value=token_data.replace(radioTxPower=t.int8s(10)).serialize(),
202+
)
203+
)
204+
ezsp_f.setTokenData = AsyncMock()
205+
ezsp_f.getNetworkParameters = AsyncMock()
206+
assert await repairs.update_tx_power(ezsp_f, tx_power=10) is False
207+
assert len(ezsp_f.setTokenData.mock_calls) == 0
208+
assert len(ezsp_f.getNetworkParameters.mock_calls) == 0
209+
210+
# Test 3: Successful TX power update
211+
ezsp_f.getTokenData = AsyncMock(
212+
return_value=GetTokenDataRsp(
213+
status=t.EmberStatus.SUCCESS,
214+
value=token_data.serialize(),
215+
)
216+
)
217+
ezsp_f.getNetworkParameters = AsyncMock(
218+
return_value=[
219+
t.EmberStatus.SUCCESS,
220+
t.EmberNodeType.COORDINATOR,
221+
t.EmberNetworkParameters(
222+
panId=t.EmberPanId(0x1234),
223+
extendedPanId=t.ExtendedPanId.convert("AA:BB:CC:DD:EE:FF:00:11"),
224+
radioChannel=t.uint8_t(15),
225+
radioTxPower=t.int8s(5),
226+
joinMethod=t.EmberJoinMethod.USE_MAC_ASSOCIATION,
227+
nwkManagerId=t.EmberNodeId(0x0000),
228+
nwkUpdateId=t.uint8_t(0),
229+
channels=t.Channels.ALL_CHANNELS,
230+
),
231+
]
232+
)
233+
ezsp_f.setTokenData = AsyncMock(return_value=[t.EmberStatus.SUCCESS])
234+
235+
assert await repairs.update_tx_power(ezsp_f, tx_power=15) is True
236+
assert ezsp_f.setTokenData.mock_calls == [
237+
call(
238+
token=t.NV3KeyId.NVM3KEY_STACK_NODE_DATA,
239+
index=0,
240+
token_data=token_data.replace(radioTxPower=t.int8s(15)).serialize(),
241+
)
242+
]

0 commit comments

Comments
 (0)