diff options
Diffstat (limited to 'misc/cgo/testplugin/plugin_test.go')
-rw-r--r-- | misc/cgo/testplugin/plugin_test.go | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/misc/cgo/testplugin/plugin_test.go b/misc/cgo/testplugin/plugin_test.go new file mode 100644 index 0000000..53e79a4 --- /dev/null +++ b/misc/cgo/testplugin/plugin_test.go @@ -0,0 +1,325 @@ +// 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") + } +} |