LoginSignup
5
1

More than 3 years have passed since last update.

workaround for panic when defining the same flag in multiple packages and go test -coverpkg.

Last updated at Posted at 2020-01-10

Issue

  • go version: 1.13.5

複数のパッケージが同じ名前のフラグを定義している時、 go test -coverpkg=all ./... のように -coverpkg をつけて go testすると、 flag redefined でpanicする問題。

$ go test -v -cover -coverpkg=all ./...
?       _/Users/y-goto/dev/go/flag-for-test/cmd1        [no test files]
?       _/Users/y-goto/dev/go/flag-for-test/cmd2        [no test files]
/var/folders/k7/2f4m6jwx6jjc2js45m62y9b00000gp/T/go-build687459203/b034/conf.test flag redefined: foo
panic: /var/folders/k7/2f4m6jwx6jjc2js45m62y9b00000gp/T/go-build687459203/b034/conf.test flag redefined: foo

TL;DR

とりあえず以下で避けられる。

  • init() でのフラグパースを避ける
  • flag.NewFlagSetを使う

How to reproduce

- cmd1
    - main.go // defines "foo" flag
- cmd2
    - main.go // defines "foo" flag
- cfg
    - conf_test.go // no-op _test

cmd1/main.go

package main

import (
        "flag"
        "fmt"
)

var foo = flag.String("foo", "", "flag foo")

func main() {
        flag.Parse()
        fmt.Println("foo", *foo)
}

cmd2/main.go

same as cmd1/main.go

cfg/conf_test.go

package conf

go test -coverpkg

$ go test -v -cover -coverpkg=all ./...
?       _/Users/y-goto/dev/go/flag-for-test/cmd1        [no test files]
?       _/Users/y-goto/dev/go/flag-for-test/cmd2        [no test files]
/var/folders/k7/2f4m6jwx6jjc2js45m62y9b00000gp/T/go-build687459203/b034/conf.test flag redefined: foo
panic: /var/folders/k7/2f4m6jwx6jjc2js45m62y9b00000gp/T/go-build687459203/b034/conf.test flag redefined: foo

goroutine 1 [running]:
flag.(*FlagSet).Var(0xc0000221e0, 0x11b58e0, 0xc0000523a0, 0x118c9d9, 0x3, 0x118d345, 0x8)
        /usr/local/go/src/flag/flag.go:848 +0x521
flag.(*FlagSet).StringVar(0xc0000221e0, 0xc0000523a0, 0x118c9d9, 0x3, 0x0, 0x0, 0x118d345, 0x8)
        /usr/local/go/src/flag/flag.go:751 +0x9e
flag.(*FlagSet).String(...)
        /usr/local/go/src/flag/flag.go:764
flag.String(0x118c9d9, 0x3, 0x0, 0x0, 0x118d345, 0x8, 0xc000052390)
        /usr/local/go/src/flag/flag.go:771 +0xab
_/Users/y-goto/dev/go/flag-for-test/cmd2.init()
        /Users/y-goto/dev/go/flag-for-test/cmd2/main.go:9 +0x53
FAIL    _/Users/y-goto/dev/go/flag-for-test/conf        0.227s
FAIL

"flag redefined: foo" と怒られる。

Workaround

どうも flag.CommandLine (デフォルトのFlagSet) に同名のFlagをセットしているとして怒られているっぽい(go testの初期化ロジックが変わった影響?)。そこで、各パッケージで個別のFlagSetを作ってそこにフラグをセットするように変更した。あと init() でフラグをパースしてもダメなのでmain()でやってください。

package main

import (
        "flag"
        "fmt"
        "os"
)

var (
        command = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
        foo     = command.String("foo", "", "flag foo")
)

func main() {
        command.Parse(os.Args[1:])
        fmt.Println("foo", *foo)
}
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1