mirror of
https://github.com/esphome/esphome.git
synced 2026-08-26 08:06:21 +00:00
[tests] Fix flaky pty log probe test on macOS (#18681)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
from collections.abc import Generator
|
||||
import errno
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
@@ -178,37 +177,34 @@ def _run_probe_on_pty(
|
||||
output = b""
|
||||
deadline = time.monotonic() + 60
|
||||
try:
|
||||
try:
|
||||
proc = subprocess.Popen(
|
||||
_probe_command(fixture_path),
|
||||
stdout=follower,
|
||||
stderr=follower if stderr_to_pty else subprocess.PIPE,
|
||||
stdin=follower,
|
||||
env=probe_env,
|
||||
)
|
||||
finally:
|
||||
os.close(follower)
|
||||
while True:
|
||||
timeout = deadline - time.monotonic()
|
||||
if timeout <= 0 or not select.select([controller], [], [], timeout)[0]:
|
||||
pytest.fail(f"pty probe produced no EOF in time; got {output!r}")
|
||||
try:
|
||||
chunk = os.read(controller, 1024)
|
||||
except OSError as err:
|
||||
# macOS raises EIO once the child closes its end of the pty;
|
||||
# anything else is a real failure, not end-of-stream.
|
||||
if err.errno != errno.EIO:
|
||||
raise
|
||||
break
|
||||
if not chunk:
|
||||
break
|
||||
proc = subprocess.Popen(
|
||||
_probe_command(fixture_path),
|
||||
stdout=follower,
|
||||
stderr=follower if stderr_to_pty else subprocess.PIPE,
|
||||
stdin=follower,
|
||||
env=probe_env,
|
||||
)
|
||||
# The parent keeps the follower open until the child has exited and
|
||||
# the controller is drained: macOS discards buffered pty output once
|
||||
# the last follower closes, so closing it early loses the probe's
|
||||
# output whenever the child finishes before the first read.
|
||||
while proc.poll() is None:
|
||||
if time.monotonic() > deadline:
|
||||
pytest.fail(f"pty probe did not exit in time; got {output!r}")
|
||||
if select.select([controller], [], [], 0.01)[0]:
|
||||
output += os.read(controller, 4096)
|
||||
# Everything the child wrote is already buffered, so drain without waiting.
|
||||
while select.select([controller], [], [], 0)[0] and (
|
||||
chunk := os.read(controller, 4096)
|
||||
):
|
||||
output += chunk
|
||||
stderr_text = ""
|
||||
if proc.stderr is not None:
|
||||
stderr_text = proc.stderr.read().decode(errors="replace")
|
||||
proc.stderr.close()
|
||||
assert proc.wait(60) == 0, stderr_text
|
||||
assert proc.returncode == 0, stderr_text
|
||||
finally:
|
||||
os.close(follower)
|
||||
os.close(controller)
|
||||
if proc is not None and proc.poll() is None:
|
||||
proc.kill()
|
||||
|
||||
Reference in New Issue
Block a user