summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-13 23:35:35 +0700
committerhathach <[email protected]>2026-06-13 23:35:35 +0700
commitff57edb3e5e8d36b2783971924bf7373b4fc39bc (patch)
treec784ebf4c6067a48bea9dbee652b0d5b37999692
parentc8c63c30617d23bb604affcad423acbb11f8d10d (diff)
hil: make serial write timeout fatal
pyserial's posix write() raises SerialTimeoutException after partial progress with the byte count lost, so the retry loop re-sent from the same offset and could duplicate bytes on the wire — surfacing as bogus data-mismatch failures that look like device firmware bugs. write_timeout is already a total per-call deadline, so the loop added duplication risk without extending the budget: write once and treat a timeout as fatal. Default bumped 2 -> 10 s to keep the old overall bound; HIL_SERIAL_WRITE_DEADLINE removed. The per-character CLI loops keep their existing pacing (the 2 ms sleep between single-byte writes already spaces them on the wire); no unbounded ser.flush()/tcdrain is added. Review follow-up for #3643 (hil_test.py l.257/264 findings). Co-Authored-By: Claude Fable 5 <[email protected]>
-rwxr-xr-xtest/hil/hil_test.py29
1 files changed, 9 insertions, 20 deletions
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index c2ba34f02..aee59e05a 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -145,8 +145,7 @@ class HilConfig(TypedDict):
CMD_TIMEOUT = int(os.getenv('HIL_CMD_TIMEOUT', '180'))
POOL_TIMEOUT = int(os.getenv('HIL_POOL_TIMEOUT', '3000'))
SERIAL_READ_TIMEOUT = float(os.getenv('HIL_SERIAL_READ_TIMEOUT', '5'))
-SERIAL_WRITE_TIMEOUT = float(os.getenv('HIL_SERIAL_WRITE_TIMEOUT', '2'))
-SERIAL_WRITE_DEADLINE = float(os.getenv('HIL_SERIAL_WRITE_DEADLINE', '10'))
+SERIAL_WRITE_TIMEOUT = float(os.getenv('HIL_SERIAL_WRITE_TIMEOUT', '10'))
def cmd_stdout_text(out: Any) -> str:
@@ -247,24 +246,14 @@ def open_serial_dev(port: str):
return ser
-def serial_write_all(ser: serial.Serial, data: bytes, deadline: float = SERIAL_WRITE_DEADLINE):
- total = 0
- end = time.monotonic() + deadline
-
- while total < len(data):
- try:
- written = ser.write(data[total:])
- except serial.SerialTimeoutException:
- written = 0
-
- if written:
- total += written
- continue
-
- if time.monotonic() >= end:
- raise AssertionError(f'Serial write timeout after {deadline:.1f}s')
-
- time.sleep(0.01)
+def serial_write_all(ser: serial.Serial, data: bytes):
+ # write_timeout is a total deadline for the whole call (pyserial keeps partial progress
+ # internally). A timeout means the device stopped draining — treat it as fatal: pyserial
+ # loses the partial-write count on raise, so retrying would duplicate bytes on the wire.
+ try:
+ ser.write(data)
+ except serial.SerialTimeoutException:
+ raise AssertionError(f'Serial write timeout after {SERIAL_WRITE_TIMEOUT:.1f}s')
def read_disk_file(uid: str, lun: int, fname: str) -> bytes: