hybridgroup.gobot/drivers/i2c/grovepi_driver_test.go

235 lines
7.6 KiB
Go

package i2c
import (
"fmt"
"strings"
"testing"
"gobot.io/x/gobot/v2"
"gobot.io/x/gobot/v2/drivers/aio"
"gobot.io/x/gobot/v2/drivers/gpio"
"gobot.io/x/gobot/v2/gobottest"
)
// this ensures that the implementation is based on i2c.Driver, which implements the gobot.Driver
// and tests all implementations, so no further tests needed here for gobot.Driver interface
var _ gobot.Driver = (*GrovePiDriver)(nil)
// must implement the DigitalReader interface
var _ gpio.DigitalReader = (*GrovePiDriver)(nil)
// must implement the DigitalWriter interface
var _ gpio.DigitalWriter = (*GrovePiDriver)(nil)
// must implement the AnalogReader interface
var _ aio.AnalogReader = (*GrovePiDriver)(nil)
// must implement the AnalogWriter interface
var _ aio.AnalogWriter = (*GrovePiDriver)(nil)
// must implement the Adaptor interface
var _ gobot.Adaptor = (*GrovePiDriver)(nil)
func initGrovePiDriverWithStubbedAdaptor() (*GrovePiDriver, *i2cTestAdaptor) {
adaptor := newI2cTestAdaptor()
return NewGrovePiDriver(adaptor), adaptor
}
func TestNewGrovePiDriver(t *testing.T) {
var di interface{} = NewGrovePiDriver(newI2cTestAdaptor())
d, ok := di.(*GrovePiDriver)
if !ok {
t.Errorf("NewGrovePiDriver() should have returned a *GrovePiDriver")
}
gobottest.Refute(t, d.Driver, nil)
gobottest.Assert(t, strings.HasPrefix(d.Name(), "GrovePi"), true)
gobottest.Assert(t, d.defaultAddress, 0x04)
gobottest.Refute(t, d.pins, nil)
}
func TestGrovePiOptions(t *testing.T) {
// This is a general test, that options are applied in constructor by using the common WithBus() option and
// least one of this driver. Further tests for options can also be done by call of "WithOption(val)(d)".
d := NewGrovePiDriver(newI2cTestAdaptor(), WithBus(2))
gobottest.Assert(t, d.GetBusOrDefault(1), 2)
}
func TestGrovePiSomeRead(t *testing.T) {
// arrange
var tests = map[string]struct {
usedPin int
wantWritten []uint8
simResponse [][]uint8
wantErr error
wantCallsRead int
wantResult int
wantResultF1 float32
wantResultF2 float32
wantResultString string
}{
"DigitalRead": {
usedPin: 2,
wantWritten: []uint8{commandSetPinMode, 2, 0, 0, commandReadDigital, 2, 0, 0},
simResponse: [][]uint8{{0}, {commandReadDigital, 3}},
wantCallsRead: 2,
wantResult: 3,
},
"AnalogRead": {
usedPin: 3,
wantWritten: []uint8{commandSetPinMode, 3, 0, 0, commandReadAnalog, 3, 0, 0},
simResponse: [][]uint8{{0}, {commandReadAnalog, 4, 5}},
wantResult: 1029,
},
"UltrasonicRead": {
usedPin: 4,
wantWritten: []uint8{commandSetPinMode, 4, 0, 0, commandReadUltrasonic, 4, 0, 0},
simResponse: [][]uint8{{0}, {commandReadUltrasonic, 5, 6}},
wantResult: 1281,
},
"FirmwareVersionRead": {
wantWritten: []uint8{commandReadFirmwareVersion, 0, 0, 0},
simResponse: [][]uint8{{commandReadFirmwareVersion, 7, 8, 9}},
wantResultString: "7.8.9",
},
"DHTRead": {
usedPin: 5,
wantWritten: []uint8{commandSetPinMode, 5, 0, 0, commandReadDHT, 5, 1, 0},
simResponse: [][]uint8{{0}, {commandReadDHT, 164, 112, 69, 193, 20, 174, 54, 66}},
wantResultF1: -12.34,
wantResultF2: 45.67,
},
"DigitalRead_error_wrong_return_cmd": {
usedPin: 15,
wantWritten: []uint8{commandSetPinMode, 15, 0, 0, commandReadDigital, 15, 0, 0},
simResponse: [][]uint8{{0}, {0, 2}},
wantErr: fmt.Errorf("answer (0) was not for command (1)"),
},
"AnalogRead_error_wrong_return_cmd": {
usedPin: 16,
wantWritten: []uint8{commandSetPinMode, 16, 0, 0, commandReadAnalog, 16, 0, 0},
simResponse: [][]uint8{{0}, {0, 3, 4}},
wantErr: fmt.Errorf("answer (0) was not for command (3)"),
},
"UltrasonicRead_error_wrong_return_cmd": {
usedPin: 17,
wantWritten: []uint8{commandSetPinMode, 17, 0, 0, commandReadUltrasonic, 17, 0, 0},
simResponse: [][]uint8{{0}, {0, 5, 6}},
wantErr: fmt.Errorf("answer (0) was not for command (7)"),
},
"FirmwareVersionRead_error_wrong_return_cmd": {
wantWritten: []uint8{commandReadFirmwareVersion, 0, 0, 0},
simResponse: [][]uint8{{0, 7, 8, 9}},
wantErr: fmt.Errorf("answer (0) was not for command (8)"),
},
"DHTRead_error_wrong_return_cmd": {
usedPin: 18,
wantWritten: []uint8{commandSetPinMode, 18, 0, 0, commandReadDHT, 18, 1, 0},
simResponse: [][]uint8{{0}, {0, 164, 112, 69, 193, 20, 174, 54, 66}},
wantErr: fmt.Errorf("answer (0) was not for command (40)"),
},
"DigitalRead_error_wrong_data_count": {
usedPin: 28,
wantWritten: []uint8{commandSetPinMode, 28, 0, 0, commandReadDigital, 28, 0, 0},
simResponse: [][]uint8{{0}, {commandReadDigital, 2, 3}},
wantErr: fmt.Errorf("read count mismatch (3 should be 2)"),
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
g, a := initGrovePiDriverWithStubbedAdaptor()
g.Start()
a.written = []byte{} // reset writes of former test and start
numCallsRead := 0
a.i2cReadImpl = func(bytes []byte) (i int, e error) {
numCallsRead++
copy(bytes, tc.simResponse[numCallsRead-1])
return len(tc.simResponse[numCallsRead-1]), nil
}
var got int
var gotF1, gotF2 float32
var gotString string
var err error
// act
switch {
case strings.Contains(name, "DigitalRead"):
got, err = g.DigitalRead(fmt.Sprintf("%d", tc.usedPin))
case strings.Contains(name, "AnalogRead"):
got, err = g.AnalogRead(fmt.Sprintf("%d", tc.usedPin))
case strings.Contains(name, "UltrasonicRead"):
got, err = g.UltrasonicRead(fmt.Sprintf("%d", tc.usedPin), 2)
case strings.Contains(name, "FirmwareVersionRead"):
gotString, err = g.FirmwareVersionRead()
case strings.Contains(name, "DHTRead"):
gotF1, gotF2, err = g.DHTRead(fmt.Sprintf("%d", tc.usedPin), 1, 2)
default:
t.Errorf("unknown command %s", name)
return
}
// assert
gobottest.Assert(t, err, tc.wantErr)
gobottest.Assert(t, a.written, tc.wantWritten)
gobottest.Assert(t, numCallsRead, len(tc.simResponse))
gobottest.Assert(t, got, tc.wantResult)
gobottest.Assert(t, gotF1, tc.wantResultF1)
gobottest.Assert(t, gotF2, tc.wantResultF2)
gobottest.Assert(t, gotString, tc.wantResultString)
})
}
}
func TestGrovePiSomeWrite(t *testing.T) {
// arrange
var tests = map[string]struct {
usedPin int
usedValue int
wantWritten []uint8
simResponse []uint8
}{
"DigitalWrite": {
usedPin: 2,
usedValue: 3,
wantWritten: []uint8{commandSetPinMode, 2, 1, 0, commandWriteDigital, 2, 3, 0},
simResponse: []uint8{4},
},
"AnalogWrite": {
usedPin: 5,
usedValue: 6,
wantWritten: []uint8{commandSetPinMode, 5, 1, 0, commandWriteAnalog, 5, 6, 0},
simResponse: []uint8{7},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
g, a := initGrovePiDriverWithStubbedAdaptor()
g.Start()
a.written = []byte{} // reset writes of former test and start
a.i2cReadImpl = func(bytes []byte) (i int, e error) {
copy(bytes, tc.simResponse)
return len(bytes), nil
}
var err error
// act
switch name {
case "DigitalWrite":
err = g.DigitalWrite(fmt.Sprintf("%d", tc.usedPin), byte(tc.usedValue))
case "AnalogWrite":
err = g.AnalogWrite(fmt.Sprintf("%d", tc.usedPin), tc.usedValue)
default:
t.Errorf("unknown command %s", name)
return
}
// assert
gobottest.Assert(t, err, nil)
gobottest.Assert(t, a.written, tc.wantWritten)
})
}
}
func TestGrovePi_getPin(t *testing.T) {
gobottest.Assert(t, getPin("a1"), "1")
gobottest.Assert(t, getPin("A16"), "16")
gobottest.Assert(t, getPin("D3"), "3")
gobottest.Assert(t, getPin("d22"), "22")
gobottest.Assert(t, getPin("22"), "22")
}