|
| 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