Go version

go version go1.22.5 darwin/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/Users/qiwang/Library/Caches/go-build'
GOENV='/Users/qiwang/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/qiwang/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/qiwang/go'
GOPRIVATE=''
GOPROXY='https://goproxy.cn,direct'
GOROOT='/usr/local/Cellar/go/1.22.5/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/Cellar/go/1.22.5/libexec/pkg/tool/darwin_amd64'
GOVCS=''
GOVERSION='go1.22.5'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/Users/qiwang/dev/aprilsh/go.mod'
GOWORK='/Users/qiwang/dev/go.work'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/ww/tyqfl83d24n5m09cng7ccb2m0000gn/T/go-build771410576=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

run test terminal as follows:

package main

import (
    "os"
    "testing"

    "golang.org/x/term"
)

func TestQT(t *testing.T) {
    if tm := term.IsTerminal(int(os.Stdout.Fd())); !tm {
        t.Fatalf("%s is not terminal\n", os.Stdout.Name())
    }
}

What did you see happen?

test passed in local directory mode:

qiwang@Qi15Pro client % go test
PASS
ok      github.com/ericwq/aprilsh/frontend/client       2.263s

test failed in package list mode:

qiwang@Qi15Pro client % go test .
--- FAIL: TestQT (0.00s)
    client_test.go:627: /dev/stdout is not terminal
FAIL
FAIL    github.com/ericwq/aprilsh/frontend/client       1.535s
FAIL

What did you expect to see?

test passed in package list mode.

Comment From: gabyhelp

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

Comment From: ericwq

thanks, I checked the above posts. none of them is same issue. #34791 is the most similar but not same issue.

In different mode, go test treat os.Stdout differently. it doesn't make sense for me.

Comment From: ericwq

test.go, 1440 line, might be the key to answer the question.

    var buf bytes.Buffer
    if len(pkgArgs) == 0 || testBench != "" || testFuzz != "" {
        // Stream test output (no buffering) when no package has
        // been given on the command line (implicit current directory)
        // or when benchmarking or fuzzing.
        // No change to stdout.
    } else {
        // If we're only running a single package under test or if parallelism is
        // set to 1, and if we're displaying all output (testShowPass), we can
        // hurry the output along, echoing it as soon as it comes in.
        // We still have to copy to &buf for caching the result. This special
        // case was introduced in Go 1.5 and is intentionally undocumented:
        // the exact details of output buffering are up to the go command and
        // subject to change. It would be nice to remove this special case
        // entirely, but it is surely very helpful to see progress being made
        // when tests are run on slow single-CPU ARM systems.
        //
        // If we're showing JSON output, then display output as soon as
        // possible even when multiple tests are being run: the JSON output
        // events are attributed to specific package tests, so interlacing them
        // is OK.
        if testShowPass() && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON {
            // Write both to stdout and buf, for possible saving
            // to cache, and for looking for the "no tests to run" message.
            stdout = io.MultiWriter(stdout, &buf)
        } else {
            stdout = &buf
        }
    }

Comment From: Zxilly

https://github.com/golang/go/issues/24929

https://github.com/golang/go/issues/46959

Seems like a intended behavior.

Comment From: ericwq

thanks, @Zxilly.

after check test.go. I am sure that the above code is the root cause of this issue. For package list mode, stdout is decorated with io.MultiWriter. io.MultiWriter is not a terminal anymore. For local directory mode, stdout is unchanged, it's still a terminal for the test.

can anyone know how to get the undecorated stdout?

anyway, In different mode, go test treat os.Stdout differently. it doesn't make sense for me.

Comment From: seankhliao

Duplicate of #34877

Comment From: ericwq

use /dev/tty instead of os.Stdout. the following test will pass for both mode.

func TestQT(t *testing.T) {
    f, _ := os.Open("/dev/tty")
    if tm := term.IsTerminal(int(f.Fd())); !tm {
        t.Errorf("%s is not terminal\n", f.Name())
    }
    f.Close()
}