diff --git a/command/device/create_test.go b/command/device/create_test.go new file mode 100644 index 00000000..56748f68 --- /dev/null +++ b/command/device/create_test.go @@ -0,0 +1,116 @@ +package device + +import ( + "testing" + + rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" +) + +// Test variables +var ( + portsNoBoards = []*rpc.DetectedPort{ + { + Address: "ACM0", + Boards: []*rpc.BoardListItem{}, + }, + { + Address: "ACM1", + Boards: []*rpc.BoardListItem{}, + }, + } + + portsTwoBoards = []*rpc.DetectedPort{ + { + Address: "ACM0", + Boards: []*rpc.BoardListItem{ + {Fqbn: "arduino:samd:nano_33_iot"}, + }, + }, + { + Address: "ACM1", + Boards: []*rpc.BoardListItem{ + {Fqbn: "arduino:avr:uno"}, + }, + }, + } +) + +func TestDeviceFromPorts(t *testing.T) { + tests := []struct { + name string + filter *CreateParams + ports []*rpc.DetectedPort + want *device + }{ + + { + name: "port-filter", + filter: &CreateParams{Fqbn: "", Port: "ACM1"}, + ports: portsTwoBoards, + want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + }, + + { + name: "fqbn-filter", + filter: &CreateParams{Fqbn: "arduino:avr:uno", Port: ""}, + ports: portsTwoBoards, + want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + }, + + { + name: "no-filter-noboards", + filter: &CreateParams{Fqbn: "", Port: ""}, + ports: portsNoBoards, + want: nil, + }, + + { + name: "no-filter", + filter: &CreateParams{Fqbn: "", Port: ""}, + ports: portsTwoBoards, + // first device found is selected + want: &device{fqbn: "arduino:samd:nano_33_iot", port: "ACM0"}, + }, + + { + name: "both-filter-noboards", + filter: &CreateParams{Fqbn: "arduino:avr:uno", Port: "ACM1"}, + ports: portsNoBoards, + want: nil, + }, + + { + name: "both-filter-found", + filter: &CreateParams{Fqbn: "arduino:avr:uno", Port: "ACM1"}, + ports: portsTwoBoards, + want: &device{fqbn: "arduino:avr:uno", port: "ACM1"}, + }, + + { + name: "both-filter-notfound", + filter: &CreateParams{Fqbn: "arduino:avr:uno", Port: "ACM0"}, + ports: portsTwoBoards, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := deviceFromPorts(tt.ports, tt.filter) + + if got == nil && tt.want == nil { + return + + } else if got != nil && tt.want == nil { + t.Errorf("Expected nil device, received not nil device with port %s and fqbn %s", got.port, got.fqbn) + + } else if got == nil && tt.want != nil { + t.Errorf("Expected not nil device with port %s and fqbn %s, received a nil device", tt.want.port, tt.want.fqbn) + + } else if got.port != tt.want.port || got.fqbn != tt.want.fqbn { + t.Errorf("Expected device with port %s and fqbn %s, received device with port %s and fqbn %s", + tt.want.port, tt.want.fqbn, got.port, got.fqbn) + } + }) + } +} diff --git a/go.mod b/go.mod index 0ab41388..55d47f4e 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.1.3 github.com/spf13/viper v1.7.0 + github.com/stretchr/testify v1.6.1 go.bug.st/serial v1.3.0 golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 diff --git a/go.sum b/go.sum index 92083fef..6b34458b 100644 --- a/go.sum +++ b/go.sum @@ -487,6 +487,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/internal/serial/mocks/Port.go b/internal/serial/mocks/Port.go new file mode 100644 index 00000000..97efa4a1 --- /dev/null +++ b/internal/serial/mocks/Port.go @@ -0,0 +1,178 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + go_bug_stserial "go.bug.st/serial" + + time "time" +) + +// Port is an autogenerated mock type for the Port type +type Port struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *Port) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetModemStatusBits provides a mock function with given fields: +func (_m *Port) GetModemStatusBits() (*go_bug_stserial.ModemStatusBits, error) { + ret := _m.Called() + + var r0 *go_bug_stserial.ModemStatusBits + if rf, ok := ret.Get(0).(func() *go_bug_stserial.ModemStatusBits); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*go_bug_stserial.ModemStatusBits) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Read provides a mock function with given fields: p +func (_m *Port) Read(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ResetInputBuffer provides a mock function with given fields: +func (_m *Port) ResetInputBuffer() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ResetOutputBuffer provides a mock function with given fields: +func (_m *Port) ResetOutputBuffer() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetDTR provides a mock function with given fields: dtr +func (_m *Port) SetDTR(dtr bool) error { + ret := _m.Called(dtr) + + var r0 error + if rf, ok := ret.Get(0).(func(bool) error); ok { + r0 = rf(dtr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetMode provides a mock function with given fields: mode +func (_m *Port) SetMode(mode *go_bug_stserial.Mode) error { + ret := _m.Called(mode) + + var r0 error + if rf, ok := ret.Get(0).(func(*go_bug_stserial.Mode) error); ok { + r0 = rf(mode) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetRTS provides a mock function with given fields: rts +func (_m *Port) SetRTS(rts bool) error { + ret := _m.Called(rts) + + var r0 error + if rf, ok := ret.Get(0).(func(bool) error); ok { + r0 = rf(rts) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SetReadTimeout provides a mock function with given fields: t +func (_m *Port) SetReadTimeout(t time.Duration) error { + ret := _m.Called(t) + + var r0 error + if rf, ok := ret.Get(0).(func(time.Duration) error); ok { + r0 = rf(t) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Write provides a mock function with given fields: p +func (_m *Port) Write(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/internal/serial/serial_test.go b/internal/serial/serial_test.go new file mode 100644 index 00000000..e8f63edd --- /dev/null +++ b/internal/serial/serial_test.go @@ -0,0 +1,84 @@ +package serial + +import ( + "bytes" + "testing" + + "github.com/bcmi-labs/iot-cloud-cli/internal/serial/mocks" + "github.com/stretchr/testify/mock" +) + +func TestSendReceive(t *testing.T) { + mockPort := &mocks.Port{} + mockSerial := &Serial{mockPort} + + want := []byte{1, 2, 3} + resp := encode(Response, want) + respIdx := 0 + + mockRead := func(msg []uint8) int { + if respIdx >= len(resp) { + return 0 + } + copy(msg, resp[respIdx:respIdx+2]) + respIdx += 2 + return 2 + } + + mockPort.On("Write", mock.AnythingOfType("[]uint8")).Return(0, nil) + mockPort.On("Read", mock.AnythingOfType("[]uint8")).Return(mockRead, nil) + + res, err := mockSerial.SendReceive(BeginStorage, []byte{1, 2}) + if err != nil { + t.Error(err) + } + + if !bytes.Equal(res, want) { + t.Errorf("Expected %v but received %v", want, res) + } +} + +func TestSend(t *testing.T) { + mockPort := &mocks.Port{} + mockSerial := &Serial{mockPort} + mockPort.On("Write", mock.AnythingOfType("[]uint8")).Return(0, nil) + + payload := []byte{1, 2} + cmd := SetDay + want := []byte{msgStart[0], msgStart[1], 1, 0, 5, 10, 1, 2, 143, 124, msgEnd[0], msgEnd[1]} + + err := mockSerial.Send(cmd, payload) + if err != nil { + t.Error(err) + } + + mockPort.AssertCalled(t, "Write", want) +} + +func TestEncode(t *testing.T) { + tests := []struct { + name string + msg []byte + want []byte + }{ + { + name: "begin-storage", + msg: []byte{byte(BeginStorage)}, + want: []byte{msgStart[0], msgStart[1], 1, 0, 3, 6, 0x95, 0x4e, msgEnd[0], msgEnd[1]}, + }, + + { + name: "set-year", + msg: append([]byte{byte(SetYear)}, []byte("2021")...), + want: []byte{msgStart[0], msgStart[1], 1, 0, 7, 0x8, 0x32, 0x30, 0x32, 0x31, 0xc3, 0x65, msgEnd[0], msgEnd[1]}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := encode(Cmd, tt.msg) + if !bytes.Equal(tt.want, got) { + t.Errorf("Expected %v, received %v", tt.want, got) + } + }) + } +}