plugin_test.go 9.5 KB

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