123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // Copyright 2019 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package plugin_test
- import (
- "bytes"
- "context"
- "flag"
- "fmt"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
- "testing"
- "time"
- )
- var gcflags string = os.Getenv("GO_GCFLAGS")
- func TestMain(m *testing.M) {
- flag.Parse()
- if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
- fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
- os.Exit(0)
- }
- log.SetFlags(log.Lshortfile)
- os.Exit(testMain(m))
- }
- // tmpDir is used to cleanup logged commands -- s/tmpDir/$TMPDIR/
- var tmpDir string
- // prettyPrintf prints lines with tmpDir sanitized.
- func prettyPrintf(format string, args ...interface{}) {
- s := fmt.Sprintf(format, args...)
- if tmpDir != "" {
- s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
- }
- fmt.Print(s)
- }
- func testMain(m *testing.M) int {
- // Copy testdata into GOPATH/src/testplugin, along with a go.mod file
- // declaring the same path.
- GOPATH, err := os.MkdirTemp("", "plugin_test")
- if err != nil {
- log.Panic(err)
- }
- defer os.RemoveAll(GOPATH)
- tmpDir = GOPATH
- modRoot := filepath.Join(GOPATH, "src", "testplugin")
- altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
- for srcRoot, dstRoot := range map[string]string{
- "testdata": modRoot,
- filepath.Join("altpath", "testdata"): altRoot,
- } {
- if err := overlayDir(dstRoot, srcRoot); err != nil {
- log.Panic(err)
- }
- prettyPrintf("mkdir -p %s\n", dstRoot)
- prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
- if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
- log.Panic(err)
- }
- prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
- }
- os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
- if err := os.Chdir(altRoot); err != nil {
- log.Panic(err)
- } else {
- prettyPrintf("cd %s\n", altRoot)
- }
- os.Setenv("PWD", altRoot)
- goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
- os.Setenv("GOPATH", GOPATH)
- if err := os.Chdir(modRoot); err != nil {
- log.Panic(err)
- } else {
- prettyPrintf("cd %s\n", modRoot)
- }
- os.Setenv("PWD", modRoot)
- os.Setenv("LD_LIBRARY_PATH", modRoot)
- goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
- goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
- so, err := os.ReadFile("plugin2.so")
- if err != nil {
- log.Panic(err)
- }
- if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
- log.Panic(err)
- }
- prettyPrintf("cp plugin2.so plugin2-dup.so\n")
- goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1")
- goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go")
- goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go")
- goCmd(nil, "build", "-o", "host.exe", "./host")
- return m.Run()
- }
- func goCmd(t *testing.T, op string, args ...string) {
- if t != nil {
- t.Helper()
- }
- run(t, "go", append([]string{op, "-gcflags", gcflags}, args...)...)
- }
- // escape converts a string to something suitable for a shell command line.
- func escape(s string) string {
- s = strings.Replace(s, "\\", "\\\\", -1)
- s = strings.Replace(s, "'", "\\'", -1)
- // Conservative guess at characters that will force quoting
- if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
- s = "'" + s + "'"
- }
- return s
- }
- // asCommandLine renders cmd as something that could be copy-and-pasted into a command line
- func asCommandLine(cwd string, cmd *exec.Cmd) string {
- s := "("
- if cmd.Dir != "" && cmd.Dir != cwd {
- s += "cd" + escape(cmd.Dir) + ";"
- }
- for _, e := range cmd.Env {
- if !strings.HasPrefix(e, "PATH=") &&
- !strings.HasPrefix(e, "HOME=") &&
- !strings.HasPrefix(e, "USER=") &&
- !strings.HasPrefix(e, "SHELL=") {
- s += " "
- s += escape(e)
- }
- }
- // These EVs are relevant to this test.
- for _, e := range os.Environ() {
- if strings.HasPrefix(e, "PWD=") ||
- strings.HasPrefix(e, "GOPATH=") ||
- strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
- s += " "
- s += escape(e)
- }
- }
- for _, a := range cmd.Args {
- s += " "
- s += escape(a)
- }
- s += " )"
- return s
- }
- func run(t *testing.T, bin string, args ...string) string {
- cmd := exec.Command(bin, args...)
- cmdLine := asCommandLine(".", cmd)
- prettyPrintf("%s\n", cmdLine)
- cmd.Stderr = new(strings.Builder)
- out, err := cmd.Output()
- if err != nil {
- if t == nil {
- log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
- } else {
- t.Helper()
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
- }
- }
- return string(bytes.TrimSpace(out))
- }
- func TestDWARFSections(t *testing.T) {
- // test that DWARF sections are emitted for plugins and programs importing "plugin"
- goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
- goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
- }
- func TestRunHost(t *testing.T) {
- run(t, "./host.exe")
- }
- func TestUniqueTypesAndItabs(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "./iface_a")
- goCmd(t, "build", "-buildmode=plugin", "./iface_b")
- goCmd(t, "build", "-o", "iface.exe", "./iface")
- run(t, "./iface.exe")
- }
- func TestIssue18676(t *testing.T) {
- // make sure we don't add the same itab twice.
- // The buggy code hangs forever, so use a timeout to check for that.
- goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
- goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- cmd := exec.CommandContext(ctx, "./issue18676.exe")
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
- }
- }
- func TestIssue19534(t *testing.T) {
- // Test that we can load a plugin built in a path with non-alpha characters.
- goCmd(t, "build", "-buildmode=plugin", "-ldflags='-pluginpath=issue.19534'", "-o", "plugin.so", "./issue19534/plugin.go")
- goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
- run(t, "./issue19534.exe")
- }
- func TestIssue18584(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
- goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
- run(t, "./issue18584.exe")
- }
- func TestIssue19418(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
- goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
- run(t, "./issue19418.exe")
- }
- func TestIssue19529(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
- }
- func TestIssue22175(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
- goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
- run(t, "./issue22175.exe")
- }
- func TestIssue22295(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
- goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
- run(t, "./issue22295.exe")
- }
- func TestIssue24351(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
- goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
- run(t, "./issue24351.exe")
- }
- func TestIssue25756(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
- goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
- // Fails intermittently, but 20 runs should cause the failure
- for n := 20; n > 0; n-- {
- t.Run(fmt.Sprint(n), func(t *testing.T) {
- t.Parallel()
- run(t, "./issue25756.exe")
- })
- }
- }
- // Test with main using -buildmode=pie with plugin for issue #43228
- func TestIssue25756pie(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
- goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
- run(t, "./issue25756pie.exe")
- }
- func TestMethod(t *testing.T) {
- // Exported symbol's method must be live.
- goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
- goCmd(t, "build", "-o", "method.exe", "./method/main.go")
- run(t, "./method.exe")
- }
- func TestMethod2(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
- goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
- run(t, "./method2.exe")
- }
- func TestMethod3(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
- goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
- run(t, "./method3.exe")
- }
- func TestIssue44956(t *testing.T) {
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
- goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
- goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
- run(t, "./issue44956.exe")
- }
- func TestForkExec(t *testing.T) {
- // Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
- t.Parallel()
- goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
- var cmd *exec.Cmd
- done := make(chan int, 1)
- go func() {
- for i := 0; i < 100; i++ {
- cmd = exec.Command("./forkexec.exe", "1")
- err := cmd.Run()
- if err != nil {
- t.Errorf("running command failed: %v", err)
- break
- }
- }
- done <- 1
- }()
- select {
- case <-done:
- case <-time.After(5 * time.Minute):
- cmd.Process.Kill()
- t.Fatalf("subprocess hang")
- }
- }
|