package packer

import (
	"bytes"
	"context"
	"io"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/packer/common/iochan"
	"golang.org/x/sync/errgroup"
)

func TestRemoteCmd_StartWithUi(t *testing.T) {
	data := []string{
		"hello",
		"world",
		"foo",
		"there",
	}

	originalOutputReader, originalOutputWriter := io.Pipe()
	uilOutputReader, uilOutputWriter := io.Pipe()

	testComm := new(MockCommunicator)
	testComm.StartStdout = strings.Join(data, "\n") + "\n"
	testUi := &BasicUi{
		Reader: new(bytes.Buffer),
		Writer: uilOutputWriter,
	}

	rc := &RemoteCmd{
		Command: "test",
		Stdout:  originalOutputWriter,
	}
	ctx := context.TODO()

	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
	}

	wg.Go(func() error { return testPrintFn(uilOutputReader, data) })
	wg.Go(func() error { return testPrintFn(originalOutputReader, data) })

	err := rc.RunWithUi(ctx, testComm, testUi)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	wg.Wait()
}

func TestRemoteCmd_Wait(t *testing.T) {
	var cmd RemoteCmd

	result := make(chan bool)
	go func() {
		cmd.Wait()
		result <- true
	}()

	cmd.SetExited(42)

	select {
	case <-result:
		// Success
	case <-time.After(500 * time.Millisecond):
		t.Fatal("never got exit notification")
	}
}
