Browse Source

categorize read and write error

Darien Raymond 7 years ago
parent
commit
c7f847c96e
5 changed files with 177 additions and 21 deletions
  1. 36 20
      common/buf/copy.go
  2. 54 0
      common/buf/copy_test.go
  3. 3 1
      common/buf/writer_test.go
  4. 2 0
      mocks.go
  5. 82 0
      testing/mocks/io.go

+ 36 - 20
common/buf/copy.go

@@ -45,24 +45,6 @@ type SizeCounter struct {
 // CopyOption is an option for copying data.
 // CopyOption is an option for copying data.
 type CopyOption func(*copyHandler)
 type CopyOption func(*copyHandler)
 
 
-// IgnoreReaderError is a CopyOption that ignores errors from reader. Copy will continue in such case.
-func IgnoreReaderError() CopyOption {
-	return func(handler *copyHandler) {
-		handler.onReadError = append(handler.onReadError, func(err error) error {
-			return nil
-		})
-	}
-}
-
-// IgnoreWriterError is a CopyOption that ignores errors from writer. Copy will continue in such case.
-func IgnoreWriterError() CopyOption {
-	return func(handler *copyHandler) {
-		handler.onWriteError = append(handler.onWriteError, func(err error) error {
-			return nil
-		})
-	}
-}
-
 // UpdateActivity is a CopyOption to update activity on each data copy operation.
 // UpdateActivity is a CopyOption to update activity on each data copy operation.
 func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
 func UpdateActivity(timer signal.ActivityUpdater) CopyOption {
 	return func(handler *copyHandler) {
 	return func(handler *copyHandler) {
@@ -81,6 +63,40 @@ func CountSize(sc *SizeCounter) CopyOption {
 	}
 	}
 }
 }
 
 
+type readError struct {
+	error
+}
+
+func (e readError) Error() string {
+	return e.error.Error()
+}
+
+func (e readError) Inner() error {
+	return e.error
+}
+
+func IsReadError(err error) bool {
+	_, ok := err.(readError)
+	return ok
+}
+
+type writeError struct {
+	error
+}
+
+func (e writeError) Error() string {
+	return e.error.Error()
+}
+
+func (e writeError) Inner() error {
+	return e.error
+}
+
+func IsWriteError(err error) bool {
+	_, ok := err.(writeError)
+	return ok
+}
+
 func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
 func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
 	for {
 	for {
 		buffer, err := handler.readFrom(reader)
 		buffer, err := handler.readFrom(reader)
@@ -90,12 +106,12 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
 			}
 			}
 
 
 			if werr := handler.writeTo(writer, buffer); werr != nil {
 			if werr := handler.writeTo(writer, buffer); werr != nil {
-				return werr
+				return writeError{werr}
 			}
 			}
 		}
 		}
 
 
 		if err != nil {
 		if err != nil {
-			return err
+			return readError{err}
 		}
 		}
 	}
 	}
 }
 }

+ 54 - 0
common/buf/copy_test.go

@@ -0,0 +1,54 @@
+package buf_test
+
+import (
+	"crypto/rand"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+
+	"v2ray.com/core/common/buf"
+	"v2ray.com/core/common/errors"
+	"v2ray.com/core/testing/mocks"
+)
+
+func TestReadError(t *testing.T) {
+	mockCtl := gomock.NewController(t)
+	defer mockCtl.Finish()
+
+	mockReader := mocks.NewReader(mockCtl)
+	mockReader.EXPECT().Read(gomock.Any()).Return(0, errors.New("error"))
+
+	err := buf.Copy(buf.NewReader(mockReader), buf.Discard)
+	if err == nil {
+		t.Fatal("expected error, but nil")
+	}
+
+	if !buf.IsReadError(err) {
+		t.Error("expected to be ReadError, but not")
+	}
+
+	if err.Error() != "error" {
+		t.Fatal("unexpected error message: ", err.Error())
+	}
+}
+
+func TestWriteError(t *testing.T) {
+	mockCtl := gomock.NewController(t)
+	defer mockCtl.Finish()
+
+	mockWriter := mocks.NewWriter(mockCtl)
+	mockWriter.EXPECT().Write(gomock.Any()).Return(0, errors.New("error"))
+
+	err := buf.Copy(buf.NewReader(rand.Reader), buf.NewWriter(mockWriter))
+	if err == nil {
+		t.Fatal("expected error, but nil")
+	}
+
+	if !buf.IsWriteError(err) {
+		t.Error("expected to be WriteError, but not")
+	}
+
+	if err.Error() != "error" {
+		t.Fatal("unexpected error message: ", err.Error())
+	}
+}

