Transfer completed Go output without copies
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package gotool
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -62,6 +63,14 @@ func runGoPreparationFixture() int {
|
||||
case "fail":
|
||||
fmt.Fprintln(os.Stderr, "fixture dependency failure")
|
||||
return 23
|
||||
case "output":
|
||||
fmt.Fprintln(os.Stdout, "fixture stdout")
|
||||
fmt.Fprintln(os.Stderr, "fixture stderr")
|
||||
return 0
|
||||
case "large-fail":
|
||||
_, _ = fmt.Fprint(os.Stdout, strings.Repeat("x", 8<<20))
|
||||
fmt.Fprintln(os.Stderr, "fixture large failure")
|
||||
return 23
|
||||
case "wait":
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
@@ -295,7 +304,7 @@ func TestPersistentSettingsPreserveCachesAndNativeCTools(t *testing.T) {
|
||||
func readSettings(t *testing.T, tool *Tool, mode Mode, target Target, names ...string) map[string]string {
|
||||
t.Helper()
|
||||
args := append([]string{"env", "-json"}, names...)
|
||||
output, err := tool.Run(context.Background(), t.TempDir(), mode, target, nil, args...)
|
||||
output, err := tool.Output(context.Background(), t.TempDir(), mode, target, nil, args...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -306,6 +315,122 @@ func readSettings(t *testing.T, tool *Tool, mode Mode, target Target, names ...s
|
||||
return settings
|
||||
}
|
||||
|
||||
func TestOutputReturnsAndForwardsCombinedOutput(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "output")
|
||||
tool := fixtureTool()
|
||||
var forwarded bytes.Buffer
|
||||
|
||||
output, err := tool.Output(context.Background(), repository, Native, Target{}, &forwarded, "version")
|
||||
if err != nil {
|
||||
t.Fatalf("Output() error = %v", err)
|
||||
}
|
||||
if !bytes.Contains(output, []byte("fixture stdout")) || !bytes.Contains(output, []byte("fixture stderr")) {
|
||||
t.Fatalf("combined output = %q", output)
|
||||
}
|
||||
if !bytes.Equal(forwarded.Bytes(), output) {
|
||||
t.Fatalf("forwarded output differs from capture: forwarded %d bytes, captured %d", forwarded.Len(), len(output))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunForwardsOutputWithoutReturningCapture(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "output")
|
||||
tool := fixtureTool()
|
||||
var forwarded bytes.Buffer
|
||||
|
||||
if err := tool.Run(context.Background(), repository, Native, Target{}, &forwarded, "version"); err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if !bytes.Contains(forwarded.Bytes(), []byte("fixture stdout")) || !bytes.Contains(forwarded.Bytes(), []byte("fixture stderr")) {
|
||||
t.Fatalf("forwarded output = %q", forwarded.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPreservesFailureDiagnosticsAndCause(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "fail")
|
||||
tool := fixtureTool()
|
||||
|
||||
err := tool.Run(context.Background(), repository, Native, Target{}, nil, "version")
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 23 || !strings.Contains(err.Error(), "fixture dependency failure") {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputReturnsLargeFailureDiagnosticsAndCause(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "large-fail")
|
||||
tool := fixtureTool()
|
||||
|
||||
output, err := tool.Output(context.Background(), repository, Native, Target{}, nil, "version")
|
||||
var exitError *exec.ExitError
|
||||
wantSize := (8 << 20) + len("fixture large failure\n")
|
||||
if len(output) != wantSize {
|
||||
t.Fatalf("captured output = %d bytes, want %d", len(output), wantSize)
|
||||
}
|
||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 23 || !strings.Contains(err.Error(), "fixture large failure") {
|
||||
t.Fatalf("Output() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputReturnsCancellation(t *testing.T) {
|
||||
repository, recordPath := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "wait")
|
||||
tool := fixtureTool()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := tool.Output(ctx, repository, Native, Target{}, nil, "version")
|
||||
done <- err
|
||||
}()
|
||||
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
for {
|
||||
if _, err := os.Stat(recordPath); err == nil {
|
||||
break
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatal("Go fixture did not start")
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
cancel()
|
||||
select {
|
||||
case err := <-done:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("Output() error = %v; want cancellation", err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("Go fixture did not stop after cancellation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutputPreservesForwardingWriterFailure(t *testing.T) {
|
||||
repository, _ := installGoPreparationFixture(t)
|
||||
t.Setenv("RELEASEKIT_GO_FIXTURE_MODE", "output")
|
||||
tool := fixtureTool()
|
||||
writerErr := errors.New("fixture output writer failed")
|
||||
|
||||
output, err := tool.Output(context.Background(), repository, Native, Target{}, failingOutputWriter{err: writerErr}, "version")
|
||||
if !errors.Is(err, writerErr) || !strings.Contains(err.Error(), "go version") {
|
||||
t.Fatalf("Output() error = %v; want wrapped writer error and command", err)
|
||||
}
|
||||
if len(output) == 0 {
|
||||
t.Fatal("Output() returned no captured bytes after forwarding failed")
|
||||
}
|
||||
}
|
||||
|
||||
type failingOutputWriter struct{ err error }
|
||||
|
||||
func (writer failingOutputWriter) Write([]byte) (int, error) { return 0, writer.err }
|
||||
|
||||
func fixtureTool() *Tool {
|
||||
return &Tool{host: Target{OS: runtime.GOOS, Arch: runtime.GOARCH}, settings: map[string]string{}}
|
||||
}
|
||||
|
||||
func clearGoSettings(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, key := range []string{
|
||||
|
||||
Reference in New Issue
Block a user