figured out how to unit test __reader()

This commit is contained in:
Mike Kinney
2021-12-25 11:39:04 -08:00
parent 9fea479af4
commit a243fab9af
4 changed files with 73 additions and 20 deletions

View File

@@ -24,7 +24,6 @@ class StreamInterface(MeshInterface):
"""Constructor, opens a connection to self.stream
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the
device will be emitted to that stream. (default: {None})
@@ -33,15 +32,16 @@ class StreamInterface(MeshInterface):
Exception: [description]
"""
if not hasattr(self, 'stream'):
if not hasattr(self, 'stream') and not noProto:
raise Exception(
"StreamInterface is now abstract (to update existing code create SerialInterface instead)")
self._rxBuf = bytes() # empty
self._wantExit = False
self.stream = None
# FIXME, figure out why daemon=True causes reader thread to exit too early
self._rxThread = threading.Thread(
target=self.__reader, args=(), daemon=True)
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto)
@@ -93,7 +93,10 @@ class StreamInterface(MeshInterface):
def _readBytes(self, length):
"""Read an array of bytes from our stream"""
return self.stream.read(length)
if self.stream:
return self.stream.read(length)
else:
return None
def _sendToRadioImpl(self, toRadio):
"""Send a ToRadio protobuf to the device"""
@@ -116,14 +119,15 @@ class StreamInterface(MeshInterface):
def __reader(self):
"""The reader thread that reads bytes from our stream"""
logging.debug('in __reader()')
empty = bytes()
try:
while not self._wantExit:
# logging.debug("reading character")
logging.debug("reading character")
b = self._readBytes(1)
# logging.debug("In reader loop")
# logging.debug(f"read returned {b}")
logging.debug("In reader loop")
logging.debug(f"read returned {b}")
if len(b) > 0:
c = b[0]
ptr = len(self._rxBuf)
@@ -144,12 +148,12 @@ class StreamInterface(MeshInterface):
if c != START2:
self._rxBuf = empty # failed to find start2
elif ptr >= HEADER_LEN - 1: # we've at least got a header
# big endian length follos header
# big endian length follows header
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
if ptr == HEADER_LEN - 1: # we _just_ finished reading the header, validate length
if packetlen > MAX_TO_FROM_RADIO_SIZE:
self._rxBuf = empty # length ws out out bounds, restart
self._rxBuf = empty # length was out out bounds, restart
if len(self._rxBuf) != 0 and ptr + 1 >= packetlen + HEADER_LEN:
try:

View File

@@ -17,18 +17,29 @@ class TCPInterface(StreamInterface):
hostname {string} -- Hostname/IP address of the device to connect to
"""
logging.debug(f"Connecting to {hostname}")
server_address = (hostname, portNumber)
sock = socket.create_connection(server_address)
# Instead of wrapping as a stream, we use the native socket API
# self.stream = sock.makefile('rw')
self.stream = None
self.socket = sock
StreamInterface.__init__(
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
self.hostname = hostname
self.portNumber = portNumber
if connectNow:
logging.debug(f"Connecting to {hostname}")
server_address = (hostname, portNumber)
sock = socket.create_connection(server_address)
self.socket = sock
else:
self.socket = None
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
connectNow=connectNow)
def myConnect(self):
"""Connect to socket"""
server_address = (self.hostname, self.portNumber)
sock = socket.create_connection(server_address)
self.socket = sock
def close(self):
"""Close a connection to the device"""

View File

@@ -8,6 +8,7 @@ import pytest
from meshtastic.__main__ import Globals
from ..mesh_interface import MeshInterface
@pytest.fixture
def reset_globals():
"""Fixture to reset globals."""

View File

@@ -1,6 +1,9 @@
"""Meshtastic unit tests for stream_interface.py"""
import logging
import re
from unittest.mock import MagicMock
import pytest
from ..stream_interface import StreamInterface
@@ -8,7 +11,41 @@ from ..stream_interface import StreamInterface
@pytest.mark.unit
def test_StreamInterface():
"""Test that we cannot instantiate a StreamInterface"""
"""Test that we cannot instantiate a StreamInterface based on noProto"""
with pytest.raises(Exception) as pytest_wrapped_e:
StreamInterface(noProto=True)
StreamInterface()
assert pytest_wrapped_e.type == Exception
@pytest.mark.unit
def test_StreamInterface_with_noProto(caplog, reset_globals):
"""Test that we can instantiate a StreamInterface based on nonProto
and we can read/write bytes from a mocked stream
"""
stream = MagicMock()
test_data = b'hello'
stream.read.return_value = test_data
with caplog.at_level(logging.DEBUG):
iface = StreamInterface(noProto=True)
iface.stream = stream
iface._writeBytes(test_data)
data = iface._readBytes(len(test_data))
assert data == test_data
@pytest.mark.unit
def test_sendToRadioImpl(caplog, reset_globals):
"""Test _sendToRadioImpl()"""
test_data = b'hello'
stream = MagicMock()
stream.read.return_value = test_data
toRadio = MagicMock()
toRadio.SerializeToString.return_value = test_data
with caplog.at_level(logging.DEBUG):
iface = StreamInterface(noProto=True, connectNow=False)
iface.stream = stream
iface.connect()
iface._sendToRadioImpl(toRadio)
assert re.search(r'Sending: ', caplog.text, re.MULTILINE)
assert re.search(r'reading character', caplog.text, re.MULTILINE)
assert re.search(r'In reader loop', caplog.text, re.MULTILINE)