File size: 3,997 Bytes
e36aeda
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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)
	}
}