Description: Fix racy tests
 This patch is a re-work of upstream commit 4b17561, available at
 <https://github.com/hashicorp/go-plugin/commit/4b17561>.
 .
 The commit was reworked to apply on the current version.
 .
 Please drop this patch as soon as the package version reaches 4b17561,
 from February 7th, 2018.
Author: Arnaud Rebillout <arnaud.rebillout@collabora.com>
Forwarded: not-needed
Last-Update: 2018-07-09
---
 client_test.go     | 11 +++++++++++
 rpc_client_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++--------
 testing.go         | 23 ++++++++++++++++++++++-
 3 files changed, 79 insertions(+), 9 deletions(-)

--- a/rpc_client_test.go
+++ b/rpc_client_test.go
@@ -3,6 +3,7 @@
 import (
 	"bytes"
 	"io"
+	"sync"
 	"testing"
 	"time"
 )
@@ -10,7 +11,7 @@
 func TestClient_App(t *testing.T) {
 	client, _ := TestPluginRPCConn(t, map[string]Plugin{
 		"test": new(testInterfacePlugin),
-	})
+	}, nil)
 	defer client.Close()
 
 	raw, err := client.Dispense("test")
@@ -30,18 +31,23 @@
 }
 
 func TestClient_syncStreams(t *testing.T) {
-	client, server := TestPluginRPCConn(t, map[string]Plugin{})
-
 	// Create streams for the server that we can talk to
 	stdout_r, stdout_w := io.Pipe()
 	stderr_r, stderr_w := io.Pipe()
-	server.Stdout = stdout_r
-	server.Stderr = stderr_r
+
+	client, _ := TestPluginRPCConn(t, map[string]Plugin{}, &TestOptions{
+		ServerStdout: stdout_r,
+		ServerStderr: stderr_r,
+	})
 
 	// Start the data copying
-	var stdout_out, stderr_out bytes.Buffer
-	stdout := bytes.NewBufferString("stdouttest")
-	stderr := bytes.NewBufferString("stderrtest")
+	var stdout_out, stderr_out safeBuffer
+	stdout := &safeBuffer{
+		b: bytes.NewBufferString("stdouttest"),
+	}
+	stderr := &safeBuffer{
+		b: bytes.NewBufferString("stderrtest"),
+	}
 	go client.SyncStreams(&stdout_out, &stderr_out)
 	go io.Copy(stdout_w, stdout)
 	go io.Copy(stderr_w, stderr)
@@ -62,3 +68,35 @@
 		t.Fatalf("bad: %s", v)
 	}
 }
+
+type safeBuffer struct {
+	sync.Mutex
+	b *bytes.Buffer
+}
+
+func (s *safeBuffer) Write(p []byte) (n int, err error) {
+	s.Lock()
+	defer s.Unlock()
+	if s.b == nil {
+		s.b = new(bytes.Buffer)
+	}
+	return s.b.Write(p)
+}
+
+func (s *safeBuffer) Read(p []byte) (n int, err error) {
+	s.Lock()
+	defer s.Unlock()
+	if s.b == nil {
+		s.b = new(bytes.Buffer)
+	}
+	return s.b.Read(p)
+}
+
+func (s *safeBuffer) String() string {
+	s.Lock()
+	defer s.Unlock()
+	if s.b == nil {
+		s.b = new(bytes.Buffer)
+	}
+	return s.b.String()
+}
--- a/testing.go
+++ b/testing.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"io"
 	"net"
 	"net/rpc"
 	"testing"
@@ -9,6 +10,18 @@
 	"google.golang.org/grpc"
 )
 
+// TestOptions allows specifying options that can affect the behavior of the
+// test functions
+type TestOptions struct {
+	//ServerStdout causes the given value to be used in place of a blank buffer
+	//for RPCServer's Stdout
+	ServerStdout io.ReadCloser
+
+	//ServerStderr causes the given value to be used in place of a blank buffer
+	//for RPCServer's Stderr
+	ServerStderr io.ReadCloser
+}
+
 // The testing file contains test helpers that you can use outside of
 // this package for making it easier to test plugins themselves.
 
@@ -60,12 +73,20 @@
 
 // TestPluginRPCConn returns a plugin RPC client and server that are connected
 // together and configured.
-func TestPluginRPCConn(t *testing.T, ps map[string]Plugin) (*RPCClient, *RPCServer) {
+func TestPluginRPCConn(t *testing.T, ps map[string]Plugin, opts *TestOptions) (*RPCClient, *RPCServer) {
 	// Create two net.Conns we can use to shuttle our control connection
 	clientConn, serverConn := TestConn(t)
 
 	// Start up the server
 	server := &RPCServer{Plugins: ps, Stdout: new(bytes.Buffer), Stderr: new(bytes.Buffer)}
+	if opts != nil {
+		if opts.ServerStdout != nil {
+			server.Stdout = opts.ServerStdout
+		}
+		if opts.ServerStderr != nil {
+			server.Stderr = opts.ServerStderr
+		}
+	}
 	go server.ServeConn(serverConn)
 
 	// Connect the client to the server
