github.com/360nenz/gumt@v0.0.0-20230623171552-8f1e25eda00d/gulang/misc/cgo/testplugin/plugin_test.go (about) 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) { 120 if t != nil { 121 t.Helper() 122 } 123 run(t, filepath.Join(goroot, "bin", "go"), append([]string{op, "-gcflags", gcflags}, args...)...) 124 } 125 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, "\\ ;#*&$~?!|[]()<>{}`") { 132 s = "'" + s + "'" 133 } 134 return s 135 } 136 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 { 139 s := "(" 140 if cmd.Dir != "" && cmd.Dir != cwd { 141 s += "cd" + escape(cmd.Dir) + ";" 142 } 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=") { 148 s += " " 149 s += escape(e) 150 } 151 } 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=") { 157 s += " " 158 s += escape(e) 159 } 160 } 161 for _, a := range cmd.Args { 162 s += " " 163 s += escape(a) 164 } 165 s += " )" 166 return s 167 } 168 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() 175 if err != nil { 176 if t == nil { 177 log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) 178 } else { 179 t.Helper() 180 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) 181 } 182 } 183 184 return string(bytes.TrimSpace(out)) 185 } 186 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") 191 } 192 193 func TestRunHost(t *testing.T) { 194 run(t, "./host.exe") 195 } 196 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") 202 } 203 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") 209 210 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 211 defer cancel() 212 cmd := exec.CommandContext(ctx, "./issue18676.exe") 213 out, err := cmd.CombinedOutput() 214 if err != nil { 215 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out) 216 } 217 } 218 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") 224 } 225 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") 230 } 231 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") 236 } 237 238 func TestIssue19529(t *testing.T) { 239 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go") 240 } 241 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") 247 } 248 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") 253 } 254 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") 259 } 260 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) { 267 t.Parallel() 268 run(t, "./issue25756.exe") 269 }) 270 } 271 } 272 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") 278 } 279 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") 285 } 286 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") 291 } 292 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") 297 } 298 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") 304 } 305 306 func TestIssue52937(t *testing.T) { 307 goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go") 308 } 309 310 func TestIssue53989(t *testing.T) { 311 goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go") 312 goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go") 313 run(t, "./issue53989.exe") 314 } 315 316 func TestForkExec(t *testing.T) { 317 // Issue 38824: importing the plugin package causes it hang in forkExec on darwin. 318 319 t.Parallel() 320 goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") 321 322 var cmd *exec.Cmd 323 done := make(chan int, 1) 324 325 go func() { 326 for i := 0; i < 100; i++ { 327 cmd = exec.Command("./forkexec.exe", "1") 328 err := cmd.Run() 329 if err != nil { 330 t.Errorf("running command failed: %v", err) 331 break 332 } 333 } 334 done <- 1 335 }() 336 select { 337 case <-done: 338 case <-time.After(5 * time.Minute): 339 cmd.Process.Kill() 340 t.Fatalf("subprocess hang") 341 } 342 }