Bound retained Go command diagnostics

This commit is contained in:
2026-11-02 03:31:59 +00:00
parent 2b23574ece
commit 9cb48a70b7
5 changed files with 230 additions and 45 deletions

View File

@@ -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")