Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: in 1.15: change in "go test" argument parsing #40763

Closed
ianlancetaylor opened this issue Aug 13, 2020 · 10 comments
Closed

cmd/go: in 1.15: change in "go test" argument parsing #40763

ianlancetaylor opened this issue Aug 13, 2020 · 10 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@ianlancetaylor
Copy link
Contributor

Reported at https://groups.google.com/forum/?oldui=1#!topic/golang-dev/2O4EdN7XNZ0 👍

all_test.go:

        package foo
        import (
            "flag"
            "os"
            "testing"
            "time"
        )
        var oFoo = flag.Duration("foo", time.Second, "")
        func TestMain(m *testing.M) {
            flag.Parse()
            os.Exit(m.Run())
        }
        func Test(t *testing.T) {
            t.Log("ok")
        }
        jnml@3900x:~/src/tmp$ go test -v -foo 1s
        === RUN   Test
            all_test.go:18: ok
        --- PASS: Test (0.00s)
        PASS
        ok      tmp 0.001s
        jnml@3900x:~/src/tmp$ go test -v -timeout 1h
        === RUN   Test
            all_test.go:18: ok
        --- PASS: Test (0.00s)
        PASS
        ok      tmp 0.002s
        jnml@3900x:~/src/tmp$ go test -v -timeout 1h -foo 1s
        === RUN   Test
            all_test.go:18: ok
        --- PASS: Test (0.00s)
        PASS
        ok      tmp 0.002s
        jnml@3900x:~/src/tmp$ go test -v -foo 1s -timeout 1h
        flag provided but not defined: -timeout
        Usage of /tmp/go-build167035760/b001/tmp.test:
          -foo duration
                 (default 1s)
          -test.bench regexp
                run only benchmarks matching regexp
          -test.benchmem
                print memory allocations for benchmarks
          -test.benchtime d
                run each benchmark for duration d (default 1s)
          -test.blockprofile file
                write a goroutine blocking profile to file
          -test.blockprofilerate rate
                set blocking profile rate (see
runtime.SetBlockProfileRate) (default 1)
          -test.count n
                run tests and benchmarks n times (default 1)
          -test.coverprofile file
                write a coverage profile to file
          -test.cpu list
                comma-separated list of cpu counts to run each test with
          -test.cpuprofile file
                write a cpu profile to file
          -test.failfast
                do not start new tests after the first test failure
          -test.list regexp
                list tests, examples, and benchmarks matching regexp then exit
          -test.memprofile file
                write an allocation profile to file
          -test.memprofilerate rate
                set memory allocation profiling rate (see
runtime.MemProfileRate)
          -test.mutexprofile string
                write a mutex contention profile to the named file
after execution
          -test.mutexprofilefraction int
                if >= 0, calls runtime.SetMutexProfileFraction() (default 1)
          -test.outputdir dir
                write profiles to dir
          -test.parallel n
                run at most n tests in parallel (default 24)
          -test.run regexp
                run only tests and examples matching regexp
          -test.short
                run smaller test suite to save time
          -test.testlogfile file
                write test action log to file (for use only by cmd/go)
          -test.timeout d
                panic test binary after duration d (default 0, timeout disabled)
          -test.trace file
                write an execution trace to file
          -test.v
                verbose: print additional output
        exit status 2
        FAIL    tmp 0.002s

In Go 1.14, the last command does this:

> ~/go1.14/bin/go test -v -foo 1s -timeout 1h
=== RUN   Test
    foo_test.go:17: ok
--- PASS: Test (0.00s)
PASS
ok  	command-line-arguments	0.010s

I see some comments in the release notes about flag parsing, but I don't see anything that says that this case that used to work no longer works. I'm not sure precisely what changed here, but this needs to either be fixed or be clearly documented in the release notes. (My personal preference would be to fix it to work as it did previously.)

CC @bcmills @jayconrod

@ianlancetaylor ianlancetaylor added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Aug 13, 2020
@ianlancetaylor ianlancetaylor added this to the Go1.16 milestone Aug 13, 2020
@rsc
Copy link
Contributor

