cmd/go: test that each script test increments at least one counter

Add code that will set a scriptGoInvoked bit for the testing.TB when
it invokes the go command. If the go command was invoked, make sure
that at least one counter was incremented.

Also add the counters cmd/go/gomodcache-entry-relative,
cmd/go/gopath-entry-relative, and cmd/go/invalid-toolchain-in-file so
we can increment counters when a test errors out before the flag
subcommand counters are processed. This enforces the invariant that at
least one counter is incremented by every test that invokes the go
command.

Add the counter cmd/go/exec-go-toolchain for when a toolchain switch
happens.

Add cmd/go/subcommand:help for invoking help without arguments and
cmd/go/help-unknown-topic for when an unknown command is provided
to help.

Change-Id: Id90f2bbe4c7e89b846da00ec1ed9595ece2b269c
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/go/+/568259
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Michael Matloob 2024-02-29 17:34:59 -05:00
parent da5871d582
commit f93f6e501e
10 changed files with 83 additions and 2 deletions

View File

@ -29,11 +29,22 @@ func TestCounterNamesUpToDate(t *testing.T) {
counters = append(counters, "cmd/go/flag:C", "cmd/go/subcommand:unknown")
counters = append(counters, flagscounters("cmd/go/flag:", *flag.CommandLine)...)
// Add help (without any arguments) as a special case. cmdcounters adds go help <cmd>
// for all subcommands, but it's also valid to invoke go help without any arguments.
counters = append(counters, "cmd/go/subcommand:help")
for _, cmd := range base.Go.Commands {
counters = append(counters, cmdcounters(nil, cmd)...)
}
cstr := []byte(strings.Join(counters, "\n") + "\n")
counters = append(counters, base.RegisteredCounterNames()...)
for _, c := range counters {
const counterPrefix = "cmd/go"
if !strings.HasPrefix(c, counterPrefix) {
t.Fatalf("registered counter %q does not start with %q", c, counterPrefix)
}
}
cstr := []byte(strings.Join(counters, "\n") + "\n")
const counterNamesFile = "testdata/counters.txt"
old, err := os.ReadFile(counterNamesFile)
if err != nil {

View File

@ -14,11 +14,14 @@ import (
"os"
"os/exec"
"reflect"
"sort"
"strings"
"sync"
"cmd/go/internal/cfg"
"cmd/go/internal/str"
"golang.org/x/telemetry/counter"
)
// A Command is an implementation of a go command
@ -221,3 +224,24 @@ func RunStdin(cmdline []string) {
// Usage is the usage-reporting function, filled in by package main
// but here for reference by other packages.
var Usage func()
var counterNames = map[string]bool{}
// NewCounter registers a new counter. It must be called from an init function
// or global variable initializer.
func NewCounter(name string) *counter.Counter {
if counterNames[name] {
panic(fmt.Errorf("counter %q initialized twice", name))
}
counterNames[name] = true
return counter.New(name)
}
func RegisteredCounterNames() []string {
var names []string
for name := range counterNames {
names = append(names, name)
}
sort.Strings(names)
return names
}

View File

@ -18,6 +18,8 @@ import (
"cmd/go/internal/base"
)
var counterErrorHelpUnknownTopic = base.NewCounter("cmd/go/error:help-unknown-topic")
// Help implements the 'help' command.
func Help(w io.Writer, args []string) {
// 'go help documentation' generates doc.go.
@ -57,6 +59,7 @@ Args:
if i > 0 {
helpSuccess += " " + strings.Join(args[:i], " ")
}
counterErrorHelpUnknownTopic.Inc()
fmt.Fprintf(os.Stderr, "go help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
base.SetExitStatus(2) // failed at 'go help cmd'
base.Exit()

View File

@ -777,6 +777,8 @@ func rewriteVersionList(ctx context.Context, dir string) (err error) {
var (
statCacheOnce sync.Once
statCacheErr error
counterErrorGOMODCACHEEntryRelative = base.NewCounter("cmd/go/error:gomodcache-entry-relative")
)
// checkCacheDir checks if the directory specified by GOMODCACHE exists. An
@ -788,6 +790,7 @@ func checkCacheDir(ctx context.Context) error {
return fmt.Errorf("module cache not found: neither GOMODCACHE nor GOPATH is set")
}
if !filepath.IsAbs(cfg.GOMODCACHE) {
counterErrorGOMODCACHEEntryRelative.Inc()
return fmt.Errorf("GOMODCACHE entry is relative; must be absolute path: %q.\n", cfg.GOMODCACHE)
}

View File

@ -81,6 +81,8 @@ func FilterEnv(env []string) []string {
return out
}
var counterErrorInvalidToolchainInFile = base.NewCounter("cmd/go/error:invalid-toolchain-in-file")
// Select invokes a different Go toolchain if directed by
// the GOTOOLCHAIN environment variable or the user's configuration
// or go.mod file.
@ -174,6 +176,7 @@ func Select() {
// has a suffix like "go1.21.1-foo" and toolchain is "go1.21.1".)
toolVers := gover.FromToolchain(toolchain)
if toolVers == "" || (!strings.HasPrefix(toolchain, "go") && !strings.Contains(toolchain, "-go")) {
counterErrorInvalidToolchainInFile.Inc()
base.Fatalf("invalid toolchain %q in %s", toolchain, base.ShortPath(file))
}
if gover.Compare(toolVers, minVers) > 0 {
@ -230,9 +233,12 @@ func Select() {
base.Fatalf("invalid GOTOOLCHAIN %q", gotoolchain)
}
counterSelectExec.Inc()
Exec(gotoolchain)
}
var counterSelectExec = base.NewCounter("cmd/go/select-exec")
// TestVersionSwitch is set in the test go binary to the value in $TESTGO_VERSION_SWITCH.
// Valid settings are:
//

View File

@ -98,10 +98,13 @@ func (s *Switcher) Switch(ctx context.Context) {
}
fmt.Fprintf(os.Stderr, "go: %v requires go >= %v; switching to %v\n", s.TooNew.What, s.TooNew.GoVersion, tv)
counterSwitchExec.Inc()
Exec(tv)
panic("unreachable")
}
var counterSwitchExec = base.NewCounter("cmd/go/switch-exec")
// SwitchOrFatal attempts a toolchain switch based on the information in err
// and otherwise falls back to base.Fatal(err).
func SwitchOrFatal(ctx context.Context, err error) {

View File

@ -90,6 +90,8 @@ func init() {
var _ = go11tag
var counterErrorGOPATHEntryRelative = base.NewCounter("cmd/go/error:gopath-entry-relative")
func main() {
log.SetFlags(0)
TelemetryStart() // Open the telemetry counter file so counters can be written to it.
@ -107,6 +109,7 @@ func main() {
cfg.CmdName = args[0] // for error messages
if args[0] == "help" {
counter.Inc("cmd/go/subcommand:" + strings.Join(append([]string{"help"}, args[1:]...), "-"))
help.Help(os.Stdout, args[1:])
return
}
@ -145,6 +148,7 @@ func main() {
// Instead of dying, uninfer it.
cfg.BuildContext.GOPATH = ""
} else {
counterErrorGOPATHEntryRelative.Inc()
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nFor more details see: 'go help gopath'\n", p)
os.Exit(2)
}

View File

@ -410,6 +410,11 @@ func checkCounters(t *testing.T, telemetryDir string) {
}
})
counters := readCounters(t, telemetryDir)
if _, ok := scriptGoInvoked.Load(testing.TB(t)); ok {
if len(counters) == 0 {
t.Fatal("go was invoked but no counters were incremented")
}
}
for name := range counters {
if !allowedCounters[name] {
t.Fatalf("incremented counter %q is not in testdata/counters.txt. "+

View File

@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"strings"
"sync"
"time"
)
@ -69,9 +70,23 @@ func scriptCC(cmdExec script.Cmd) script.Cmd {
})
}
var scriptGoInvoked sync.Map // testing.TB → go command was invoked
// scriptGo runs the go command.
func scriptGo(cancel func(*exec.Cmd) error, waitDelay time.Duration) script.Cmd {
return script.Program(testGo, cancel, waitDelay)
cmd := script.Program(testGo, cancel, waitDelay)
// Inject code to update scriptGoInvoked before invoking the Go command.
return script.Command(*cmd.Usage(), func(state *script.State, s ...string) (script.WaitFunc, error) {
t, ok := tbFromContext(state.Context())
if !ok {
return nil, errors.New("script Context unexpectedly missing testing.TB key")
}
_, dup := scriptGoInvoked.LoadOrStore(t, true)
if !dup {
t.Cleanup(func() { scriptGoInvoked.Delete(t) })
}
return cmd.Run(state, s...)
})
}
// scriptStale checks that the named build targets are stale.

View File

@ -40,6 +40,7 @@ cmd/go/flag:test.v
cmd/go/flag:testsum
cmd/go/flag:testwork
cmd/go/flag:update
cmd/go/subcommand:help
cmd/go/subcommand:bug
cmd/go/flag:bug-C
cmd/go/flag:bug-v
@ -659,3 +660,9 @@ cmd/go/subcommand:help-private
cmd/go/subcommand:help-testflag
cmd/go/subcommand:help-testfunc
cmd/go/subcommand:help-vcs
cmd/go/error:gomodcache-entry-relative
cmd/go/error:gopath-entry-relative
cmd/go/error:help-unknown-topic
cmd/go/error:invalid-toolchain-in-file
cmd/go/select-exec
cmd/go/switch-exec