]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: make go test build multiple executables
authorMaksadbek Akhmedov <a.maksadbek@gmail.com>
Wed, 8 Feb 2023 13:58:04 +0000 (14:58 +0100)
committerGopher Robot <gobot@golang.org>
Mon, 27 Feb 2023 20:25:37 +0000 (20:25 +0000)
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 <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/cmd/go/internal/test/test.go
src/cmd/go/testdata/script/test_compile_multi_pkg.txt [new file with mode: 0644]

index 904144f2799ac2fc69d93d9e1d8970206c8a1b97..a986718abf707fdbf3c447a53341e4b1c017fa39 100644 (file)
@@ -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
@@ -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)
@@ -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()
 
@@ -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 {
@@ -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 {
@@ -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"
+}
diff --git a/src/cmd/go/testdata/script/test_compile_multi_pkg.txt b/src/cmd/go/testdata/script/test_compile_multi_pkg.txt
new file mode 100644 (file)
index 0000000..1f298b6
--- /dev/null
@@ -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
\ No newline at end of file