Skip to content

Commit 5bcb73e

Browse files
bitfieldnk-87
andcommitted
Fix Bytes/AppendFile/WriteFile to return read errs
Calling any sink method (such as Bytes) should return a non-nil error if there is an error reading the pipe to completion. Due to a bug, Bytes, AppendFile, and WriteFile failed to do this. This commit fixes the bug, and also tests that the other sink methods behave correctly (which they do). Fixes #170. Co-authored-by: Namho Kim <git@namho.kim>
1 parent d5e838d commit 5bcb73e

File tree

2 files changed

+99
-3
lines changed

2 files changed

+99
-3
lines changed

script.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func (p *Pipe) Bytes() ([]byte, error) {
221221
if err != nil {
222222
p.SetError(err)
223223
}
224-
return data, nil
224+
return data, p.Error()
225225
}
226226

227227
// Close closes the pipe's associated reader. This is a no-op if the reader is
@@ -894,9 +894,8 @@ func (p *Pipe) writeOrAppendFile(path string, mode int) (int64, error) {
894894
wrote, err := io.Copy(out, p)
895895
if err != nil {
896896
p.SetError(err)
897-
return 0, err
898897
}
899-
return wrote, nil
898+
return wrote, p.Error()
900899
}
901900

902901
// ReadAutoCloser wraps an [io.ReadCloser] so that it will be automatically

script_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"regexp"
1616
"strings"
1717
"testing"
18+
"testing/iotest"
1819

1920
"github.com/bitfield/script"
2021
"github.com/google/go-cmp/cmp"
@@ -1259,6 +1260,16 @@ func TestStdinReadsFromProgramStandardInput(t *testing.T) {
12591260
}
12601261
}
12611262

1263+
func TestStdoutReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1264+
t.Parallel()
1265+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1266+
_, err := script.NewPipe().WithStdout(io.Discard).
1267+
WithReader(brokenReader).Stdout()
1268+
if err == nil {
1269+
t.Fatal(nil)
1270+
}
1271+
}
1272+
12621273
func TestStdoutSendsPipeContentsToConfiguredStandardOutput(t *testing.T) {
12631274
t.Parallel()
12641275
buf := &bytes.Buffer{}
@@ -1307,6 +1318,18 @@ func TestAppendFile_AppendsAllItsInputToSpecifiedFile(t *testing.T) {
13071318
}
13081319
}
13091320

1321+
func TestAppendFile_ReturnsBytesWrittenAndErrorGivenReadErrorOnPipe(t *testing.T) {
1322+
t.Parallel()
1323+
var want int64 = 1
1324+
got, err := script.NewPipe().WithReader(partialErrReader{}).AppendFile(t.TempDir() + "/tmp")
1325+
if err == nil {
1326+
t.Fatal("want error reading pipe with error status, got nil")
1327+
}
1328+
if !cmp.Equal(want, got) {
1329+
t.Error(cmp.Diff(want, got))
1330+
}
1331+
}
1332+
13101333
func TestBytesOutputsInputBytesUnchanged(t *testing.T) {
13111334
t.Parallel()
13121335
want := []byte{8, 0, 0, 16}
@@ -1320,6 +1343,15 @@ func TestBytesOutputsInputBytesUnchanged(t *testing.T) {
13201343
}
13211344
}
13221345

1346+
func TestBytesReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1347+
t.Parallel()
1348+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1349+
_, err := script.NewPipe().WithReader(brokenReader).Bytes()
1350+
if err == nil {
1351+
t.Fatal(nil)
1352+
}
1353+
}
1354+
13231355
func TestCountLines_CountsCorrectNumberOfLinesInInput(t *testing.T) {
13241356
t.Parallel()
13251357
want := 3
@@ -1344,6 +1376,15 @@ func TestCountLines_Counts0LinesInEmptyInput(t *testing.T) {
13441376
}
13451377
}
13461378

1379+
func TestCountLines_ReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1380+
t.Parallel()
1381+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1382+
_, err := script.NewPipe().WithReader(brokenReader).CountLines()
1383+
if err == nil {
1384+
t.Fatal(nil)
1385+
}
1386+
}
1387+
13471388
func TestSHA256Sum_OutputsCorrectHash(t *testing.T) {
13481389
t.Parallel()
13491390
tcs := []struct {
@@ -1379,6 +1420,24 @@ func TestSHA256Sum_OutputsCorrectHash(t *testing.T) {
13791420
}
13801421
}
13811422

1423+
func TestSHA256Sum_ReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1424+
t.Parallel()
1425+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1426+
_, err := script.NewPipe().WithReader(brokenReader).SHA256Sum()
1427+
if err == nil {
1428+
t.Fatal(nil)
1429+
}
1430+
}
1431+
1432+
func TestSliceSink_ReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1433+
t.Parallel()
1434+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1435+
_, err := script.NewPipe().WithReader(brokenReader).Slice()
1436+
if err == nil {
1437+
t.Fatal(nil)
1438+
}
1439+
}
1440+
13821441
func TestSliceSink_(t *testing.T) {
13831442
t.Parallel()
13841443
tests := []struct {
@@ -1441,6 +1500,15 @@ func TestStringOutputsInputStringUnchanged(t *testing.T) {
14411500
}
14421501
}
14431502

1503+
func TestStringReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1504+
t.Parallel()
1505+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1506+
_, err := script.NewPipe().WithReader(brokenReader).String()
1507+
if err == nil {
1508+
t.Fatal(nil)
1509+
}
1510+
}
1511+
14441512
func TestWaitReadsPipeSourceToCompletion(t *testing.T) {
14451513
t.Parallel()
14461514
source := bytes.NewBufferString("hello")
@@ -1470,6 +1538,25 @@ func TestWriteFile_WritesInputToFileCreatingItIfNecessary(t *testing.T) {
14701538
}
14711539
}
14721540

1541+
// partialErrReader returns 1 and a non-EOF error on reading.
1542+
type partialErrReader struct{}
1543+
1544+
func (r partialErrReader) Read(p []byte) (int, error) {
1545+
return 1, errors.New("oh no")
1546+
}
1547+
1548+
func TestWriteFile_ReturnsBytesWrittenAndErrorGivenReadErrorOnPipe(t *testing.T) {
1549+
t.Parallel()
1550+
var want int64 = 1
1551+
got, err := script.NewPipe().WithReader(partialErrReader{}).WriteFile(t.TempDir() + "/tmp")
1552+
if err == nil {
1553+
t.Fatal("want error reading pipe with error status, got nil")
1554+
}
1555+
if !cmp.Equal(want, got) {
1556+
t.Error(cmp.Diff(want, got))
1557+
}
1558+
}
1559+
14731560
func TestWriteFile_TruncatesExistingFile(t *testing.T) {
14741561
t.Parallel()
14751562
want := "Hello, world"
@@ -1629,6 +1716,16 @@ func TestReadReturnsEOFOnUninitialisedPipe(t *testing.T) {
16291716
}
16301717
}
16311718

1719+
func TestReadReturnsErrorGivenReadErrorOnPipe(t *testing.T) {
1720+
t.Parallel()
1721+
brokenReader := iotest.ErrReader(errors.New("oh no"))
1722+
buf := make([]byte, 0)
1723+
_, err := script.NewPipe().WithReader(brokenReader).Read(buf)
1724+
if err == nil {
1725+
t.Fatal(nil)
1726+
}
1727+
}
1728+
16321729
func ExampleArgs() {
16331730
script.Args().Stdout()
16341731
// prints command-line arguments

0 commit comments

Comments
 (0)