diff --git a/README.md b/README.md index 7c5c536..8b80dbb 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,7 @@ pytest -m unit pytest -m int ``` -* To run the smoke test with only one device connected serially: +* To run the smoke test with only one device connected serially (aka smoke1): ``` pytest -m smoke1 @@ -216,10 +216,26 @@ pytest -m smoke1 CAUTION: Running smoke1 will reset values on the device, including the region to 1 (US). Be sure to hit the reset button on the device after the test is completed. +* To run the smoke test with only two device connected serially (aka smoke2): + +``` +pytest -m smoke2 +``` + +* To run the wifi smoke test: + +``` +pytest -m smokewifi +``` + * To run a specific test: ``` pytest -msmoke1 meshtastic/test/test_smoke1.py::test_smoke1_info +# or to run a specific smoke2 test +pytest -m smoke2 meshtastic/test/test_smoke2.py::test_smoke2_info +# or to run a specific smoke_wifi test +pytest -m smokewifi meshtastic/test/test_smoke_wifi.py::test_smokewifi_info ``` * To add another classification of tests such as "unit" or "smoke1", see [pytest.ini](pytest.ini). diff --git a/meshtastic/__main__.py b/meshtastic/__main__.py index 163c4f6..e0cc2b9 100644 --- a/meshtastic/__main__.py +++ b/meshtastic/__main__.py @@ -599,7 +599,9 @@ def common(): parser.print_help(sys.stderr) sys.exit(1) elif args.test: - test.testAll() + result = test.testAll() + if not result: + our_exit("Warning: Test was not successful.") else: if args.seriallog == "stdout": logfile = sys.stdout @@ -761,7 +763,7 @@ def initParser(): "--setlon", help="Set device longitude (allows use without GPS)") parser.add_argument( - "--pos-fields", help="Specify position message fields (? for more info)", + "--pos-fields", help="Specify position message fields. Use '0' for list of valid values. Can pass multiple values as a space separated list like this: '--pos-fields POS_ALTITUDE POS_ALT_MSL'", nargs="*", action="store") parser.add_argument("--debug", help="Show API library debug log messages", diff --git a/meshtastic/test.py b/meshtastic/test.py index caed13a..e4c6af6 100644 --- a/meshtastic/test.py +++ b/meshtastic/test.py @@ -1,4 +1,5 @@ -""" Testing +""" With two radios connected serially, send and receive test + messages and report back if successful. """ import logging import time @@ -10,6 +11,7 @@ from . import util from .__init__ import BROADCAST_NUM from .serial_interface import SerialInterface from .tcp_interface import TCPInterface +from .util import our_exit """The interfaces we are using for our tests""" interfaces = None @@ -107,25 +109,23 @@ def runTests(numTests=50, wantAck=False, maxFailures=0): logging.info( f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far") - # if numFail >= 3: - # for i in interfaces: - # i.close() - # return - time.sleep(1) if numFail > maxFailures: logging.error("Too many failures! Test failed!") - - return numFail + return False + return True -def testThread(numTests=50): +def testThread(numTests=5): """Test thread""" logging.info("Found devices, starting tests...") - runTests(numTests, wantAck=True) - # Allow a few dropped packets - runTests(numTests, wantAck=False, maxFailures=5) + result = runTests(numTests, wantAck=True) + if result: + # Run another test + # Allow a few dropped packets + result = runTests(numTests, wantAck=False, maxFailures=1) + return result def onConnection(topic=pub.AUTO_TOPIC): @@ -143,13 +143,12 @@ def openDebugLog(portName): def testAll(): """ Run a series of tests using devices we can find. + This is called from the cli with the "--test" option. - Raises: - Exception: If not enough devices are found """ ports = util.findPorts() if len(ports) < 2: - raise Exception("Must have at least two devices connected to USB") + our_exit("Warning: Must have at least two devices connected to USB.") pub.subscribe(onConnection, "meshtastic.connection") pub.subscribe(onReceive, "meshtastic.receive") @@ -158,11 +157,13 @@ def testAll(): port, debugOut=openDebugLog(port), connectNow=True), ports)) logging.info("Ports opened, starting test") - testThread() + result = testThread() for i in interfaces: i.close() + return result + def testSimulator(): """ diff --git a/meshtastic/test/test_smoke1.py b/meshtastic/test/test_smoke1.py index 42d1874..dc0afae 100644 --- a/meshtastic/test/test_smoke1.py +++ b/meshtastic/test/test_smoke1.py @@ -1,4 +1,4 @@ -"""Meshtastic smoke tests with a single device""" +"""Meshtastic smoke tests with a single device via USB""" import re import subprocess import time @@ -39,6 +39,42 @@ def test_smoke1_info(): assert return_value == 0 +@pytest.mark.smoke1 +def test_smoke1_sendping(): + """Test --sendping""" + return_value, out = subprocess.getstatusoutput('meshtastic --sendping') + assert re.match(r'Connected to radio', out) + assert re.search(r'^Sending ping message', out, re.MULTILINE) + assert return_value == 0 + + +@pytest.mark.smoke1 +def test_smoke1_pos_fields(): + """Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)""" + return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY') + assert re.match(r'Connected to radio', out) + assert re.search(r'^Setting position fields to 35', out, re.MULTILINE) + assert return_value == 0 + # pause for the radio + time.sleep(PAUSE_AFTER_COMMAND) + return_value, out = subprocess.getstatusoutput('meshtastic --pos-fields') + assert re.match(r'Connected to radio', out) + assert re.search(r'POS_ALTITUDE', out, re.MULTILINE) + assert re.search(r'POS_ALT_MSL', out, re.MULTILINE) + assert re.search(r'POS_BATTERY', out, re.MULTILINE) + assert return_value == 0 + + +@pytest.mark.smoke1 +def test_smoke1_test(): + """Test --test + Note: Since only one device is connected, it will not do much. + """ + return_value, out = subprocess.getstatusoutput('meshtastic --test') + assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE) + assert return_value == 1 + + @pytest.mark.smoke1 def test_smoke1_debug(): """Test --debug""" @@ -348,6 +384,21 @@ def test_smoke1_configure(): assert re.search('^Writing modified preferences to device', out, re.MULTILINE) +@pytest.mark.smoke1 +def test_smoke1_set_ham(): + """Test --set-ham + Note: Do a factory reset after this setting so it is very short-lived. + """ + return_value, out = subprocess.getstatusoutput('meshtastic --set-ham KI1234') + assert re.search(r'Setting HAM ID', out, re.MULTILINE) + assert return_value == 0 + # pause for the radio + time.sleep(PAUSE_AFTER_REBOOT) + return_value, out = subprocess.getstatusoutput('meshtastic --info') + assert re.search(r'Owner: KI1234', out, re.MULTILINE) + assert return_value == 0 + + @pytest.mark.smoke1 def test_smoke1_factory_reset(): """Test factory reset""" diff --git a/meshtastic/test/test_smoke2.py b/meshtastic/test/test_smoke2.py new file mode 100644 index 0000000..4788a2e --- /dev/null +++ b/meshtastic/test/test_smoke2.py @@ -0,0 +1,23 @@ +"""Meshtastic smoke tests with 2 devices connected via USB""" +import re +import subprocess + +import pytest + + +@pytest.mark.smoke2 +def test_smoke2_info(): + """Test --info with 2 devices connected serially""" + return_value, out = subprocess.getstatusoutput('meshtastic --info') + assert re.search(r'Warning: Multiple', out, re.MULTILINE) + assert return_value == 1 + + +@pytest.mark.smoke2 +def test_smoke2_test(): + """Test --test""" + return_value, out = subprocess.getstatusoutput('meshtastic --test') + assert re.search(r'Writing serial debugging', out, re.MULTILINE) + assert re.search(r'Ports opened', out, re.MULTILINE) + assert re.search(r'Running 5 tests', out, re.MULTILINE) + assert return_value == 0 diff --git a/meshtastic/test/test_smoke_wifi.py b/meshtastic/test/test_smoke_wifi.py new file mode 100644 index 0000000..de37de8 --- /dev/null +++ b/meshtastic/test/test_smoke_wifi.py @@ -0,0 +1,23 @@ +"""Meshtastic smoke tests a device setup with wifi. + + Need to have run the following on an esp32 device: + meshtastic --set wifi_ssid 'foo' --set wifi_password 'sekret' +""" +import re +import subprocess + +import pytest + + +@pytest.mark.smokewifi +def test_smokewifi_info(): + """Test --info""" + return_value, out = subprocess.getstatusoutput('meshtastic --info --host meshtastic.local') + assert re.search(r'^Owner', out, re.MULTILINE) + assert re.search(r'^My info', out, re.MULTILINE) + assert re.search(r'^Nodes in mesh', out, re.MULTILINE) + assert re.search(r'^Preferences', out, re.MULTILINE) + assert re.search(r'^Channels', out, re.MULTILINE) + assert re.search(r'^ PRIMARY', out, re.MULTILINE) + assert re.search(r'^Primary channel URL', out, re.MULTILINE) + assert return_value == 0 diff --git a/pytest.ini b/pytest.ini index 54ebba1..62e2a7b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,8 +1,10 @@ [pytest] -addopts = -m "not smoke1" +addopts = -m "not smoke1 and not smoke2 and not smokewifi" markers = unit: marks tests as unit tests int: marks tests as integration tests - smoke1: runs smoke test on one device + smoke1: runs smoke tests on a single device connected via USB + smoke2: runs smoke tests on a two devices connected via USB + smokewifi: runs smoke test on an esp32 device setup with wifi