Skip to content

testing, cmd/go: dependencies in init functions are not recorded in the 'go test' cache key #44625

@aronatkins

Description

@aronatkins

What version of Go are you using (go version)?

$ go version
go version go1.16 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/aron/Library/Caches/go-build"
GOENV="/Users/aron/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/aron/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/aron/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.16"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/aron/go/src/foo/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/h8/bl1lkr792xz68jzf9s70jzwm0000gn/T/go-build2392571666=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

This is a three-package layout with foo, foo/a and foo/b. Both sub-packages have identical non-test code, but slightly different test code. The foo package uses code from a and b in identical ways. The foo package only has test code (to simplify the example; the real situation has code in all three packages).

  • foo - uses values initialized from environment variables in foo/a and foo/b
  • foo/a - uses value initialized from environment variable; test code contains additional environment variable read
  • foo/b - uses value initialized from environment variable
Code to exercise issue
// file: foo_test.go
package foo

import (
	"testing"

	"foo/a"
	"foo/b"
)

func TestA(t *testing.T) {
	cur := a.IsConfigured()
	if cur != a.Configured {
		t.Errorf("Configured = %v; want %v", cur, a.Configured)
	}
}

func TestB(t *testing.T) {
	cur := b.IsConfigured()
	if cur != b.Configured {
		t.Errorf("Configured = %v; want %v", cur, b.Configured)
	}
}
// file: foo/a.go
package a

import "os"

var Configured bool

func init() {
	Configured = os.Getenv("SETTING") == "ok"
}

func IsConfigured() bool {
	return Configured
}
// file: foo/a_test.go
package a

import "os"
import "testing"

func TestConfigured(t *testing.T) {
	raw := os.Getenv("SETTING")
	cur := IsConfigured()
	if cur != Configured {
		t.Errorf("Configured = %v; want %v; raw %v", cur, Configured, raw)
	}
}
// file: foo/b.go
package b

import "os"

var Configured bool

func init() {
	Configured = os.Getenv("SETTING") == "ok"
}

func IsConfigured() bool {
	return Configured
}
// file: b_test.go
package b

import "testing"

func TestConfigured(t *testing.T) {
	cur := IsConfigured()
	if cur != Configured {
		t.Errorf("Configured = %v; want %v", cur, Configured)
	}
}

Testing these packages with and without the SETTING environment variable uses a cached result for the foo and foo/b packages. The foo/a package, in contrast, runs each time SETTING is changed.

$ go clean -testcache
$ go test foo/...
ok  	foo	0.062s
ok  	foo/a	0.105s
ok  	foo/b	0.149s
$ SETTING=ok go test foo/...
ok  	foo	(cached)
ok  	foo/a	0.059s
ok  	foo/b	(cached)
$ SETTING=not-ok go test foo/...
ok  	foo	(cached)
ok  	foo/a	0.057s
ok  	foo/b	(cached)

What did you expect to see?

All packages that use SETTING, either directly or indirectly, should be run for each change to the SETTING value.

What did you see instead?

Only foo/a, with a read of SETTING from test code, is run each time SETTING is changed. The other packages are skipped with cached results.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoCommandcmd/goNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions