Skip to content

Commit 0be4ef3

Browse files
committed
cmd/go: ensure streaming test's stdout, stderr are same as cmd/go's
Fixes #18153 Change-Id: Ie8a32dd6fe306f00e51cde77dd4ea353f7109940 Reviewed-on: https://go-review.googlesource.com/34010 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 94a4485 commit 0be4ef3

File tree

4 files changed

+96
-11
lines changed

4 files changed

+96
-11
lines changed

src/cmd/dist/test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"os/exec"
1616
"path/filepath"
1717
"regexp"
18+
"runtime"
1819
"strconv"
1920
"strings"
2021
"sync"
@@ -331,6 +332,10 @@ func (t *tester) registerRaceBenchTest(pkg string) {
331332
})
332333
}
333334

335+
// stdOutErrAreTerminals is defined in test_linux.go, to report
336+
// whether stdout & stderr are terminals.
337+
var stdOutErrAreTerminals func() bool
338+
334339
func (t *tester) registerTests() {
335340
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-vetall") {
336341
// Run vet over std and cmd and call it quits.
@@ -347,6 +352,27 @@ func (t *tester) registerTests() {
347352
return
348353
}
349354

355+
// This test needs its stdout/stderr to be terminals, so we don't run it from cmd/go's tests.
356+
// See issue 18153.
357+
if runtime.GOOS == "linux" {
358+
t.tests = append(t.tests, distTest{
359+
name: "cmd_go_test_terminal",
360+
heading: "cmd/go terminal test",
361+
fn: func(dt *distTest) error {
362+
t.runPending(dt)
363+
if !stdOutErrAreTerminals() {
364+
fmt.Println("skipping terminal test; stdout/stderr not terminals")
365+
return nil
366+
}
367+
cmd := exec.Command("go", "test")
368+
cmd.Dir = filepath.Join(os.Getenv("GOROOT"), "src/cmd/go/testdata/testterminal18153")
369+
cmd.Stdout = os.Stdout
370+
cmd.Stderr = os.Stderr
371+
return cmd.Run()
372+
},
373+
})
374+
}
375+
350376
// Fast path to avoid the ~1 second of `go list std cmd` when
351377
// the caller lists specific tests to run. (as the continuous
352378
// build coordinator does).

src/cmd/dist/test_linux.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build linux
6+
7+
package main
8+
9+
import (
10+
"syscall"
11+
"unsafe"
12+
)
13+
14+
const ioctlReadTermios = syscall.TCGETS
15+
16+
// isTerminal reports whether fd is a terminal.
17+
func isTerminal(fd uintptr) bool {
18+
var termios syscall.Termios
19+
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
20+
return err == 0
21+
}
22+
23+
func init() {
24+
stdOutErrAreTerminals = func() bool {
25+
return isTerminal(1) && isTerminal(2)
26+
}
27+
}

src/cmd/go/test.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"go/doc"
1414
"go/parser"
1515
"go/token"
16-
"io"
1716
"os"
1817
"os/exec"
1918
"path"
@@ -1122,12 +1121,8 @@ func (b *builder) runTest(a *action) error {
11221121
cmd.Env = envForDir(cmd.Dir, origEnv)
11231122
var buf bytes.Buffer
11241123
if testStreamOutput {
1125-
// The only way to keep the ordering of the messages and still
1126-
// intercept its contents. os/exec will share the same Pipe for
1127-
// both Stdout and Stderr when running the test program.
1128-
mw := io.MultiWriter(os.Stdout, &buf)
1129-
cmd.Stdout = mw
1130-
cmd.Stderr = mw
1124+
cmd.Stdout = os.Stdout
1125+
cmd.Stderr = os.Stderr
11311126
} else {
11321127
cmd.Stdout = &buf
11331128
cmd.Stderr = &buf
@@ -1192,7 +1187,7 @@ func (b *builder) runTest(a *action) error {
11921187
t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
11931188
if err == nil {
11941189
norun := ""
1195-
if testShowPass && !testStreamOutput {
1190+
if testShowPass {
11961191
a.testOutput.Write(out)
11971192
}
11981193
if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
@@ -1204,9 +1199,7 @@ func (b *builder) runTest(a *action) error {
12041199

12051200
setExitStatus(1)
12061201
if len(out) > 0 {
1207-
if !testStreamOutput {
1208-
a.testOutput.Write(out)
1209-
}
1202+
a.testOutput.Write(out)
12101203
// assume printing the test binary's exit status is superfluous
12111204
} else {
12121205
fmt.Fprintf(a.testOutput, "%s\n", err)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2016 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build linux
6+
7+
// This test is run by src/cmd/dist/test.go (cmd_go_test_terminal),
8+
// and not by cmd/go's tests. This is because this test requires that
9+
// that it be called with its stdout and stderr being a terminal.
10+
// dist doesn't run `cmd/go test` against this test directory if
11+
// dist's stdout/stderr aren't terminals.
12+
//
13+
// See issue 18153.
14+
15+
package p
16+
17+
import (
18+
"syscall"
19+
"testing"
20+
"unsafe"
21+
)
22+
23+
const ioctlReadTermios = syscall.TCGETS
24+
25+
// isTerminal reports whether fd is a terminal.
26+
func isTerminal(fd uintptr) bool {
27+
var termios syscall.Termios
28+
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
29+
return err == 0
30+
}
31+
32+
func TestIsTerminal(t *testing.T) {
33+
if !isTerminal(1) {
34+
t.Errorf("stdout is not a terminal")
35+
}
36+
if !isTerminal(2) {
37+
t.Errorf("stderr is not a terminal")
38+
}
39+
}

0 commit comments

Comments
 (0)