Skip to content

Commit

Permalink
cmd/go: make go test build multiple executables
Browse files Browse the repository at this point in the history
If -c is set while testing multiple packages, then allow
to build testing binary executables to the current directory
or to the directory that -o refers to.

$ go test -c -o /tmp ./pkg1 ./pkg2 ./pkg2
$ ls /tmp
pkg1.test pkg2.test pkg3.test

Fixes #15513.

Change-Id: I3aba01bebfa90e61e59276f2832d99c0d323b82e
Reviewed-on: https://go-review.googlesource.com/c/go/+/466397
Run-TryBot: Bryan Mills <[email protected]>
Auto-Submit: Bryan Mills <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Bryan Mills <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
  • Loading branch information
maksadbek authored and gopherbot committed Feb 27, 2023
1 parent 1a5cf03 commit b611b3a
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 21 deletions.
91 changes: 70 additions & 21 deletions src/cmd/go/internal/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,8 @@ var (
testCacheExpire time.Time // ignore cached test results before this time

testBlockProfile, testCPUProfile, testMemProfile, testMutexProfile, testTrace string // profiling flag that limits test to one package

testODir = false
)

// testProfile returns the name of an arbitrary single-package profiling flag
Expand Down Expand Up @@ -694,12 +696,6 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
base.Fatalf("no packages to test")
}

if testC && len(pkgs) != 1 {
base.Fatalf("cannot use -c flag with multiple packages")
}
if testO != "" && len(pkgs) != 1 {
base.Fatalf("cannot use -o flag with multiple packages")
}
if testFuzz != "" {
if !platform.FuzzSupported(cfg.Goos, cfg.Goarch) {
base.Fatalf("-fuzz flag is not supported on %s/%s", cfg.Goos, cfg.Goarch)
Expand Down Expand Up @@ -749,6 +745,42 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) {
if testProfile() != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile())
}

if testO != "" {
if strings.HasSuffix(testO, "/") || strings.HasSuffix(testO, string(os.PathSeparator)) {
testODir = true
} else if fi, err := os.Stat(testO); err == nil && fi.IsDir() {
testODir = true
}
}

if len(pkgs) > 1 && (testC || testO != "") && !base.IsNull(testO) {
if testO != "" && !testODir {
base.Fatalf("with multiple packages, -o must refer to a directory or %s", os.DevNull)
}

pkgsForBinary := map[string][]*load.Package{}

for _, p := range pkgs {
testBinary := testBinaryName(p)
pkgsForBinary[testBinary] = append(pkgsForBinary[testBinary], p)
}

for testBinary, pkgs := range pkgsForBinary {
if len(pkgs) > 1 {
var buf strings.Builder
for _, pkg := range pkgs {
buf.WriteString(pkg.ImportPath)
buf.WriteString("\n")
}

base.Errorf("cannot write test binary %s for multiple packages:\n%s", testBinary, buf.String())
}
}

base.ExitIfErrors()
}

initCoverProfile()
defer closeCoverProfile()

Expand Down Expand Up @@ -978,17 +1010,7 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
buildTest.Deps = append(buildTest.Deps, buildP)
}

// Use last element of import path, not package name.
// They differ when package name is "main".
// But if the import path is "command-line-arguments",
// like it is during 'go run', use the package name.
var elem string
if p.ImportPath == "command-line-arguments" {
elem = p.Name
} else {
elem = p.DefaultExecName()
}
testBinary := elem + ".test"
testBinary := testBinaryName(p)

testDir := b.NewObjdir()
if err := b.Mkdir(testDir); err != nil {
Expand Down Expand Up @@ -1048,14 +1070,25 @@ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts,
// -c or profiling flag: create action to copy binary to ./test.out.
target := filepath.Join(base.Cwd(), testBinary+cfg.ExeSuffix)
isNull := false

if testO != "" {
target = testO
if base.IsNull(target) {
isNull = true
} else if !filepath.IsAbs(target) {
target = filepath.Join(base.Cwd(), target)

if testODir {
if filepath.IsAbs(target) {
target = filepath.Join(target, testBinary+cfg.ExeSuffix)
} else {
target = filepath.Join(base.Cwd(), target, testBinary+cfg.ExeSuffix)
}
} else {
if base.IsNull(target) {
isNull = true
} else if !filepath.IsAbs(target) {
target = filepath.Join(base.Cwd(), target)
}
}
}

if isNull {
runAction = buildAction
} else {
Expand Down Expand Up @@ -1862,3 +1895,19 @@ func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error
}
return nil
}

// testBinaryName can be used to create name for test binary executable.
// Use last element of import path, not package name.
// They differ when package name is "main".
// But if the import path is "command-line-arguments",
// like it is during 'go run', use the package name.
func testBinaryName(p *load.Package) string {
var elem string
if p.ImportPath == "command-line-arguments" {
elem = p.Name
} else {
elem = p.DefaultExecName()
}

return elem + ".test"
}
46 changes: 46 additions & 0 deletions src/cmd/go/testdata/script/test_compile_multi_pkg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
[short] skip 'links test binaries'

# Verify test -c can output multiple executables to a directory.

go test -c -o $WORK/some/nonexisting/directory/ ./pkg/...
exists -exec $WORK/some/nonexisting/directory/pkg1.test$GOEXE
exists -exec $WORK/some/nonexisting/directory/pkg2.test$GOEXE

go test -c ./pkg/...
exists -exec pkg1.test$GOEXE
exists -exec pkg2.test$GOEXE

! go test -c -o $WORK/bin/test/bin.test.exe ./pkg/...
stderr '^with multiple packages, -o must refer to a directory or '$devnull

! go test -c ./...
stderr '^cannot write test binary pkg1.test for multiple packages:\nexample/anotherpkg/pkg1\nexample/pkg/pkg1'

! go test -c -o $WORK/bin/test/ ./...
stderr '^cannot write test binary pkg1.test for multiple packages:\nexample/anotherpkg/pkg1\nexample/pkg/pkg1'

! go test -o $WORK/bin/filename.exe ./pkg/...
stderr '^with multiple packages, -o must refer to a directory or '$devnull

! go test -o $WORK/bin/ ./...
stderr '^cannot write test binary pkg1.test for multiple packages:\nexample/anotherpkg/pkg1\nexample/pkg/pkg1'

go test -c -o $devnull ./...

rm pkg1.test$GOEXE
rm pkg2.test$GOEXE
go test -o . ./pkg/...
exists -exec pkg1.test$GOEXE
exists -exec pkg2.test$GOEXE

-- go.mod --
module example

-- pkg/pkg1/pkg1_test.go --
package pkg1

-- pkg/pkg2/pkg2_test.go --
package pkg2

-- anotherpkg/pkg1/pkg1_test.go --
package pkg1

0 comments on commit b611b3a

Please sign in to comment.