github.com/blueinnovationsgroup/can-go@v0.0.0-20230518195432-d0567cda0028/pkg/socketcan/dialraw_linux_test.go (about) 1 //go:build linux && go1.12 2 3 package socketcan 4 5 import ( 6 "context" 7 "fmt" 8 "net" 9 "runtime" 10 "testing" 11 "time" 12 13 "github.com/blueinnovationsgroup/can-go" 14 "golang.org/x/sync/errgroup" 15 "gotest.tools/v3/assert" 16 is "gotest.tools/v3/assert/cmp" 17 ) 18 19 func TestDial_CANRaw(t *testing.T) { 20 requireVCAN0(t) 21 conn, err := Dial("can", "vcan0") 22 assert.NilError(t, err) 23 assert.NilError(t, conn.Close()) 24 } 25 26 func TestDialContext_CANRaw(t *testing.T) { 27 requireVCAN0(t) 28 ctx, done := context.WithTimeout(context.Background(), time.Second) 29 defer done() 30 conn, err := DialContext(ctx, "can", "vcan0") 31 assert.NilError(t, err) 32 assert.NilError(t, conn.Close()) 33 } 34 35 func TestConn_DialFail(t *testing.T) { 36 t.Run("bad file name", func(t *testing.T) { 37 _, err := Dial("can", "badFileName#") 38 assert.ErrorContains(t, err, "dial") 39 }) 40 t.Run("timeout", func(t *testing.T) { 41 ctx, cancel := context.WithCancel(context.Background()) 42 cancel() 43 _, err := DialContext(ctx, "can", "vcan0") 44 assert.ErrorContains(t, err, "context canceled") 45 }) 46 } 47 48 func TestConn_Addr(t *testing.T) { 49 requireVCAN0(t) 50 conn, err := Dial("can", "vcan0") 51 assert.NilError(t, err) 52 assert.Assert(t, is.Nil(conn.LocalAddr())) // SocketCAN connections don't have a local connection 53 assert.Equal(t, "can", conn.RemoteAddr().Network()) 54 assert.Equal(t, "vcan0", conn.RemoteAddr().String()) 55 } 56 57 func TestConn_SetDeadline(t *testing.T) { 58 requireVCAN0(t) 59 // Given that a vcan device exists and that I can open a connection to it 60 receiver, err := Dial("can", "vcan0") 61 assert.NilError(t, err) 62 // When I set the can 63 timeout := 20 * time.Millisecond 64 assert.NilError(t, receiver.SetDeadline(time.Now().Add(timeout))) 65 // Then I expect a read without a corresponding write to time out 66 data := make([]byte, lengthOfFrame) 67 n, err := receiver.Read(data) 68 assert.Equal(t, 0, n) 69 assert.Assert(t, is.ErrorContains(err, "")) 70 // When I clear the timeouts 71 assert.NilError(t, receiver.SetDeadline(time.Time{})) 72 // Then I don't expect the read to timeout anymore 73 errChan := make(chan error, 1) 74 go func() { 75 _, err = receiver.Read(data) 76 errChan <- err 77 }() 78 select { 79 case <-errChan: 80 t.Fatal("unexpected read result") 81 case <-time.After(timeout): 82 assert.NilError(t, receiver.Close()) 83 assert.Assert(t, is.ErrorContains(<-errChan, "")) 84 } 85 } 86 87 func TestConn_ReadWrite(t *testing.T) { 88 requireVCAN0(t) 89 // given a reader and writer 90 reader, err := Dial("can", "vcan0") 91 assert.NilError(t, err) 92 writer, err := Dial("can", "vcan0") 93 assert.NilError(t, err) 94 // when the reader reads 95 var g errgroup.Group 96 var readFrame can.Frame 97 g.Go(func() error { 98 rec := NewReceiver(reader) 99 if !rec.Receive() { 100 return fmt.Errorf("receive") 101 } 102 readFrame = rec.Frame() 103 return reader.Close() 104 }) 105 // and the writer writes 106 writeFrame := can.Frame{ID: 32} 107 tr := NewTransmitter(writer) 108 ctx, done := context.WithTimeout(context.Background(), time.Second) 109 defer done() 110 assert.NilError(t, tr.TransmitFrame(ctx, writeFrame)) 111 assert.NilError(t, writer.Close()) 112 // then the written and read frames should be identical 113 assert.NilError(t, g.Wait()) 114 assert.DeepEqual(t, writeFrame, readFrame) 115 } 116 117 func TestConn_WriteOnClosedFails(t *testing.T) { 118 requireVCAN0(t) 119 conn, err := Dial("can", "vcan0") 120 assert.NilError(t, err) 121 tr := NewTransmitter(conn) 122 ctx, done := context.WithTimeout(context.Background(), time.Second) 123 defer done() 124 assert.NilError(t, tr.TransmitFrame(ctx, can.Frame{})) 125 // When I close the connection and then write to it 126 assert.NilError(t, conn.Close()) 127 // Then it should fail 128 assert.Assert(t, is.ErrorContains(tr.TransmitFrame(ctx, can.Frame{}), ""), "WriteFrame on a closed Conn should fail") 129 } 130 131 func TestConn_ReadOnClose(t *testing.T) { 132 requireVCAN0(t) 133 t.Run("close then read", func(t *testing.T) { 134 conn, err := Dial("can", "vcan0") 135 assert.NilError(t, err) 136 // When I close the connection and then read from it 137 assert.NilError(t, conn.Close()) 138 rec := NewReceiver(conn) 139 assert.Assert(t, !rec.Receive()) 140 assert.Assert(t, is.ErrorContains(rec.Err(), "")) 141 }) 142 t.Run("read then close", func(t *testing.T) { 143 conn, err := Dial("can", "vcan0") 144 assert.NilError(t, err) 145 // And when I read from a connection 146 var g errgroup.Group 147 var receiveErr error 148 g.Go(func() error { 149 rec := NewReceiver(conn) 150 if rec.Receive() { 151 return fmt.Errorf("receive") 152 } 153 receiveErr = rec.Err() 154 return nil 155 }) 156 runtime.Gosched() 157 // And then close it 158 assert.NilError(t, conn.Close()) 159 // Then the read operation should fail 160 assert.NilError(t, g.Wait()) 161 assert.Assert(t, is.ErrorContains(receiveErr, "")) 162 }) 163 } 164 165 func requireVCAN0(t *testing.T) { 166 t.Helper() 167 if _, err := net.InterfaceByName("vcan0"); err != nil { 168 t.Skip("device vcan0 not available") 169 } 170 }