Compare commits

...

11 Commits

Author SHA1 Message Date
mkinney
c049d3424a Merge pull request #205 from mkinney/remove_nested_keys
remove nested keys from nodes so we do not display garbage
2022-01-02 11:28:07 -08:00
Mike Kinney
471535853b bump version 2022-01-02 11:20:15 -08:00
Mike Kinney
676148cc14 meant to use decoded not decode 2022-01-02 11:19:17 -08:00
Mike Kinney
a915b05240 remove nested keys from nodes so we do not display garbage 2022-01-02 11:15:19 -08:00
Jm Casler
a1668e8c66 updating proto submodule to latest 2022-01-01 23:25:22 -08:00
mkinney
e7664cb40b Merge pull request #204 from mkinney/add_more_unit_tests
get last two lines covered in node
2022-01-01 15:51:21 -08:00
Mike Kinney
83c18f4008 working on more unit tests 2022-01-01 15:48:33 -08:00
Mike Kinney
8b6321ce7f add a few more tests 2022-01-01 15:21:53 -08:00
Mike Kinney
9fac981ba6 test heartbeatTimer 2022-01-01 14:53:57 -08:00
Mike Kinney
ccc71930f7 get last two lines covered in node 2022-01-01 13:25:36 -08:00
mkinney
9380f048fa Update setup.py
bump version
2022-01-01 11:11:54 -08:00
7 changed files with 145 additions and 18 deletions

View File

@@ -68,8 +68,7 @@ class MeshInterface:
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None and exc_value is not None: if exc_type is not None and exc_value is not None:
logging.error( logging.error(f'An exception of type {exc_type} with value {exc_value} has occurred')
f'An exception of type {exc_type} with value {exc_value} has occurred')
if traceback is not None: if traceback is not None:
logging.error(f'Traceback: {traceback}') logging.error(f'Traceback: {traceback}')
self.close() self.close()
@@ -84,10 +83,10 @@ class MeshInterface:
nodes = "" nodes = ""
if self.nodes: if self.nodes:
for n in self.nodes.values(): for n in self.nodes.values():
# when the TBeam is first booted, it sometimes shows the 'raw' data # when the TBeam is first booted, it sometimes shows the raw data
# so, we will just remove any raw keys # so, we will just remove any raw keys
n2 = remove_keys_from_dict('raw', n) keys_to_remove = ('raw', 'decoded', 'payload')
n2 = remove_keys_from_dict('decode', n2) n2 = remove_keys_from_dict(keys_to_remove, n)
# if we have 'macaddr', re-format it # if we have 'macaddr', re-format it
if 'macaddr' in n2['user']: if 'macaddr' in n2['user']:
@@ -394,11 +393,11 @@ class MeshInterface:
return user.get('shortName', None) return user.get('shortName', None)
return None return None
def _waitConnected(self): def _waitConnected(self, timeout=15.0):
"""Block until the initial node db download is complete, or timeout """Block until the initial node db download is complete, or timeout
and raise an exception""" and raise an exception"""
if not self.noProto: if not self.noProto:
if not self.isConnected.wait(15.0): # timeout after x seconds if not self.isConnected.wait(timeout): # timeout after x seconds
raise Exception("Timed out waiting for connection completion") raise Exception("Timed out waiting for connection completion")
# If we failed while connecting, raise the connection to the client # If we failed while connecting, raise the connection to the client
@@ -416,8 +415,7 @@ class MeshInterface:
def _disconnected(self): def _disconnected(self):
"""Called by subclasses to tell clients this interface has disconnected""" """Called by subclasses to tell clients this interface has disconnected"""
self.isConnected.clear() self.isConnected.clear()
publishingThread.queueWork(lambda: pub.sendMessage( publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.lost", interface=self))
"meshtastic.connection.lost", interface=self))
def _startHeartbeat(self): def _startHeartbeat(self):
"""We need to send a heartbeat message to the device every X seconds""" """We need to send a heartbeat message to the device every X seconds"""
@@ -443,8 +441,7 @@ class MeshInterface:
if not self.isConnected.is_set(): if not self.isConnected.is_set():
self.isConnected.set() self.isConnected.set()
self._startHeartbeat() self._startHeartbeat()
publishingThread.queueWork(lambda: pub.sendMessage( publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.established", interface=self))
"meshtastic.connection.established", interface=self))
def _startConfig(self): def _startConfig(self):
"""Start device packets flowing""" """Start device packets flowing"""

View File

