diff options
authorPaul Tan <>2015-08-04 14:08:49 (GMT)
committerJunio C Hamano <>2015-08-12 17:33:46 (GMT)
commit18d8c26930acbba1627d2a8b7323be30ac2bd9bb (patch)
parente97a5e765db06995c3b027a5d7dceb6b4f2cba02 (diff)
test_terminal: redirect child process' stdin to a pty
When resuming, git-am detects if we are trying to feed it patches or not by checking if stdin is a TTY. However, the test library redirects stdin to /dev/null. This makes it difficult, for instance, to test the behavior of "git am -3" when resuming, as git-am will think we are trying to feed it patches and error out. Support this use case by extending test-terminal.perl to create a pseudo-tty for the child process' standard input as well. Note that due to the way the code is structured, the child's stdin pseudo-tty will be closed when we finish reading from our stdin. This means that in the common case, where our stdin is attached to /dev/null, the child's stdin pseudo-tty will be closed immediately. Some operations like isatty(), which git-am uses, require the file descriptor to be open, and hence if the success of the command depends on such functions, test_terminal's stdin should be redirected to a source with large amount of data to ensure that the child's stdin is not closed, e.g. test_terminal git am --3way </dev/zero Cc: Jonathan Nieder <> Cc: Jeff King <> Signed-off-by: Paul Tan <> Signed-off-by: Junio C Hamano <>
1 files changed, 28 insertions, 4 deletions
diff --git a/t/test-terminal.perl b/t/test-terminal.perl
index 1fb373f..96b6a03 100755
--- a/t/test-terminal.perl
+++ b/t/test-terminal.perl
@@ -5,15 +5,17 @@ use warnings;
use IO::Pty;
use File::Copy;
-# Run @$argv in the background with stdio redirected to $out and $err.
+# Run @$argv in the background with stdio redirected to $in, $out and $err.
sub start_child {
- my ($argv, $out, $err) = @_;
+ my ($argv, $in, $out, $err) = @_;
my $pid = fork;
if (not defined $pid) {
die "fork failed: $!"
} elsif ($pid == 0) {
+ open STDIN, "<&", $in;
open STDOUT, ">&", $out;
open STDERR, ">&", $err;
+ close $in;
close $out;
exec(@$argv) or die "cannot exec '$argv->[0]': $!"
@@ -49,6 +51,17 @@ sub xsendfile {
copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+sub copy_stdin {
+ my ($in) = @_;
+ my $pid = fork;
+ if (!$pid) {
+ xsendfile($in, \*STDIN);
+ exit 0;
+ }
+ close($in);
+ return $pid;
sub copy_stdio {
my ($out, $err) = @_;
my $pid = fork;
@@ -67,14 +80,25 @@ sub copy_stdio {
if ($#ARGV < 1) {
die "usage: test-terminal program args";
+my $master_in = new IO::Pty;
my $master_out = new IO::Pty;
my $master_err = new IO::Pty;
-my $pid = start_child(\@ARGV, $master_out->slave, $master_err->slave);
+my $pid = start_child(\@ARGV, $master_in->slave, $master_out->slave, $master_err->slave);
+close $master_in->slave;
close $master_out->slave;
close $master_err->slave;
+my $in_pid = copy_stdin($master_in);
copy_stdio($master_out, $master_err);
+my $ret = finish_child($pid);
+# If the child process terminates before our copy_stdin() process is able to
+# write all of its data to $master_in, the copy_stdin() process could stall.
+# Send SIGTERM to it to ensure it terminates.
+kill 'TERM', $in_pid;