1
2
3
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
23 func TestMain(m *testing.M) {
24 flag.Parse()
25 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
26 fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
27 os.Exit(0)
28 }
29 log.SetFlags(log.Lshortfile)
30 os.Exit(testMain(m))
31 }
32
33
34 var tmpDir string
35
36
37 func prettyPrintf(format string, args ...interface{}) {
38 s := fmt.Sprintf(format, args...)
39 if tmpDir != "" {
40 s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
41 }
42 fmt.Print(s)
43 }
44
45 func testMain(m *testing.M) int {
46
47
48
49 GOPATH, err := os.MkdirTemp("", "plugin_test")
50 if err != nil {
51 log.Panic(err)
52 }
53 defer os.RemoveAll(GOPATH)
54 tmpDir = GOPATH
55
56 modRoot := filepath.Join(GOPATH, "src", "testplugin")
57 altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
58 for srcRoot, dstRoot := range map[string]string{
59 "testdata": modRoot,
60 filepath.Join("altpath", "testdata"): altRoot,
61 } {
62 if err := overlayDir(dstRoot, srcRoot); err != nil {
63 log.Panic(err)
64 }
65 prettyPrintf("mkdir -p %s\n", dstRoot)
66 prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
67
68 if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
69 log.Panic(err)
70 }
71 prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
72 }
73
74 os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
75 if err := os.Chdir(altRoot); err != nil {
76 log.Panic(err)
77 } else {
78 prettyPrintf("cd %s\n", altRoot)
79 }
80 os.Setenv("PWD", altRoot)
81 goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
82
83 os.Setenv("GOPATH", GOPATH)
84 if err := os.Chdir(modRoot); err != nil {
85 log.Panic(err)
86 } else {
87 prettyPrintf("cd %s\n", modRoot)
88 }
89 os.Setenv("PWD", modRoot)
90
91 os.Setenv("LD_LIBRARY_PATH", modRoot)
92
93 goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
94 goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
95 so, err := os.ReadFile("plugin2.so")
96 if err != nil {
97 log.Panic(err)
98 }
99 if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
100 log.Panic(err)
101 }
102 prettyPrintf("cp plugin2.so plugin2-dup.so\n")
103
104 goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1")
105 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go")
106 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go")
107 goCmd(nil, "build", "-o", "host.exe", "./host")
108
109 return m.Run()
110 }
111
112 func goCmd(t *testing.T, op string, args ...string) {
113 if t != nil {
114 t.Helper()
115 }
116 run(t, "go", append([]string{op, "-gcflags", gcflags}, args...)...)
117 }
118
119
120 func escape(s string) string {
121 s = strings.Replace(s, "\\", "\\\\", -1)
122 s = strings.Replace(s, "'", "\\'", -1)
123
124 if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
125 s = "'" + s + "'"
126 }
127 return s
128 }
129
130
131 func asCommandLine(cwd string, cmd *exec.Cmd) string {
132 s := "("
133 if cmd.Dir != "" && cmd.Dir != cwd {
134 s += "cd" + escape(cmd.Dir) + ";"
135 }
136 for _, e := range cmd.Env {
137 if !strings.HasPrefix(e, "PATH=") &&
138 !strings.HasPrefix(e, "HOME=") &&
139 !strings.HasPrefix(e, "USER=") &&
140 !strings.HasPrefix(e, "SHELL=") {
141 s += " "
142 s += escape(e)
143 }
144 }
145
146 for _, e := range os.Environ() {
147 if strings.HasPrefix(e, "PWD=") ||
148 strings.HasPrefix(e, "GOPATH=") ||
149 strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
150 s += " "
151 s += escape(e)
152 }
153 }
154 for _, a := range cmd.Args {
155 s += " "
156 s += escape(a)
157 }
158 s += " )"
159 return s
160 }
161
162 func run(t *testing.T, bin string, args ...string) string {
163 cmd := exec.Command(bin, args...)
164 cmdLine := asCommandLine(".", cmd)
165 prettyPrintf("%s\n", cmdLine)
166 cmd.Stderr = new(strings.Builder)
167 out, err := cmd.Output()
168 if err != nil {
169 if t == nil {
170 log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
171 } else {
172 t.Helper()
173 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
174 }
175 }
176
177 return string(bytes.TrimSpace(out))
178 }
179
180 func TestDWARFSections(t *testing.T) {
181
182 goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
183 goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
184 }
185
186 func TestRunHost(t *testing.T) {
187 run(t, "./host.exe")
188 }
189
190 func TestUniqueTypesAndItabs(t *testing.T) {
191 goCmd(t, "build", "-buildmode=plugin", "./iface_a")
192 goCmd(t, "build", "-buildmode=plugin", "./iface_b")
193 goCmd(t, "build", "-o", "iface.exe", "./iface")
194 run(t, "./iface.exe")
195 }
196
197 func TestIssue18676(t *testing.T) {
198
199
200 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
201 goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
202
203 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
204 defer cancel()
205 cmd := exec.CommandContext(ctx, "./issue18676.exe")
206 out, err := cmd.CombinedOutput()
207 if err != nil {
208 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
209 }
210 }
211
212 func TestIssue19534(t *testing.T) {
213
214 goCmd(t, "build", "-buildmode=plugin", "-ldflags='-pluginpath=issue.19534'", "-o", "plugin.so", "./issue19534/plugin.go")
215 goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
216 run(t, "./issue19534.exe")
217 }
218
219 func TestIssue18584(t *testing.T) {
220 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
221 goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
222 run(t, "./issue18584.exe")
223 }
224
225 func TestIssue19418(t *testing.T) {
226 goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
227 goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
228 run(t, "./issue19418.exe")
229 }
230
231 func TestIssue19529(t *testing.T) {
232 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
233 }
234
235 func TestIssue22175(t *testing.T) {
236 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
237 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
238 goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
239 run(t, "./issue22175.exe")
240 }
241
242 func TestIssue22295(t *testing.T) {
243 goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
244 goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
245 run(t, "./issue22295.exe")
246 }
247
248 func TestIssue24351(t *testing.T) {
249 goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
250 goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
251 run(t, "./issue24351.exe")
252 }
253
254 func TestIssue25756(t *testing.T) {
255 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
256 goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
257
258 for n := 20; n > 0; n-- {
259 t.Run(fmt.Sprint(n), func(t *testing.T) {
260 t.Parallel()
261 run(t, "./issue25756.exe")
262 })
263 }
264 }
265
266
267 func TestIssue25756pie(t *testing.T) {
268 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
269 goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
270 run(t, "./issue25756pie.exe")
271 }
272
273 func TestMethod(t *testing.T) {
274
275 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
276 goCmd(t, "build", "-o", "method.exe", "./method/main.go")
277 run(t, "./method.exe")
278 }
279
280 func TestMethod2(t *testing.T) {
281 goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
282 goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
283 run(t, "./method2.exe")
284 }
285
286 func TestMethod3(t *testing.T) {
287 goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
288 goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
289 run(t, "./method3.exe")
290 }
291
292 func TestIssue44956(t *testing.T) {
293 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
294 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
295 goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
296 run(t, "./issue44956.exe")
297 }
298
299 func TestForkExec(t *testing.T) {
300
301
302 t.Parallel()
303 goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
304
305 var cmd *exec.Cmd
306 done := make(chan int, 1)
307
308 go func() {
309 for i := 0; i < 100; i++ {
310 cmd = exec.Command("./forkexec.exe", "1")
311 err := cmd.Run()
312 if err != nil {
313 t.Errorf("running command failed: %v", err)
314 break
315 }
316 }
317 done <- 1
318 }()
319 select {
320 case <-done:
321 case <-time.After(5 * time.Minute):
322 cmd.Process.Kill()
323 t.Fatalf("subprocess hang")
324 }
325 }
326
View as plain text