| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package scripttest adapts the script engine for use in tests. |
| package scripttest |
| |
| import ( |
| "internal/testenv" |
| "io" |
| "os" |
| "path/filepath" |
| "runtime" |
| "strings" |
| "sync" |
| "testing" |
| ) |
| |
| // SetupTestGoRoot sets up a temporary GOROOT for use with script test |
| // execution. It copies the existing goroot bin and pkg dirs using |
| // symlinks (if possible) or raw copying. Return value is the path to |
| // the newly created testgoroot dir. |
| func SetupTestGoRoot(t *testing.T, tmpdir string, goroot string) string { |
| mustMkdir := func(path string) { |
| if err := os.MkdirAll(path, 0777); err != nil { |
| t.Fatalf("SetupTestGoRoot mkdir %s failed: %v", path, err) |
| } |
| } |
| |
| replicateDir := func(srcdir, dstdir string) { |
| files, err := os.ReadDir(srcdir) |
| if err != nil { |
| t.Fatalf("inspecting %s: %v", srcdir, err) |
| } |
| for _, file := range files { |
| fn := file.Name() |
| linkOrCopy(t, filepath.Join(srcdir, fn), filepath.Join(dstdir, fn)) |
| } |
| } |
| |
| // Create various dirs in testgoroot. |
| findToolOnce.Do(func() { findToolSub(t) }) |
| if toolsub == "" { |
| t.Fatal("failed to find toolsub") |
| } |
| |
| tomake := []string{ |
| "bin", |
| "src", |
| "pkg", |
| filepath.Join("pkg", "include"), |
| toolsub, |
| } |
| made := []string{} |
| tgr := filepath.Join(tmpdir, "testgoroot") |
| mustMkdir(tgr) |
| for _, targ := range tomake { |
| path := filepath.Join(tgr, targ) |
| mustMkdir(path) |
| made = append(made, path) |
| } |
| |
| // Replicate selected portions of the content. |
| replicateDir(filepath.Join(goroot, "bin"), made[0]) |
| replicateDir(filepath.Join(goroot, "src"), made[1]) |
| replicateDir(filepath.Join(goroot, "pkg", "include"), made[3]) |
| replicateDir(filepath.Join(goroot, toolsub), made[4]) |
| |
| return tgr |
| } |
| |
| // ReplaceGoToolInTestGoRoot replaces the go tool binary toolname with |
| // an alternate executable newtoolpath within a test GOROOT directory |
| // previously created by SetupTestGoRoot. |
| func ReplaceGoToolInTestGoRoot(t *testing.T, testgoroot, toolname, newtoolpath string) { |
| findToolOnce.Do(func() { findToolSub(t) }) |
| if toolsub == "" { |
| t.Fatal("failed to find toolsub") |
| } |
| |
| exename := toolname |
| if runtime.GOOS == "windows" { |
| exename += ".exe" |
| } |
| toolpath := filepath.Join(testgoroot, toolsub, exename) |
| if err := os.Remove(toolpath); err != nil { |
| t.Fatalf("removing %s: %v", toolpath, err) |
| } |
| linkOrCopy(t, newtoolpath, toolpath) |
| } |
| |
| // toolsub is the tool subdirectory underneath GOROOT. |
| var toolsub string |
| |
| // findToolOnce runs findToolSub only once. |
| var findToolOnce sync.Once |
| |
| // findToolSub sets toolsub to the value used by the current go command. |
| func findToolSub(t *testing.T) { |
| gocmd := testenv.Command(t, testenv.GoToolPath(t), "env", "GOHOSTARCH") |
| gocmd = testenv.CleanCmdEnv(gocmd) |
| goHostArchBytes, err := gocmd.CombinedOutput() |
| if err != nil { |
| t.Fatalf("%s failed: %v\n%s", gocmd, err, goHostArchBytes) |
| } |
| goHostArch := strings.TrimSpace(string(goHostArchBytes)) |
| toolsub = filepath.Join("pkg", "tool", runtime.GOOS+"_"+goHostArch) |
| } |
| |
| // linkOrCopy creates a link to src at dst, or if the symlink fails |
| // (platform doesn't support) then copies src to dst. |
| func linkOrCopy(t *testing.T, src, dst string) { |
| err := os.Symlink(src, dst) |
| if err == nil { |
| return |
| } |
| fi, err := os.Stat(src) |
| if err != nil { |
| t.Fatalf("copying %s to %s: %v", src, dst, err) |
| } |
| if fi.IsDir() { |
| if err := os.CopyFS(dst, os.DirFS(src)); err != nil { |
| t.Fatalf("copying %s to %s: %v", src, dst, err) |
| } |
| return |
| } |
| srcf, err := os.Open(src) |
| if err != nil { |
| t.Fatalf("copying %s to %s: %v", src, dst, err) |
| } |
| defer srcf.Close() |
| perm := os.O_WRONLY | os.O_CREATE | os.O_EXCL |
| dstf, err := os.OpenFile(dst, perm, 0o777) |
| if err != nil { |
| t.Fatalf("copying %s to %s: %v", src, dst, err) |
| } |
| _, err = io.Copy(dstf, srcf) |
| if closeErr := dstf.Close(); err == nil { |
| err = closeErr |
| } |
| if err != nil { |
| t.Fatalf("copying %s to %s: %v", src, dst, err) |
| } |
| } |