From 436531d2baf6b375fb94ec5489f2c720308bfba0 Mon Sep 17 00:00:00 2001 From: Yash Anand <164618230+YashAnand69@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:36:59 +0530 Subject: [PATCH 1/2] fix: allow CircuitPython UART construction without parity --- pslab/bus/busio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pslab/bus/busio.py b/pslab/bus/busio.py index 97771dc..1c2c519 100644 --- a/pslab/bus/busio.py +++ b/pslab/bus/busio.py @@ -440,7 +440,8 @@ def __init__( if stop not in (1, 2): raise ValueError("Invalid number of stop bits") - pd += parity.value + if parity is not None: + pd += parity.value try: self._set_uart_mode(pd, stop - 1) From cb2ac29160eb5e8d4fed477143502c183304c51c Mon Sep 17 00:00:00 2001 From: Yash Anand <164618230+YashAnand69@users.noreply.github.com> Date: Sun, 11 Oct 2026 21:36:59 +0530 Subject: [PATCH 2/2] test: cover UART parity and stop-bit configuration --- tests/test_busio_uart_configuration.py | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/test_busio_uart_configuration.py diff --git a/tests/test_busio_uart_configuration.py b/tests/test_busio_uart_configuration.py new file mode 100644 index 0000000..3e141ec --- /dev/null +++ b/tests/test_busio_uart_configuration.py @@ -0,0 +1,81 @@ +"""Hardware-independent regressions for CircuitPython UART configuration.""" + +from unittest.mock import Mock, call + +import pytest + +import pslab.protocol as CP +from pslab.bus.busio import Parity, UART +from pslab.bus.uart import _UARTPrimitive +from pslab.connection import ConnectionHandler + + +@pytest.fixture +def device(): + """Provide a connection without opening a physical device.""" + connection = Mock(spec=ConnectionHandler) + connection.version = "PSLab V6" + return connection + + +@pytest.fixture(autouse=True) +def restore_uart_configuration(): + """Keep process-wide cached UART settings independent between cases.""" + brgval, mode = _UARTPrimitive._brgval, _UARTPrimitive._mode + yield + _UARTPrimitive._save_config(brgval, mode) + + +@pytest.mark.parametrize( + "options, mode", + [({}, 0), ({"bits": 9}, 6), ({"stop": 2}, 1), ({"bits": 9, "stop": 2}, 7)], +) +def test_uart_without_parity(device, options, mode): + """Default and nine-bit UARTs encode no parity without an Enum value.""" + uart = UART(device, **options) + + assert device.send_byte.call_args_list == [ + call(CP.UART_2), + call(CP.SET_BAUD), + call(CP.UART_2), + call(CP.SET_MODE), + call(mode), + ] + device.send_int.assert_called_once_with(_UARTPrimitive._get_uart_brgval(9600)) + assert device.get_ack.call_count == 2 + assert uart.timeout == 1 + assert _UARTPrimitive._mode == (mode >> 1, mode & 1) + + +@pytest.mark.parametrize("parity", [Parity.EVEN, Parity.ODD]) +@pytest.mark.parametrize("stop", [1, 2]) +def test_uart_with_parity(device, parity, stop): + """Supported parity options retain their existing mode encoding.""" + UART(device, parity=parity, stop=stop) + + assert device.send_byte.call_args_list[-3:] == [ + call(CP.UART_2), + call(CP.SET_MODE), + call((parity.value << 1) | (stop - 1)), + ] + assert _UARTPrimitive._mode == (parity.value, stop - 1) + + +def test_uart_without_parity_on_older_firmware(device): + """No-parity construction retains the unsupported-mode fallback.""" + device.version = "PSLab V5" + + uart = UART(device, timeout=0.25) + + assert device.send_byte.call_args_list == [call(CP.UART_2), call(CP.SET_BAUD)] + assert device.get_ack.call_count == 1 + assert uart.timeout == 0.25 + + +@pytest.mark.parametrize("parity", [Parity.EVEN, Parity.ODD]) +def test_nine_bit_uart_rejects_parity(device, parity): + """The no-parity fix does not permit parity with nine data bits.""" + with pytest.raises(ValueError, match="Invalid parity"): + UART(device, bits=9, parity=parity) + + assert device.send_byte.call_args_list == [call(CP.UART_2), call(CP.SET_BAUD)]