diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index a76ae1df9..b3fe9a4e5 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -2,6 +2,7 @@ package iostreams import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -41,6 +42,7 @@ type IOStreams struct { stderrTTYOverride bool stderrIsTTY bool termWidthOverride int + ttySize func() (int, int, error) pagerCommand string pagerProcess *os.Process @@ -273,7 +275,7 @@ func (s *IOStreams) ForceTerminal(spec string) { return } - ttyWidth, _, err := ttySize() + ttyWidth, _, err := s.ttySize() if err != nil { return } @@ -284,7 +286,6 @@ func (s *IOStreams) ForceTerminal(spec string) { s.termWidthOverride = int(float64(s.termWidthOverride) * (float64(p) / 100)) } } - } func (s *IOStreams) ColorScheme() *ColorScheme { @@ -325,6 +326,7 @@ func System() *IOStreams { colorEnabled: EnvColorForced() || (!EnvColorDisabled() && stdoutIsTTY), is256enabled: Is256ColorSupported(), pagerCommand: os.Getenv("PAGER"), + ttySize: ttySize, } if stdoutIsTTY && stderrIsTTY { @@ -345,6 +347,9 @@ func Test() (*IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) { In: ioutil.NopCloser(in), Out: out, ErrOut: errOut, + ttySize: func() (int, int, error) { + return -1, -1, errors.New("ttySize not implemented in tests") + }, }, in, out, errOut } @@ -359,6 +364,7 @@ func isCygwinTerminal(w io.Writer) bool { return false } +// terminalSize measures the viewport of the terminal that the output stream is connected to func terminalSize(w io.Writer) (int, int, error) { if f, isFile := w.(*os.File); isFile { return term.GetSize(int(f.Fd())) diff --git a/pkg/iostreams/iostreams_test.go b/pkg/iostreams/iostreams_test.go new file mode 100644 index 000000000..fc4ead08b --- /dev/null +++ b/pkg/iostreams/iostreams_test.go @@ -0,0 +1,68 @@ +package iostreams + +import ( + "errors" + "testing" +) + +func TestIOStreams_ForceTerminal(t *testing.T) { + tests := []struct { + name string + iostreams *IOStreams + arg string + wantTTY bool + wantWidth int + }{ + { + name: "explicit width", + iostreams: &IOStreams{}, + arg: "72", + wantTTY: true, + wantWidth: 72, + }, + { + name: "measure width", + iostreams: &IOStreams{ + ttySize: func() (int, int, error) { + return 72, 0, nil + }, + }, + arg: "true", + wantTTY: true, + wantWidth: 72, + }, + { + name: "measure width fails", + iostreams: &IOStreams{ + ttySize: func() (int, int, error) { + return -1, -1, errors.New("ttySize sabotage!") + }, + }, + arg: "true", + wantTTY: true, + wantWidth: 80, + }, + { + name: "apply percentage", + iostreams: &IOStreams{ + ttySize: func() (int, int, error) { + return 72, 0, nil + }, + }, + arg: "50%", + wantTTY: true, + wantWidth: 36, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.iostreams.ForceTerminal(tt.arg) + if isTTY := tt.iostreams.IsStdoutTTY(); isTTY != tt.wantTTY { + t.Errorf("IOStreams.IsStdoutTTY() = %v, want %v", isTTY, tt.wantTTY) + } + if tw := tt.iostreams.TerminalWidth(); tw != tt.wantWidth { + t.Errorf("IOStreams.TerminalWidth() = %v, want %v", tw, tt.wantWidth) + } + }) + } +} diff --git a/pkg/iostreams/tty_size.go b/pkg/iostreams/tty_size.go index 8370fc916..767000ce7 100644 --- a/pkg/iostreams/tty_size.go +++ b/pkg/iostreams/tty_size.go @@ -8,6 +8,7 @@ import ( "golang.org/x/term" ) +// ttySize measures the size of the controlling terminal for the current process func ttySize() (int, int, error) { f, err := os.Open("/dev/tty") if err != nil {