Skip to content

Commit e49b73e

Browse files
committed
fix: stop test scripts from inheriting the terminal
Scripts inherited the TUI's terminal as stdin, so a tool like flutter could switch it back to line mode with echo on exit. Mouse events then printed as raw escape codes and shortcuts stopped working.
1 parent 16bcf5d commit e49b73e

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

‎render_machine/render_utils.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def execute_script( # noqa: C901
109109
start_time = time.time()
110110
proc = subprocess.Popen(
111111
cmd,
112+
stdin=subprocess.DEVNULL,
112113
stdout=subprocess.PIPE,
113114
stderr=subprocess.STDOUT,
114115
text=True,

‎tests/test_execute_script.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Tests for running test scripts with render_utils.execute_script."""
2+
3+
import os
4+
import stat
5+
import sys
6+
from pathlib import Path
7+
8+
import pytest
9+
10+
from render_machine.render_utils import execute_script
11+
12+
13+
@pytest.fixture
14+
def stdin_with_input():
15+
"""Replace fd 0 with a pipe holding a line, as a terminal would after a keypress.
16+
17+
A pipe stands in for the terminal: whether a script can reach codeplain's stdin
18+
does not depend on what kind of file stdin is, and a pipe works in CI too.
19+
"""
20+
read_fd, write_fd = os.pipe()
21+
os.write(write_fd, b"keypress\n")
22+
os.close(write_fd)
23+
# A child process inherits file descriptor 0, not sys.stdin, so fd 0 is what has to change.
24+
saved_stdin = os.dup(0)
25+
os.dup2(read_fd, 0)
26+
os.close(read_fd)
27+
yield
28+
os.dup2(saved_stdin, 0)
29+
os.close(saved_stdin)
30+
31+
32+
@pytest.mark.skipif(sys.platform == "win32", reason="shell scripts are not supported on Windows")
33+
def test_script_does_not_inherit_stdin(tmp_path: Path, stdin_with_input):
34+
# A tool such as flutter resets the terminal it reads from, which breaks the TUI.
35+
# The script reports what it finds on its stdin: the waiting line if it inherited
36+
# codeplain's stdin, end-of-file if it was given an empty one.
37+
script = tmp_path / "read_stdin.sh"
38+
script.write_text('#!/bin/sh\nif read -r line; then echo "read:$line"; else echo "eof"; fi\n')
39+
script.chmod(script.stat().st_mode | stat.S_IEXEC)
40+
41+
returncode, output, _ = execute_script(str(script), [], "Unit Tests")
42+
43+
assert returncode == 0
44+
assert output.strip() == "eof"

0 commit comments

Comments
 (0)