packer/rpc: use public MockCommunicator, tests pass

This commit is contained in:
Mitchell Hashimoto 2013-08-23 18:29:46 -07:00
parent 46e02209a9
commit 8b21b5b713
5 changed files with 162 additions and 118 deletions

View File

@ -1,34 +1,80 @@
package packer
import (
"bytes"
"io"
"sync"
)
// MockCommunicator is a valid Communicator implementation that can be
// used for tests.
type MockCommunicator struct {
Stderr io.Reader
Stdout io.Reader
StartCalled bool
StartCmd *RemoteCmd
StartStderr string
StartStdout string
StartStdin string
StartExitStatus int
UploadCalled bool
UploadPath string
UploadData string
DownloadCalled bool
DownloadPath string
DownloadData string
}
func (c *MockCommunicator) Start(rc *RemoteCmd) error {
c.StartCalled = true
c.StartCmd = rc
go func() {
rc.Lock()
defer rc.Unlock()
if rc.Stdout != nil && c.Stdout != nil {
io.Copy(rc.Stdout, c.Stdout)
var wg sync.WaitGroup
if rc.Stdout != nil && c.StartStdout != "" {
wg.Add(1)
go func() {
rc.Stdout.Write([]byte(c.StartStdout))
wg.Done()
}()
}
if rc.Stderr != nil && c.Stderr != nil {
io.Copy(rc.Stderr, c.Stderr)
if rc.Stderr != nil && c.StartStderr != "" {
wg.Add(1)
go func() {
rc.Stderr.Write([]byte(c.StartStderr))
wg.Done()
}()
}
if rc.Stdin != nil {
wg.Add(1)
go func() {
defer wg.Done()
var data bytes.Buffer
io.Copy(&data, rc.Stdin)
c.StartStdin = data.String()
}()
}
wg.Wait()
rc.SetExited(c.StartExitStatus)
}()
return nil
}
func (c *MockCommunicator) Upload(string, io.Reader) error {
func (c *MockCommunicator) Upload(path string, r io.Reader) error {
c.UploadCalled = true
c.UploadPath = path
var data bytes.Buffer
if _, err := io.Copy(&data, r); err != nil {
panic(err)
}
c.UploadData = data.String()
return nil
}
@ -36,6 +82,10 @@ func (c *MockCommunicator) UploadDir(string, string, []string) error {
return nil
}
func (c *MockCommunicator) Download(string, io.Writer) error {
func (c *MockCommunicator) Download(path string, w io.Writer) error {
c.DownloadCalled = true
c.DownloadPath = path
w.Write([]byte(c.DownloadData))
return nil
}

View File

@ -11,14 +11,10 @@ func TestRemoteCmd_StartWithUi(t *testing.T) {
data := "hello\nworld\nthere"
originalOutput := new(bytes.Buffer)
rcOutput := new(bytes.Buffer)
uiOutput := new(bytes.Buffer)
rcOutput.WriteString(data)
testComm := &MockCommunicator{
Stdout: rcOutput,
}
testComm := new(MockCommunicator)
testComm.StartStdout = data
testUi := &BasicUi{
Reader: new(bytes.Buffer),
Writer: uiOutput,
@ -29,22 +25,20 @@ func TestRemoteCmd_StartWithUi(t *testing.T) {
Stdout: originalOutput,
}
go func() {
time.Sleep(100 * time.Millisecond)
rc.SetExited(0)
}()
err := rc.StartWithUi(testComm, testUi)
if err != nil {
t.Fatalf("err: %s", err)
}
if uiOutput.String() != strings.TrimSpace(data)+"\n" {
rc.Wait()
expected := strings.TrimSpace(data)
if uiOutput.String() != expected+"\n" {
t.Fatalf("bad output: '%s'", uiOutput.String())
}
if originalOutput.String() != data {
t.Fatalf("original is bad: '%s'", originalOutput.String())
if originalOutput.String() != expected {
t.Fatalf("bad: %#v", originalOutput.String())
}
}

View File

@ -123,6 +123,10 @@ func (c *communicator) Upload(path string, r io.Reader) (err error) {
return
}
func (c *communicator) UploadDir(dst string, src string, exclude []string) error {
return nil
}
func (c *communicator) Download(path string, w io.Writer) (err error) {
// We need to create a server that can proxy that data downloaded
// into the writer because we can't gob encode a writer directly.

View File

@ -2,52 +2,15 @@ package rpc
import (
"bufio"
"cgl.tideland.biz/asserts"
"github.com/mitchellh/packer/packer"
"io"
"net/rpc"
"testing"
"time"
)
type testCommunicator struct {
startCalled bool
startCmd *packer.RemoteCmd
uploadCalled bool
uploadPath string
uploadData string
downloadCalled bool
downloadPath string
}
func (t *testCommunicator) Start(cmd *packer.RemoteCmd) error {
t.startCalled = true
t.startCmd = cmd
return nil
}
func (t *testCommunicator) Upload(path string, reader io.Reader) (err error) {
t.uploadCalled = true
t.uploadPath = path
t.uploadData, err = bufio.NewReader(reader).ReadString('\n')
return
}
func (t *testCommunicator) Download(path string, writer io.Writer) error {
t.downloadCalled = true
t.downloadPath = path
writer.Write([]byte("download\n"))
return nil
}
func TestCommunicatorRPC(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
// Create the interface to test
c := new(testCommunicator)
c := new(packer.MockCommunicator)
// Start the server
server := rpc.NewServer()
@ -56,7 +19,9 @@ func TestCommunicatorRPC(t *testing.T) {
// Create the client over RPC and run some methods to verify it works
client, err := rpc.Dial("tcp", address)
assert.Nil(err, "should be able to connect")
if err != nil {
t.Fatalf("err: %s", err)
}
remote := Communicator(client)
// The remote command we'll use
@ -70,56 +35,74 @@ func TestCommunicatorRPC(t *testing.T) {
cmd.Stdout = stdout_w
cmd.Stderr = stderr_w
// Send some data on stdout and stderr from the mock
c.StartStdout = "outfoo\n"
c.StartStderr = "errfoo\n"
c.StartExitStatus = 42
// Test Start
err = remote.Start(&cmd)
assert.Nil(err, "should not have an error")
// Test that we can read from stdout
c.startCmd.Stdout.Write([]byte("outfoo\n"))
bufOut := bufio.NewReader(stdout_r)
data, err := bufOut.ReadString('\n')
assert.Nil(err, "should have no problem reading stdout")
assert.Equal(data, "outfoo\n", "should be correct stdout")
// Test that we can read from stderr
c.startCmd.Stderr.Write([]byte("errfoo\n"))
bufErr := bufio.NewReader(stderr_r)
data, err = bufErr.ReadString('\n')
assert.Nil(err, "should have no problem reading stderr")
assert.Equal(data, "errfoo\n", "should be correct stderr")
// Test that we can write to stdin
stdin_w.Write([]byte("infoo\n"))
bufIn := bufio.NewReader(c.startCmd.Stdin)
data, err = bufIn.ReadString('\n')
assert.Nil(err, "should have no problem reading stdin")
assert.Equal(data, "infoo\n", "should be correct stdin")
// Test that we can get the exit status properly
c.startCmd.SetExited(42)
for i := 0; i < 5; i++ {
cmd.Lock()
exited := cmd.Exited
cmd.Unlock()
if exited {
assert.Equal(cmd.ExitStatus, 42, "should have proper exit status")
break
}
time.Sleep(50 * time.Millisecond)
if err != nil {
t.Fatalf("err: %s", err)
}
assert.True(cmd.Exited, "should have exited")
// Test that we can read from stdout
bufOut := bufio.NewReader(stdout_r)
data, err := bufOut.ReadString('\n')
if err != nil {
t.Fatalf("err: %s", err)
}
if data != "outfoo\n" {
t.Fatalf("bad data: %s", data)
}
// Test that we can read from stderr
bufErr := bufio.NewReader(stderr_r)
data, err = bufErr.ReadString('\n')
if err != nil {
t.Fatalf("err: %s", err)
}
if data != "errfoo\n" {
t.Fatalf("bad data: %s", data)
}
// Test that we can write to stdin
stdin_w.Write([]byte("info\n"))
stdin_w.Close()
cmd.Wait()
if c.StartStdin != "info\n" {
t.Fatalf("bad data: %s", data)
}
// Test that we can get the exit status properly
if cmd.ExitStatus != 42 {
t.Fatalf("bad exit: %d", cmd.ExitStatus)
}
// Test that we can upload things
uploadR, uploadW := io.Pipe()
go uploadW.Write([]byte("uploadfoo\n"))
go func() {
defer uploadW.Close()
uploadW.Write([]byte("uploadfoo\n"))
}()
err = remote.Upload("foo", uploadR)
assert.Nil(err, "should not error")
assert.True(c.uploadCalled, "should be called")
assert.Equal(c.uploadPath, "foo", "should be correct path")
assert.Equal(c.uploadData, "uploadfoo\n", "should have the proper data")
if err != nil {
t.Fatalf("err: %s", err)
}
if !c.UploadCalled {
t.Fatal("should have uploaded")
}
if c.UploadPath != "foo" {
t.Fatalf("path: %s", c.UploadPath)
}
if c.UploadData != "uploadfoo\n" {
t.Fatalf("bad: %s", c.UploadData)
}
// Test that we can download things
downloadR, downloadW := io.Pipe()
@ -133,21 +116,34 @@ func TestCommunicatorRPC(t *testing.T) {
downloadDone <- true
}()
c.DownloadData = "download\n"
err = remote.Download("bar", downloadW)
assert.Nil(err, "should not error")
assert.True(c.downloadCalled, "should have called download")
assert.Equal(c.downloadPath, "bar", "should have correct download path")
if err != nil {
t.Fatalf("err: %s", err)
}
if !c.DownloadCalled {
t.Fatal("download should be called")
}
if c.DownloadPath != "bar" {
t.Fatalf("bad: %s", c.DownloadPath)
}
<-downloadDone
assert.Nil(downloadErr, "should not error reading download data")
assert.Equal(downloadData, "download\n", "should have the proper data")
if downloadErr != nil {
t.Fatalf("err: %s", downloadErr)
}
if downloadData != "download\n" {
t.Fatalf("bad: %s", downloadData)
}
}
func TestCommunicator_ImplementsCommunicator(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
var r packer.Communicator
c := Communicator(nil)
assert.Implementor(c, &r, "should be a Communicator")
var raw interface{}
raw = Communicator(nil)
if _, ok := raw.(packer.Communicator); !ok {
t.Fatal("should be a Communicator")
}
}

View File

@ -52,7 +52,7 @@ func TestProvisionerRPC(t *testing.T) {
// Test Provision
ui := &testUi{}
comm := &testCommunicator{}
comm := new(packer.MockCommunicator)
pClient.Provision(ui, comm)
assert.True(p.provCalled, "provision should be called")