Files
pynamics/diagnostics/os_diagnostics.py

935 lines
36 KiB
Python
Raw Normal View History

2026-03-06 16:34:42 +00:00
from pybricks.parameters import Port
from uerrno import EAGAIN, EBUSY, ECANCELED, EINVAL, EIO, ENODEV, EOPNOTSUPP, EPERM, ETIMEDOUT
2026-03-13 12:49:13 +00:00
import uio
2026-03-13 13:28:40 +00:00
import ujson
2026-03-19 18:33:33 +00:00
import umath
import uselect
import ustruct
import usys
2026-03-14 23:03:43 +00:00
import urandom
from urandom import random
2026-03-11 22:01:14 +00:00
from pybricks.tools import wait, multitask, run_task
2026-03-20 21:57:25 +00:00
from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
import pybricks as pybricksforvers
2026-03-11 21:56:37 +00:00
class FakeUART:
def __init__(self, port, baudrate, timeout):
self.timeout = timeout
2026-03-11 22:01:14 +00:00
self._force_error = None
2026-03-11 21:56:37 +00:00
print("Warning: No physical UART detected. Using simulator.")
2026-03-11 22:01:14 +00:00
def set_error(self, errno):
self._force_error = errno
2026-03-11 21:56:37 +00:00
def read(self, length=1):
2026-03-11 22:01:14 +00:00
if self._force_error is not None:
err = self._force_error
self._force_error = None
raise OSError(err)
2026-03-11 21:56:37 +00:00
if self.timeout is not None:
wait(self.timeout)
raise OSError(ETIMEDOUT)
else:
while True:
wait(1000)
def write(self, data):
2026-03-11 22:01:14 +00:00
if self._force_error is not None:
err = self._force_error
self._force_error = None
raise OSError(err)
2026-03-11 21:56:37 +00:00
def UARTDevice(port, baudrate=9600, timeout=None):
2026-03-20 21:57:25 +00:00
return FakeUART(port, baudrate, timeout)
2026-03-11 22:01:14 +00:00
2026-03-06 18:35:52 +00:00
class OSDiagnostics:
def __init__(self, hub, motorclass):
2026-03-11 22:01:14 +00:00
self.hub = hub
2026-03-06 18:35:52 +00:00
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
2026-03-14 23:03:43 +00:00
def testUErrno(self):
uerrnotestobject = UerrnoTest(self.hub, self.motorclass)
uerrnotestobject.testeagain()
uerrnotestobject.testebusy()
uerrnotestobject.testecanceled()
uerrnotestobject.testeinval()
uerrnotestobject.testeio()
uerrnotestobject.testenodev()
uerrnotestobject.testeopnotsupp()
uerrnotestobject.testeperm()
uerrnotestobject.testetimedout()
uerrnotestobject.print_results()
self.successfultests += uerrnotestobject.successfultests
self.failedtests.update(uerrnotestobject.failedtests)
2026-03-13 12:59:36 +00:00
def testUIO(self):
uiotestobject = UIOTest(self.hub, self.motorclass)
uiotestobject.print_results()
self.successfultests += uiotestobject.successfultests
self.failedtests.update(uiotestobject.failedtests)
2026-03-13 13:28:40 +00:00
def testUJSON(self):
ujsontestobject = UJSONTest(self.hub, self.motorclass)
ujsontestobject.print_results()
self.successfultests += ujsontestobject.successfultests
self.failedtests.update(ujsontestobject.failedtests)
2026-03-13 13:33:09 +00:00
def testUMath(self):
umathtestobject = UMathTest(self.hub, self.motorclass)
umathtestobject.print_results()
self.successfultests += umathtestobject.successfultests
self.failedtests.update(umathtestobject.failedtests)
2026-03-14 23:03:43 +00:00
def testURandom(self):
urandtestobject = URandomTest(self.hub, self.motorclass)
urandtestobject.print_results()
self.successfultests += urandtestobject.successfultests
self.failedtests.update(urandtestobject.failedtests)
def testUSelect(self):
uselecttestobject = USelectTest(self.hub, self.motorclass)
uselecttestobject.print_results()
self.successfultests += uselecttestobject.successfultests
self.failedtests.update(uselecttestobject.failedtests)
def testUStruct(self):
ustructtestobject = UStructTest(self.hub, self.motorclass)
ustructtestobject.print_results()
self.successfultests += ustructtestobject.successfultests
self.failedtests.update(ustructtestobject.failedtests)
def testUSys(self):
usystestobject = USysTest(self.hub, self.motorclass)
usystestobject.print_results()
self.successfultests += usystestobject.successfultests
self.failedtests.update(usystestobject.failedtests)
2026-03-20 21:57:25 +00:00
def testAll(self):
self.testUErrno()
self.testUIO()
self.testUJSON()
self.testUMath()
self.testURandom()
self.testUSelect()
self.testUStruct()
self.testUSys()
print(f"\n=== Results: {self.successfultests}/62 tests passed ===")
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
else:
print("No tests failed. Great job!")
2026-03-13 12:49:13 +00:00
class UerrnoTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
2026-03-11 22:01:14 +00:00
def testeagain(self):
print("Starting Test 1/9: EAGAIN - Try Again Error")
2026-03-20 21:57:25 +00:00
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(EAGAIN)
2026-03-11 22:01:14 +00:00
try:
2026-03-20 21:57:25 +00:00
uart.read(1)
2026-03-11 22:01:14 +00:00
print("No error raised.\nCompleted Test 1/9: EAGAIN - FAILED")
self.failedtests["EAGAIN"] = "No error raised"
except OSError as ex:
if ex.errno == EAGAIN:
print("EAGAIN can be thrown and caught.\nCompleted Test 1/9: EAGAIN - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 1/9: EAGAIN - FAILED")
self.failedtests["EAGAIN"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 1/9: EAGAIN - FAILED")
self.failedtests["EAGAIN"] = ex.errno
def testebusy(self):
# No reliable hardware trigger; use FakeUART
print("Starting Test 2/9: EBUSY - Device Busy Error")
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(EBUSY)
try:
uart.read(1)
print("No error raised.\nCompleted Test 2/9: EBUSY - FAILED")
self.failedtests["EBUSY"] = "No error raised"
except OSError as ex:
if ex.errno == EBUSY:
print("EBUSY can be thrown and caught.\nCompleted Test 2/9: EBUSY - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 2/9: EBUSY - FAILED")
self.failedtests["EBUSY"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 2/9: EBUSY - FAILED")
self.failedtests["EBUSY"] = ex.errno
def testecanceled(self):
# No reliable hardware trigger; use FakeUART
print("Starting Test 3/9: ECANCELED - Operation Canceled Error")
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(ECANCELED)
try:
uart.read(1)
print("No error raised.\nCompleted Test 3/9: ECANCELED - FAILED")
self.failedtests["ECANCELED"] = "No error raised"
except OSError as ex:
if ex.errno == ECANCELED:
print("ECANCELED can be thrown and caught.\nCompleted Test 3/9: ECANCELED - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 3/9: ECANCELED - FAILED")
self.failedtests["ECANCELED"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 3/9: ECANCELED - FAILED")
self.failedtests["ECANCELED"] = ex.errno
def testeinval(self):
# Triggered by passing an out-of-range value to motor.control.limits()
print("Starting Test 4/9: EINVAL - Invalid Argument Error")
try:
2026-03-20 21:57:25 +00:00
usys.stderr.flush()
except (OSError) as ex:
2026-03-11 22:01:14 +00:00
if ex.errno == EINVAL:
print("EINVAL can be thrown and caught.\nCompleted Test 4/9: EINVAL - SUCCESSFUL")
self.successfultests += 1
2026-03-20 21:57:25 +00:00
elif errno_val == EIO:
2026-03-11 22:01:14 +00:00
print("An unspecified error occurred (EIO).\nCompleted Test 4/9: EINVAL - FAILED")
self.failedtests["EINVAL"] = "EIO - Unspecified Error"
else:
2026-03-20 21:57:25 +00:00
print(f"Another error occurred with code: {ex}.\nCompleted Test 4/9: EINVAL - FAILED")
self.failedtests["EINVAL"] = str(ex)
2026-03-11 22:01:14 +00:00
def testeio(self):
# No reliable scriptable trigger (requires physical unplug); use FakeUART
print("Starting Test 5/9: EIO - I/O Error")
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(EIO)
try:
uart.read(1)
print("No error raised.\nCompleted Test 5/9: EIO - FAILED")
self.failedtests["EIO"] = "No error raised"
except OSError as ex:
if ex.errno == EIO:
print("EIO can be thrown and caught.\nCompleted Test 5/9: EIO - SUCCESSFUL")
self.successfultests += 1
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 5/9: EIO - FAILED")
self.failedtests["EIO"] = ex.errno
2026-03-11 01:03:44 +00:00
def testenodev(self):
2026-03-11 22:01:14 +00:00
# Triggered by initializing a motor on an empty port
print("Starting Test 6/9: ENODEV - Device Not Found Error")
input("Make sure Port A doesn't have anything plugged in, then press Enter.")
2026-03-06 18:35:52 +00:00
try:
my_motor = self.motorclass(Port.A)
print("OS detected a motor when there was nothing. You may have allowed missing motors. This is useful for debugging, but not recommended for production as it can cause issues with device control.")
2026-03-11 22:01:14 +00:00
self.failedtests["ENODEV"] = "No error raised"
2026-03-06 18:35:52 +00:00
except OSError as ex:
if ex.errno == ENODEV:
print("There is no motor on this port. ENODEV can be thrown and caught.\nCompleted Test 6/9: ENODEV - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 6/9: ENODEV - FAILED")
self.failedtests["ENODEV"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 6/9: ENODEV - FAILED")
self.failedtests["ENODEV"] = ex.errno
2026-03-11 22:01:14 +00:00
def testeopnotsupp(self):
# No reliable scriptable trigger without specific hardware; use FakeUART
print("Starting Test 7/9: EOPNOTSUPP - Operation Not Supported Error")
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(EOPNOTSUPP)
try:
uart.read(1)
print("No error raised.\nCompleted Test 7/9: EOPNOTSUPP - FAILED")
self.failedtests["EOPNOTSUPP"] = "No error raised"
except OSError as ex:
if ex.errno == EOPNOTSUPP:
print("EOPNOTSUPP can be thrown and caught.\nCompleted Test 7/9: EOPNOTSUPP - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 7/9: EOPNOTSUPP - FAILED")
self.failedtests["EOPNOTSUPP"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 7/9: EOPNOTSUPP - FAILED")
self.failedtests["EOPNOTSUPP"] = ex.errno
def testeperm(self):
print("Starting Test 8/9: EPERM - Operation Not Permitted Error")
2026-03-20 21:57:25 +00:00
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
uart.set_error(EPERM)
2026-03-11 22:01:14 +00:00
try:
2026-03-20 21:57:25 +00:00
uart.read(1)
2026-03-11 22:01:14 +00:00
print("No error raised.\nCompleted Test 8/9: EPERM - FAILED")
self.failedtests["EPERM"] = "No error raised"
except OSError as ex:
if ex.errno == EPERM:
print("EPERM can be thrown and caught.\nCompleted Test 8/9: EPERM - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 8/9: EPERM - FAILED")
self.failedtests["EPERM"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 8/9: EPERM - FAILED")
self.failedtests["EPERM"] = ex.errno
2026-03-11 21:56:37 +00:00
def testetimedout(self):
2026-03-11 22:01:14 +00:00
# Triggered by FakeUART (or real UART) timing out on read
print("Starting Test 9/9: ETIMEDOUT - Timed Out Error")
uart = UARTDevice(Port.A, baudrate=9600, timeout=1000)
2026-03-11 21:56:37 +00:00
try:
2026-03-11 22:01:14 +00:00
data = uart.read(10)
print("No error raised.\nCompleted Test 9/9: ETIMEDOUT - FAILED")
self.failedtests["ETIMEDOUT"] = "No error raised"
2026-03-11 21:56:37 +00:00
except OSError as ex:
if ex.errno == ETIMEDOUT:
print("Timed out with synthetic UART device. ETIMEDOUT can be thrown and caught.\nCompleted Test 9/9: ETIMEDOUT - SUCCESSFUL")
self.successfultests += 1
elif ex.errno == EIO:
print("An unspecified error occurred (EIO).\nCompleted Test 9/9: ETIMEDOUT - FAILED")
self.failedtests["ETIMEDOUT"] = "EIO - Unspecified Error"
else:
print(f"Another error occurred with code: {ex.errno}.\nCompleted Test 9/9: ETIMEDOUT - FAILED")
self.failedtests["ETIMEDOUT"] = ex.errno
2026-03-11 22:01:14 +00:00
2026-03-11 01:03:44 +00:00
def print_results(self):
2026-03-11 22:01:14 +00:00
print(f"\n=== Results: {self.successfultests}/9 tests passed ===")
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
2026-03-13 12:49:13 +00:00
print(f" {key}: {value}")
class UIOTest(OSDiagnostics):
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
2026-03-13 12:59:36 +00:00
# uio contains BytesIO, StringIO, and FileIO, but due to SPIKE Prime's lack of a filesystem, the test will omit FileIO and only include BytesIO and StringIO
2026-03-13 12:49:13 +00:00
def testbytesio(self):
try:
2026-03-20 21:57:25 +00:00
buffer = uio.BytesIO()
2026-03-13 12:49:13 +00:00
buffer.write(b'Hello, ')
2026-03-13 12:59:36 +00:00
buffer.write(b'Pybricks byte stream!')
2026-03-13 12:49:13 +00:00
current_content = buffer.getvalue()
print(f"Buffer content (via getvalue()): {current_content}")
2026-03-13 12:59:36 +00:00
print(f"Current cursor position: {buffer.tell()}")
2026-03-13 12:49:13 +00:00
# TODO: After testing that BytesIO actually works on this system, add checks to make sure that the outputs match what they should.
buffer.seek(0)
read_data = buffer.read()
print(f"Read data (via read()): {read_data}")
2026-03-13 12:59:36 +00:00
print(f"Current cursor position after reading: {buffer.tell()}")
2026-03-13 12:49:13 +00:00
buffer.close()
print("Buffer was closed successfully.")
print("Completed Test 1/2: BytesIO - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("An unexpected error occured.")
print("Completed Test 1/2: BytesIO - FAILED")
2026-03-13 12:59:36 +00:00
self.failedtests["BytesIO"] = ex.errno
def teststringio(self):
try:
2026-03-20 21:57:25 +00:00
buffer = uio.StringIO()
2026-03-13 12:59:36 +00:00
buffer.write('Hello, ')
buffer.write('Pybricks string stream!')
current_content = buffer.getvalue()
print(f"Buffer content (via getvalue()): {current_content}")
print(f"Current cursor position: {buffer.tell()}")
# TODO: After testing that StringIO actually works on this system, add checks to make sure that the outputs match what they should.
buffer.seek(0)
read_data = buffer.read()
print(f"Read data (via read()): {read_data}")
print(f"Current cursor position after reading: {buffer.tell()}")
buffer.close()
print("Buffer was closed successfully.")
print("Completed Test 2/2: StringIO - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("An unexpected error occured.")
print("Completed Test 2/2: StringIO - FAILED")
self.failedtests["StringIO"] = ex.errno
def print_results(self):
self.testbytesio()
self.teststringio()
print(f"\n=== Results: {self.successfultests}/2 tests passed ===")
2026-03-13 13:28:40 +00:00
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class UJSONTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
# Tests ujson.dumps() and ujson.loads()
def testdumpsloads(self):
try:
original = {"robot": "Pybricks", "speed": 500, "active": True}
json_str = ujson.dumps(original)
print(f"Serialized (via dumps()): {json_str}")
restored = ujson.loads(json_str)
print(f"Deserialized (via loads()): {restored}")
# TODO: After confirming dumps/loads works on this system, add equality tests
print("Completed Test 1/3: dumps/loads - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("An unexpected error occurred.")
print("Completed Test 1/3: dumps/loads - FAILED")
self.failedtests["dumps/loads"] = getattr(ex, "errno", str(ex))
# Tests ujson.loads() raising ValueError on malformed input
def testloadsinvalid(self):
try:
ujson.loads("{not valid json}")
print("No error raised.")
print("Completed Test 2/3: loads invalid - FAILED")
self.failedtests["loads_invalid"] = "No error raised"
except ValueError:
print("ValueError raised on malformed JSON, as expected.")
print("Completed Test 2/3: loads invalid - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("An unexpected error occurred.")
print("Completed Test 2/3: loads invalid - FAILED")
self.failedtests["loads_invalid"] = getattr(ex, "errno", str(ex))
# Tests ujson.dump() and ujson.load() using a uio.StringIO stream
def testdumpload(self):
try:
original = {"hub": "SPIKE Prime", "port": "A", "value": 42}
stream = uio.StringIO()
ujson.dump(original, stream)
print(f"Serialized to stream (via dump()): {stream.getvalue()}")
stream.seek(0)
restored = ujson.load(stream)
print(f"Deserialized from stream (via load()): {restored}")
stream.close()
# TODO: After confirming dump/load works on this system, add
# equality assertions to verify round-trip fidelity.
print("Completed Test 3/3: dump/load (stream) - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("An unexpected error occurred.")
print("Completed Test 3/3: dump/load (stream) - FAILED")
self.failedtests["dump/load"] = getattr(ex, "errno", str(ex))
def print_results(self):
self.testdumpsloads()
self.testloadsinvalid()
self.testdumpload()
print(f"\n=== Results: {self.successfultests}/3 tests passed ===")
2026-03-13 13:33:09 +00:00
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class UMathTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
def test_math(self):
2026-03-20 21:57:25 +00:00
EPSILON = 0.0001
2026-03-14 23:03:43 +00:00
if(umath.ceil(87.21) == 88):
self.successfultests += 1
else:
self.failedtests["ceilpos"] = "Failed"
if(umath.floor(14.61) == 14):
self.successfultests += 1
else:
self.failedtests["floorpos"] = "Failed"
if(umath.ceil(-87.21) == -87):
self.successfultests += 1
else:
self.failedtests["ceilneg"] = "Failed"
if(umath.floor(-14.61) == -15):
self.successfultests += 1
else:
self.failedtests["floorneg"] = "Failed"
if(umath.trunc(33.22) == 33):
self.successfultests += 1
else:
self.failedtests["truncpos"] = "Failed"
if(umath.trunc(-33.22) == -33):
self.successfultests += 1
else:
self.failedtests["truncneg"] = "Failed"
if(umath.fmod(6040, 3) == 1):
self.successfultests += 1
else:
self.failedtests["fmod"] = "Failed"
if(umath.fabs(88273) == 88273):
self.successfultests += 1
else:
self.failedtests["fabspos"] = "Failed"
2026-03-13 13:33:09 +00:00
2026-03-14 23:03:43 +00:00
if(umath.fabs(-27482) == 27482):
self.successfultests += 1
else:
self.failedtests["fabsneg"] = "Failed"
if(umath.fabs(-2742.233) == 2742.233):
self.successfultests += 1
else:
self.failedtests["fabsflt"] = "Failed"
if(umath.copysign(3928, -182) == -3928):
self.successfultests += 1
else:
self.failedtests["copysign"] = "Failed"
if(umath.exp(umath.log(1)) == 1):
self.successfultests += 2
else:
self.failedtests["eexp"] = "Failed"
self.failedtests["ln"] = "Failed"
2026-03-20 21:57:25 +00:00
if(abs(umath.e - 2.718282) < EPSILON):
2026-03-14 23:03:43 +00:00
self.successfultests += 1
else:
self.failedtests["e"] = "Failed"
if(umath.pow(19, 7) == 893871739):
self.successfultests += 1
else:
self.failedtests["pow"] = "Failed"
if(umath.sqrt(242064) == 492):
self.successfultests += 1
else:
self.failedtests["sqrt"] = "Failed"
2026-03-20 21:57:25 +00:00
if(abs(umath.pi - 3.141593) < EPSILON):
2026-03-14 23:03:43 +00:00
self.successfultests += 1
else:
self.failedtests["pi"] = "Failed"
2026-03-20 21:57:25 +00:00
2026-03-14 23:03:43 +00:00
if abs(umath.degrees(umath.pi * 3) - 540) < EPSILON:
self.successfultests += 1
else:
self.failedtests["degrees"] = "Failed"
if abs(umath.radians(270) - umath.pi * 1.5) < EPSILON:
self.successfultests += 1
else:
self.failedtests["radians"] = "Failed"
if(abs(umath.sin(umath.pi)) < EPSILON):
self.successfultests += 1
else:
self.failedtests["sin"] = "Failed"
if(abs(umath.asin(1) - umath.pi * 0.5) < EPSILON):
self.successfultests += 1
else:
self.failedtests["asin"] = "Failed"
if(abs(umath.cos(umath.pi) + 1) < EPSILON):
self.successfultests += 1
else:
self.failedtests["cos"] = "Failed"
if(abs(umath.acos(1)) < EPSILON):
self.successfultests += 1
else:
self.failedtests["acos"] = "Failed"
if(abs(umath.tan(umath.pi)) < EPSILON):
self.successfultests += 1
else:
self.failedtests["tan"] = "Failed"
if(abs(umath.atan(1) - umath.pi * 0.25) < EPSILON):
self.successfultests += 1
else:
self.failedtests["atan"] = "Failed"
if(abs(umath.atan2(1, -1) - umath.pi * 0.75) < EPSILON):
self.successfultests += 1
else:
self.failedtests["atan2"] = "Failed"
infinitenum = float('inf')
finitenum = 123456
if(umath.isfinite(finitenum) == True):
self.successfultests += 1
else:
self.failedtests["isfinitefinite"] = "Failed"
if(umath.isfinite(infinitenum) == False):
self.successfultests += 1
else:
self.failedtests["isfiniteinfinite"] = "Failed"
2026-03-20 21:57:25 +00:00
if(umath.isinf(finitenum) == False):
2026-03-14 23:03:43 +00:00
self.successfultests += 1
else:
self.failedtests["isinfinitefinite"] = "Failed"
2026-03-20 21:57:25 +00:00
if(umath.isinf(infinitenum) == True):
2026-03-14 23:03:43 +00:00
self.successfultests += 1
else:
self.failedtests["isinfiniteinfinite"] = "Failed"
nannum = float("nan")
if(umath.isnan(nannum) == True):
self.successfultests += 1
else:
self.failedtests["isnannan"] = "Failed"
if(umath.isnan(finitenum) == False):
self.successfultests += 1
else:
self.failedtests["isnannotnan"] = "Failed"
frac, integer = umath.modf(87.21)
2026-03-20 21:57:25 +00:00
if abs(integer - 87.0) < 0.01 and abs(frac - 0.21) < 0.01:
2026-03-14 23:03:43 +00:00
self.successfultests += 1
2026-03-20 21:57:25 +00:00
2026-03-14 23:03:43 +00:00
else:
self.failedtests["modf"] = "Failed"
mantissa, exponent = umath.frexp(64)
if mantissa == 0.5 and exponent == 7:
self.successfultests += 1
else:
self.failedtests["frexp"] = "Failed"
result = umath.ldexp(0.5, 7)
if result == 64:
self.successfultests += 1
else:
self.failedtests["ldexp"] = "Failed"
def print_results(self):
self.test_math()
2026-03-20 21:57:25 +00:00
print(f"\n=== Results: {self.successfultests}/35 tests passed ===")
2026-03-14 23:03:43 +00:00
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class URandomTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
def testrandom(self):
2026-03-20 21:57:25 +00:00
NUM_SAMPLES = 6553
2026-03-14 23:03:43 +00:00
NUM_BUCKETS = 10
CHART_WIDTH = 50
SAMPLE_PEEK = 20
samples = [random() for _ in range(NUM_SAMPLES)]
bucket_counts = [0] * NUM_BUCKETS
for x in samples:
i = int(x * NUM_BUCKETS)
if i == NUM_BUCKETS:
i -= 1
bucket_counts[i] += 1
mean = sum(samples) / NUM_SAMPLES
ideal = NUM_SAMPLES / NUM_BUCKETS
max_count = max(bucket_counts)
print("=" * 66)
print(" random() distribution test n=" + str(NUM_SAMPLES))
print("=" * 66)
print("")
print(" Range Count Bar")
print(" -------------- ----- " + "-" * CHART_WIDTH)
for i in range(NUM_BUCKETS):
lo = i / NUM_BUCKETS
hi = (i + 1) / NUM_BUCKETS
count = bucket_counts[i]
2026-03-20 21:57:25 +00:00
bar = int(round(count / max_count * CHART_WIDTH))
2026-03-14 23:03:43 +00:00
dev = abs(count - ideal) / ideal
if dev <= 0.10:
marker = "#"
elif dev <= 0.20:
marker = "+"
else:
marker = "."
lo_str = "{:.2f}".format(lo)
hi_str = "{:.2f}".format(hi)
2026-03-20 21:57:25 +00:00
print(" " + "{:.2f}".format(lo) + " - " + "{:.2f}".format(hi) + " " + str(count) + " " + marker * bar)
2026-03-14 23:03:43 +00:00
print("")
print(" Samples : " + str(NUM_SAMPLES))
print(" Mean : " + "{:.6f}".format(mean) + " (ideal 0.500000)")
print(" Min : " + "{:.6f}".format(min(samples)))
print(" Max : " + "{:.6f}".format(max(samples)))
print(" Legend : # within 10% + within 20% . beyond 20%")
print("")
print(" First " + str(SAMPLE_PEEK) + " raw values:")
row = " "
for idx, val in enumerate(samples[:SAMPLE_PEEK], 1):
row += "{:.4f} ".format(val)
if idx % 5 == 0:
print(row)
row = " "
if row.strip():
print(row)
print("")
2026-03-20 21:57:25 +00:00
if(abs(0.5 - mean) < 0.06):
2026-03-14 23:03:43 +00:00
print("Random Distribution Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Random Distribution Test: FAILED")
self.failedtests["randomdistribution"] = "Too much error"
def test_other_rands(self):
N = 1000
choicelist = ["apple", "banana", "grape", "orange"]
randint_vals = [urandom.randint(1, 100) for _ in range(N)]
randint_mean = sum(randint_vals) / N
print("Randint mean (ideally 50.5): {:.4f}".format(randint_mean))
2026-03-20 21:57:25 +00:00
if abs(50.5 - randint_mean) < 3.0:
2026-03-14 23:03:43 +00:00
print("Randint Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Randint Test: FAILED")
self.failedtests["randint"] = "Too much error"
getrandbits_vals = [urandom.getrandbits(7) for _ in range(N)]
getrandbits_mean = sum(getrandbits_vals) / N
print("Getrandbits(7) mean (ideally 63.5): {:.4f}".format(getrandbits_mean))
2026-03-20 21:57:25 +00:00
if abs(63.5 - getrandbits_mean) < 4:
2026-03-14 23:03:43 +00:00
print("Getrandbits Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Getrandbits Test: FAILED")
self.failedtests["getrandbits"] = "Too much error"
randrange_vals = [urandom.randrange(1, 1001, 4) for _ in range(N)]
randrange_mean = sum(randrange_vals) / N
print("Randrange(1,1001,4) mean (ideally 501.0): {:.4f}".format(randrange_mean))
2026-03-20 21:57:25 +00:00
if abs(501.0 - randrange_mean) < 20.0:
2026-03-14 23:03:43 +00:00
print("Randrange Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Randrange Test: FAILED")
self.failedtests["randrange"] = "Too much error"
uniform_vals = [urandom.uniform(1, 10) for _ in range(N)]
uniform_mean = sum(uniform_vals) / N
print("Uniform(1,10) mean (ideally 5.5): {:.4f}".format(uniform_mean))
2026-03-20 21:57:25 +00:00
if abs(5.5 - uniform_mean) < 0.3:
2026-03-14 23:03:43 +00:00
print("Uniform Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Uniform Test: FAILED")
self.failedtests["uniform"] = "Too much error"
choice_counts = {}
for item in choicelist:
choice_counts[item] = 0
for _ in range(N):
choice_counts[urandom.choice(choicelist)] += 1
ideal_pct = 100.0 / len(choicelist)
print("Choice distribution (ideally {:.1f}% each):".format(ideal_pct))
choice_ok = True
for item in choicelist:
pct = choice_counts[item] / N * 100
print(" " + item + ": {:.1f}%".format(pct))
if abs(pct - ideal_pct) > 5.0:
choice_ok = False
if choice_ok:
print("Choice Test: SUCCESSFUL")
self.successfultests += 1
else:
print("Choice Test: FAILED")
self.failedtests["choice"] = "Too much error"
def print_results(self):
self.testrandom()
self.test_other_rands()
print(f"\n=== Results: {self.successfultests}/6 tests passed ===")
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class USelectTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
def print_results(self):
# Register the standard input so we can read keyboard presses.
2026-03-20 21:57:25 +00:00
keyboard = uselect.poll()
keyboard.register(usys.stdin)
2026-03-19 18:33:33 +00:00
print("Type a few keys, make sure you get back what character you typed, then press Escape.")
2026-03-14 23:03:43 +00:00
while True:
# Check if a key has been pressed.
if keyboard.poll(0):
# Read the key and print it.
2026-03-20 21:57:25 +00:00
key = usys.stdin.read(1)
2026-03-14 23:03:43 +00:00
if key == '\x1b':
print("Escape key pressed. Exiting...")
break
else:
print("Pressed:", key)
2026-03-20 21:57:25 +00:00
result = input("Input Y if the results were accurate:")
if(result == "Y" or result == "y"):
2026-03-14 23:03:43 +00:00
print(f"\n=== Results: 1/1 tests passed ===")
self.successfultests += 1
else:
self.failedtests["input"] = "Unsatisfied"
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class UStructTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
def print_results(self):
2026-03-20 21:57:25 +00:00
packed = ustruct.pack('ii', 42, 100)
2026-03-17 17:40:53 +00:00
print(f'Packed bytes using pack(): {packed}')
2026-03-20 21:57:25 +00:00
unpacked = ustruct.unpack('ii', packed)
2026-03-17 17:40:53 +00:00
print(f'Unpacked values using unpack(): {unpacked}')
print(unpacked == (42, 100))
2026-03-18 12:18:08 +00:00
if(unpacked == (42, 100)):
print("Completed Test 1/2: pack - SUCCESSFUL")
self.successfultests += 1
else:
print("Completed Test 1/2: pack - FAILED")
self.failedtests["pack"] = "Failed"
2026-03-17 17:40:53 +00:00
format_string = 'hhl'
2026-03-20 21:57:25 +00:00
size = ustruct.calcsize(format_string)
2026-03-17 17:40:53 +00:00
buffer = bytearray(size)
2026-03-20 21:57:25 +00:00
ustruct.pack_into(format_string, buffer, 0, 5, 10, 15)
2026-03-17 17:40:53 +00:00
print("Packed buffer using pack_into():", buffer)
2026-03-20 21:57:25 +00:00
unpackedfrom = ustruct.unpack_from(format_string, buffer, 0)
2026-03-17 17:40:53 +00:00
print("Unpacked buffer using unpack_from():", unpackedfrom)
if(unpackedfrom == (5, 10, 15)):
print("Completed Test 2/2: pack_into - SUCCESSFUL")
2026-03-18 12:18:08 +00:00
self.successfultests += 1
2026-03-17 17:40:53 +00:00
else:
print("Completed Test 2/2: pack_into - FAILED")
2026-03-18 12:18:08 +00:00
self.failedtests["pack_into"] = "Failed"
print(f"\n=== Results: {self.successfultests}/2 tests passed ===")
2026-03-14 23:03:43 +00:00
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
class USysTest:
def __init__(self, hub, motorclass):
self.hub = hub
self.motorclass = motorclass
self.successfultests = 0
self.failedtests = {}
2026-03-19 18:09:02 +00:00
def printVersionDiagnostics(self):
try:
2026-03-20 21:57:25 +00:00
print("Hub version information:", pybricksforvers.version)
print("MicroPython version:", usys.version)
print("Pybricks version information:", usys.version_info)
print("MicroPython information:", usys.implementation)
2026-03-19 18:33:33 +00:00
self.successfultests += 1
print("Completed Test 1/4: versioninfo - SUCCESSFUL")
2026-03-19 18:09:02 +00:00
except Exception as ex:
2026-03-20 21:57:25 +00:00
self.failedtests["versioninfo"] = ex.errno
2026-03-19 18:33:33 +00:00
print("Completed Test 4/4: versioninfo - FAILED")
def teststdin(self):
# Register the standard input so we can read keyboard presses.
2026-03-20 21:57:25 +00:00
keyboard = uselect.poll()
keyboard.register(usys.stdin)
2026-03-19 18:33:33 +00:00
print("Type a few keys, make sure you get back what character you typed, then press Escape.")
while True:
# Check if a key has been pressed.
if keyboard.poll(0):
# Read the key and print it.
2026-03-20 21:57:25 +00:00
key = usys.stdin.read(1)
2026-03-19 18:33:33 +00:00
if key == '\x1b':
print("Escape key pressed. Exiting...")
break
else:
print("Pressed:", key)
2026-03-20 21:57:25 +00:00
result = input("Input Y if the results were accurate:")
if(result == "Y" or result == "y"):
2026-03-19 18:33:33 +00:00
print("Completed Test 1/4: stdin - SUCCESSFUL")
self.successfultests += 1
else:
self.failedtests["stdin"] = "Unsatisfied"
print("Completed Test 1/4: stdin - FAILED")
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
print(f" {key}: {value}")
def teststdout(self):
usys.stdout.flush()
try:
2026-03-20 21:57:25 +00:00
usys.stdout.buffer.write(b"stdout worked!\n")
2026-03-19 18:33:33 +00:00
print("Completed Test 2/4: stdout - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("Completed Test 2/4: stdout - FAILED")
self.failedtests["stdout"] = ex.errno
def teststderr(self):
2026-03-20 21:57:25 +00:00
usys.stdout.flush()
2026-03-19 18:33:33 +00:00
try:
2026-03-20 21:57:25 +00:00
usys.stderr.buffer.write(b"stderr worked!\n")
2026-03-19 18:33:33 +00:00
print("Completed Test 3/4: stderr - SUCCESSFUL")
self.successfultests += 1
except Exception as ex:
print("Completed Test 3/4: stderr - FAILED")
self.failedtests["stderr"] = ex.errno
2026-03-13 13:33:09 +00:00
def print_results(self):
2026-03-19 18:33:33 +00:00
self.teststdin()
self.teststdout()
self.teststderr()
2026-03-20 21:57:25 +00:00
self.printVersionDiagnostics()
print(f"\n=== Results: {self.successfultests}/4 tests passed ===")
2026-03-13 12:59:36 +00:00
if self.failedtests:
print("Failed tests:")
for key, value in self.failedtests.items():
2026-03-20 21:57:25 +00:00
print(f" {key}: {value}")
diag = OSDiagnostics(hub=PrimeHub(), motorclass=Motor)
diag.testAll()