]> Cypherpunks.ru repositories - gostls13.git/blob - misc/cgo/testplugin/plugin_test.go
8960694351738580dfd8e07538f8f7124d733b91
[gostls13.git] / misc / cgo / testplugin / plugin_test.go
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.
4
5 package plugin_test
6
7 import (
8         "bytes"
9         "context"
10         "flag"
11         "fmt"
12         "log"
13         "os"
14         "os/exec"
15         "path/filepath"
16         "strings"
17         "testing"
18         "time"
19 )
20
21 var gcflags string = os.Getenv("GO_GCFLAGS")
22 var goroot string
23
24 func TestMain(m *testing.M) {
25         flag.Parse()
26         if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
27                 fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
28                 os.Exit(0)
29         }
30         log.SetFlags(log.Lshortfile)
31         os.Exit(testMain(m))
32 }
33
34 // tmpDir is used to cleanup logged commands -- s/tmpDir/$TMPDIR/
35 var tmpDir string
36
37 // prettyPrintf prints lines with tmpDir sanitized.
38 func prettyPrintf(format string, args ...interface{}) {
39         s := fmt.Sprintf(format, args...)
40         if tmpDir != "" {
41                 s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
42         }
43         fmt.Print(s)
44 }
45
46 func testMain(m *testing.M) int {
47         cwd, err := os.Getwd()
48         if err != nil {
49                 log.Fatal(err)
50         }
51         goroot = filepath.Join(cwd, "../../..")
52
53         // Copy testdata into GOPATH/src/testplugin, along with a go.mod file
54         // declaring the same path.
55
56         GOPATH, err := os.MkdirTemp("", "plugin_test")
57         if err != nil {
58                 log.Panic(err)
59         }
60         defer os.RemoveAll(GOPATH)
61         tmpDir = GOPATH
62
63         modRoot := filepath.Join(GOPATH, "src", "testplugin")
64         altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
65         for srcRoot, dstRoot := range map[string]string{
66                 "testdata":                           modRoot,
67                 filepath.Join("altpath", "testdata"): altRoot,
68         } {
69                 if err := overlayDir(dstRoot, srcRoot); err != nil {
70                         log.Panic(err)
71                 }
72                 prettyPrintf("mkdir -p %s\n", dstRoot)
73                 prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
74
75                 if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
76                         log.Panic(err)
77                 }
78                 prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
79         }
80
81         os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
82         if err := os.Chdir(altRoot); err != nil {
83                 log.Panic(err)
84         } else {
85                 prettyPrintf("cd %s\n", altRoot)
86         }
87         os.Setenv("PWD", altRoot)
88         goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
89
90         os.Setenv("GOPATH", GOPATH)
91         if err := os.Chdir(modRoot); err != nil {
92                 log.Panic(err)
93         } else {
94                 prettyPrintf("cd %s\n", modRoot)
95         }
96         os.Setenv("PWD", modRoot)
97
98         os.Setenv("LD_LIBRARY_PATH", modRoot)
99
100         goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
101         goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
102         so, err := os.ReadFile("plugin2.so")
103         if err != nil {
104                 log.Panic(err)
105         }
106         if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
107                 log.Panic(err)
108         }
109         prettyPrintf("cp plugin2.so plugin2-dup.so\n")
110
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")
115
116         return m.Run()
117 }
118
119 func goCmd(t *testing.T, op string, args ...string) string {
120         if t != nil {
121                 t.Helper()
122         }
123         var flags []string
124         if op != "tool" {
125                 flags = []string{"-gcflags", gcflags}
126         }
127         return run(t, filepath.Join(goroot, "bin", "go"), append(append([]string{op}, flags...), args...)...)
128 }
129
130 // escape converts a string to something suitable for a shell command line.
131 func escape(s string) string {
132         s = strings.Replace(s, "\\", "\\\\", -1)
133         s = strings.Replace(s, "'", "\\'", -1)
134         // Conservative guess at characters that will force quoting
135         if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
136                 s = "'" + s + "'"
137         }
138         return s
139 }
140
141 // asCommandLine renders cmd as something that could be copy-and-pasted into a command line
142 func asCommandLine(cwd string, cmd *exec.Cmd) string {
143         s := "("
144         if cmd.Dir != "" && cmd.Dir != cwd {
145                 s += "cd" + escape(cmd.Dir) + ";"
146         }
147         for _, e := range cmd.Env {
148                 if !strings.HasPrefix(e, "PATH=") &&
149                         !strings.HasPrefix(e, "HOME=") &&
150                         !strings.HasPrefix(e, "USER=") &&
151                         !strings.HasPrefix(e, "SHELL=") {
152                         s += " "
153                         s += escape(e)
154                 }
155         }
156         // These EVs are relevant to this test.
157         for _, e := range os.Environ() {
158                 if strings.HasPrefix(e, "PWD=") ||
159                         strings.HasPrefix(e, "GOPATH=") ||
160                         strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
161                         s += " "
162                         s += escape(e)
163                 }
164         }
165         for _, a := range cmd.Args {
166                 s += " "
167                 s += escape(a)
168         }
169         s += " )"
170         return s
171 }
172
173 func run(t *testing.T, bin string, args ...string) string {
174         cmd := exec.Command(bin, args...)
175         cmdLine := asCommandLine(".", cmd)
176         prettyPrintf("%s\n", cmdLine)
177         cmd.Stderr = new(strings.Builder)
178         out, err := cmd.Output()
179         if err != nil {
180                 if t == nil {
181                         log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
182                 } else {
183                         t.Helper()
184                         t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
185                 }
186         }
187
188         return string(bytes.TrimSpace(out))
189 }
190
191 func TestDWARFSections(t *testing.T) {
192         // test that DWARF sections are emitted for plugins and programs importing "plugin"
193         goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
194         goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
195 }
196
197 func TestBuildID(t *testing.T) {
198         // check that plugin has build ID.
199         b := goCmd(t, "tool", "buildid", "plugin1.so")
200         if len(b) == 0 {
201                 t.Errorf("build id not found")
202         }
203 }
204
205 func TestRunHost(t *testing.T) {
206         run(t, "./host.exe")
207 }
208
209 func TestUniqueTypesAndItabs(t *testing.T) {
210         goCmd(t, "build", "-buildmode=plugin", "./iface_a")
211         goCmd(t, "build", "-buildmode=plugin", "./iface_b")
212         goCmd(t, "build", "-o", "iface.exe", "./iface")
213         run(t, "./iface.exe")
214 }
215
216 func TestIssue18676(t *testing.T) {
217         // make sure we don't add the same itab twice.
218         // The buggy code hangs forever, so use a timeout to check for that.
219         goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
220         goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
221
222         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
223         defer cancel()
224         cmd := exec.CommandContext(ctx, "./issue18676.exe")
225         out, err := cmd.CombinedOutput()
226         if err != nil {
227                 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
228         }
229 }
230
231 func TestIssue19534(t *testing.T) {
232         // Test that we can load a plugin built in a path with non-alpha characters.
233         goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go")
234         goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
235         run(t, "./issue19534.exe")
236 }
237
238 func TestIssue18584(t *testing.T) {
239         goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
240         goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
241         run(t, "./issue18584.exe")
242 }
243
244 func TestIssue19418(t *testing.T) {
245         goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
246         goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
247         run(t, "./issue19418.exe")
248 }
249
250 func TestIssue19529(t *testing.T) {
251         goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
252 }
253
254 func TestIssue22175(t *testing.T) {
255         goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
256         goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
257         goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
258         run(t, "./issue22175.exe")
259 }
260
261 func TestIssue22295(t *testing.T) {
262         goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
263         goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
264         run(t, "./issue22295.exe")
265 }
266
267 func TestIssue24351(t *testing.T) {
268         goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
269         goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
270         run(t, "./issue24351.exe")
271 }
272
273 func TestIssue25756(t *testing.T) {
274         goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
275         goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
276         // Fails intermittently, but 20 runs should cause the failure
277         for n := 20; n > 0; n-- {
278                 t.Run(fmt.Sprint(n), func(t *testing.T) {
279                         t.Parallel()
280                         run(t, "./issue25756.exe")
281                 })
282         }
283 }
284
285 // Test with main using -buildmode=pie with plugin for issue #43228
286 func TestIssue25756pie(t *testing.T) {
287         goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
288         goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
289         run(t, "./issue25756pie.exe")
290 }
291
292 func TestMethod(t *testing.T) {
293         // Exported symbol's method must be live.
294         goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
295         goCmd(t, "build", "-o", "method.exe", "./method/main.go")
296         run(t, "./method.exe")
297 }
298
299 func TestMethod2(t *testing.T) {
300         goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
301         goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
302         run(t, "./method2.exe")
303 }
304
305 func TestMethod3(t *testing.T) {
306         goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
307         goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
308         run(t, "./method3.exe")
309 }
310
311 func TestIssue44956(t *testing.T) {
312         goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
313         goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
314         goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
315         run(t, "./issue44956.exe")
316 }
317
318 func TestIssue52937(t *testing.T) {
319         goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go")
320 }
321
322 func TestIssue53989(t *testing.T) {
323         goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go")
324         goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go")
325         run(t, "./issue53989.exe")
326 }
327
328 func TestForkExec(t *testing.T) {
329         // Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
330
331         t.Parallel()
332         goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
333
334         var cmd *exec.Cmd
335         done := make(chan int, 1)
336
337         go func() {
338                 for i := 0; i < 100; i++ {
339                         cmd = exec.Command("./forkexec.exe", "1")
340                         err := cmd.Run()
341                         if err != nil {
342                                 t.Errorf("running command failed: %v", err)
343                                 break
344                         }
345                 }
346                 done <- 1
347         }()
348         select {
349         case <-done:
350         case <-time.After(5 * time.Minute):
351                 cmd.Process.Kill()
352                 t.Fatalf("subprocess hang")
353         }
354 }