Repository navigation
[cmake] Don't let tests inherit the terminal of ctest as stdin - #23651
guitargeek wants to merge 1 commit into
Conversation
Test Results 23 files 23 suites 3d 20h 54m 54s ⏱️ For more details on these failures, see this check. Results for commit 494a260. ♻️ This comment has been updated with latest results. |
| # background process group, so any terminal access (like the tcsetattr() | ||
| # in the pdb.Pdb constructor since Python 3.15, which doctest triggers) | ||
| # would stop them with SIGTTOU until the test times out. | ||
| set(_input INPUT_FILE /dev/null) |
There was a problem hiding this comment.
This is likely effective but is that the canonical ctest way to say there is no access to the terminal? Also don't we have the same/similar problem on Windows?
Related question why is "the pdb.Pdb constructor calling tcsetattr()"? Isn't there a missing argument to the python invocation to set 'this is a batch mode'?
There was a problem hiding this comment.
Hi, thanks for the many questions and again looking at one of my Python 3.15 compatibility PRs! Let me try to unpack the concerns and address them one-by-one.
ctest: As far as I can tell there is no dedicated mechanism. ctest passes its own stdin to the tests (checked with a minimal project run from a pty: isatty(0) is true in the test), and there is no test property for it. The driver runs the command with execute_process, which inherits stdin unless INPUT_FILE is given, so INPUT_FILE /dev/null is the standard way to say "no stdin". It's also what tests already get in CI.
Windows: Not affected. The hang comes from POSIX job control: timeout puts the test in a background process group, where reading from or reconfiguring the terminal raises SIGTTIN/SIGTTOU and stops the process. We don't use timeout on Windows (if(TIMEOUT_BINARY AND NOT MSVC)), and the Windows console has no background groups, so I limited the change to UNIX.
pdb: Since 3.15, pdb uses the new _pyrepl line editor when stdin is a TTY (gh-145378), and Pdb.__init__ sets it up eagerly. The resulting UnixConsole calls tcsetattr() on stdin, which triggers SIGTTOU from a background group even though the settings it writes are unchanged. doctest creates a Pdb on every run to support pdb.set_trace() in examples, so every doctest hits this.
There's no batch-mode flag. The closest is the PYTHON_BASIC_REPL env variable, which pdb also honors. But that only fixes this one symptom: the real problem is that tests are given a terminal they don't own, and detaching stdin fixes that for every test.
There was a problem hiding this comment.
Fair enough. I agree that this is the simplest way forward.
| set(_input INPUT_FILE /dev/null) | ||
| endif() |
There was a problem hiding this comment.
I recommend
| set(_input INPUT_FILE /dev/null) | |
| endif() | |
| set(_input INPUT_FILE /dev/null) | |
| else() # or elseif(WIN32) | |
| set(_input INPUT_FILE NUL) | |
| endif() |
for symmetry and to avoid surprises.
Tests added with ROOTTEST_ADD_TEST are wrapped in coreutils timeout, which moves them into a background process group. When ctest is run from a terminal, the tests inherit it as stdin, and any terminal access from a background process group stops the process with SIGTTOU. The test then hangs until the ctest timeout kills it. Since Python 3.15, this happens for every doctest: doctest.testmod() creates a pdb.Pdb, whose constructor sets up the new _pyrepl console when stdin is a TTY, which calls tcsetattr() on it. The JupyROOT doctests therefore always timed out when running ctest interactively, while passing in the CI where there is no terminal. Use /dev/null as stdin for tests that don't specify an input file, like in non-interactive environments. 🤖 Done with the help of AI
494a260 to
5ef57c3
Compare
Tests added with ROOTTEST_ADD_TEST are wrapped in coreutils timeout, which moves them into a background process group. When ctest is run from a terminal, the tests inherit it as stdin, and any terminal access from a background process group stops the process with SIGTTOU. The test then hangs until the ctest timeout kills it.
Since Python 3.15, this happens for every doctest: doctest.testmod() creates a pdb.Pdb, whose constructor sets up the new _pyrepl console when stdin is a TTY, which calls tcsetattr() on it. The JupyROOT doctests therefore always timed out when running ctest interactively, while passing in the CI where there is no terminal.
Use /dev/null as stdin for tests that don't specify an input file, like in non-interactive environments.
🤖 Done with the help of AI