How to fool a subprocess into thinking its stdout/stderr was a TTY while still reading output of its stdout/stderr?

I have written a CLI app that behaves differently depending on whether its stdout/stderr was a TTY. Now I want to test it. The problem is: Stdio::piped() can't fake a TTY while Stdio::inherit() can't capture stdout/stderr output.

I think your best bet here would be to tweak the CLI app with some "hidden"/internal setting/knob, such as a flag or an env var, that could override its tty detection.

  • if you don't mind recompiling the CLI app just for this, using a compile-time flag such as a Cargo feature flag and cfgs, then you could even ensure that option does not exist for public releases of your app.

For third-party programs, a trick I've personally used, on UNIX systems, is to:

echo 'int isatty(int fd) { return 1; }' | cc -shared -o -xc -

to generate a shared library, and then use LD_PRELOAD="$PWD"/ env setting before running the process.

But since you are the one maintaining the CLI app, offering a knob directly seems best :sweat_smile:


Alternatively, you can make a pseudo-terminal to use for stdout/err, but then you’ll have to handle all of the terminal emulation yourself. (This is the mechanism that xterm and friends use, so it should be a faithful test of the detection code as well as the altered behavior)


It seems that my binary does not respect LD_PRELOAD. How do I make it read LD_PRELOAD (but only in cfg(test) context)?

Longform answer:

  • create a new pseudoterminal with libc::openpty
  • install it with libc::login_tty in pre_exec
1 Like
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.