Code

tests: test terminal output to both stdout and stderr
[git.git] / t / test-terminal.perl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use IO::Pty;
5 use File::Copy;
7 # Run @$argv in the background with stdio redirected to $out and $err.
8 sub start_child {
9         my ($argv, $out, $err) = @_;
10         my $pid = fork;
11         if (not defined $pid) {
12                 die "fork failed: $!"
13         } elsif ($pid == 0) {
14                 open STDOUT, ">&", $out;
15                 open STDERR, ">&", $err;
16                 close $out;
17                 exec(@$argv) or die "cannot exec '$argv->[0]': $!"
18         }
19         return $pid;
20 }
22 # Wait for $pid to finish.
23 sub finish_child {
24         # Simplified from wait_or_whine() in run-command.c.
25         my ($pid) = @_;
27         my $waiting = waitpid($pid, 0);
28         if ($waiting < 0) {
29                 die "waitpid failed: $!";
30         } elsif ($? & 127) {
31                 my $code = $? & 127;
32                 warn "died of signal $code";
33                 return $code - 128;
34         } else {
35                 return $? >> 8;
36         }
37 }
39 sub xsendfile {
40         my ($out, $in) = @_;
42         # Note: the real sendfile() cannot read from a terminal.
44         # It is unspecified by POSIX whether reads
45         # from a disconnected terminal will return
46         # EIO (as in AIX 4.x, IRIX, and Linux) or
47         # end-of-file.  Either is fine.
48         copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
49 }
51 sub copy_stdio {
52         my ($out, $err) = @_;
53         my $pid = fork;
54         defined $pid or die "fork failed: $!";
55         if (!$pid) {
56                 close($out);
57                 xsendfile(\*STDERR, $err);
58                 exit 0;
59         }
60         close($err);
61         xsendfile(\*STDOUT, $out);
62         finish_child($pid) == 0
63                 or exit 1;
64 }
66 if ($#ARGV < 1) {
67         die "usage: test-terminal program args";
68 }
69 my $master_out = new IO::Pty;
70 my $master_err = new IO::Pty;
71 my $pid = start_child(\@ARGV, $master_out->slave, $master_err->slave);
72 close $master_out->slave;
73 close $master_err->slave;
74 copy_stdio($master_out, $master_err);
75 exit(finish_child($pid));