aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRuss Egan <russell.egan@thalesgroup.com>2026-03-05 00:46:20 +0000
committerGopher Robot <gobot@golang.org>2026-03-06 10:42:12 -0800
commit6c083034f82ddb2a91d3fbe0f96e39f1ecd194d8 (patch)
tree5adefb16c5e80b9bf0a94261f5dfcb713bd16253 /src
parent73db2f85aab25c06f47c832364600d2c5e243ffa (diff)
downloadgo-6c083034f82ddb2a91d3fbe0f96e39f1ecd194d8.tar.xz
testing: fix construction of the testing artifacts path
The existing implementation had a few problems: - It constructs a path which starts with a forward slash, which is then immediately rejected by filepath.Localize() as invalid. - It did not correctly remove the module path from the import path if the test was in the root package of the module: it would do the equivalent of strings.TrimPrefix("example.com/jdoe/loader", "example.com/jdoe/loader/"), which trims nothing, and leaves the full module name in the base. - Tests didn't check for any behaviors related to which artifact path was selected. Removed leading slash, handle tests in the root package by omitting the <pkg> component from the artifact dir path, and add tests. Fixes #77763 Change-Id: I00fc7381b939e4d8a8a9811e2a95fc2d8c5a6e84 GitHub-Last-Rev: 9c39392a691a81cef48adde88ab45dc40ed81105 GitHub-Pull-Request: golang/go#77778 Reviewed-on: https://go-review.googlesource.com/c/go/+/748581 Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Damien Neil <dneil@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/testing/testing.go42
-rw-r--r--src/testing/testing_test.go95
2 files changed, 121 insertions, 16 deletions
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 34b45b41b9..bf95f1cfbb 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -1363,6 +1363,21 @@ func (c *common) makeArtifactDir() (string, error) {
return c.makeTempDir()
}
+ artifactBase := filepath.Join(artifactDir, c.relativeArtifactBase())
+ if err := os.MkdirAll(artifactBase, 0o777); err != nil {
+ return "", err
+ }
+ dir, err := os.MkdirTemp(artifactBase, "")
+ if err != nil {
+ return "", err
+ }
+ if c.chatty != nil {
+ c.chatty.Updatef(c.name, "=== ARTIFACTS %s %v\n", c.name, dir)
+ }
+ return dir, nil
+}
+
+func (c *common) relativeArtifactBase() string {
// If the test name is longer than maxNameSize, truncate it and replace the last
// hashSize bytes with a hash of the full name.
const maxNameSize = 64
@@ -1373,11 +1388,17 @@ func (c *common) makeArtifactDir() (string, error) {
}
// Remove the module path prefix from the import path.
- pkg := strings.TrimPrefix(c.importPath, c.modulePath+"/")
+ // If this is the root package, pkg will be empty.
+ pkg := strings.TrimPrefix(c.importPath, c.modulePath)
+ // Remove the leading slash.
+ pkg = strings.TrimPrefix(pkg, "/")
- // Join with /, not filepath.Join: the import path is /-separated,
- // and we don't want removeSymbolsExcept to strip \ separators on Windows.
- base := "/" + pkg + "/" + name
+ base := name
+ if pkg != "" {
+ // Join with /, not filepath.Join: the import path is /-separated,
+ // and we don't want removeSymbolsExcept to strip \ separators on Windows.
+ base = pkg + "/" + name
+ }
base = removeSymbolsExcept(base, "!#$%&()+,-.=@^_{}~ /")
base, err := filepath.Localize(base)
if err != nil {
@@ -1386,18 +1407,7 @@ func (c *common) makeArtifactDir() (string, error) {
base = ""
}
- artifactBase := filepath.Join(artifactDir, base)
- if err := os.MkdirAll(artifactBase, 0o777); err != nil {
- return "", err
- }
- dir, err := os.MkdirTemp(artifactBase, "")
- if err != nil {
- return "", err
- }
- if c.chatty != nil {
- c.chatty.Updatef(c.name, "=== ARTIFACTS %s %v\n", c.name, dir)
- }
- return dir, nil
+ return base
}
func removeSymbolsExcept(s, allowed string) string {
diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go
index 167f4a0b45..afa3c267df 100644
--- a/src/testing/testing_test.go
+++ b/src/testing/testing_test.go
@@ -1123,6 +1123,101 @@ func TestArtifactDirConsistent(t *testing.T) {
}
}
+func TestArtifactDirectoryPaths(t *testing.T) {
+ testenv.MustHaveExec(t)
+ t.Parallel()
+
+ tempDir := t.TempDir()
+
+ // 1. Setup the temporary module.
+ if err := os.WriteFile(filepath.Join(tempDir, "go.mod"), []byte("module example.com/testmod\n"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ writeTmpl := func(pkgDir string, content string) {
+ fullDir := filepath.Join(tempDir, pkgDir)
+ if err := os.MkdirAll(fullDir, 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(fullDir, "a_test.go"), []byte(content), 0666); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Root package test
+ writeTmpl(".", `package root_test
+import "testing"
+func TestRootNiceName(t *testing.T) { t.ArtifactDir() }
+`)
+
+ // Subpackage test
+ writeTmpl("subpkg", `package subpkg_test
+import "testing"
+func TestSubNiceName(t *testing.T) { t.ArtifactDir() }
+`)
+
+ // Deep subpackage test with various scenarios
+ writeTmpl("deep/nested/pkg", `package pkg_test
+import "testing"
+func TestNiceName(t *testing.T) { t.ArtifactDir() }
+func TestParent(t *testing.T) {
+ t.Run("SubTest", func(t *testing.T) { t.ArtifactDir() })
+}
+func TestVeryLongNameThatExceedsSixtyFourCharactersAndThereforeMustBeTruncatedAndHashed(t *testing.T) { t.ArtifactDir() }
+func TestInvalid_Chars_In_Name(t *testing.T) { t.ArtifactDir() }
+`)
+ // We use "TestInvalid_Chars_In_Name" because go test framework parses functions by ^Test.
+ // But let's actually make it have invalid path chars:
+ writeTmpl("deep/nested/pkg2", `package pkg2_test
+import "testing"
+func TestInvalid_Chars_In_Name(t *testing.T) {
+ t.Run("SubTest:with*stars", func(t *testing.T) { t.ArtifactDir() })
+}
+`)
+
+ // 2. Run the tests.
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "test", "-v", "-artifacts", "./...")
+ cmd.Dir = tempDir
+ cmd = testenv.CleanCmdEnv(cmd)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v failed: %v\n%s", cmd, err, out)
+ }
+
+ // 3. Parse and verify output.
+ tests := []struct {
+ name string
+ wantPrefix string
+ }{
+ // Root package tests don't have a package prefix because modulePath == importPath
+ {"TestRootNiceName", "TestRootNiceName"},
+ {"TestSubNiceName", "subpkg/TestSubNiceName"},
+ {"TestNiceName", "deep/nested/pkg/TestNiceName"},
+ {"TestParent/SubTest", "deep/nested/pkg/TestParent__SubTest"},
+ {"TestVeryLongNameThatExceedsSixtyFourCharactersAndThereforeMustBeTruncatedAndHashed", `deep/nested/pkg/TestVeryLongNameThatExceedsSixtyFourCharactersAn[0-9a-f]+`},
+ {`TestInvalid_Chars_In_Name/SubTest:with\*stars`, `deep/nested/pkg2/TestInvalid_Chars_In_Name__SubTestwithstars`},
+ }
+
+ for _, tt := range tests {
+ re := regexp.MustCompile(`=== ARTIFACTS ` + tt.name + ` ([^\n]+)`)
+ match := re.FindSubmatch(out)
+ if match == nil {
+ t.Errorf("expected output matching %q, got\n%q", re, out)
+ continue
+ }
+ artifactDir := string(match[1])
+
+ slashDir := filepath.ToSlash(artifactDir)
+
+ wantSuffixPattern := `_artifacts/` + tt.wantPrefix + `/[^/]+$`
+ wantRegex := regexp.MustCompile(wantSuffixPattern)
+
+ if !wantRegex.MatchString(slashDir) {
+ t.Errorf("artifact directory for %s: got %s, want suffix matching %s", tt.name, slashDir, wantSuffixPattern)
+ }
+ }
+}
+
func checkArtifactDir(t *testing.T, out []byte, testName, outputDir string) {
t.Helper()