Bound retained Go command diagnostics
This commit is contained in:
@@ -6,11 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -453,6 +455,96 @@ func TestOutputReturnsLargeFailureDiagnosticsAndCause(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCaptureRetainsTailAndForwardsAll(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
size int
|
||||
chunkSize int
|
||||
}{
|
||||
{"below", statusDiagnosticLimit - 1, 8191},
|
||||
{"exact", statusDiagnosticLimit, 8191},
|
||||
{"above", statusDiagnosticLimit + 1, 8191},
|
||||
{"single oversized", statusDiagnosticLimit*2 + 3, statusDiagnosticLimit*2 + 3},
|
||||
{"wrapped small writes", statusDiagnosticLimit*3 + 17, 113},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
input := make([]byte, test.size)
|
||||
for i := range input {
|
||||
input[i] = byte(i % 251)
|
||||
}
|
||||
var forwarded bytes.Buffer
|
||||
writer := &combinedWriter{mode: captureStatus, output: &forwarded}
|
||||
for start := 0; start < len(input); start += test.chunkSize {
|
||||
chunk := input[start:min(start+test.chunkSize, len(input))]
|
||||
if n, err := writer.Write(chunk); n != len(chunk) || err != nil {
|
||||
t.Fatalf("write = %d, %v", n, err)
|
||||
}
|
||||
}
|
||||
if writer.omitted() != (len(input) > statusDiagnosticLimit) || cap(writer.tail) > statusDiagnosticLimit {
|
||||
t.Fatalf("omitted = %t, capacity = %d", writer.omitted(), cap(writer.tail))
|
||||
}
|
||||
want := input[max(0, len(input)-statusDiagnosticLimit):]
|
||||
if got := writer.take(); !bytes.Equal(got, want) || !bytes.Equal(forwarded.Bytes(), input) {
|
||||
t.Fatalf("capture size = %d, forwarded size = %d", len(got), forwarded.Len())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCaptureSerializesConcurrentWriters(t *testing.T) {
|
||||
var forwarded bytes.Buffer
|
||||
writer := &combinedWriter{mode: captureStatus, output: &forwarded}
|
||||
var group sync.WaitGroup
|
||||
for _, value := range []byte{'a', 'b'} {
|
||||
group.Add(1)
|
||||
go func() {
|
||||
defer group.Done()
|
||||
chunk := bytes.Repeat([]byte{value}, 4096)
|
||||
for i := 0; i < 300; i++ {
|
||||
_, _ = writer.Write(chunk)
|
||||
}
|
||||
}()
|
||||
}
|
||||
group.Wait()
|
||||
contents := forwarded.Bytes()
|
||||
if !writer.omitted() || !bytes.Equal(writer.take(), contents[len(contents)-statusDiagnosticLimit:]) {
|
||||
t.Fatal("concurrent capture differs from forwarded tail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCommandsRetainBoundedFailureDiagnostics(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "large-fail")
|
||||
var forwarded bytes.Buffer
|
||||
err := fixtureTool().Run(context.Background(), repository, Native, Target{}, &forwarded, "version")
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 23 || !strings.Contains(err.Error(), "earlier Go output omitted") || !strings.Contains(err.Error(), "fixture large failure") || len(err.Error()) > statusDiagnosticLimit+200 || forwarded.Len() != (8<<20)+len("fixture large failure\n") {
|
||||
t.Fatalf("Run() error = %v, forwarded = %d bytes", err, forwarded.Len())
|
||||
}
|
||||
err = PrepareBuildDependencies(context.Background(), repository)
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 23 || !strings.Contains(err.Error(), "earlier Go output omitted") || !strings.Contains(err.Error(), "fixture large failure") || len(err.Error()) > statusDiagnosticLimit+200 {
|
||||
t.Fatalf("preparation error did not retain bounded diagnostics: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusCommandPreservesForwardingError(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "output")
|
||||
writerErr := errors.New("forwarding failed")
|
||||
err := fixtureTool().Run(context.Background(), repository, Native, Target{}, failingOutputWriter{err: writerErr}, "version")
|
||||
if !errors.Is(err, writerErr) {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
err = fixtureTool().Run(context.Background(), repository, Native, Target{}, shortOutputWriter{}, "version")
|
||||
if !errors.Is(err, io.ErrShortWrite) {
|
||||
t.Fatalf("Run() short-write error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type shortOutputWriter struct{}
|
||||
|
||||
func (shortOutputWriter) Write([]byte) (int, error) { return 0, nil }
|
||||
|
||||
func TestOutputReturnsCancellation(t *testing.T) {
|
||||
repository, recordPath := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "wait")
|
||||
|
||||
Reference in New Issue
Block a user