rsc commented Aug 14, 2020

I don't know precisely why this changed, but the old behavior was technically wrong.
The processing assumed that -timeout was a flag, even though strictly speaking it might not have been.
If -foo were a boolean flag, then at the end of flag.Parse
you'd expect flag.Args() = ["1s", "-timeout", "1h"],
and it would be weird to find ["1s", "-test.timeout", "1h"] instead,
which is what earlier versions of Go did.

I tried back to Go 1.11 and they all do this rewrite of -timeout to -test.timeout though.
As much as I pedantically disagree with it (see above), it's probably too late to break these scripts.
I'd like to know why it broke first, but assuming it was an unintentional change, I lean toward undoing it.

If we do undo it, it should be undone in Go 1.15.1, not Go 1.16.

@rsc
Copy link
Contributor

rsc commented Aug 14, 2020

For what it's worth, if the change was intentional, it was probably for a test binary with its own -v flag.
If you do go test -foo -v expecting to pass -v to the binary and it rewrites to -foo -test.v, that's annoying.

@bcmills bcmills self-assigned this Aug 14, 2020
@bcmills
Copy link
Contributor

bcmills commented Aug 14, 2020

https://golang.org/cl/14826 (in 2015) caused “test flags after the package name” to no longer be processed as test flags.

The change in 1.15 was that we no longer assumed that -foo is a non-boolean flag, and thus no longer assumed that -timeout was a flag rather than a positional argument. (If -foo is a boolean flag, then 1s should rightly be interpreted as either a package name or a positional argument to the test binary.)

I think we can restore the non-boolean assumption, but it will result in misparsed arguments if it turns out that one of the unknown flags really is a boolean after all.

@mvdan
Copy link
Member

mvdan commented Aug 14, 2020

I think we can restore the non-boolean assumption, but it will result in misparsed arguments if it turns out that one of the unknown flags really is a boolean after all.

Personally, I think that's the right approach, as most flag "kinds" are non-boolean, and it was the previous behavior.

That said, I think we should actively encourage Go developers to not rely on ambiguous flag parsing for go test. I realise that ship has sailed in terms of design and backwards compatibility, but we should still show how to have good "test flag hygiene".

@bcmills
Copy link
Contributor

bcmills commented Aug 14, 2020

To make @rsc's comment more explicit, consider this test program:

package foo

import (
	"flag"
	"strings"
	"testing"
	"time"
)

var _ = flag.Duration("foo", time.Second, "")
var _ = flag.Bool("bar", false, "")

func Test(t *testing.T) {
	t.Logf("args: %s", strings.Join(flag.Args(), " "))
	t.Logf("test.timeout: %v", flag.Lookup("test.timeout").Value)
}

If the flag is non-boolean, then go1.14.7 parses the arguments consistent with how the test binary parses them. However, if the flag is a boolean, then the go command interprets the -timeout flag as -test.timeout, but the test binary itself interprets it as a positional argument, resulting in the argument having the effect of both a flag and a (rewritten) positional argument:

example.com$ go1.14.7 test -v -foo 1s -timeout 25h
=== RUN   Test
    example_test.go:14: args:
    example_test.go:15: test.timeout: 25h0m0s
--- PASS: Test (0.00s)
PASS
ok      example.com     0.009s

example.com$ go1.14.7 test -v -bar 1s -timeout 25h
=== RUN   Test
    example_test.go:14: args: 1s -test.timeout=25h
    example_test.go:15: test.timeout: 25h0m0s
--- PASS: Test (0.00s)
PASS
ok      example.com     0.009s

@bcmills
Copy link
Contributor

bcmills commented Aug 14, 2020

In go1.12.17 the type of the flag could actually change whether the test respected the -timeout flag at all:

example.com$ go1.12.17 test -v . -foo 1s -timeout 25h
=== RUN   Test
--- PASS: Test (0.00s)
    example_test.go:14: args:
    example_test.go:15: test.timeout: 25h0m0s
