1 // Copyright 2019 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.
21 var gcflags string = os.Getenv("GO_GCFLAGS")
24 func TestMain(m *testing.M) {
26 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
27 fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
30 log.SetFlags(log.Lshortfile)
34 // tmpDir is used to cleanup logged commands -- s/tmpDir/$TMPDIR/
37 // prettyPrintf prints lines with tmpDir sanitized.
38 func prettyPrintf(format string, args ...interface{}) {
39 s := fmt.Sprintf(format, args...)
41 s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
46 func testMain(m *testing.M) int {
47 cwd, err := os.Getwd()
51 goroot = filepath.Join(cwd, "../../..")
53 // Copy testdata into GOPATH/src/testplugin, along with a go.mod file
54 // declaring the same path.
56 GOPATH, err := os.MkdirTemp("", "plugin_test")
60 defer os.RemoveAll(GOPATH)
63 modRoot := filepath.Join(GOPATH, "src", "testplugin")
64 altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
65 for srcRoot, dstRoot := range map[string]string{
67 filepath.Join("altpath", "testdata"): altRoot,
69 if err := overlayDir(dstRoot, srcRoot); err != nil {
72 prettyPrintf("mkdir -p %s\n", dstRoot)
73 prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
75 if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
78 prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
81 os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
82 if err := os.Chdir(altRoot); err != nil {
85 prettyPrintf("cd %s\n", altRoot)
87 os.Setenv("PWD", altRoot)
88 goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
90 os.Setenv("GOPATH", GOPATH)
91 if err := os.Chdir(modRoot); err != nil {
94 prettyPrintf("cd %s\n", modRoot)
96 os.Setenv("PWD", modRoot)
98 os.Setenv("LD_LIBRARY_PATH", modRoot)
100 goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
101 goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
102 so, err := os.ReadFile("plugin2.so")
106 if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
109 prettyPrintf("cp plugin2.so plugin2-dup.so\n")
111 goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1")
112 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go")
113 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go")
114 goCmd(nil, "build", "-o", "host.exe", "./host")
119 func goCmd(t *testing.T, op string, args ...string) {
123 run(t, filepath.Join(goroot, "bin", "go"), append([]string{op, "-gcflags", gcflags}, args...)...)
126 // escape converts a string to something suitable for a shell command line.
127 func escape(s string) string {
128 s = strings.Replace(s, "\\", "\\\\", -1)
129 s = strings.Replace(s, "'", "\\'", -1)
130 // Conservative guess at characters that will force quoting
131 if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
137 // asCommandLine renders cmd as something that could be copy-and-pasted into a command line
138 func asCommandLine(cwd string, cmd *exec.Cmd) string {
140 if cmd.Dir != "" && cmd.Dir != cwd {
141 s += "cd" + escape(cmd.Dir) + ";"
143 for _, e := range cmd.Env {
144 if !strings.HasPrefix(e, "PATH=") &&
145 !strings.HasPrefix(e, "HOME=") &&
146 !strings.HasPrefix(e, "USER=") &&
147 !strings.HasPrefix(e, "SHELL=") {
152 // These EVs are relevant to this test.
153 for _, e := range os.Environ() {
154 if strings.HasPrefix(e, "PWD=") ||
155 strings.HasPrefix(e, "GOPATH=") ||
156 strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
161 for _, a := range cmd.Args {
169 func run(t *testing.T, bin string, args ...string) string {
170 cmd := exec.Command(bin, args...)
171 cmdLine := asCommandLine(".", cmd)
172 prettyPrintf("%s\n", cmdLine)
173 cmd.Stderr = new(strings.Builder)
174 out, err := cmd.Output()
177 log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
180 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
184 return string(bytes.TrimSpace(out))
187 func TestDWARFSections(t *testing.T) {
188 // test that DWARF sections are emitted for plugins and programs importing "plugin"
189 goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
190 goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
193 func TestRunHost(t *testing.T) {
197 func TestUniqueTypesAndItabs(t *testing.T) {
198 goCmd(t, "build", "-buildmode=plugin", "./iface_a")
199 goCmd(t, "build", "-buildmode=plugin", "./iface_b")
200 goCmd(t, "build", "-o", "iface.exe", "./iface")
201 run(t, "./iface.exe")
204 func TestIssue18676(t *testing.T) {
205 // make sure we don't add the same itab twice.
206 // The buggy code hangs forever, so use a timeout to check for that.
207 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
208 goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
210 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
212 cmd := exec.CommandContext(ctx, "./issue18676.exe")
213 out, err := cmd.CombinedOutput()
215 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
219 func TestIssue19534(t *testing.T) {
220 // Test that we can load a plugin built in a path with non-alpha characters.
221 goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go")
222 goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
223 run(t, "./issue19534.exe")
226 func TestIssue18584(t *testing.T) {
227 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
228 goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
229 run(t, "./issue18584.exe")
232 func TestIssue19418(t *testing.T) {
233 goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
234 goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
235 run(t, "./issue19418.exe")
238 func TestIssue19529(t *testing.T) {
239 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
242 func TestIssue22175(t *testing.T) {
243 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
244 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
245 goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
246 run(t, "./issue22175.exe")
249 func TestIssue22295(t *testing.T) {
250 goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
251 goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
252 run(t, "./issue22295.exe")
255 func TestIssue24351(t *testing.T) {
256 goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
257 goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
258 run(t, "./issue24351.exe")
261 func TestIssue25756(t *testing.T) {
262 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
263 goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
264 // Fails intermittently, but 20 runs should cause the failure
265 for n := 20; n > 0; n-- {
266 t.Run(fmt.Sprint(n), func(t *testing.T) {
268 run(t, "./issue25756.exe")
273 // Test with main using -buildmode=pie with plugin for issue #43228
274 func TestIssue25756pie(t *testing.T) {
275 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
276 goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
277 run(t, "./issue25756pie.exe")
280 func TestMethod(t *testing.T) {
281 // Exported symbol's method must be live.
282 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
283 goCmd(t, "build", "-o", "method.exe", "./method/main.go")
284 run(t, "./method.exe")
287 func TestMethod2(t *testing.T) {
288 goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
289 goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
290 run(t, "./method2.exe")
293 func TestMethod3(t *testing.T) {
294 goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
295 goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
296 run(t, "./method3.exe")
299 func TestIssue44956(t *testing.T) {
300 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
301 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
302 goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
303 run(t, "./issue44956.exe")
306 func TestIssue52937(t *testing.T) {
307 goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go")
310 func TestForkExec(t *testing.T) {
311 // Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
314 goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
317 done := make(chan int, 1)
320 for i := 0; i < 100; i++ {
321 cmd = exec.Command("./forkexec.exe", "1")
324 t.Errorf("running command failed: %v", err)
332 case <-time.After(5 * time.Minute):
334 t.Fatalf("subprocess hang")