+ 3 - 1
common/buf/writer_test.go

@@ -41,7 +41,9 @@ func TestBytesWriterReadFrom(t *testing.T) {
 	writer.SetBuffered(false)
 	writer.SetBuffered(false)
 	nBytes, err := reader.WriteTo(writer)
 	nBytes, err := reader.WriteTo(writer)
 	assert(nBytes, Equals, int64(size))
 	assert(nBytes, Equals, int64(size))
-	assert(err, IsNil)
+	if err != nil {
+		t.Fatal("expect success, but actually error: ", err.Error())
+	}
 
 
 	mb, err := pReader.ReadMultiBuffer()
 	mb, err := pReader.ReadMultiBuffer()
 	assert(err, IsNil)
 	assert(err, IsNil)

+ 2 - 0
mocks.go

@@ -2,5 +2,7 @@ package core
 
 
 //go:generate go get -u github.com/golang/mock/gomock
 //go:generate go get -u github.com/golang/mock/gomock
 //go:generate go install github.com/golang/mock/mockgen
 //go:generate go install github.com/golang/mock/mockgen
+
+//go:generate mockgen -package mocks -destination testing/mocks/io.go -mock_names Reader=Reader,Writer=Writer io Reader,Writer
 //go:generate mockgen -package mocks -destination testing/mocks/dns.go -mock_names Client=DNSClient v2ray.com/core/features/dns Client
 //go:generate mockgen -package mocks -destination testing/mocks/dns.go -mock_names Client=DNSClient v2ray.com/core/features/dns Client
 //go:generate mockgen -package mocks -destination testing/mocks/proxy.go -mock_names Inbound=ProxyInbound,Outbound=ProxyOutbound v2ray.com/core/proxy Inbound,Outbound
 //go:generate mockgen -package mocks -destination testing/mocks/proxy.go -mock_names Inbound=ProxyInbound,Outbound=ProxyOutbound v2ray.com/core/proxy Inbound,Outbound

+ 82 - 0
testing/mocks/io.go

@@ -0,0 +1,82 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: io (interfaces: Reader,Writer)
+
+// Package mocks is a generated GoMock package.
+package mocks
+
+import (
+	gomock "github.com/golang/mock/gomock"
+	reflect "reflect"
+)
+
+// Reader is a mock of Reader interface
+type Reader struct {
+	ctrl     *gomock.Controller
+	recorder *ReaderMockRecorder
+}
+
+// ReaderMockRecorder is the mock recorder for Reader
+type ReaderMockRecorder struct {
+	mock *Reader
+}
+
+// NewReader creates a new mock instance
+func NewReader(ctrl *gomock.Controller) *Reader {
+	mock := &Reader{ctrl: ctrl}
+	mock.recorder = &ReaderMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *Reader) EXPECT() *ReaderMockRecorder {
+	return m.recorder
+}
+
+// Read mocks base method
+func (m *Reader) Read(arg0 []byte) (int, error) {
+	ret := m.ctrl.Call(m, "Read", arg0)
+	ret0, _ := ret[0].(int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Read indicates an expected call of Read
+func (mr *ReaderMockRecorder) Read(arg0 interface{}) *gomock.Call {
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Reader)(nil).Read), arg0)
+}
+
+// Writer is a mock of Writer interface
+type Writer struct {
+	ctrl     *gomock.Controller
+	recorder *WriterMockRecorder
+}
+
+// WriterMockRecorder is the mock recorder for Writer
+type WriterMockRecorder struct {
+	mock *Writer
+}
+
+// NewWriter creates a new mock instance
+func NewWriter(ctrl *gomock.Controller) *Writer {
+	mock := &Writer{ctrl: ctrl}
+	mock.recorder = &WriterMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use
+func (m *Writer) EXPECT() *WriterMockRecorder {
+	return m.recorder
+}
+
+// Write mocks base method
+func (m *Writer) Write(arg0 []byte) (int, error) {
+	ret := m.ctrl.Call(m, "Write", arg0)
+	ret0, _ := ret[0].(int)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Write indicates an expected call of Write
+func (mr *WriterMockRecorder) Write(arg0 interface{}) *gomock.Call {
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Writer)(nil).Write), arg0)
+}