PASS
ok      example.com     0.008s

example.com$ go1.12.17 test -v . -bar 1s -timeout 25h
=== RUN   Test
--- PASS: Test (0.00s)
    example_test.go:14: args: 1s -test.timeout=25h
    example_test.go:15: test.timeout: 0s
PASS
ok      example.com     0.009s

That, at least, was fixed in 1.13:

example.com$ go1.13.14 test -v . -foo 1s -timeout 25h
=== RUN   Test
--- PASS: Test (0.00s)
    example_test.go:14: args:
    example_test.go:15: test.timeout: 25h0m0s
PASS
ok      example.com     0.011s

example.com$ go1.13.14 test -v . -bar 1s -timeout 25h
=== RUN   Test
--- PASS: Test (0.00s)
    example_test.go:14: args: 1s -test.timeout=25h
    example_test.go:15: test.timeout: 25h0m0s
PASS
ok      example.com     0.008s

@bcmills
Copy link
Contributor

bcmills commented Aug 14, 2020

@gopherbot, please backport to 1.15

@gopherbot
Copy link

Backport issue(s) opened: #40802 (for 1.15).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases.

@gopherbot
Copy link

Change https://golang.org/cl/248618 mentions this issue: cmd/go/internal/test: keep looking for go command flags after ambiguous test flag

@bcmills bcmills changed the title cmd/go: in 1.15: change in of "go test" argument parsing cmd/go: in 1.15: change in "go test" argument parsing Aug 14, 2020
@gopherbot
Copy link

Change https://golang.org/cl/248726 mentions this issue: [release-branch.go1.15] cmd/go/internal/test: keep looking for go command flags after ambiguous test flag

sayboras added a commit to sayboras/golangci-lint that referenced this issue Aug 18, 2020
Golang 1.15 comes to few improvements, one of them is to have smaller
binary.

This PR is to make golang 1.15 as default version in CI, I also update
Docker based image to golang:1.15* as well.

Two issues faced with golang 1.15:
- Conflict between -v in `golangci-lint` and `go test`. Update to --verbose
to avoid the same.
- `nolintlint_unused.go` testdata is not matching regex. Correct by adding one
space after //

[1]: golang/go#40763

Signed-off-by: Tam Mach <sayboras@yahoo.com>
sayboras added a commit to golangci/golangci-lint that referenced this issue Aug 18, 2020
Golang 1.15 comes to few improvements, one of them is to have smaller
binary.

This PR is to make golang 1.15 as default version in CI, I also update
Docker based image to golang:1.15* as well.

Two issues faced with golang 1.15:
- Conflict between -v in `golangci-lint` and `go test`. Update to --verbose
to avoid the same. [1]
- `nolintlint_unused.go` testdata is not matching regex. Correct by adding one
space after //

[1]: golang/go#40763

Signed-off-by: Tam Mach <sayboras@yahoo.com>
@dmitshur dmitshur added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Sep 2, 2020
gopherbot pushed a commit that referenced this issue Sep 2, 2020
…mand flags after ambiguous test flag

Updates #40763
Fixes #40802

Change-Id: I275970d1f8561414571a5b93e368d68fa052c60f
Reviewed-on: https://go-review.googlesource.com/c/go/+/248618
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
(cherry picked from commit 797124f)
Reviewed-on: https://go-review.googlesource.com/c/go/+/248726
claucece pushed a commit to claucece/go that referenced this issue Oct 22, 2020
…mand flags after ambiguous test flag

Updates golang#40763
Fixes golang#40802

Change-Id: I275970d1f8561414571a5b93e368d68fa052c60f
Reviewed-on: https://go-review.googlesource.com/c/go/+/248618
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
(cherry picked from commit 797124f)
Reviewed-on: https://go-review.googlesource.com/c/go/+/248726
@golang golang locked and limited conversation to collaborators Sep 2, 2021
@rsc rsc unassigned bcmills Jun 23, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

6 participants