Skip to content

Commit 0bef137

Browse files
committed
tests of new BLEError functionality
1 parent f9a1357 commit 0bef137

2 files changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Meshtastic unit tests for ble_interface.py"""
2+
3+
from unittest.mock import MagicMock, patch
4+
5+
import pytest
6+
from bleak.exc import BleakError
7+
8+
from ..ble_interface import BLEInterface
9+
10+
11+
@pytest.mark.unit
12+
def test_ble_error_default_kind_unknown():
13+
"""BLEError defaults to UNKNOWN kind."""
14+
error = BLEInterface.BLEError("test")
15+
assert error.kind == BLEInterface.BLEError.UNKNOWN
16+
17+
18+
@pytest.mark.unit
19+
def test_ble_find_device_not_found_sets_kind():
20+
"""find_device emits DEVICE_NOT_FOUND for no scan results."""
21+
iface = object.__new__(BLEInterface)
22+
with patch("meshtastic.ble_interface.BLEInterface.scan", return_value=[]):
23+
with pytest.raises(BLEInterface.BLEError) as excinfo:
24+
iface.find_device("missing")
25+
assert excinfo.value.kind == BLEInterface.BLEError.DEVICE_NOT_FOUND
26+
27+
28+
@pytest.mark.unit
29+
def test_ble_find_device_multiple_sets_kind():
30+
"""find_device emits MULTIPLE_DEVICES for ambiguous matches."""
31+
iface = object.__new__(BLEInterface)
32+
first = MagicMock()
33+
first.name = "dup"
34+
first.address = "AA:AA:AA:AA:AA:01"
35+
second = MagicMock()
36+
second.name = "dup"
37+
second.address = "AA:AA:AA:AA:AA:02"
38+
with patch(
39+
"meshtastic.ble_interface.BLEInterface.scan", return_value=[first, second]
40+
):
41+
with pytest.raises(BLEInterface.BLEError) as excinfo:
42+
iface.find_device("dup")
43+
assert excinfo.value.kind == BLEInterface.BLEError.MULTIPLE_DEVICES
44+
45+
46+
@pytest.mark.unit
47+
def test_ble_send_to_radio_wraps_write_errors_with_kind():
48+
"""_sendToRadioImpl wraps write failures with WRITE_ERROR."""
49+
iface = object.__new__(BLEInterface)
50+
iface.client = MagicMock()
51+
iface.client.write_gatt_char.side_effect = RuntimeError("boom")
52+
to_radio = MagicMock()
53+
to_radio.SerializeToString.return_value = b"\x01"
54+
with pytest.raises(BLEInterface.BLEError) as excinfo:
55+
iface._sendToRadioImpl(to_radio)
56+
assert excinfo.value.kind == BLEInterface.BLEError.WRITE_ERROR
57+
58+
59+
@pytest.mark.unit
60+
def test_ble_receive_wraps_unexpected_bleak_error_with_kind():
61+
"""_receiveFromRadioImpl wraps unexpected BleakError with READ_ERROR."""
62+
iface = object.__new__(BLEInterface)
63+
iface.should_read = True
64+
iface._want_receive = True
65+
iface.client = MagicMock()
66+
iface.client.read_gatt_char.side_effect = BleakError("some other BLE failure")
67+
with pytest.raises(BLEInterface.BLEError) as excinfo:
68+
iface._receiveFromRadioImpl()
69+
assert excinfo.value.kind == BLEInterface.BLEError.READ_ERROR

meshtastic/tests/test_main.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from unittest.mock import mock_open, MagicMock, patch
1010

1111
import pytest
12+
import meshtastic.__main__ as mt_main
1213

1314
from meshtastic.__main__ import (
1415
export_config,
@@ -25,6 +26,7 @@
2526
from ..protobuf.channel_pb2 import Channel # pylint: disable=E0611
2627

2728
# from ..ble_interface import BLEInterface
29+
from ..mesh_interface import MeshInterface
2830
from ..node import Node
2931

3032
# from ..radioconfig_pb2 import UserPreferences
@@ -259,6 +261,113 @@ def test_main_info_with_permission_error(patched_getlogin, capsys, caplog):
259261
assert err == ""
260262

261263

264+
@pytest.mark.unit
265+
@pytest.mark.usefixtures("reset_mt_config")
266+
def test_main_ble_device_not_found_message(capsys):
267+
"""Test BLE device-not-found help text."""
268+
sys.argv = ["", "--info", "--ble", "any"]
269+
mt_config.args = sys.argv
270+
271+
with patch("meshtastic.__main__.BLEInterface.__init__") as mock_ble_init:
272+
mock_ble_init.side_effect = mt_main.BLEInterface.BLEError(
273+
"missing",
274+
mt_main.BLEInterface.BLEError.DEVICE_NOT_FOUND,
275+
)
276+
with pytest.raises(SystemExit) as excinfo:
277+
main()
278+
279+
out, err = capsys.readouterr()
280+
assert excinfo.value.code == 1
281+
assert re.search(r"BLE device not found", out, re.MULTILINE)
282+
assert re.search(r"--ble-scan", out, re.MULTILINE)
283+
assert err == ""
284+
285+
286+
@pytest.mark.unit
287+
@pytest.mark.usefixtures("reset_mt_config")
288+
def test_main_ble_multiple_devices_message(capsys):
289+
"""Test BLE multiple-devices help text."""
290+
sys.argv = ["", "--info", "--ble", "any"]
291+
mt_config.args = sys.argv
292+
293+
with patch("meshtastic.__main__.BLEInterface.__init__") as mock_ble_init:
294+
mock_ble_init.side_effect = mt_main.BLEInterface.BLEError(
295+
"multiple",
296+
mt_main.BLEInterface.BLEError.MULTIPLE_DEVICES,
297+
)
298+
with pytest.raises(SystemExit) as excinfo:
299+
main()
300+
301+
out, err = capsys.readouterr()
302+
assert excinfo.value.code == 1
303+
assert re.search(r"Multiple Meshtastic BLE devices found", out, re.MULTILINE)
304+
assert re.search(r"meshtastic --ble <name_or_address>", out, re.MULTILINE)
305+
assert err == ""
306+
307+
308+
@pytest.mark.unit
309+
@pytest.mark.usefixtures("reset_mt_config")
310+
def test_main_ble_write_error_message(capsys):
311+
"""Test BLE write-error help text."""
312+
sys.argv = ["", "--info", "--ble", "any"]
313+
mt_config.args = sys.argv
314+
315+
with patch("meshtastic.__main__.BLEInterface.__init__") as mock_ble_init:
316+
mock_ble_init.side_effect = mt_main.BLEInterface.BLEError(
317+
"write fail",
318+
mt_main.BLEInterface.BLEError.WRITE_ERROR,
319+
)
320+
with pytest.raises(SystemExit) as excinfo:
321+
main()
322+
323+
out, err = capsys.readouterr()
324+
assert excinfo.value.code == 1
325+
assert re.search(r"Failed to write to BLE device", out, re.MULTILINE)
326+
assert re.search(r"user not in 'bluetooth' group", out, re.MULTILINE)
327+
assert err == ""
328+
329+
330+
@pytest.mark.unit
331+
@pytest.mark.usefixtures("reset_mt_config")
332+
def test_main_ble_read_error_message(capsys):
333+
"""Test BLE read-error help text."""
334+
sys.argv = ["", "--info", "--ble", "any"]
335+
mt_config.args = sys.argv
336+
337+
with patch("meshtastic.__main__.BLEInterface.__init__") as mock_ble_init:
338+
mock_ble_init.side_effect = mt_main.BLEInterface.BLEError(
339+
"read fail",
340+
mt_main.BLEInterface.BLEError.READ_ERROR,
341+
)
342+
with pytest.raises(SystemExit) as excinfo:
343+
main()
344+
345+
out, err = capsys.readouterr()
346+
assert excinfo.value.code == 1
347+
assert re.search(r"Failed to read from BLE device", out, re.MULTILINE)
348+
assert re.search(r"Move closer to the device", out, re.MULTILINE)
349+
assert err == ""
350+
351+
352+
@pytest.mark.unit
353+
@pytest.mark.usefixtures("reset_mt_config")
354+
def test_main_serial_timeout_message(capsys):
355+
"""Test serial timeout help text."""
356+
sys.argv = ["", "--info"]
357+
mt_config.args = sys.argv
358+
359+
with patch("meshtastic.serial_interface.SerialInterface") as mock_serial:
360+
mock_serial.side_effect = MeshInterface.MeshInterfaceError("Timed out waiting")
361+
with pytest.raises(SystemExit) as excinfo:
362+
main()
363+
364+
out, err = capsys.readouterr()
365+
assert excinfo.value.code == 1
366+
assert re.search(r"Connection timed out", out, re.MULTILINE)
367+
assert re.search(r"Device is rebooting", out, re.MULTILINE)
368+
assert err == ""
369+
370+
262371
@pytest.mark.unit
263372
@pytest.mark.usefixtures("reset_mt_config")
264373
def test_main_info_with_tcp_interface(capsys):

0 commit comments

Comments
 (0)