From 6e3d7fee94ca99fe8cdfb5c6a6ee3a0548a4e7ed Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sat, 23 May 2026 17:57:02 -0400 Subject: [PATCH] Parse the settings correctly lol. --- .../speechDriver/hardwareSerialDriver.py | 12 ++- tests/unit/test_hardware_speech_drivers.py | 79 +++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/fenrirscreenreader/speechDriver/hardwareSerialDriver.py b/src/fenrirscreenreader/speechDriver/hardwareSerialDriver.py index 665cc310..a32cdd8b 100644 --- a/src/fenrirscreenreader/speechDriver/hardwareSerialDriver.py +++ b/src/fenrirscreenreader/speechDriver/hardwareSerialDriver.py @@ -46,8 +46,8 @@ class hardware_serial_driver(speech_driver): self.env = environment self._is_initialized = False settings_manager = self.env["runtime"]["SettingsManager"] - self.device = settings_manager.get_setting( - "speech", "hardware_device" + self.device = self._clean_device_setting( + settings_manager.get_setting("speech", "hardware_device") ) self.baud_rate = settings_manager.get_setting_as_int( "speech", "hardware_baud_rate" @@ -69,6 +69,12 @@ class hardware_serial_driver(speech_driver): ) self.worker_thread.start() + def _clean_device_setting(self, device): + if not isinstance(device, str): + return "auto" + device = device.split("#", 1)[0].split(";", 1)[0].strip() + return device or "auto" + def shutdown(self): if not self._is_initialized: return @@ -207,7 +213,7 @@ class hardware_serial_driver(speech_driver): attrs[0] &= ~(termios.IXON | termios.IXOFF | termios.IXANY) termios.tcsetattr(port, termios.TCSANOW, attrs) return port - except OSError as error: + except (OSError, termios.error) as error: self._debug( f"Hardware speech device open failed: {device}: {error}", debug.DebugLevel.ERROR, diff --git a/tests/unit/test_hardware_speech_drivers.py b/tests/unit/test_hardware_speech_drivers.py index ab4d84f5..100b4947 100644 --- a/tests/unit/test_hardware_speech_drivers.py +++ b/tests/unit/test_hardware_speech_drivers.py @@ -1,5 +1,6 @@ import os import select +import termios import time from unittest.mock import ANY from unittest.mock import Mock @@ -128,6 +129,19 @@ def test_auto_device_detection_includes_classic_serial( speech_driver.shutdown() +def test_configured_device_strips_inline_comment(serial_pair): + master_fd, slave_name = serial_pair + device_setting = f"{slave_name} # built-in serial port" + speech_driver = litetalkDriver.driver() + speech_driver.initialize(build_environment(device_setting)) + try: + assert speech_driver.device == slave_name + speech_driver.speak("Specific") + assert read_available(master_fd, 9) == b"Specific\r" + finally: + speech_driver.shutdown() + + def test_auto_device_detection_prefers_probe_response(monkeypatch): opened_ports = [] closed_ports = [] @@ -260,6 +274,71 @@ def test_auto_device_detection_falls_back_without_probe_response( speech_driver.shutdown() +def test_auto_device_detection_skips_termios_failures(monkeypatch): + opened_ports = [] + closed_ports = [] + + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.glob.glob", + lambda pattern: ["/dev/ttyUSB0"] + if pattern == "/dev/ttyUSB*" + else ["/dev/ttyS0"] + if pattern == "/dev/ttyS*" + else [], + ) + + def fake_open(device, flags): + port = 100 + len(opened_ports) + opened_ports.append((device, port)) + return port + + def fake_tcgetattr(port): + if port == 101: + raise termios.error(5, "Input/output error") + return [0, 0, 0, 0, 0, 0, [0] * 32] + + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.os.open", + fake_open, + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.os.close", + lambda port: closed_ports.append(port), + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.os.write", + lambda port, data: len(data), + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.select.select", + lambda readable, writable, exceptional, timeout: ( + [], + writable, + exceptional, + ), + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.termios.tcgetattr", + fake_tcgetattr, + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.termios.tcsetattr", + lambda port, when, attrs: None, + ) + monkeypatch.setattr( + "fenrirscreenreader.speechDriver.hardwareSerialDriver.tty.setraw", + lambda port: None, + ) + + speech_driver = litetalkDriver.driver() + speech_driver.initialize(build_environment("auto")) + try: + assert opened_ports == [("/dev/ttyUSB0", 100), ("/dev/ttyS0", 101)] + assert speech_driver.device == "/dev/ttyUSB0" + finally: + speech_driver.shutdown() + + def test_auto_device_detection_fails_when_no_serial_device(monkeypatch): monkeypatch.setattr( "fenrirscreenreader.speechDriver.hardwareSerialDriver.glob.glob",