@@ -10,6 +10,8 @@ from ..mesh_interface import MeshInterface
from ..node import Node from ..node import Node
from .. import mesh_pb2 from .. import mesh_pb2
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
from ..radioconfig_pb2 import RadioConfig
from ..util import Timeout
@pytest.mark.unit @pytest.mark.unit
@@ -164,6 +166,22 @@ def test_sendPosition(reset_globals, caplog):
assert re.search(r'p.time:', caplog.text, re.MULTILINE) assert re.search(r'p.time:', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_close_with_heartbeatTimer(reset_globals, caplog):
"""Test close() with heartbeatTimer"""
iface = MeshInterface(noProto=True)
anode = Node('foo', 'bar')
radioConfig = RadioConfig()
radioConfig.preferences.phone_timeout_secs = 10
anode.radioConfig = radioConfig
iface.localNode = anode
assert iface.heartbeatTimer is None
with caplog.at_level(logging.DEBUG):
iface._startHeartbeat()
assert iface.heartbeatTimer is not None
iface.close()
@pytest.mark.unit @pytest.mark.unit
def test_handleFromRadio_empty_payload(reset_globals, caplog): def test_handleFromRadio_empty_payload(reset_globals, caplog):
"""Test _handleFromRadio""" """Test _handleFromRadio"""
@@ -543,3 +561,70 @@ def test_getOrCreateByNum(capsys, reset_globals, iface_with_nodes):
iface.myInfo.my_node_num = 2475227164 iface.myInfo.my_node_num = 2475227164
tmp = iface._getOrCreateByNum(2475227164) tmp = iface._getOrCreateByNum(2475227164)
assert tmp['num'] == 2475227164 assert tmp['num'] == 2475227164
@pytest.mark.unit
def test_enter():
"""Test __enter__()"""
iface = MeshInterface(noProto=True)
assert iface == iface.__enter__()
@pytest.mark.unit
def test_exit_with_exception(caplog):
"""Test __exit__()"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.ERROR):
iface.__exit__('foo', 'bar', 'baz')
assert re.search(r'An exception of type foo with value bar has occurred', caplog.text, re.MULTILINE)
assert re.search(r'Traceback: baz', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_showNodes_exclude_self(capsys, caplog, reset_globals, iface_with_nodes):
"""Test that we hit that continue statement"""
with caplog.at_level(logging.DEBUG):
iface = iface_with_nodes
iface.localNode.nodeNum = 2475227164
iface.showNodes()
iface.showNodes(includeSelf=False)
capsys.readouterr()
@pytest.mark.unit
def test_waitForConfig(caplog, capsys):
"""Test waitForConfig()"""
iface = MeshInterface(noProto=True)
# override how long to wait
iface._timeout = Timeout(0.01)
with pytest.raises(Exception) as pytest_wrapped_e:
iface.waitForConfig()
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
assert out == ''
@pytest.mark.unit
def test_waitConnected_raises_an_exception(caplog, capsys):
"""Test waitConnected()"""
iface = MeshInterface(noProto=True)
with pytest.raises(Exception) as pytest_wrapped_e:
iface.failure = "warn about something"
iface._waitConnected(0.01)
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'warn about something', err, re.MULTILINE)
assert out == ''
@pytest.mark.unit
def test_waitConnected_isConnected_timeout(caplog, capsys):
"""Test waitConnected()"""
with pytest.raises(Exception) as pytest_wrapped_e:
iface = MeshInterface()
iface._waitConnected(0.01)
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'warn about something', err, re.MULTILINE)
assert out == ''

View File

@@ -11,6 +11,7 @@ from ..serial_interface import SerialInterface
from ..admin_pb2 import AdminMessage from ..admin_pb2 import AdminMessage
from ..channel_pb2 import Channel from ..channel_pb2 import Channel
from ..radioconfig_pb2 import RadioConfig from ..radioconfig_pb2 import RadioConfig
from ..util import Timeout
@pytest.mark.unit @pytest.mark.unit
@@ -90,6 +91,16 @@ def test_setOwner_no_short_name_and_long_name_has_words(caplog):
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE) assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setOwner_long_name_no_short(caplog):
"""Test setOwner"""
anode = Node('foo', 'bar', noProto=True)
with caplog.at_level(logging.DEBUG):
anode.setOwner(long_name ='Aabo', is_licensed=True)
assert re.search(r'p.set_owner.long_name:Aabo:', caplog.text, re.MULTILINE)
assert re.search(r'p.set_owner.short_name:Aab:', caplog.text, re.MULTILINE)
@pytest.mark.unit @pytest.mark.unit
def test_exitSimulator(caplog): def test_exitSimulator(caplog):
"""Test exitSimulator""" """Test exitSimulator"""
@@ -869,3 +880,14 @@ def test_onResponseRequestSetting_with_error(capsys):
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert re.search(r'Error on response', out) assert re.search(r'Error on response', out)
assert err == '' assert err == ''
@pytest.mark.unit
def test_waitForConfig():
"""Test waitForConfig()"""
anode = Node('foo', 'bar')
radioConfig = RadioConfig()
anode.radioConfig = radioConfig
anode._timeout = Timeout(0.01)
result = anode.waitForConfig()
assert not result

View File

@@ -184,6 +184,23 @@ def test_remove_keys_from_dict():
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1} assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
@pytest.mark.unit
def test_remove_keys_from_dict_multiple_keys():
"""Test remove_keys_from_dict()"""
keys = ('a', 'b')
adict = {'a': 1, 'b': 2, 'c': 3}
assert remove_keys_from_dict(keys, adict) == {'c':3}
@pytest.mark.unit
def test_remove_keys_from_dict_nested():
"""Test remove_keys_from_dict()"""
keys = ('b')
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
exp = {'a': {}, 'c': 3}
assert remove_keys_from_dict(keys, adict) == exp
@pytest.mark.unitslow @pytest.mark.unitslow
def test_Timeout_not_found(): def test_Timeout_not_found():
"""Test Timeout()""" """Test Timeout()"""

View File

@@ -204,12 +204,18 @@ def support_info():
def remove_keys_from_dict(keys, adict): def remove_keys_from_dict(keys, adict):
"""Return a dictionary without some keys in it.""" """Return a dictionary without some keys in it.
newdict = adict Will removed nested keys.
"""
for key in keys: for key in keys:
if key in adict: try:
del newdict[key] del adict[key]
return newdict except:
pass
for val in adict.values():
if isinstance(val, dict):
remove_keys_from_dict(keys, val)
return adict
def hexstr(barray): def hexstr(barray):

2
proto

Submodule proto updated: 1d3b4806ab...7b80bde421

View File

@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
# This call to setup() does all the work # This call to setup() does all the work
setup( setup(
name="meshtastic", name="meshtastic",
version="1.2.49", version="1.2.51",
description="Python API & client shell for talking to Meshtastic devices", description="Python API & client shell for talking to Meshtastic devices",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",