diff --git a/packer/communicator_mock.go b/packer/communicator_mock.go index 8db93e138..a0563f302 100644 --- a/packer/communicator_mock.go +++ b/packer/communicator_mock.go @@ -5,6 +5,7 @@ import ( "context" "io" "os" + "strings" "sync" ) @@ -44,7 +45,7 @@ func (c *MockCommunicator) Start(ctx context.Context, rc *RemoteCmd) error { if rc.Stdout != nil && c.StartStdout != "" { wg.Add(1) go func() { - rc.Stdout.Write([]byte(c.StartStdout)) + io.Copy(rc.Stdout, strings.NewReader(c.StartStdout)) wg.Done() }() } @@ -52,7 +53,7 @@ func (c *MockCommunicator) Start(ctx context.Context, rc *RemoteCmd) error { if rc.Stderr != nil && c.StartStderr != "" { wg.Add(1) go func() { - rc.Stderr.Write([]byte(c.StartStderr)) + io.Copy(rc.Stderr, strings.NewReader(c.StartStderr)) wg.Done() }() } diff --git a/packer/communicator_test.go b/packer/communicator_test.go index c7146cdf0..8e80a5518 100644 --- a/packer/communicator_test.go +++ b/packer/communicator_test.go @@ -3,48 +3,69 @@ package packer import ( "bytes" "context" + "io" "strings" "testing" "time" "github.com/google/go-cmp/cmp" + "github.com/mitchellh/iochan" + "golang.org/x/sync/errgroup" ) func TestRemoteCmd_StartWithUi(t *testing.T) { - data := "hello\nworld\nthere" + data := []string{ + "hello", + "world", + "foo", + "there", + } - originalOutput := new(bytes.Buffer) - uiOutput := new(bytes.Buffer) + originalOutputReader, originalOutputWriter := io.Pipe() + uilOutputReader, uilOutputWriter := io.Pipe() testComm := new(MockCommunicator) - testComm.StartStdout = data + testComm.StartStdout = strings.Join(data, "\n") + "\n" testUi := &BasicUi{ Reader: new(bytes.Buffer), - Writer: uiOutput, + Writer: uilOutputWriter, } rc := &RemoteCmd{ Command: "test", - Stdout: originalOutput, + Stdout: originalOutputWriter, } ctx := context.TODO() - err := rc.RunWithUi(ctx, testComm, testUi) - if err != nil { - t.Fatalf("err: %s", err) + wg := errgroup.Group{} + + testPrintFn := func(in io.Reader, expected []string) error { + i := 0 + got := []string{} + for output := range iochan.LineReader(in) { + got = append(got, output) + i++ + if i == len(expected) { + // here ideally the LineReader chan should be closed, but since + // the stream virtually has no ending we need to leave early. + break + } + } + if diff := cmp.Diff(got, expected); diff != "" { + t.Fatalf("bad output: %s", diff) + } + return nil } - // sometimes cmd has returned and everything can be printed later on - time.Sleep(1 * time.Second) + wg.Go(func() error { return testPrintFn(uilOutputReader, data) }) + wg.Go(func() error { return testPrintFn(originalOutputReader, data) }) - expected := strings.TrimSpace(data) - if diff := cmp.Diff(strings.TrimSpace(uiOutput.String()), expected); diff != "" { - t.Fatalf("bad output: %s", diff) + err := rc.RunWithUi(ctx, testComm, testUi) + if err != nil { + t.Fatalf("err: %s", err) } - if originalOutput.String() != expected { - t.Fatalf("bad: %#v", originalOutput.String()) - } + wg.Wait() } func TestRemoteCmd_Wait(t *testing.T) {