github.com/jordan-bonecutter/can-go@v0.0.0-20230901155856-d83995b18e50/pkg/socketcan/fileconn_test.go (about)

     1  package socketcan
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"go.einride.tech/can/internal/mocks/gen/mocksocketcan"
    12  	"gotest.tools/v3/assert"
    13  )
    14  
    15  func TestUnwrapPathError(t *testing.T) {
    16  	innerErr := fmt.Errorf("inner error")
    17  	for _, tt := range []struct {
    18  		msg      string
    19  		err      error
    20  		expected error
    21  	}{
    22  		{
    23  			msg:      "no path error",
    24  			err:      innerErr,
    25  			expected: innerErr,
    26  		},
    27  		{
    28  			msg:      "single path error",
    29  			err:      &os.PathError{Op: "read", Err: innerErr},
    30  			expected: innerErr,
    31  		},
    32  		{
    33  			msg:      "double path error",
    34  			err:      &os.PathError{Op: "read", Err: &os.PathError{Op: "read", Err: innerErr}},
    35  			expected: &os.PathError{Op: "read", Err: innerErr},
    36  		},
    37  	} {
    38  		tt := tt
    39  		t.Run(tt.msg, func(t *testing.T) {
    40  			assert.Error(t, unwrapPathError(tt.err), tt.expected.Error())
    41  		})
    42  	}
    43  }
    44  
    45  func TestFileConn_ReadWrite(t *testing.T) {
    46  	for _, tt := range []struct {
    47  		op     string
    48  		fn     func(file, []byte) (int, error)
    49  		mockFn func(*mocksocketcan.MockfileMockRecorder, interface{}) *gomock.Call
    50  	}{
    51  		{
    52  			op:     "read",
    53  			fn:     file.Read,
    54  			mockFn: (*mocksocketcan.MockfileMockRecorder).Read,
    55  		},
    56  		{
    57  			op:     "write",
    58  			fn:     file.Write,
    59  			mockFn: (*mocksocketcan.MockfileMockRecorder).Write,
    60  		},
    61  	} {
    62  		tt := tt
    63  		t.Run(tt.op, func(t *testing.T) {
    64  			ctrl := gomock.NewController(t)
    65  			defer ctrl.Finish()
    66  			f := mocksocketcan.NewMockfile(ctrl)
    67  			fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}}
    68  			t.Run("no error", func(t *testing.T) {
    69  				var data []byte
    70  				tt.mockFn(f.EXPECT(), data).Return(42, nil)
    71  				n, err := tt.fn(fc, data)
    72  				assert.Equal(t, 42, n)
    73  				assert.NilError(t, err)
    74  			})
    75  			t.Run("error", func(t *testing.T) {
    76  				var data []byte
    77  				cause := fmt.Errorf("boom")
    78  				tt.mockFn(f.EXPECT(), data).Return(0, &os.PathError{Err: cause})
    79  				n, err := tt.fn(fc, data)
    80  				assert.Equal(t, 0, n)
    81  				assert.ErrorContains(t, &net.OpError{Op: tt.op, Net: fc.net, Addr: fc.RemoteAddr(), Err: err}, "boom")
    82  			})
    83  		})
    84  	}
    85  }
    86  
    87  func TestFileConn_Addr(t *testing.T) {
    88  	fc := &fileConn{la: &canRawAddr{device: "can0"}, ra: &canRawAddr{device: "can1"}}
    89  	t.Run("local", func(t *testing.T) {
    90  		assert.Equal(t, fc.la, fc.LocalAddr())
    91  	})
    92  	t.Run("remote", func(t *testing.T) {
    93  		assert.Equal(t, fc.ra, fc.RemoteAddr())
    94  	})
    95  }
    96  
    97  func TestFileConn_SetDeadlines(t *testing.T) {
    98  	for _, tt := range []struct {
    99  		op     string
   100  		fn     func(file, time.Time) error
   101  		mockFn func(*mocksocketcan.MockfileMockRecorder, interface{}) *gomock.Call
   102  	}{
   103  		{
   104  			op:     "set deadline",
   105  			fn:     file.SetDeadline,
   106  			mockFn: (*mocksocketcan.MockfileMockRecorder).SetDeadline,
   107  		},
   108  		{
   109  			op:     "set read deadline",
   110  			fn:     file.SetReadDeadline,
   111  			mockFn: (*mocksocketcan.MockfileMockRecorder).SetReadDeadline,
   112  		},
   113  		{
   114  			op:     "set write deadline",
   115  			fn:     file.SetWriteDeadline,
   116  			mockFn: (*mocksocketcan.MockfileMockRecorder).SetWriteDeadline,
   117  		},
   118  	} {
   119  		tt := tt
   120  		t.Run(tt.op, func(t *testing.T) {
   121  			ctrl := gomock.NewController(t)
   122  			defer ctrl.Finish()
   123  			f := mocksocketcan.NewMockfile(ctrl)
   124  			fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}}
   125  			t.Run("no error", func(t *testing.T) {
   126  				tt.mockFn(f.EXPECT(), time.Unix(0, 1)).Return(nil)
   127  				assert.NilError(t, tt.fn(fc, time.Unix(0, 1)))
   128  			})
   129  			t.Run("error", func(t *testing.T) {
   130  				cause := fmt.Errorf("boom")
   131  				tt.mockFn(f.EXPECT(), time.Unix(0, 1)).Return(&os.PathError{Err: cause})
   132  				err := tt.fn(fc, time.Unix(0, 1))
   133  				assert.Error(t, err, (&net.OpError{Op: tt.op, Net: fc.net, Addr: fc.RemoteAddr(), Err: cause}).Error())
   134  			})
   135  		})
   136  	}
   137  }
   138  
   139  func TestFileConn_Close(t *testing.T) {
   140  	ctrl := gomock.NewController(t)
   141  	defer ctrl.Finish()
   142  	f := mocksocketcan.NewMockfile(ctrl)
   143  	fc := &fileConn{f: f, net: "can", ra: &canRawAddr{device: "can0"}}
   144  	t.Run("no error", func(t *testing.T) {
   145  		f.EXPECT().Close().Return(nil)
   146  		assert.NilError(t, fc.Close())
   147  	})
   148  	t.Run("error", func(t *testing.T) {
   149  		cause := fmt.Errorf("boom")
   150  		f.EXPECT().Close().Return(&os.PathError{Err: cause})
   151  		err := fc.Close()
   152  		assert.Error(t, err, (&net.OpError{Op: "close", Net: fc.net, Addr: fc.RemoteAddr(), Err: cause}).Error())
   153  	})
   154  }