New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[bug] Write error not returned if reader already signals EOF #494
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1176,11 +1176,13 @@ func (f *File) writeToSequential(w io.Writer) (written int64, err error) { | |
if n > 0 { | ||
f.offset += int64(n) | ||
|
||
m, err2 := w.Write(b[:n]) | ||
m, wErr := w.Write(b[:n]) | ||
written += int64(m) | ||
|
||
if err == nil { | ||
err = err2 | ||
if wErr != nil { | ||
if err == nil || err == io.EOF { | ||
err = wErr | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to prioritize returning an error from
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was in doubt about this one myself, I do like the early return more too, I'll change it. |
||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1249,6 +1249,44 @@ func TestClientReadSequential(t *testing.T) { | |
} | ||
} | ||
|
||
type writerFunc func(b []byte) (int, error) | ||
|
||
func (f writerFunc) Write(b []byte) (int, error) { | ||
return f(b) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not an efficient way to build mocks in Go. We’re already writing a receiver method here for a type, so we don’t need to wrap a generic function like this.
|
||
|
||
func TestClientWriteSequential_WriterErr(t *testing.T) { | ||
sftp, cmd := testClient(t, READONLY, NODELAY) | ||
defer cmd.Wait() | ||
defer sftp.Close() | ||
|
||
sftp.disableConcurrentReads = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You’ve copied code from another test that is irrelevant to the testing here. Please remove this condition as it is irrelevant noise. |
||
d, err := ioutil.TempDir("", "sftptest-writesequential") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want this directory name to be as precisely narrow as possible. If we make a Add |
||
require.NoError(t, err) | ||
|
||
defer os.RemoveAll(d) | ||
|
||
f, err := ioutil.TempFile(d, "write-sequential-test") | ||
require.NoError(t, err) | ||
fname := f.Name() | ||
content := []byte("hello world") | ||
f.Write(content) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
f.Close() | ||
|
||
sftpFile, err := sftp.Open(fname) | ||
require.NoError(t, err) | ||
defer sftpFile.Close() | ||
|
||
want := errors.New("error writing") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test errors should be as clearly test errors as possible. Prefer |
||
n, got := io.Copy(writerFunc(func(b []byte) (int, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do not know for sure here that we’re calling into |
||
return 10, want | ||
}), sftpFile) | ||
|
||
require.Error(t, got) | ||
assert.ErrorIs(t, want, got) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The appropriate scope of assertion here is that it is not We just need to know it’s not an |
||
assert.Equal(t, int64(10), n) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This magic number is greater than it needs to be, and specified in two places. We want to ensure that it returns whatever short write value we return, but it should be so clearly under the number of bytes expected to be copied that we can be sure at a glance that it has done a short write, and still returned the short write values. For a value of For this, an ideal value is going to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Testing just a single write here for
|
||
} | ||
|
||
func TestClientReadDir(t *testing.T) { | ||
sftp1, cmd1 := testClient(t, READONLY, NODELAY) | ||
sftp2, cmd2 := testClientGoSvr(t, READONLY, NODELAY) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there’s no need to rename
err
error. Since we now have an early return, there is no need to access the twoerr
variables at the same time, so shadowing is no longer a problem.