From f587698d2a691031a0d0d62d4086b4f72cbe9d67 Mon Sep 17 00:00:00 2001 From: Mike Kinney Date: Thu, 9 Dec 2021 12:26:17 -0800 Subject: [PATCH] move functions from main to util and add unit tests --- meshtastic/__main__.py | 58 +-------------------------------- meshtastic/test/test_globals.py | 4 +-- meshtastic/test/test_main.py | 4 +-- meshtastic/test/test_util.py | 40 ++++++++++++++++++++++- meshtastic/util.py | 50 ++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 62 deletions(-) diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 5d2db44..24217bb 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -7,7 +7,6 @@ import platform import logging import sys import time -import os import yaml from pubsub import pub import pyqrcode @@ -18,7 +17,7 @@ from .ble_interface import BLEInterface from . import test, remote_hardware from . import portnums_pb2, channel_pb2, mesh_pb2, radioconfig_pb2 from . import tunnel -from .util import support_info, our_exit +from .util import support_info, our_exit, genPSK256, fromPSK, fromStr from .globals import Globals """We only import the tunnel code if we are on a platform that can run it""" @@ -60,61 +59,6 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): print(f"Connection changed: {topic.getName()}") -trueTerms = {"t", "true", "yes"} -falseTerms = {"f", "false", "no"} - - -def genPSK256(): - """Generate a random preshared key""" - return os.urandom(32) - - -def fromPSK(valstr): - """A special version of fromStr that assumes the user is trying to set a PSK. - In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN - """ - if valstr == "random": - return genPSK256() - elif valstr == "none": - return bytes([0]) # Use the 'no encryption' PSK - elif valstr == "default": - return bytes([1]) # Use default channel psk - elif valstr.startswith("simple"): - # Use one of the single byte encodings - return bytes([int(valstr[6:]) + 1]) - else: - return fromStr(valstr) - - -def fromStr(valstr): - """try to parse as int, float or bool (and fallback to a string as last resort) - - Returns: an int, bool, float, str or byte array (for strings of hex digits) - - Args: - valstr (string): A user provided string - """ - if len(valstr) == 0: # Treat an emptystring as an empty bytes - val = bytes() - elif valstr.startswith('0x'): - # if needed convert to string with asBytes.decode('utf-8') - val = bytes.fromhex(valstr[2:]) - elif valstr in trueTerms: - val = True - elif valstr in falseTerms: - val = False - else: - try: - val = int(valstr) - except ValueError: - try: - val = float(valstr) - except ValueError: - val = valstr # Not a float or an int, assume string - - return val - - never = 0xffffffff oneday = 24 * 60 * 60 diff --git a/meshtastic/test/test_globals.py b/meshtastic/test/test_globals.py index aba911c..da9d08b 100644 --- a/meshtastic/test/test_globals.py +++ b/meshtastic/test/test_globals.py @@ -9,7 +9,7 @@ from ..globals import Globals @pytest.mark.unit def test_globals_get_instaance(): """Test that we can instantiate a Globals instance""" - ourglobals = Globals() + ourglobals = Globals.getInstance() ourglobals2 = Globals.getInstance() assert ourglobals == ourglobals2 @@ -17,7 +17,7 @@ def test_globals_get_instaance(): @pytest.mark.unit def test_globals_there_can_be_only_one(): """Test that we can cannot create two Globals instances""" - # ensure we have at least one instance created + # if we have an instance, delete it Globals.getInstance() with pytest.raises(Exception) as pytest_wrapped_e: # try to create another instance diff --git a/meshtastic/test/test_main.py b/meshtastic/test/test_main.py index bc9a3a5..c745d5a 100644 --- a/meshtastic/test/test_main.py +++ b/meshtastic/test/test_main.py @@ -10,7 +10,7 @@ from meshtastic.__main__ import initParser, Globals @pytest.mark.unit -def test_main_no_args(capsys): +def test_main_init_parser_no_args(capsys): """Test no arguments""" sys.argv = [''] args = sys.argv @@ -25,7 +25,7 @@ def test_main_no_args(capsys): @pytest.mark.unit -def test_main_version(capsys): +def test_main_init_parser_version(capsys): """Test --version""" sys.argv = ['', '--version'] args = sys.argv diff --git a/meshtastic/test/test_util.py b/meshtastic/test/test_util.py index cf5f638..20e095d 100644 --- a/meshtastic/test/test_util.py +++ b/meshtastic/test/test_util.py @@ -4,7 +4,45 @@ import re import pytest -from meshtastic.util import fixme, stripnl, pskToString, our_exit, support_info +from meshtastic.util import fixme, stripnl, pskToString, our_exit, support_info, genPSK256, fromStr, fromPSK + + +@pytest.mark.unit +def test_genPSK256(): + """Test genPSK256""" + assert genPSK256() != '' + + +@pytest.mark.unit +def test_fromStr(): + """Test fromStr""" + assert fromStr('') == b'' + assert fromStr('0x12') == b'\x12' + assert fromStr('t') + assert fromStr('T') + assert fromStr('true') + assert fromStr('True') + assert fromStr('yes') + assert fromStr('Yes') + assert fromStr('f') is False + assert fromStr('F') is False + assert fromStr('false') is False + assert fromStr('False') is False + assert fromStr('no') is False + assert fromStr('No') is False + assert fromStr('100.01') == 100.01 + assert fromStr('123') == 123 + assert fromStr('abc') == 'abc' + + +@pytest.mark.unit +def test_fromPSK(): + """Test fromPSK""" + assert fromPSK('random') != '' + assert fromPSK('none') == b'\x00' + assert fromPSK('default') == b'\x01' + assert fromPSK('simple22') == b'\x17' + assert fromPSK('trash') == 'trash' @pytest.mark.unit diff --git a/meshtastic/util.py b/meshtastic/util.py index 77a69b8..dc9d0b2 100644 --- a/meshtastic/util.py +++ b/meshtastic/util.py @@ -2,6 +2,7 @@ """ import traceback from queue import Queue +import os import sys import time import platform @@ -15,6 +16,55 @@ import pkg_resources blacklistVids = dict.fromkeys([0x1366]) +def genPSK256(): + """Generate a random preshared key""" + return os.urandom(32) + + +def fromPSK(valstr): + """A special version of fromStr that assumes the user is trying to set a PSK. + In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN + """ + if valstr == "random": + return genPSK256() + elif valstr == "none": + return bytes([0]) # Use the 'no encryption' PSK + elif valstr == "default": + return bytes([1]) # Use default channel psk + elif valstr.startswith("simple"): + # Use one of the single byte encodings + return bytes([int(valstr[6:]) + 1]) + else: + return fromStr(valstr) + + +def fromStr(valstr): + """try to parse as int, float or bool (and fallback to a string as last resort) + Returns: an int, bool, float, str or byte array (for strings of hex digits) + + Args: + valstr (string): A user provided string + """ + if len(valstr) == 0: # Treat an emptystring as an empty bytes + val = bytes() + elif valstr.startswith('0x'): + # if needed convert to string with asBytes.decode('utf-8') + val = bytes.fromhex(valstr[2:]) + elif valstr.lower() in {"t", "true", "yes"}: + val = True + elif valstr.lower() in {"f", "false", "no"}: + val = False + else: + try: + val = int(valstr) + except ValueError: + try: + val = float(valstr) + except ValueError: + val = valstr # Not a float or an int, assume string + return val + + def pskToString(psk: bytes): """Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string""" if len(psk) == 0: