golang / vscode-go

Go extension for Visual Studio Code
https://marketplace.visualstudio.com/items?itemName=golang.Go
Other
3.78k stars 728 forks source link

Table-driven test names with slashes show up as sub-sub-tests #3286

Closed Eggbertx closed 2 months ago

Eggbertx commented 3 months ago

What version of Go, VS Code & VS Code Go extension are you using?

Version Information
* Run `go version` to get version of Go from _the VS Code integrated terminal_. - go version go1.22.1 linux/amd64 * Run `gopls -v version` to get version of Gopls from _the VS Code integrated terminal_. - golang.org/x/tools/gopls v0.15.2 * Run `code -v` or `code-insiders -v` to get version of VS Code or VS Code Insiders. - 1.87.2 * Check your installed extensions to get the version of the VS Code Go extension - v0.41.2 * Run Ctrl+Shift+P (Cmd+Shift+P on Mac OS) > `Go: Locate Configured Go Tools` command. # Tools Configuration ## Environment GOBIN: undefined toolsGopath: gopath: /home/eggbertx/go GOROOT: /usr/lib/go PATH: /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/lib/rustup/bin:/home/eggbertx/go/bin ## Tools go: /usr/bin/go: go version go1.22.1 linux/amd64 gopls: /home/eggbertx/go/bin/gopls (version: v0.15.2 built with go: go1.22.1) gotests: /home/eggbertx/go/bin/gotests (version: v1.6.0 built with go: go1.22.0) gomodifytags: /home/eggbertx/go/bin/gomodifytags (version: v1.16.0 built with go: go1.22.0) impl: /home/eggbertx/go/bin/impl (version: v1.1.0 built with go: go1.22.0) goplay: /home/eggbertx/go/bin/goplay (version: v1.0.0 built with go: go1.22.0) dlv: /home/eggbertx/go/bin/dlv (version: v1.22.0 built with go: go1.22.0) golangci-lint: /home/eggbertx/go/bin/golangci-lint (version: v1.56.2 built with go: go1.22.1) ## Go env Workspace Folder (gochan): /home/eggbertx/src/gochan GO111MODULE='' GOARCH='amd64' GOBIN='' GOCACHE='/home/eggbertx/.cache/go-build' GOENV='/home/eggbertx/.config/go/env' GOEXE='' GOEXPERIMENT='' GOFLAGS='' GOHOSTARCH='amd64' GOHOSTOS='linux' GOINSECURE='' GOMODCACHE='/home/eggbertx/go/pkg/mod' GONOPROXY='' GONOSUMDB='' GOOS='linux' GOPATH='/home/eggbertx/go' GOPRIVATE='' GOPROXY='https://proxy.golang.org,direct' GOROOT='/usr/lib/go' GOSUMDB='sum.golang.org' GOTMPDIR='' GOTOOLCHAIN='auto' GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64' GOVCS='' GOVERSION='go1.22.1' GCCGO='gccgo' GOAMD64='v1' AR='ar' CC='gcc' CXX='g++' CGO_ENABLED='1' GOMOD='/home/eggbertx/src/gochan/go.mod' GOWORK='' CGO_CFLAGS='-O2 -g' CGO_CPPFLAGS='' CGO_CXXFLAGS='-O2 -g' CGO_FFLAGS='-O2 -g' CGO_LDFLAGS='-O2 -g' PKG_CONFIG='pkg-config' GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3445950728=/tmp/go-build -gno-record-gcc-switches'

Share the Go related settings you have added/edited

Run Preferences: Open Settings (JSON) command to open your settings.json file. Share all the settings with the go. or ["go"] or gopls prefixes.

"go.toolsManagement.autoUpdate": true,
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"]

Describe the bug

Table-driven tests with a "/" character in the name are incorrectly shown as sub-sub tests. For example, the following test

func Test(t *testing.T) {
    testCases := []struct{ desc string }{{desc: "foo/bar"}}
    for _, tC := range testCases {
        t.Run(tC.desc, func(t *testing.T) {})
    }
}

is split up as seen in the screenshot below.

Steps to reproduce the behavior:

  1. Create and run a test file with the above test (or run any test that has a "/" in the name argument passed to t.Run())
  2. Go to the Testing pane
  3. Expand the test entry to view the sub-tests
  4. Instead of showing foo/bar as a sub-test of Test as seen in the output of go test, it shows foo as a sub-test, with bar incorrectly additionally nested

Screenshots or recordings

image

hyangah commented 2 months ago

Thanks for the report @Eggbertx

The slashes in test name are somewhat special. In go test, the two subtests have identical names, so they are deduplicated by the second a/b is suffixed by #01.

    t.Run("a/b", func(t *testing.T) {}) // First
    t.Run("a", func(t *testing.T) {
        t.Run("b", func(t *testing.T) {}) // Second
    })
=== RUN   TestF/a/b
=== RUN   TestF/a
=== RUN   TestF/a/b#01

And, go test -run="/^a$/" -v will run not only the second a/b, but also the first a/b.

It may be possible to guess the level by counting the test result PASS/FAIL line's indentation level or ordering. However, I am afraid that will add significant complexity and fragile.

In fact, from an internal team discussion on why t.Run does not escape slashes in names, I learned that not escaping "/" in names is intentional so it can allow users to write

t.Run("a/b", func(t *testing.T) {...})

instead of

t.Run("a", func(t *testing.T) {
   t.Run("b", ...)
}

So, this is unfortunate, but working as intended. If the hierarchical view is undesirable, please escape "/" from their subtest names. (e.g. replacing with a different slash-like character, ... )