Environment
Summary
WIFIStream.putc() makes one socket.send() call for an entire XMODEM frame and returns immediately. Python documents that socket.send() returns the number of bytes sent and that applications are responsible for transmitting any remaining bytes. A successful call may therefore accept only a prefix of the frame.
The XMODEM sender supplies the complete frame to putc() once, ignores the callback's return count, and waits for the receiver's ACK. On a partial TCP write, the rest of that frame is never queued by the controller.
Root cause
Deterministic reproduction
Use a socket double whose send() accepts only 2,048 bytes from a valid 8,199-byte XMODEM-8K CRC frame. Call WIFIStream.putc(frame) once and assert that the complete frame reached the simulated wire.
Command run against pinned develop:
PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 \
PYTHONPATH=/path/to/pinned-develop \
python3.11 -m pytest -c /dev/null -p no:cacheprovider \
--rootdir=/tmp --import-mode=importlib tests/unit/test_wifi_stream.py -q
Observed failure:
F [100%]
E AssertionError: assert 2048 == 8199
1 failed
The socket accepted exactly the configured 2,048-byte prefix, while the complete frame length was 8,199 bytes.
Impact
This can cause uploads to retry, time out, or fail when the OS accepts only part of a frame in a send() call. The XMODEM CRC and ACK/NAK exchange prevent the truncated frame from being silently accepted as valid, so this is a transfer reliability/availability issue rather than demonstrated silent data corruption.
Expected behavior
WIFIStream.putc() should not return successfully until every byte in the supplied XMODEM frame has been sent, or the socket raises an error. Python's socket.sendall() provides that contract.
Acceptance criteria
- Send the entire byte string supplied to
WIFIStream.putc() before reporting success.
- Return the input length so the callback preserves its existing integer success contract.
- Propagate socket errors rather than reporting a complete write.
- Add a deterministic regression using a valid 8,199-byte XMODEM-8K CRC frame and a socket double that models a 2,048-byte short write.
Environment
develop2ea66c10e96d88013a04441134d3d4f8e8c88510Summary
WIFIStream.putc()makes onesocket.send()call for an entire XMODEM frame and returns immediately. Python documents thatsocket.send()returns the number of bytes sent and that applications are responsible for transmitting any remaining bytes. A successful call may therefore accept only a prefix of the frame.The XMODEM sender supplies the complete frame to
putc()once, ignores the callback's return count, and waits for the receiver's ACK. On a partial TCP write, the rest of that frame is never queued by the controller.Root cause
WIFIStream.putc()returns the result of a singlesocket.send(data)call.putc()and does not inspect its return value.WIFIStream.upload()uses this callback for XMODEM uploads.Deterministic reproduction
Use a socket double whose
send()accepts only 2,048 bytes from a valid 8,199-byte XMODEM-8K CRC frame. CallWIFIStream.putc(frame)once and assert that the complete frame reached the simulated wire.Command run against pinned
develop:Observed failure:
The socket accepted exactly the configured 2,048-byte prefix, while the complete frame length was 8,199 bytes.
Impact
This can cause uploads to retry, time out, or fail when the OS accepts only part of a frame in a
send()call. The XMODEM CRC and ACK/NAK exchange prevent the truncated frame from being silently accepted as valid, so this is a transfer reliability/availability issue rather than demonstrated silent data corruption.Expected behavior
WIFIStream.putc()should not return successfully until every byte in the supplied XMODEM frame has been sent, or the socket raises an error. Python'ssocket.sendall()provides that contract.Acceptance criteria
WIFIStream.putc()before reporting success.