package tools
import (
"context"
"strings"
"sync"
"testing"
"time"
)
func TestACommandsOutputArrivesBeforeItFinishes(t *testing.T) {
// The whole point: a two-minute test run should not be a blank row for two
// minutes. The command prints, then sleeps; if nothing is streaming, the
// first chunk cannot arrive until the sleep is over.
first := make(chan string, 1)
var mu sync.Mutex
var all strings.Builder
ctx := WithProgress(context.Background(), func(chunk string) {
mu.Lock()
all.WriteString(chunk)
mu.Unlock()
select {
case first <- chunk:
default:
}
})
done := make(chan string, 1)
go func() {
report, _ := RunCommand(ctx, t.TempDir(), []string{"bash", "-c", "echo early; sleep 1"}, 10*time.Second)
done <- report
}()
select {
case chunk := <-first:
if !strings.Contains(chunk, "early") {
t.Errorf("first chunk = %q", chunk)
}
case <-done:
t.Fatal("the output only arrived once the command had finished")
case <-time.After(5 * time.Second):
t.Fatal("no output arrived at all")
}
report := <-done
// The report is unaffected: streaming is an addition, not a replacement.
if !strings.Contains(report, "early") {
t.Errorf("report lost the output: %q", report)
}
mu.Lock()
defer mu.Unlock()
if !strings.Contains(all.String(), "early") {
t.Errorf("streamed output = %q", all.String())
}
}
func TestBothStreamsReachTheSameSink(t *testing.T) {
var mu sync.Mutex
var got strings.Builder
ctx := WithProgress(context.Background(), func(chunk string) {
mu.Lock()
defer mu.Unlock()
got.WriteString(chunk)
})
RunCommand(ctx, t.TempDir(), []string{"bash", "-c", "echo out; echo err >&2"}, 10*time.Second)
mu.Lock()
defer mu.Unlock()
// A reader watching a build wants what a terminal would have shown.
for _, want := range []string{"out", "err"} {
if !strings.Contains(got.String(), want) {
t.Errorf("the sink never saw %q: %q", want, got.String())
}
}
}
func TestWithNobodyWatchingNothingChanges(t *testing.T) {
if p := ProgressFrom(context.Background()); p != nil {
t.Fatal("a bare context should carry no sink")
}
// And a nil sink must not be installed, so ProgressFrom stays the cheap
// "is anyone watching" check that tools branch on.
if p := ProgressFrom(WithProgress(context.Background(), nil)); p != nil {
t.Error("a nil sink was installed")
}
report, failed := RunCommand(context.Background(), t.TempDir(), []string{"bash", "-c", "echo hello"}, 10*time.Second)
if failed || !strings.Contains(report, "hello") {
t.Errorf("report = %q, failed = %v", report, failed)
}
}