175 lines
6.3 KiB
Go
175 lines
6.3 KiB
Go
package commandkit
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
)
|
|
|
|
type reporterTestWriter struct {
|
|
writes int
|
|
n int
|
|
err error
|
|
output bytes.Buffer
|
|
}
|
|
|
|
func (w *reporterTestWriter) Write(p []byte) (int, error) {
|
|
w.writes++
|
|
if w.err != nil || w.n < len(p) {
|
|
n := w.n
|
|
if n > len(p) {
|
|
n = len(p)
|
|
}
|
|
if n > 0 {
|
|
_, _ = w.output.Write(p[:n])
|
|
}
|
|
return n, w.err
|
|
}
|
|
return w.output.Write(p)
|
|
}
|
|
|
|
func TestReporterOutputSubmitsOnWrite(t *testing.T) {
|
|
var destination bytes.Buffer
|
|
reporter := newReporter(Streams{Out: &destination})
|
|
writer := reporter.Output()
|
|
|
|
if got := reporter.state.presentation().Output.State; got != DeliveryNone {
|
|
t.Fatalf("state after obtaining output = %v, want no delivery", got)
|
|
}
|
|
if n, err := writer.Write([]byte("report")); err != nil || n != len("report") {
|
|
t.Fatalf("Write() = %d, %v", n, err)
|
|
}
|
|
if destination.String() != "report" {
|
|
t.Fatalf("output = %q", destination.String())
|
|
}
|
|
if got := reporter.state.presentation().Output.State; got != DeliveryComplete {
|
|
t.Fatalf("output state = %v, want complete", got)
|
|
}
|
|
}
|
|
|
|
func TestReporterQuietOutputDiscardsSuccessfully(t *testing.T) {
|
|
var destination bytes.Buffer
|
|
reporter := newReporter(Streams{Out: &destination})
|
|
reporter.setVisibility(modeQuiet, false, false)
|
|
writer := reporter.Output()
|
|
|
|
if n, err := writer.Write([]byte("hidden")); err != nil || n != len("hidden") {
|
|
t.Fatalf("quiet Write() = %d, %v", n, err)
|
|
}
|
|
if destination.Len() != 0 {
|
|
t.Fatalf("quiet output reached destination: %q", destination.String())
|
|
}
|
|
if got := reporter.state.presentation().Output.State; got != DeliverySuppressed {
|
|
t.Fatalf("output state = %v, want suppressed", got)
|
|
}
|
|
}
|
|
|
|
func TestReporterRetainsFirstWriteFailureAndStopsUsingDestination(t *testing.T) {
|
|
first := errors.New("first write failed")
|
|
second := errors.New("later write failed")
|
|
destination := &reporterTestWriter{n: 1, err: first}
|
|
reporter := newReporter(Streams{Out: destination})
|
|
writer := reporter.Output()
|
|
|
|
if n, err := writer.Write([]byte("first")); n != 1 || !errors.Is(err, first) {
|
|
t.Fatalf("first Write() = %d, %v", n, err)
|
|
}
|
|
if n, err := writer.Write([]byte("second")); n != 0 || !errors.Is(err, first) || errors.Is(err, second) {
|
|
t.Fatalf("second Write() = %d, %v, want retained first error", n, err)
|
|
}
|
|
if destination.writes != 1 {
|
|
t.Fatalf("destination writes = %d, want 1", destination.writes)
|
|
}
|
|
result := reporter.state.presentation().Output
|
|
if result.State != DeliveryIncomplete || !errors.Is(result.WriteError, first) {
|
|
t.Fatalf("delivery result = %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestReporterNormalizesShortWriteAndRetainsIgnoredFailure(t *testing.T) {
|
|
destination := &reporterTestWriter{n: 2}
|
|
reporter := newReporter(Streams{Out: destination})
|
|
_, _ = reporter.Output().Write([]byte("short"))
|
|
|
|
result := reporter.state.presentation().Output
|
|
if result.State != DeliveryIncomplete || !errors.Is(result.WriteError, io.ErrShortWrite) {
|
|
t.Fatalf("delivery result = %#v, want short-write failure", result)
|
|
}
|
|
}
|
|
|
|
func TestReporterRetainsFirstRenderFailurePerDestination(t *testing.T) {
|
|
firstOutputErr := errors.New("first output render failed")
|
|
secondOutputErr := errors.New("later output render failed")
|
|
diagnosticErr := errors.New("diagnostic render failed")
|
|
reporter := newReporter(Streams{Out: io.Discard, Err: io.Discard})
|
|
|
|
reporter.state.mu.Lock()
|
|
reporter.state.recordRenderErrorLocked(destinationOutput, firstOutputErr)
|
|
reporter.state.recordRenderErrorLocked(destinationOutput, secondOutputErr)
|
|
reporter.state.recordRenderErrorLocked(destinationDiagnostics, diagnosticErr)
|
|
reporter.state.mu.Unlock()
|
|
|
|
result := reporter.state.presentation()
|
|
if !errors.Is(result.Output.RenderError, firstOutputErr) || errors.Is(result.Output.RenderError, secondOutputErr) {
|
|
t.Fatalf("output render error = %v, want first error only", result.Output.RenderError)
|
|
}
|
|
if !errors.Is(result.Diagnostics.RenderError, diagnosticErr) {
|
|
t.Fatalf("diagnostic render error = %v", result.Diagnostics.RenderError)
|
|
}
|
|
if result.Output.State != DeliveryIncomplete || result.Diagnostics.State != DeliveryIncomplete {
|
|
t.Fatalf("render failures were not incomplete: %#v", result)
|
|
}
|
|
}
|
|
|
|
func TestReporterDestinationsTrackFailuresIndependently(t *testing.T) {
|
|
outputErr := errors.New("output failed")
|
|
output := &reporterTestWriter{n: 1, err: outputErr}
|
|
var diagnostics bytes.Buffer
|
|
reporter := newReporter(Streams{Out: output, Err: &diagnostics})
|
|
|
|
_, _ = reporter.Output().Write([]byte("out"))
|
|
if n, err := reporter.state.write(destinationDiagnostics, []byte("warning")); err != nil || n != len("warning") {
|
|
t.Fatalf("diagnostic Write() = %d, %v", n, err)
|
|
}
|
|
if diagnostics.String() != "warning" {
|
|
t.Fatalf("diagnostic output = %q", diagnostics.String())
|
|
}
|
|
result := reporter.state.presentation()
|
|
if result.Output.State != DeliveryIncomplete || !errors.Is(result.Output.WriteError, outputErr) {
|
|
t.Fatalf("output result = %#v", result.Output)
|
|
}
|
|
if result.Diagnostics.State != DeliveryComplete || result.Diagnostics.WriteError != nil {
|
|
t.Fatalf("diagnostic result = %#v", result.Diagnostics)
|
|
}
|
|
}
|
|
|
|
func TestReporterClosedHandlesReturnClosedErrorIncludingQuiet(t *testing.T) {
|
|
var destination bytes.Buffer
|
|
reporter := newReporter(Streams{Out: &destination})
|
|
writer := reporter.Output()
|
|
reporter.setVisibility(modeQuiet, false, false)
|
|
reporter.close()
|
|
|
|
if n, err := writer.Write([]byte("late")); n != 0 || !errors.Is(err, ErrReporterClosed) {
|
|
t.Fatalf("late Write() = %d, %v", n, err)
|
|
}
|
|
if destination.Len() != 0 {
|
|
t.Fatalf("late output reached destination: %q", destination.String())
|
|
}
|
|
}
|
|
|
|
func TestNewReporterNormalizesNilDestinationsAndStartsOrdinary(t *testing.T) {
|
|
reporter := newReporter(Streams{})
|
|
if reporter.state.output.writer != io.Discard || reporter.state.diagnostics.writer != io.Discard {
|
|
t.Fatalf("nil destinations were not normalized: %#v", reporter.state)
|
|
}
|
|
if reporter.state.mode != modeOrdinary || reporter.state.progress || reporter.state.progressSet {
|
|
t.Fatalf("initial visibility = mode %v, progress %v (set %v)", reporter.state.mode, reporter.state.progress, reporter.state.progressSet)
|
|
}
|
|
reporter.setVisibility(modeDebug, true, true)
|
|
if reporter.state.mode != modeDebug || !reporter.state.progress || !reporter.state.progressSet {
|
|
t.Fatalf("visibility was not retained: mode %v, progress %v (set %v)", reporter.state.mode, reporter.state.progress, reporter.state.progressSet)
|
|
}
|
|
}
|