Source file src/cmd/internal/script/scripttest/setup.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package scripttest adapts the script engine for use in tests.
     6  package scripttest
     7  
     8  import (
     9  	"internal/testenv"
    10  	"io"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  )
    18  
    19  // SetupTestGoRoot sets up a temporary GOROOT for use with script test
    20  // execution. It copies the existing goroot bin and pkg dirs using
    21  // symlinks (if possible) or raw copying. Return value is the path to
    22  // the newly created testgoroot dir.
    23  func SetupTestGoRoot(t *testing.T, tmpdir string, goroot string) string {
    24  	mustMkdir := func(path string) {
    25  		if err := os.MkdirAll(path, 0777); err != nil {
    26  			t.Fatalf("SetupTestGoRoot mkdir %s failed: %v", path, err)
    27  		}
    28  	}
    29  
    30  	replicateDir := func(srcdir, dstdir string) {
    31  		files, err := os.ReadDir(srcdir)
    32  		if err != nil {
    33  			t.Fatalf("inspecting %s: %v", srcdir, err)
    34  		}
    35  		for _, file := range files {
    36  			fn := file.Name()
    37  			linkOrCopy(t, filepath.Join(srcdir, fn), filepath.Join(dstdir, fn))
    38  		}
    39  	}
    40  
    41  	// Create various dirs in testgoroot.
    42  	findToolOnce.Do(func() { findToolSub(t) })
    43  	if toolsub == "" {
    44  		t.Fatal("failed to find toolsub")
    45  	}
    46  
    47  	tomake := []string{
    48  		"bin",
    49  		"src",
    50  		"pkg",
    51  		filepath.Join("pkg", "include"),
    52  		toolsub,
    53  	}
    54  	made := []string{}
    55  	tgr := filepath.Join(tmpdir, "testgoroot")
    56  	mustMkdir(tgr)
    57  	for _, targ := range tomake {
    58  		path := filepath.Join(tgr, targ)
    59  		mustMkdir(path)
    60  		made = append(made, path)
    61  	}
    62  
    63  	// Replicate selected portions of the content.
    64  	replicateDir(filepath.Join(goroot, "bin"), made[0])
    65  	replicateDir(filepath.Join(goroot, "src"), made[1])
    66  	replicateDir(filepath.Join(goroot, "pkg", "include"), made[3])
    67  	replicateDir(filepath.Join(goroot, toolsub), made[4])
    68  
    69  	return tgr
    70  }
    71  
    72  // ReplaceGoToolInTestGoRoot replaces the go tool binary toolname with
    73  // an alternate executable newtoolpath within a test GOROOT directory
    74  // previously created by SetupTestGoRoot.
    75  func ReplaceGoToolInTestGoRoot(t *testing.T, testgoroot, toolname, newtoolpath string) {
    76  	findToolOnce.Do(func() { findToolSub(t) })
    77  	if toolsub == "" {
    78  		t.Fatal("failed to find toolsub")
    79  	}
    80  
    81  	exename := toolname
    82  	if runtime.GOOS == "windows" {
    83  		exename += ".exe"
    84  	}
    85  	toolpath := filepath.Join(testgoroot, toolsub, exename)
    86  	if err := os.Remove(toolpath); err != nil {
    87  		t.Fatalf("removing %s: %v", toolpath, err)
    88  	}
    89  	linkOrCopy(t, newtoolpath, toolpath)
    90  }
    91  
    92  // toolsub is the tool subdirectory underneath GOROOT.
    93  var toolsub string
    94  
    95  // findToolOnce runs findToolSub only once.
    96  var findToolOnce sync.Once
    97  
    98  // findToolSub sets toolsub to the value used by the current go command.
    99  func findToolSub(t *testing.T) {
   100  	gocmd := testenv.Command(t, testenv.GoToolPath(t), "env", "GOHOSTARCH")
   101  	gocmd = testenv.CleanCmdEnv(gocmd)
   102  	goHostArchBytes, err := gocmd.CombinedOutput()
   103  	if err != nil {
   104  		t.Fatalf("%s failed: %v\n%s", gocmd, err, goHostArchBytes)
   105  	}
   106  	goHostArch := strings.TrimSpace(string(goHostArchBytes))
   107  	toolsub = filepath.Join("pkg", "tool", runtime.GOOS+"_"+goHostArch)
   108  }
   109  
   110  // linkOrCopy creates a link to src at dst, or if the symlink fails
   111  // (platform doesn't support) then copies src to dst.
   112  func linkOrCopy(t *testing.T, src, dst string) {
   113  	err := os.Symlink(src, dst)
   114  	if err == nil {
   115  		return
   116  	}
   117  	srcf, err := os.Open(src)
   118  	if err != nil {
   119  		t.Fatalf("copying %s to %s: %v", src, dst, err)
   120  	}
   121  	defer srcf.Close()
   122  	perm := os.O_WRONLY | os.O_CREATE | os.O_EXCL
   123  	dstf, err := os.OpenFile(dst, perm, 0o777)
   124  	if err != nil {
   125  		t.Fatalf("copying %s to %s: %v", src, dst, err)
   126  	}
   127  	_, err = io.Copy(dstf, srcf)
   128  	if closeErr := dstf.Close(); err == nil {
   129  		err = closeErr
   130  	}
   131  	if err != nil {
   132  		t.Fatalf("copying %s to %s: %v", src, dst, err)
   133  	}
   134  }
   135  

View as plain text